// 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 Meshes(Person); for (const USkeletalMeshComponent* Mesh : Meshes) { if (Mesh && Mesh->GetSkinnedAsset() && Mesh->DoesSocketExist(HeadSocket)) { return Mesh->GetSocketLocation(HeadSocket); } } if (const ACharacter* Character = Cast(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 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(GI->GetClass()->FindPropertyByName(PersonPropertyName)); const AActor* Person = Prop ? Cast(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; }