ERR-038 : sous-systeme LPRoomTransition, personnages de la piece precedente caches des le changement de lieu

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
edujiro5972 2026-10-02 10:46:01 +02:00
parent 4c66dd0c1e
commit 27758d3453
3 changed files with 169 additions and 0 deletions

View file

@ -914,3 +914,4 @@ Validation L7c/L7d (2026-10-02, parc, même cadrage) : la nuit reste claire (sol
| CHG-067 | 2026-10-02 | O2b | `Plugins/ModdingExtra` : `GetAllFilesFromFolder` reconstruit la liste des `.uasset`/`.umap` depuis le registre d'assets quand le disque n'en renvoie aucun (contenu compressé IoStore) ; dépendance `AssetRegistry` | régression d'O2 constatée sur le build Shipping : aucune ville proposée (`UI/CityMenu` liste les fichiers `Heavy/StreetMap/*.uasset` du disque, `UI/City` vérifie chaque ville par suffixe `/<nom>.uasset`) ; seul appel concerné, les autres lisent des fichiers texte de `Modules` ; build `LifePlay_Build_5.5_Shipping_V1.2` reconstruit | `git checkout O2b-avant -- Plugins/ModdingExtra` |
| CHG-068 | 2026-10-02 | V1.2 | `README.md` (section V1.2, branche `ue5.5-v1.2`) ; tag annoté `v1.2` ; archive `D:\Game\LifePlay_Publication\LifePlay_Revival_V1.2_UE5.5_Full_Source_Code.7z` (4 937 914 101 octets, 25 681 fichiers, SHA-256 `fb298583743497799dd274cc62b8cf80fbef3445766be5898201924c64674f1a`, script `make_archive_v1_2.cmd`) | publication de la V1.2 (optimisation) ; contrôles : test 7-Zip, aucun `.mcp.json`/`.claude`/`McpAutomationBridge`/`Saved`/`Intermediate`, `Config`, `.uproject` et `ModdingExtra` identiques au tag | lien Mega à ajouter après envoi |
| CHG-069 | 2026-10-02 | Réconciliation | fusion `-s ours` de `vinfamy/master` dans `main` (commit `82a64d9`, arbre inchangé) ; `Docs/Migration/12_Reconciliation_Vinfamy.md` | historique de l'auteur rattaché ; aucun fichier récupérable ; les 33 scènes `corruption` vides de `vin_Base` existent dans `vin_Corruption` (non restaurées : doublons) | `git reset --hard reconcil-avant` (avant toute autre publication) |
| CHG-070 | 2026-10-02 | ERR-038 | nouveau `Source/LifePlay/LPRoomTransitionSubsystem.h/.cpp` | relevé (`ERR038_releve_20261002.log`) : la nouvelle pièce est construite ≈ 2 s avant que `destroyDummy` rende les personnages de l'ancienne à la réserve ; le sous-système cache aussitôt, au changement de pièce, les personnages actifs juste avant (hors compagnon et scène en cours), cesse de les suivre dès que le jeu les rend ou les réaffiche, et les réaffiche après 10 s par sécurité ; aucun Blueprint modifié ; build `LifePlay_Build_5.5_ERR038` ; test en jeu en attente | supprimer les 2 fichiers (`git checkout ERR038-avant -- Source`) |

View file

@ -0,0 +1,129 @@
// Portage UE 5.5 - voir LPRoomTransitionSubsystem.h
#include "LPRoomTransitionSubsystem.h"
#include "Components/SceneComponent.h"
#include "Engine/GameInstance.h"
#include "GameFramework/Actor.h"
#include "HAL/PlatformTime.h"
#include "LifePlay.h"
#include "UObject/UnrealType.h"
namespace
{
constexpr double SafetySeconds = 10.0;
template <typename T>
T* GetObjectProperty(const UObject* Container, const TCHAR* Name)
{
const FObjectPropertyBase* Prop = Container ? CastField<FObjectPropertyBase>(Container->GetClass()->FindPropertyByName(Name)) : nullptr;
return Prop ? Cast<T>(Prop->GetObjectPropertyValue_InContainer(Container)) : nullptr;
}
/** Contenu d'une variable tableau d'acteurs de GI (DummyPersonsInUse, PersonsUsedThisScene). */
TArray<AActor*> GetActorArray(const UObject* Container, const TCHAR* Name)
{
TArray<AActor*> Result;
const FArrayProperty* Prop = Container ? CastField<FArrayProperty>(Container->GetClass()->FindPropertyByName(Name)) : nullptr;
const FObjectPropertyBase* Inner = Prop ? CastField<FObjectPropertyBase>(Prop->Inner) : nullptr;
if (!Inner)
{
return Result;
}
FScriptArrayHelper Helper(Prop, Prop->ContainerPtrToValuePtr<void>(Container));
for (int32 i = 0; i < Helper.Num(); ++i)
{
if (AActor* Actor = Cast<AActor>(Inner->GetObjectPropertyValue(Helper.GetRawPtr(i))))
{
Result.Add(Actor);
}
}
return Result;
}
/** Valeur texte d'une variable de GI, quel que soit son type (chaine, nom, texte). */
FString GetPropertyText(const UObject* Container, const TCHAR* Name)
{
FString Text;
if (const FProperty* Prop = Container ? Container->GetClass()->FindPropertyByName(Name) : nullptr)
{
Prop->ExportTextItem_Direct(Text, Prop->ContainerPtrToValuePtr<void>(Container), nullptr, nullptr, PPF_None);
}
return Text;
}
}
void ULPRoomTransitionSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &ULPRoomTransitionSubsystem::Tick));
}
void ULPRoomTransitionSubsystem::Deinitialize()
{
FTSTicker::GetCoreTicker().RemoveTicker(TickHandle);
Super::Deinitialize();
}
bool ULPRoomTransitionSubsystem::Tick(float DeltaTime)
{
const UGameInstance* GI = GetGameInstance();
const AActor* Room = GetObjectProperty<AActor>(GI, TEXT("Room"));
const USceneComponent* Root = IsValid(Room) ? Room->GetRootComponent() : nullptr;
if (!Root)
{
return true;
}
const TArray<AActor*> InUse = GetActorArray(GI, TEXT("DummyPersonsInUse"));
const double Now = FPlatformTime::Seconds();
// 1. Suivi des personnages caches : rendus a la reserve, reaffiches par le jeu, ou delai de securite depasse.
for (auto It = Hidden.CreateIterator(); It; ++It)
{
AActor* Actor = It.Key().Get();
if (!IsValid(Actor) || !InUse.Contains(Actor) || !Actor->IsHidden())
{
It.RemoveCurrent();
}
else if (Now - It.Value() > SafetySeconds)
{
Actor->SetActorHiddenInGame(false);
UE_LOG(LogLifePlay, Log, TEXT("Changement de lieu : %s toujours actif apres %.0f s, reaffiche"), *Actor->GetName(), SafetySeconds);
It.RemoveCurrent();
}
}
// 2. Changement de piece : cacher les personnages actifs au tick precedent, sauf compagnon et scene en cours.
const FString Signature = GetPropertyText(GI, TEXT("CurrentAmenity")) + TEXT("|") + Root->GetRelativeScale3D().ToString();
if (!LastRoomSignature.IsEmpty() && Signature != LastRoomSignature)
{
const AActor* Companion = GetObjectProperty<AActor>(GI, TEXT("Companion"));
const TArray<AActor*> ScenePersons = GetActorArray(GI, TEXT("PersonsUsedThisScene"));
int32 Count = 0;
for (const TWeakObjectPtr<AActor>& Weak : LastInUse)
{
AActor* Actor = Weak.Get();
if (!IsValid(Actor) || Actor == Companion || ScenePersons.Contains(Actor) || Actor->IsHidden() || !InUse.Contains(Actor))
{
continue;
}
Actor->SetActorHiddenInGame(true);
Hidden.Add(Actor, Now);
++Count;
}
if (Count > 0)
{
UE_LOG(LogLifePlay, Log, TEXT("Changement de lieu (%s -> %s) : %d personnage(s) de la piece precedente caches"),
*LastRoomSignature, *Signature, Count);
}
}
LastRoomSignature = Signature;
LastInUse.Reset(InUse.Num());
for (AActor* Actor : InUse)
{
LastInUse.Add(Actor);
}
return true;
}

View file

@ -0,0 +1,39 @@
// Portage UE 5.5 - ERR-038 (amelioration : defaut deja present en 4.27).
//
// Tous les lieux sont reconstruits par l'acteur Room dans le meme studio. Au changement de lieu, GI.MoveTo
// reconstruit la piece puis rend les personnages de la piece precedente a la reserve (destroyDummy : retrait de
// DummyPersonsInUse, SetActorHiddenInGame(true), deplacement) environ 2 s plus tard ; entre-temps ils restent
// visibles dans la nouvelle piece (releve ERR038_releve_20261002 : 4 personnages du fast-food 2 s dans la maison).
//
// Ce sous-systeme detecte le changement de piece (lieu courant ou echelle de la racine de Room) et cache aussitot
// les personnages qui etaient actifs juste avant, sauf le compagnon et les personnes de la scene en cours. Il cesse
// de suivre un personnage des que le jeu le rend a la reserve ou le reaffiche lui-meme ; au-dela de 10 s sans l'un
// ni l'autre, il le reaffiche (on retombe alors sur le comportement d'origine). Aucun Blueprint n'est modifie.
#pragma once
#include "CoreMinimal.h"
#include "Containers/Ticker.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "LPRoomTransitionSubsystem.generated.h"
UCLASS()
class LIFEPLAY_API ULPRoomTransitionSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
private:
bool Tick(float DeltaTime);
FTSTicker::FDelegateHandle TickHandle;
/** Signature de la piece au tick precedent (lieu courant + echelle de la racine de Room). */
FString LastRoomSignature;
/** Personnages actifs au tick precedent. */
TArray<TWeakObjectPtr<AActor>> LastInUse;
/** Personnages caches par ce sous-systeme, avec l'heure a laquelle ils l'ont ete. */
TMap<TWeakObjectPtr<AActor>, double> Hidden;
};