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:
edujiro5972 2026-10-01 22:32:42 +02:00
parent af1861b989
commit ed4bc46ba9
3 changed files with 158 additions and 0 deletions

View file

@ -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).

View 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;
}

View 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;
};