L7j : camera SecondScreen dotee d'une racine et placee (ERR-031) ; L7l : diagnostic du sol de neige (ERR-036)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
edujiro5972 2026-10-01 22:08:14 +02:00
parent 928826139f
commit af1861b989
6 changed files with 228 additions and 6 deletions

Binary file not shown.

View file

@ -150,8 +150,8 @@ Ordre indicatif, à réviser selon le registre :
| L7g | Packaging | build Shipping `LifePlay_Build_5.5_Shipping` construite (CHG-046), même structure que la version de l'auteur ; `McpAutomationBridge` déjà limité à l'éditeur ; ✅ validée en jeu le 2026-10-01 |
| L7h | Vérification StreetMap | ✅ 280 villes et 280 maillages identiques 4.27/5.5 (ERR-018 clos, CHG-044) |
| L7i | Variables DSL `Person` vides | ✅ ERR-030, ERR-037, ERR-003 préexistants en 4.27 ; ERR-005 corrigé par CX-B12 (385 nœuds `Contains`/`Split`/`FindSubstring`), validé en jeu le 2026-10-01 |
| L7j | Écran secondaire (amélioration) | ERR-031, préexistant en 4.27 : caméra `SecondScreen` sans racine ; seulement si souhaité |
| L7l | Personnages enfoncés dans le sol en extérieur (amélioration) | ERR-036, préexistant en 4.27 ; seulement si souhaité |
| L7j | Écran secondaire (amélioration) | caméra `SecondScreen` dotée d'une racine et placée sur `CameraScene` − 10 000 000 Y (CHG-052) ; test en jeu en attente |
| L7l | Personnages enfoncés dans le sol en extérieur (amélioration) | diagnostic (CHG-053) : sol de neige `fnt_new208` étiré ×100 en hauteur dans `forest___vin01.lpworld` (donnée de module) ; correction à caler par une mesure en jeu |
| L7k | Erreurs d'exécution fréquentes (ex-L7b) | ✅ ERR-006 et ERR-007 revenus au niveau 4.27 avec CX-B12 ; signatures restantes au niveau 4.27 (CHG-045) |
## 4. Décisions

View file

@ -823,3 +823,34 @@ le rendu passait donc déjà par SkyAtmosphere. Éclairage précalculé : `Centr
sont déjà actifs ; ils exécutent les 34 actions et axes hérités de `DefaultInput.ini`. Une migration complète
(Input Actions, Mapping Contexts, réécriture des événements d'entrée des Blueprints) n'apporterait rien au joueur
et demanderait un test manuel des 34 commandes : reportée.
### L7j — image du contact pendant un appel (ERR-031, amélioration), 2026-10-01
| CHG | Date | Lot | Fichiers | Raison | Retour arrière |
|---|---|---|---|---|---|
| CHG-052 | 2026-10-01 | L7j | `Source/LifePlayEditorTools/LPFixSceneCaptureCommandlet` (nouveau) ; `Content/Scenes/CentralLondon.umap` | ERR-031 (présent en 4.27) : la caméra `SecondScreen` filme le ciel | `Tools\Restore.ps1 -Lot L7j` |
Chaîne (L6f et L7j) : `GI::secondScreen` marque le contact (`Person.secondScreen`) ; `GI::Show` le place sur la position
de son emplacement de scène (`ActorTransforms[i]`) **décalée de −10 000 000 en Y**, sur la copie de studio
`SecondScreenFloor` (−3 131 543 ; −24 967 212, soit le studio des scènes − 10 000 000 Y) ; l'encadré affiche
`Render/SecondScreen`, rendu par la caméra `SecondScreen`. Aucun Blueprint ne déplace cette caméra ; dans la carte
(4.27 comme 5.5) elle n'a pas de racine (`RootComponent = None`) et ses composants sont à l'origine.
Correctif : racine = son `SceneComponent` existant, capture rattachée, position = `CameraScene` (cadrage des scènes du
studio : −3 131 132 ; −14 967 241 ; 50, lacet 180°) − 10 000 000 Y. Contrôle intégré (racine, rattachement, position
monde = cible) puis diff `LPDumpAssets` de la carte : **3 lignes modifiées**, toutes sur `SecondScreen`
(`Saved/Logs/portage/L7d_map_apres.txt` → `L7j_map_apres.txt`). Le cadrage (scène entière plutôt que portrait) est à
ajuster d'après le test en jeu si nécessaire (`-From=CameraPortrait`).
### L7l — personnages enfoncés dans le sol en extérieur (ERR-036, amélioration), 2026-10-01 : diagnostic
| CHG | Date | Lot | Fichiers | Raison | Retour arrière |
|---|---|---|---|---|---|
| CHG-053 | 2026-10-01 | L7l | `Source/LifePlayEditorTools/LPCheckMeshesCommandlet` (colonnes `MinZ`, `MaxZ`) | diagnostic | — |
Le lieu `Modules/vin_Base/World/forest___vin01.lpworld` (donnée de module, identique en 4.27) pose comme sol le meuble
`fnt_new208` (dalle 230 × 99 × 0,6) en Z = −31,2 avec une échelle (0,713 ; 1,481 ; **100**) ; ailleurs (`bathroom`,
`shower`, `sports_centre`) le même meuble est un petit objet (Z = 60, échelle 5). Les meubles sont rattachés à un point
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 »).

View file

@ -42,7 +42,7 @@ int32 ULPCheckMeshesCommandlet::Main(const FString& Params)
Assets.Sort([](const FAssetData& A, const FAssetData& B) { return A.AssetName.LexicalLess(B.AssetName); });
TArray<FString> Lines;
Lines.Add(TEXT("Mesh;LODs;SommetsSource;TrianglesSource;SommetsRendu;TrianglesRendu;Sections;Materiaux;TailleX;TailleY;TailleZ"));
Lines.Add(TEXT("Mesh;LODs;SommetsSource;TrianglesSource;SommetsRendu;TrianglesRendu;Sections;Materiaux;TailleX;TailleY;TailleZ;MinZ;MaxZ"));
int32 Failures = 0;
for (const FAssetData& AssetData : Assets)
@ -66,11 +66,12 @@ int32 ULPCheckMeshesCommandlet::Main(const FString& Params)
const int32 RenderVerts = Mesh->GetNumVertices(0);
const int32 RenderTris = Mesh->GetNumTriangles(0);
const int32 Sections = Mesh->GetNumSections(0);
const FVector Size = Mesh->GetBoundingBox().GetSize();
const FBox Box = Mesh->GetBoundingBox();
const FVector Size = Box.GetSize();
Lines.Add(FString::Printf(TEXT("%s;%d;%d;%d;%d;%d;%d;%d;%.0f;%.0f;%.0f"),
Lines.Add(FString::Printf(TEXT("%s;%d;%d;%d;%d;%d;%d;%d;%.0f;%.0f;%.0f;%.2f;%.2f"),
*AssetData.AssetName.ToString(), Mesh->GetNumLODs(), SrcVerts, SrcTris, RenderVerts, RenderTris,
Sections, Mesh->GetStaticMaterials().Num(), Size.X, Size.Y, Size.Z));
Sections, Mesh->GetStaticMaterials().Num(), Size.X, Size.Y, Size.Z, Box.Min.Z, Box.Max.Z));
}
if (!FFileHelper::SaveStringArrayToFile(Lines, *CsvPath, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM))

View file

@ -0,0 +1,159 @@
// Portage UE 5.5 - voir LPFixSceneCaptureCommandlet.h
#include "LPFixSceneCaptureCommandlet.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Engine/Level.h"
#include "Engine/SceneCapture2D.h"
#include "Engine/World.h"
#include "Misc/PackageName.h"
#include "UObject/Package.h"
#include "UObject/SavePackage.h"
DEFINE_LOG_CATEGORY_STATIC(LogLPFixSceneCapture, Log, All);
namespace
{
AActor* FindActorByLabelOrName(ULevel* Level, const FString& Name)
{
for (AActor* Actor : Level->Actors)
{
if (Actor && (Actor->GetActorLabel() == Name || Actor->GetName() == Name))
{
return Actor;
}
}
return nullptr;
}
}
ULPFixSceneCaptureCommandlet::ULPFixSceneCaptureCommandlet()
{
IsClient = false;
IsServer = false;
IsEditor = true;
LogToConsole = true;
}
int32 ULPFixSceneCaptureCommandlet::Main(const FString& Params)
{
TArray<FString> Tokens, Switches;
TMap<FString, FString> ParamsMap;
ParseCommandLine(*Params, Tokens, Switches, ParamsMap);
const bool bDryRun = Switches.Contains(TEXT("DryRun"));
const FString MapPath = ParamsMap.FindRef(TEXT("Map"));
const FString ActorName = ParamsMap.FindRef(TEXT("Actor"));
const FString FromName = ParamsMap.FindRef(TEXT("From"));
FVector Offset = FVector::ZeroVector;
{
TArray<FString> Parts;
ParamsMap.FindRef(TEXT("Offset")).ParseIntoArray(Parts, TEXT(","), true);
if (Parts.Num() == 3)
{
Offset = FVector(FCString::Atod(*Parts[0]), FCString::Atod(*Parts[1]), FCString::Atod(*Parts[2]));
}
}
if (MapPath.IsEmpty() || ActorName.IsEmpty() || FromName.IsEmpty())
{
UE_LOG(LogLPFixSceneCapture, Error, TEXT("Usage : -Map=/Game/Scenes/X -Actor=<capture> -From=<acteur de reference> [-Offset=x,y,z] [-DryRun]"));
return 1;
}
const FString WorldPath = MapPath + TEXT(".") + FPackageName::GetShortName(MapPath);
UWorld* World = LoadObject<UWorld>(nullptr, *WorldPath);
if (!World || !World->PersistentLevel)
{
UE_LOG(LogLPFixSceneCapture, Error, TEXT("Carte introuvable : %s"), *WorldPath);
return 1;
}
World->AddToRoot();
ASceneCapture2D* Capture = Cast<ASceneCapture2D>(FindActorByLabelOrName(World->PersistentLevel, ActorName));
AActor* From = FindActorByLabelOrName(World->PersistentLevel, FromName);
if (!Capture || !From || !From->GetRootComponent())
{
UE_LOG(LogLPFixSceneCapture, Error, TEXT("Acteur introuvable : %s (capture) ou %s (reference, avec racine)"), *ActorName, *FromName);
World->RemoveFromRoot();
return 1;
}
USceneCaptureComponent2D* CaptureComp = Capture->GetCaptureComponent2D();
USceneComponent* NewRoot = nullptr;
for (UActorComponent* Comp : Capture->GetComponents())
{
USceneComponent* SceneComp = Cast<USceneComponent>(Comp);
if (SceneComp && SceneComp != CaptureComp && SceneComp->GetClass() == USceneComponent::StaticClass())
{
NewRoot = SceneComp;
break;
}
}
if (!NewRoot)
{
NewRoot = CaptureComp;
}
const FTransform FromTransform = From->GetRootComponent()->GetRelativeTransform();
const FTransform Target(FromTransform.GetRotation(), FromTransform.GetLocation() + Offset, FVector::OneVector);
UE_LOG(LogLPFixSceneCapture, Warning, TEXT("=== %s : racine %s -> %s, cible %s (= %s + %s)%s ==="),
*Capture->GetActorLabel(), *GetNameSafe(Capture->GetRootComponent()), *GetNameSafe(NewRoot),
*Target.ToString(), *FromName, *Offset.ToString(), bDryRun ? TEXT(" [DRY RUN]") : TEXT(""));
if (bDryRun)
{
World->RemoveFromRoot();
return 0;
}
Capture->Modify();
Capture->SetRootComponent(NewRoot);
if (CaptureComp && CaptureComp != NewRoot)
{
CaptureComp->AttachToComponent(NewRoot, FAttachmentTransformRules::KeepRelativeTransform);
CaptureComp->SetRelativeTransform(FTransform::Identity);
}
NewRoot->SetRelativeTransform(Target);
NewRoot->UpdateComponentToWorld();
if (CaptureComp && CaptureComp != NewRoot)
{
CaptureComp->UpdateComponentToWorld();
}
// Verification : racine, rattachement, position monde de la capture.
int32 Errors = 0;
if (Capture->GetRootComponent() != NewRoot) { ++Errors; UE_LOG(LogLPFixSceneCapture, Error, TEXT(" racine non affectee")); }
if (CaptureComp && CaptureComp != NewRoot && CaptureComp->GetAttachParent() != NewRoot) { ++Errors; UE_LOG(LogLPFixSceneCapture, Error, TEXT(" capture non rattachee")); }
const FTransform Final = CaptureComp ? CaptureComp->GetComponentTransform() : NewRoot->GetComponentTransform();
if (!Final.Equals(Target, 0.01))
{
++Errors;
UE_LOG(LogLPFixSceneCapture, Error, TEXT(" position finale %s differente de la cible %s"), *Final.ToString(), *Target.ToString());
}
UE_LOG(LogLPFixSceneCapture, Warning, TEXT(" capture : position monde %s"), *Final.ToString());
int32 Result = 0;
if (Errors == 0)
{
UPackage* Package = World->GetOutermost();
const FString Filename = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetMapPackageExtension());
FSavePackageArgs SaveArgs;
SaveArgs.TopLevelFlags = RF_Standalone;
SaveArgs.Error = GWarn;
if (UPackage::SavePackage(Package, World, *Filename, SaveArgs))
{
UE_LOG(LogLPFixSceneCapture, Warning, TEXT(" -> carte sauvegardee"));
}
else
{
UE_LOG(LogLPFixSceneCapture, Error, TEXT(" ECHEC de sauvegarde"));
Result = 1;
}
}
else
{
UE_LOG(LogLPFixSceneCapture, Error, TEXT(" %d ecart(s) : carte NON sauvegardee"), Errors);
Result = 1;
}
World->RemoveFromRoot();
return Result;
}

View file

@ -0,0 +1,31 @@
// Portage UE 5.5 - lot L7j (ERR-031, amelioration : defaut deja present en 4.27).
//
// La camera SceneCapture2D "SecondScreen" de CentralLondon (image du contact dans l'encadre d'appel) n'a
// pas de composant racine (RootComponent = None) et ses composants sont a l'origine : elle filme le ciel.
// Les Blueprints ne la deplacent pas. Le contact affiche est place a la position de son emplacement de
// scene decalee de -10 000 000 en Y (GI::Show), sur la copie de scene SecondScreenFloor.
//
// Cette commandlet donne a l'acteur vise un composant racine (son SceneComponent existant, ou le
// composant de capture a defaut), y rattache le composant de capture, et le place sur la transformation
// d'un acteur de reference (camera du portrait) augmentee d'un decalage. Tout est verifie apres coup ;
// en cas d'ecart, la carte n'est pas sauvegardee.
//
// Usage :
// UnrealEditor-Cmd.exe <projet> -run=LPFixSceneCapture -Map=/Game/Scenes/CentralLondon -Actor=SecondScreen
// -From=CameraPortrait -Offset=0,-10000000,0 [-DryRun]
#pragma once
#include "CoreMinimal.h"
#include "Commandlets/Commandlet.h"
#include "LPFixSceneCaptureCommandlet.generated.h"
UCLASS()
class ULPFixSceneCaptureCommandlet : public UCommandlet
{
GENERATED_BODY()
public:
ULPFixSceneCaptureCommandlet();
virtual int32 Main(const FString& Params) override;
};