diff --git a/Docs/Migration/Journal_Modifications.md b/Docs/Migration/Journal_Modifications.md index 5418307..d946f58 100644 --- a/Docs/Migration/Journal_Modifications.md +++ b/Docs/Migration/Journal_Modifications.md @@ -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). diff --git a/Source/LifePlay/LPSecondScreenSubsystem.cpp b/Source/LifePlay/LPSecondScreenSubsystem.cpp new file mode 100644 index 0000000..ecb4332 --- /dev/null +++ b/Source/LifePlay/LPSecondScreenSubsystem.cpp @@ -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 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; +} diff --git a/Source/LifePlay/LPSecondScreenSubsystem.h b/Source/LifePlay/LPSecondScreenSubsystem.h new file mode 100644 index 0000000..41e11af --- /dev/null +++ b/Source/LifePlay/LPSecondScreenSubsystem.h @@ -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 Capture; + TWeakObjectPtr CaptureWorld; +};