L7j : sous-systeme LPSecondScreen, camera d'appel cadree sur le visage du contact
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
af1861b989
commit
ed4bc46ba9
3 changed files with 158 additions and 0 deletions
|
|
@ -854,3 +854,13 @@ Le lieu `Modules/vin_Base/World/forest___vin01.lpworld` (donnée de module, iden
|
|||
d'ancrage en Z = 60 (`Room::addObject`). La hauteur exacte de la surface de neige par rapport au sol de marche dépend
|
||||
de la chaîne de composants du meuble : une mesure en jeu est nécessaire avant de corriger le fichier (ou d'ajuster la
|
||||
dalle avec l'éditeur de lieu intégré au jeu, « Edit building »).
|
||||
|
||||
Test utilisateur (build `LifePlay_Build_5.5_L7cdj`) : le contact apparaît désormais dans l'encadré d'appel (en pied,
|
||||
avec le sol de `SecondScreenFloor`) ; demande : cadrer le visage.
|
||||
|
||||
| CHG | Date | Lot | Fichiers | Raison | Retour arrière |
|
||||
|---|---|---|---|---|---|
|
||||
| CHG-054 | 2026-10-01 | L7j | `Source/LifePlay/LPSecondScreenSubsystem.h/.cpp` (nouveau sous-système d'instance de jeu) | cadrer le visage du contact quel que soit son emplacement de scène : à chaque image, la caméra `SecondScreen` est placée à 80 cm devant la tête de `GI.SecondScreenPerson` (os `head`, sinon haut de la capsule), dans l'axe du personnage, champ 30° ; composant rendu mobile au premier usage. Aucun asset ni Blueprint modifié | `git checkout L7j2-avant -- Source` |
|
||||
|
||||
Compilation `LifePlayEditor` Development et `LifePlay` Shipping : 0 erreur. La position fixe posée par CHG-052 reste
|
||||
la position de repli (avant tout appel).
|
||||
|
|
|
|||
112
Source/LifePlay/LPSecondScreenSubsystem.cpp
Normal file
112
Source/LifePlay/LPSecondScreenSubsystem.cpp
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// Portage UE 5.5 - voir LPSecondScreenSubsystem.h
|
||||
|
||||
#include "LPSecondScreenSubsystem.h"
|
||||
|
||||
#include "Components/CapsuleComponent.h"
|
||||
#include "Components/SceneCaptureComponent2D.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "Engine/SceneCapture2D.h"
|
||||
#include "Engine/World.h"
|
||||
#include "EngineUtils.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "LifePlay.h"
|
||||
#include "UObject/UnrealType.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
// Cadrage du visage : distance camera-tete (cm) et champ horizontal (degres).
|
||||
constexpr float FaceDistance = 80.f;
|
||||
constexpr float FaceFOV = 30.f;
|
||||
const FName CaptureActorName(TEXT("SecondScreen"));
|
||||
const FName PersonPropertyName(TEXT("SecondScreenPerson"));
|
||||
const FName HeadSocket(TEXT("head"));
|
||||
|
||||
/** Position de la tete : os "head" du premier maillage qui le porte, sinon haut de la capsule. */
|
||||
FVector HeadLocation(const AActor* Person)
|
||||
{
|
||||
TInlineComponentArray<USkeletalMeshComponent*> Meshes(Person);
|
||||
for (const USkeletalMeshComponent* Mesh : Meshes)
|
||||
{
|
||||
if (Mesh && Mesh->GetSkinnedAsset() && Mesh->DoesSocketExist(HeadSocket))
|
||||
{
|
||||
return Mesh->GetSocketLocation(HeadSocket);
|
||||
}
|
||||
}
|
||||
if (const ACharacter* Character = Cast<ACharacter>(Person))
|
||||
{
|
||||
if (const UCapsuleComponent* Capsule = Character->GetCapsuleComponent())
|
||||
{
|
||||
return Capsule->GetComponentLocation() + FVector(0.f, 0.f, Capsule->GetScaledCapsuleHalfHeight() * 0.8f);
|
||||
}
|
||||
}
|
||||
return Person->GetActorLocation();
|
||||
}
|
||||
}
|
||||
|
||||
void ULPSecondScreenSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::Initialize(Collection);
|
||||
TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &ULPSecondScreenSubsystem::Tick));
|
||||
}
|
||||
|
||||
void ULPSecondScreenSubsystem::Deinitialize()
|
||||
{
|
||||
FTSTicker::GetCoreTicker().RemoveTicker(TickHandle);
|
||||
Super::Deinitialize();
|
||||
}
|
||||
|
||||
bool ULPSecondScreenSubsystem::Tick(float DeltaTime)
|
||||
{
|
||||
const UGameInstance* GI = GetGameInstance();
|
||||
UWorld* World = GI ? GI->GetWorld() : nullptr;
|
||||
if (!World)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// La camera est recherchee une fois par monde (par nom d'objet : l'etiquette n'existe pas en Shipping).
|
||||
if (CaptureWorld.Get() != World)
|
||||
{
|
||||
CaptureWorld = World;
|
||||
Capture.Reset();
|
||||
for (TActorIterator<ASceneCapture2D> It(World); It; ++It)
|
||||
{
|
||||
if (It->GetFName() == CaptureActorName)
|
||||
{
|
||||
Capture = *It;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ASceneCapture2D* CaptureActor = Capture.Get();
|
||||
USceneCaptureComponent2D* CaptureComp = CaptureActor ? CaptureActor->GetCaptureComponent2D() : nullptr;
|
||||
if (!CaptureComp)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const FObjectPropertyBase* Prop = CastField<FObjectPropertyBase>(GI->GetClass()->FindPropertyByName(PersonPropertyName));
|
||||
const AActor* Person = Prop ? Cast<AActor>(Prop->GetObjectPropertyValue_InContainer(GI)) : nullptr;
|
||||
if (!IsValid(Person))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const FVector Head = HeadLocation(Person);
|
||||
FVector Forward = Person->GetActorForwardVector();
|
||||
Forward.Z = 0.f;
|
||||
if (!Forward.Normalize())
|
||||
{
|
||||
Forward = FVector::ForwardVector;
|
||||
}
|
||||
const FVector CameraLocation = Head + Forward * FaceDistance;
|
||||
if (CaptureComp->Mobility != EComponentMobility::Movable)
|
||||
{
|
||||
// Composant pose dans la carte : un composant statique refuse d'etre deplace en jeu.
|
||||
CaptureComp->SetMobility(EComponentMobility::Movable);
|
||||
}
|
||||
CaptureComp->SetWorldLocationAndRotation(CameraLocation, (Head - CameraLocation).Rotation());
|
||||
CaptureComp->FOVAngle = FaceFOV;
|
||||
return true;
|
||||
}
|
||||
36
Source/LifePlay/LPSecondScreenSubsystem.h
Normal file
36
Source/LifePlay/LPSecondScreenSubsystem.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Portage UE 5.5 - lot L7j (ERR-031, amelioration : defaut deja present en 4.27).
|
||||
//
|
||||
// Pendant un appel, l'encadre affiche l'image de la camera SceneCapture2D "SecondScreen" de CentralLondon.
|
||||
// Le contact (GI.SecondScreenPerson) y est place sur son emplacement de scene, decale de -10 000 000 en Y
|
||||
// (GI::Show) : sa position depend donc de la scene et de son rang. Aucun Blueprint ne deplace la camera.
|
||||
//
|
||||
// Ce sous-systeme, a chaque image ou un contact est affiche, place la camera devant le visage du contact
|
||||
// (os "head" du premier maillage squelettique qui le porte, sinon haut de la capsule), dans l'axe du
|
||||
// personnage, et resserre le champ : l'encadre montre le visage quel que soit l'emplacement.
|
||||
// Aucun Blueprint ni asset n'est modifie ; sans camera "SecondScreen" ou sans contact, il ne fait rien.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Containers/Ticker.h"
|
||||
#include "Subsystems/GameInstanceSubsystem.h"
|
||||
#include "LPSecondScreenSubsystem.generated.h"
|
||||
|
||||
class ASceneCapture2D;
|
||||
|
||||
UCLASS()
|
||||
class LIFEPLAY_API ULPSecondScreenSubsystem : public UGameInstanceSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
|
||||
private:
|
||||
bool Tick(float DeltaTime);
|
||||
|
||||
FTSTicker::FDelegateHandle TickHandle;
|
||||
TWeakObjectPtr<ASceneCapture2D> Capture;
|
||||
TWeakObjectPtr<UWorld> CaptureWorld;
|
||||
};
|
||||
Loading…
Reference in a new issue