LifePlay-Revival/Docs/Migration/etat/CAM/CAM-B2p-G-VAL.md
edujiro5972 8084622450 LifePlay UE 5.8 : état de développement au 2026-10-19
Tickets : REP1-T4

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 22:57:42 +02:00

33 KiB
Raw Blame History

Fiche CAM-B2p-G-VAL — Niveau N1 — Modèle prévu sonnet | utilisé claude-sonnet-5-5 — Agent lp-validator — Skills sk-protocol, sk-recount, debug-verification, lp-validate

CAM-B2p-G-VAL : validation statique de la partie C++ (recadrage, suivi du couple, C3, témoin G3) (2026-10-14)

Portée : VAL statique (aucune compilation, aucun éditeur, aucun commandlet, aucun MCP, aucune écriture git). Copie D:\Game\LifePlay-master 5.8, branche ue5.8, UE 5.8 (BuildSettingsVersion V7 : /we4456 /we4458 /we4459). Les fichiers Source/LifePlay/LPCameraModeSubsystem.cpp et .h sont non suivis par git (git status --short : ??) : il n'existe donc pas de tag CAM-B2p-G-avant ; le diff est pris contre l'instantané D:\Game\LifePlay_Backup_5.5\CAM-B2p-G\ par git diff --no-index. Deux passes : la VAL initiale (sections 1 à 6, .cpp 6D88EDDA…) et la Reprise (section finale, .cpp A5F041FF…) qui revérifie la correction de N1 et N2. Le verdict final est celui de la Reprise.

1. Fiches amont relues

  • Docs/Migration/etat/CAM/CAM-B2p-G-SPEC.md (92 lignes, lue en entier).
  • Docs/Migration/etat/CAM/CAM-B2p-G-IMPL.md (72 lignes, lue en entier ; seule IMPL du lot, partie C++). La correction de N1 et N2 n'a pas de livrable IMPL propre : elle est vérifiée dans le fichier source (Reprise).
  • Amont de la base : Docs/Migration/etat/ST/STG/STG-COMMUN11-BUILD.md (lignes 21, 30, 134, 163-164 : le build COMMUN-11 a compilé LPCameraModeSubsystem.cpp 03D85D28… sans avertissement et contient la chaîne Show couple : place reprise).
  • Lu en lecture seule : Source/LifePlay/LPShowCoupleSubsystem.cpp (l. 285-393, 750-824), LPShowCoupleSubsystem.h.
  • Gabarit VAL lu : D:\Game\LifePlay-Sandbox\Tools\claude\skills\lp-doc\gabarits.md.

2. Points à confirmer des IMPL (IMPL § 4, un par ligne)

# Point de l'IMPL Verdict Preuve
P1 Lignes « vue recadree » à l'entrée en Scene et en C3 PASS-STATIC (code) ; en jeu : reporté V7 .cpp l. 841-850 (deux appels), ligne de journal en fin de RecenterSceneCamera ; format 6 % pour 6 arguments
P2 Flèches et molette : la caméra suit le couple PASS-STATIC (code) ; en jeu : reporté V7 (CAM-79) .cpp l. 851-858 (avant reprise) ; appel avant la mise à jour de LastCouplePivot (l. 766-767, 828-829)
P3 Distance de 900 cm au plus PASS-STATIC (code) ; en jeu : reporté V7 .cpp constante FollowMaxDistance, bloc de suivi
P4 Ligne « G3 NotifyCoupleTeleported appele » à chaque pose qui déplace le meneur PASS-STATIC (code) ; en jeu : reporté V7 bloc « relâcher » de TickCoupleMouse ; une ligne par appel, condition > 1.0 inchangée
P5 Compilation BLOCKED-compilation (VAL statique : aucune compilation autorisée). Relecture C++ UE 5.8 sans défaut trouvé (§ 3, contrôles 3 et 4, puis Reprise) à confier à BUILD
P6 Essais CAM-76 à CAM-80 reporté (V7, en jeu) aucune exécution possible ici

3. Critères rejoués (commande → résultat) — VAL initiale

# Contrôle Commande / lecture Résultat brut Verdict
1a MD5 md5sum des quatre fichiers .cpp 6d88edda0033e12d6891821d47f1267f ; .h 00d439937dbceab44f459c4a834f34f0 ; instantané .cpp 03d85d282a6306e6d2494d7dc2c253fc, .h 4ae6599447750371cff7e0aac74eb93e PASS : égalent l'IMPL (état d'avant la Reprise)
1b Lignes wc -l .cpp 1570 (instantané 1437) ; .h 141 (instantané 132) PASS (1437+133 = 1570 ; 132+9 = 141)
1c CR tr -cd '\r' < f | wc -c (sans grep -c) sur les quatre fichiers 0, 0, 0, 0 ; premiers octets 2f 2f 20 (pas de BOM), dernier octet 0a PASS : LF, sans BOM, fin de ligne finale
2a Diff compte git diff --no-index --numstat .cpp : 136 3 ; .h : 9 0 PASS : 136 = 10 (constantes) + 11 (fin de sexe) + 1 + 1 (deux appels) + 100 (deux fonctions et ligne vide) + 8 (témoin G3) + 5 (étiquette) ; 3 retraits = 2 lignes if de G3 + 1 ligne return de l'étiquette
2b Périmètre diff lu en entier (6 hunks .cpp, 1 hunk .h) uniquement : constantes B2p-G ; TickCoupleBounds (remise à zéro hors sexe, deux appels) ; TickSceneCamera et RecenterSceneCamera ; bloc « relâcher » de TickCoupleMouse ; ComputeLabel ; membres du .h PASS : aucun ajout non demandé
2c const AActor* View existants diff + Grep des déclarations de View déclarations aux l. 611, 1317, 1458 (const AActor*) et 989 (AActor*) : aucune ligne de diff n'y touche ; les nouvelles AActor* View (l. 837) sont dans TickSceneCamera seulement PASS
3 Relecture C++ UE 5.8 Grep dans D:\Game\Launcher\Engine\UE_5.8\Engine\Source (détail ci-dessous) aucune erreur de type, de const ni de format trouvée PASS-STATIC
4 Ombrage C4456/C4457/C4458/C4459 lecture de chaque portée + Grep des membres et des noms globaux (détail ci-dessous) aucun ombrage trouvé PASS-STATIC
5 Logique (a) à (g) lecture (détail ci-dessous) conforme, trois réserves non bloquantes (N1 à N3) PASS-STATIC
6 G3 lecture de LPShowCoupleSubsystem.cpp voir § « G3 » ci-dessous PASS-STATIC
7 Cohérence SPEC/IMPL lecture croisée voir § « SPEC / IMPL » ci-dessous PASS avec écarts acceptables

Contrôle 3, API UE 5.8 (sources lues dans D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime)

  • AActor::AddActorWorldOffset(FVector, bool=false, FHitResult*=nullptr, ETeleportType=None) : Engine/Classes/GameFramework/Actor.h:1756. Appel conforme.
  • SetActorLocationAndRotation(FVector, FRotator, …) et surcharge FQuat : Actor.h:1701-1702 ; (Target - Desired).Rotation() rend FRotator (Core/Public/Math/Vector.h:832) : pas d'ambiguïté. SetActorLocation(const FVector&, …) : Actor.h:1636.
  • FVector::Normalize(T Tolerance=UE_SMALL_NUMBER) rend bool : Vector.h:630. FVector::ForwardVector déjà utilisé dans le module (LPSecondScreenSubsystem.cpp:101).
  • FVector(0.0, 0.0, float) : constructeur non gabarit TVector(T InX, T InY, T InZ) (Vector.h:168), conversion float vers double implicite. Flat * float : operator*(FArg) arithmétique (Vector.h:311-312) ; -Couple[0]->GetActorForwardVector() : moins unaire sur le résultat de l'appel.
  • TWeakObjectPtr<AActor> : LastSexView = View (AActor*) même forme que GrabCamera = View (déjà compilé) ; LastSexView.Get() != SceneCamera compare AActor* à AActor* ; View == SceneCamera : deux AActor* non const. Dans ComputeLabel les comparaisons existantes sur const restent inchangées.
  • FCollisionQueryParams(FName, bool, const AActor*), SCENE_QUERY_STAT (nom unique LPRecenter, une seule occurrence dans Source), AddIgnoredActors(const TArray<AActor*>&), AddIgnoredActor(const AActor*) : Engine/Public/CollisionQueryParams.h:39, 231, 243, 257. Même forme que le code déjà compilé de TickCoupleMouse. Cast<AActor>(ReadObject(...)) sans déréférence nulle.
  • LineTraceSingleByChannel sur const UWorld* : même usage que dans TickCoupleMouse (World const, déjà compilé). FHitResult() appelle Init() qui fait Memzero (Engine/Classes/Engine/HitResult.h:148-176) : Hit.bBlockingHit vaut faux après une trace sans impact.
  • FMath::Clamp : tous les arguments sont double (Desired.X/Y/Z double, Area.Min.X + Margin double, Interior.FloorTop + 30.0 double, FMath::Max(double, double)). FMath::Min(LowestRoot, cond ? double : double). Bornes du Clamp Z : minimum FloorTop+30, maximum Max(FloorTop+30, Ceiling-20) donc minimum inférieur ou égal au maximum, y compris plafond bas.
  • Formats UE_LOG :
    • recadrage : %s, %.0f, %.0f, %.0f, %.0f, %s = 6 ; arguments Reason, FVector::Dist (double), Target.X/Y/Z (double), ternaire TEXT(…) : TEXT("") = 6. %s reçoit un const TCHAR* (les deux littéraux de tailles différentes se réduisent en pointeur dans le ternaire, usage courant) ; Hit.bBlockingHit (champ de bits) en condition : valide.
    • G3 : %s, %.0f = 2 ; arguments : ternaire de TEXT et LeadMoved (double) = 2. Condition du ternaire : pointeur ShowCouple, conversion contextuelle en bool.
  • Const-correctness : ReadBool(const UObject*, FName) reçoit const APlayerController* PC et APlayerController* EndPC ; WriteBool(UObject*, …) reçoit EndPC non const. ReadObject(const UObject*, …) reçoit AActor* Room et UGameInstance* GI. ComputeRoomInterior(const AActor*, …) reçoit AActor* Room. GetFirstPlayerController() et GetViewTarget() appelables sur const (mêmes formes que dans le code déjà compilé).
  • Qualification : tous les appels aux fonctions et constantes de l'espace de noms sont préfixés LPCameraModeLocal:: (relu ligne à ligne) ; PCSexC3, RecenterDistance, RecenterHeight, RecenterTargetAboveRoot, RecenterWallMargin, FollowMaxDistance, FollowMinStep définies dans l'espace de noms.
  • Signatures : .h l. 127 et 129 identiques aux définitions .cpp. Aucun paramètre ni variable inutilisé. GI n'est jamais nul dans TickSceneCamera (retour anticipé si !GI).

Contrôle 4, ombrage de tout le fichier

Méthode : lecture de chaque fonction touchée, plus Grep (a) des membres du .h (l. 49-140), (b) des déclarations globales et des espaces de noms anonymes de Source/LifePlay (espaces de noms nommés ou anonymes dans 25 fichiers ; aucune déclaration de portée espace de noms nommée comme un des nouveaux noms ; les occurrences de Desired, Hit, Query, World, PC, View, Mesh, Character, Person, Reason hors module sont des locales de fonction ou un membre de struct : LPBonusRewardSubsystem.h:79, LPSaveRelaunch.cpp:369, etc.).

Nouveau nom Portée Conflit
EndWorld, EndPC bloc de retour anticipé de TickCoupleBounds aucun : pas d'homonyme dans la fonction (World et PC des l. 748-749 sont dans un bloc if sans lien)
World, PC, View, SceneCamera, bC3, bOnScene, Moved, Away (TickSceneCamera) corps de fonction ; Moved et Away dans le bloc else if paramètres GI, Room, Couple, Pivot, bGrabEnded : aucun homonyme (C4457 exclu) ; membres LastSexView, bSexC3Was, LastCouplePivot, bHasLastPivot, CoupleGrabMode : aucun homonyme local (C4458 exclu)
World, LowestRoot, Person, Character, Mesh, Target, Flat, Desired, Interior, Area, Margin, Query, Wall, CamRoom, Hit (RecenterSceneCamera) Person, Character, Mesh dans la boucle for ; Area et Margin dans le bloc if ; Wall, CamRoom en initialisation de if paramètres GI, Room, SceneCamera, Couple, Pivot, Reason : aucun homonyme (C4457 exclu) ; aucun nom répété dans une portée englobante (C4456 exclu) ; aucun membre homonyme (C4458 exclu)
LeadMoved, ShowCouple (TickCoupleMouse) bloc !bHeld, bloc if (LeadMoved > 1.0) ShowCouple existait déjà en initialisation de if (baseline) : aucun autre ShowCouple ni LeadMoved dans la fonction (paramètres GI, PC, View, Room, Couple, Pivot)
C4459 (globaux visibles en unité unifiée) — aucun nom nouveau de portée globale ; les constantes ont le préfixe d'espace de noms nommé ; seul global proche : CVarShowCouple (LPShowCoupleSubsystem.cpp:28), nom différent de ShowCouple
La baseline 03D85D28… a été compilée sans avertissement (STG-COMMUN11-BUILD.md:30) ; les noms déjà présents (Margin, Area, Interior, LowestRoot, Moved, Query, Hit, Wall, CamRoom, Target, SceneCamera, bC3, World, PC, View) restent sans conflit global ; seuls EndWorld, EndPC, bOnScene, Away, Flat, Desired, LeadMoved, Reason étaient nouveaux et ne figurent dans aucune portée globale (Grep ci-dessus). Axis et Reach, ajoutés à la Reprise, sont traités dans la Reprise.

Contrôle 5, logique

  • (a) G-b : LastSexView remis à zéro hors sexe ; en sexe, bOnScene && LastSexView.Get() != SceneCamera déclenche un recadrage une fois, puis LastSexView = View ; plus de recadrage tant que la vue reste SceneCamera. Retour depuis le dernier POV : la vue change donc nouvelle entrée, nouveau recadrage. PASS-STATIC.
  • (b) G-d : else if (bOnScene && bC3 && !bSexC3Was) ; bSexC3Was = bC3 chaque image. Sans variable SexC3 dans le PC, ReadBool rend faux (FindPropertyByName nul) : bC3 faux, rien ne change ; l'étiquette reste « Camera: Scene ». Priorité du POV conservée dans ComputeLabel. PASS-STATIC.
  • (c) G-c : condition bOnScene && bHasLastPivot && CoupleGrabMode == 0 && !bGrabEnded ; vecteur Pivot - LastCouplePivot ; appel avant LastCouplePivot = Pivot (pièce cachée et chemin normal) ; Pivot déjà corrigé par E1 (Pivot += Correction précède l'appel du chemin normal). La distance de 900 cm est évaluée à chaque image du bloc. Enchaînement exclusif en else if : une image recadrée ne suit pas. L'ordre de tick est établi : TickSwitchKey (intervalle 0) appelle TickSexCamera (qui contient TickCoupleMouse, donc bGrabJustEnded et CoupleGrabMode de la même image) puis TickCoupleBounds. PASS-STATIC.
  • (d) RecenterSceneCamera : garde !World || Couple.Num() == 0 ; bornes F1 seulement si !Room->IsHidden() && ComputeRoomInterior ; bornes XY seulement si la pièce fait plus de deux marges ; clamp Z toujours cohérent ; tracé Visibility cible vers caméra avec couple, SceneCamera, Wall et CamRoom ignorés ; rapprochement devant l'obstacle (version corrigée : Reprise) ; rotation vers la cible. PASS-STATIC.
  • (e) Hors sexe : PC.SexC3 remis à faux s'il était vrai ; bSexC3Was et LastSexView remis à zéro. WriteBool ne fait rien si la propriété est absente. PASS-STATIC.
  • (f) Témoin G3 : LeadMoved calculé une fois, condition > 1.0 identique à l'ancienne ; une ligne par appel avec le texte appele ou non appele, sous-systeme absent ; l'appel NotifyCoupleTeleported reste conditionné par ShowCouple non nul. PASS-STATIC.
  • (g) Déréférences : GI non nul ; Room valide ; PC testé avant GetViewTarget ; SceneCamera utilisé seulement si bOnScene (donc valide et non nul) ; Couple[0] après Couple.Num() == 0 ; Mesh testé avant usage ; ReadBool(PC, …) accepte PC nul. PASS-STATIC.

G3 (contrôle 6)

  • KeepTogether (LPShowCoupleSubsystem.cpp:750-805) écrit LastSafe = Who->GetActorLocation(); en l. 804, à la fin de chaque passage qui va au bout. Deux retours anticipés existent : l. 756-758 (meneur, partenaire, volume ou monde absent) et l. 762-765 (Delta.IsNearlyZero(), auquel cas LastSafe égale déjà Now à la tolérance de IsNearlyZero). La formulation de l'IMPL « se termine à chaque image » est donc exacte à ce détail près ; le résultat est le même : LastSafe suit la place du meneur à chaque image où la scène est engagée. KeepTogether est appelée l. 388 (par AfterActors, branché sur FWorldDelegates::OnWorldPostActorTick, l. 275-285).
  • NotifyCoupleTeleported (l. 807-824) : retour sans effet si !bEngaged || !Who || !Partner || !GetWorld() (l. 812-814) ; Show couple : place reprise apres teleportation n'est écrite (l. 819-820) que si !(Now - LastSafe).IsNearlyZero() (l. 817) ; l. 822-823 : LastSafe = Now et LastBlocker.Reset() toujours.
  • Ordre dans l'image (établi, Launch/Private/LaunchEngineLoop.cpp 5.8) : GEngine->Tick (l. 5859) précède FTSTicker::GetCoreTicker().Tick (l. 6103). OnWorldPostActorTick (donc KeepTogether) s'exécute dans le tick du monde ; les tickers de LPCameraModeSubsystem (FTSTicker, l. 527-529) s'exécutent après. Au relâcher, le meneur n'a donc pas bougé depuis le dernier passage de KeepTogether, qui avait déjà remis LastSafe à sa place : Now − LastSafe est nul et aucune ligne n'est écrite.
  • Conclusion sur COMMUN-11 : l'absence de ligne « place reprise » au journal COMMUN-11 est attendue et ne prouve pas que G3 manque. Le build COMMUN-11 contient G3 (établi : STG-COMMUN11-BUILD.md:21 compile LPShowCoupleSubsystem.cpp et LPCameraModeSubsystem.cpp ; l. 134 la chaîne Show couple : place reprise passe de 0 à 1 ; l. 163 la CAM 03D85D28…, état B2p-F + G3). Le critère d'essai de COMMUN-11-TESTS.md:38 (« place reprise après chaque pose qui déplace le couple ») suppose à tort que la ligne paraît à chaque pose ; elle ne paraît que si KeepTogether a laissé LastSafe en arrière (pas bloqué ou ramené d'un pas). Le nouveau témoin (P4) tranchera en jeu que l'appel part. Niveau de preuve : ordre des tickers et résultat établis par lecture ; comportement observé en jeu non rejoué.

SPEC / IMPL (contrôle 7)

  • Conforme à la SPEC : G-a (cible, distance 300, hauteur 120, azimut gardé, F1 à 20 cm, plafond, tracé Visibility, regard, ligne de journal), G-b, G-c (même vecteur, figé pendant le glisser, 900 cm), G-d côté C++ (front montant, étiquette « Camera: C3 Couple », remise à faux hors sexe), G-e (Room non touché : aucun asset ni Blueprint dans le périmètre).
  • Écarts acceptables, tous annoncés par l'IMPL : partie PC de G-d (variable SexC3 et deux branches) laissée à MCP sous UE-GO ; le recadrage d'entrée en Scene remplace le cadrage pickPreset à chaque retour de POV (voulu, SPEC G-b) ; témoin G3 côté appelant (demande de Big Boss, hors SPEC mais cité et sans effet de comportement) ; la ligne de journal du recadrage est plus riche que la ligne minimale de la SPEC (ajout de distance, cible, obstacle : acceptable).
  • Écart de gabarit (non bloquant) : voir Constats C1 et C2.

4. Diff lu en entier

Fichier Instantané → actuel Hunks Compte brut Lecture
Source/LifePlay/LPCameraModeSubsystem.cpp 1437 → 1570 lignes (avant Reprise) ; 1437 → 1575 (Reprise) 6 : constantes ; fin de sexe ; deux appels ; TickSceneCamera et RecenterSceneCamera ; G3 ; ComputeLabel 136 3 (avant Reprise) ; 141 3 (Reprise) entier, deux fois
Source/LifePlay/LPCameraModeSubsystem.h 132 → 141 lignes 1 : deux déclarations et deux membres (l. 125-133) 9 0 (inchangé) entier
Aucun autre fichier du dossier de l'instantané (manifest.csv plus les deux fichiers). LPShowCoupleSubsystem.cpp/.h (M dans git status, travail STE) : lus seulement, rien écrit par ce lot (non prouvable par instantané, qui ne les contient pas ; l'IMPL § 2 annonce « rien écrit »).

5. Verdict

VAL initiale : PASS-STATIC, aucun défaut bloquant. Ce verdict ne couvre ni la compilation (P5, BLOCKED-compilation, à confier à BUILD), ni le comportement en jeu (P1 à P4 et P6, reportés à V7). Verdict final après la Reprise : PASS-STATIC (N1 et N2 levés ; voir la section Reprise pour une réserve nouvelle, N7, non bloquante).

6. Points à confirmer par l'utilisateur (cases)

  • BUILD : compilation de LifePlay (module touché doit figurer dans les unités compilées) avec 0 erreur et 0 avertissement nouveau sous V7 (P5).
  • N1 : levé à la Reprise (seuil 0,01 cm). Reste à confirmer en jeu que la caméra ne tremble pas.
  • N2 : levé à la Reprise (distance minimale de 50 cm). Compromis noté (N2b).
  • N3 : accepter qu'un recadrage déclenché pendant une prise soit défait par la caméra figée, ou demander de le différer.
  • N7 : décider du renommage de Reach (voir Reprise), ou laisser BUILD trancher.
  • Décision sur le critère COMMUN-11-TESTS.md:38 (« place reprise après chaque pose ») à reformuler à la lumière du G3 ci-dessus.
  • Essais en jeu CAM-76 à CAM-80 et lignes « vue recadree » / « G3 NotifyCoupleTeleported » (V7), après la partie PC de G-d.

Tours : 29 / 40 (VAL initiale 22, Reprise 7)

Constats

Défauts bloquants

Aucun (preuves : contrôles 1 à 7 ci-dessus et Reprise).

Défauts non bloquants

# Ligne (VAL initiale) Constat Niveau de preuve État
N1 .cpp 855 Le suivi ignorait tout pas de pivot de 0,5 cm ou moins par image, et LastCouplePivot est réécrit à chaque image : un mouvement lent n'était jamais suivi. estimé levé à la Reprise
N2 .cpp 926 Si le tracé touchait à moins de 20 cm de la cible, la caméra passait de l'autre côté de la cible. estimé levé à la Reprise (N2b : compromis)
N3 .cpp 844-850, 866 ; 1304-1306 Un recadrage déclenché pendant une prise (touche C avec le bouton de souris tenu, ou entrée en Scene en pleine prise) est défait à l'image suivante par TickCoupleMouse qui remet la pose de prise (GrabCameraLocation) ; bSexC3Was est déjà à vrai, donc pas de nouvelle tentative, alors que la ligne « vue recadree » a été écrite. De même, un front montant de SexC3 pendant lequel la vue n'est pas SceneCamera est perdu. estimé (lecture, non rejoué) ouvert
N4 .cpp 719-726 Hors sexe, la lecture par réflexion de PC.SexC3 s'exécute à chaque image ; même ordre de coût que ReadBool(GI, SexOn). Sans importance mesurable attendue. estimé ouvert, négligeable
N5 .cpp 859-864 La borne de 900 cm n'est évaluée que hors prise ; une prise suivie d'un relâcher peut laisser la caméra au-delà de 900 cm pendant la prise, puis la ramener d'un saut. La pièce fait 7,25 × 7 m (SPEC § 1) : cas pratiquement impossible. estimé ouvert, négligeable
N6 .cpp 840-841 Si le jeu change la vue avec un fondu (SetViewTargetWithBlend à durée non nulle), GetViewTarget() ne rend la nouvelle cible qu'en fin de fondu et le recadrage d'entrée se fait alors ; comportement de Room non relu. incertain (essai : CAM-78) ouvert
N7 .cpp 930 (Reprise) Nouveau nom local Reach homonyme de const float Reach dans l'espace de noms anonyme de LPDirectControlSubsystem.cpp:58 : C4459 possible (erreur sous /we4459) si ce fichier précédait LPCameraModeSubsystem.cpp dans une unité unifiée. Voir Reprise. estimé (ordre alphabétique constaté dans Module.LifePlay.cpp) ouvert, non bloquant

Écarts de gabarit des livrables amont (écart = section absente)

  • C1, IMPL : le gabarit IMPL exige la tête commune (Fiche … — Niveau … — Modèle prévu … | utilisé … — Agent … — Skills …), absente (la ligne 1 est le titre) ; les sections ne suivent pas l'ordre 1 Fichiers modifiés (chemins, lignes), 2 Écarts à la SPEC, 3 Instantané, 4 Ce qui n'a pas été fait, 5 Points à confirmer en VAL, 6 Fichiers à commiter : l'IMPL a « 1. Écritures », « 2. G3 », « 3. Écarts », « 4. Points à confirmer en V7 », « 5. Fichiers à commiter » ; l'instantané n'est pas une section (sans manifeste cité ; le dossier contient manifest.csv), « Ce qui n'a pas été fait » est fondu dans § 3 ; la ligne Tours : est absente ; le dépôt n'est pas précisé dans « Fichiers à commiter ». Non bloquant ; le contenu est exact (MD5, lignes, périmètre recomptés). La correction N1/N2 n'est pas consignée dans l'IMPL (MD5 annoncé de l'IMPL : 6D88EDDA…, périmé) : à mettre à jour.
  • C2, SPEC : le gabarit SPEC exige 1 Objectif mesurable, 2 Fichiers touchés et ordre, 3 Repli, 4 Risques R# et questions Q#, 5 Interdits, 6 Valeurs moteur lues (chemin:ligne), 7 Critères de VAL (commande → attendu), plus la tête commune. La SPEC a DIAG, Conception, Estimation, Risques : sans objectif mesurable, sans interdits, sans valeurs moteur, sans critères de VAL. Les critères de cette VAL viennent donc de la consigne de l'orchestrateur. Non bloquant.
  • C3, IMPL : § 2 de l'IMPL écrit « KeepTogether … se termine à chaque image par LastSafe » : voir le détail des retours anticipés (G3 ci-dessus). Imprécision, sans conséquence sur la conclusion.

Non mesuré

Compilation et avertissements ; exécution du moindre essai en jeu ; journal de la session COMMUN-11 relu (seul le rapport de build a été lu) ; vitesse des flèches et de la molette ; comportement du Blueprint Room au changement de vue (N6) ; vérification de l'affirmation du commentaire « l'animation ne bouge pas le pivot » (donc absence de tremblement avec le seuil de 0,01 cm) ; LPTestStringCompat et porte G-U3 (sans objet : aucun changement de chaîne, de configuration ni de compilation dans ce lot).

Fichiers à commiter (VAL)

A Docs/Migration/etat/CAM/CAM-B2p-G-VAL.md (dépôt du jeu, branche ue5.8). Fichiers modifiés par la VAL : ce livrable seulement.

À verser

lp-validate § 1 : un fichier source non suivi par git n'a pas de tag <lot>-avant ; comparer à l'instantané par git diff --no-index --numstat (code 1 = différences). lp-build/lp-ue-compat : ordre de tick établi en 5.8 — GEngine->Tick (LaunchEngineLoop.cpp:5859) avant FTSTicker::GetCoreTicker().Tick (l. 6103), donc OnWorldPostActorTick précède les tickers FTSTicker des sous-systèmes. lp-validate § 4 (C4459) : l'unité unifiée est lisible dans Intermediate/Build/Win64/x64/UnrealEditor/Development/LifePlay/Module.LifePlay.cpp (ordre alphabétique insensible à la casse des #include ; LPCameraModeSubsystem.cpp en est absent quand il est modifié, compilé seul [NoUba]) ; vérifier l'ordre avant de conclure sur un nom homonyme d'un espace de noms anonyme d'un autre fichier du module.

Reprise (2026-10-14) : correction de N1 et N2

Statique, aucune écriture hors ce livrable. Entrée : message de l'orchestrateur (.cpp annoncé A5F041FFA8562F9E75560EAD01725E2B, 1575 lignes ; .h inchangé 00D43993…).

R1. Contrôles rejoués

Contrôle Commande Résultat brut Verdict
MD5 md5sum .cpp a5f041ffa8562f9e75560ead01725e2b ; .h 00d439937dbceab44f459c4a834f34f0 PASS : égalent l'annonce
Lignes wc -l .cpp 1575 ; .h 141 PASS (1570 + 5)
CR tr -cd '\r' < f | wc -c 0 et 0 ; premiers octets 2f 2f 20, derniers 7d 0a PASS : LF, sans BOM
Diff contre l'instantané git diff --no-index --numstat puis diff -U2 lu en entier .cpp 141 3 (avant : 136 3) ; .h 9 0 PASS : +5 = +2 (ligne de constantes : commentaire et RecenterMinDistance ajoutés autour de FollowMinStep) +3 (bloc d'obstacle : 1 ligne remplacée par commentaire, Axis, Reach, Desired) ; les 3 retraits sont inchangés ; tous les autres hunks sont identiques à ceux de la VAL initiale
Périmètre lecture seules les constantes de suivi et le bloc if (LineTraceSingleByChannel…) de RecenterSceneCamera diffèrent de la VAL initiale PASS : aucun ajout non demandé

R2. N1 : constante de suivi

  • Lignes (diff) : const float FollowMinStep = 0.01f; précédée d'un commentaire (« Le pivot est la place des acteurs (l'animation ne la bouge pas) : tout pas compte, meme lent »), et const float RecenterMinDistance = 50.f; (cm). Utilisation inchangée : Moved.Size() > LPCameraModeLocal::FollowMinStep (double contre float, sans avertissement).
  • Effet : le seuil par image tombe de 0,5 à 0,01 cm, soit 0,6 cm/s à 60 i/s : tout mouvement volontaire (flèches, molette, borne E1) est suivi, y compris à 240 i/s. N1 levé (PASS-STATIC).
  • Réserve non mesurée : si un acteur du couple dérivait de plus de 0,01 cm par image sans intervention (bruit numérique, recalage du jeu), la caméra suivrait cette dérive ; c'est le comportement voulu par G-c (« le jeu »), mais l'absence de tremblement en jeu n'est pas rejouée. L'affirmation du commentaire sur l'animation n'est pas vérifiée ici.

R3. N2 : bloc d'obstacle

Code lu (.cpp l. 926-932) :

if (World->LineTraceSingleByChannel(Hit, Target, Desired, ECC_Visibility, Query))
{
    const FVector Axis = (Desired - Target).GetSafeNormal();
    const double Reach = FMath::Max(FVector::Dist(Target, Hit.Location) - LPCameraModeLocal::RecenterWallMargin, static_cast<double>(LPCameraModeLocal::RecenterMinDistance));
    Desired = Target + Axis * Reach;
}
  • Types : FVector::Dist rend T = double (Core/Public/Math/Vector.h:1015, 2464) ; Hit.Location (FVector_NetQuantize, dérivée de FVector) se lie à const TVector<double>& par conversion vers la base (même emploi que LPDirectControlSubsystem.cpp:1582) ; double - float donne double ; static_cast<double>(float) : second argument double ; FMath::Max(double, double) : même type, même forme qu'à .cpp l. 909 (FMath::Max(Interior.FloorTop + 30.0, Interior.Ceiling - Margin)) et à TickCoupleMouse (FMath::Max(OutZ, FloorBox.Max.Z)) ; Axis * Reach : FVector * double (operator*(FArg) arithmétique, Vector.h:311-312) ; Target + … : FVector ; Desired non const. Aucune erreur de type. PASS-STATIC.
  • Ordre d'évaluation : Axis est calculé avant l'affectation de Desired et ne dépend que de Desired d'avant ; la ligne remplacée de la VAL initiale n'existe plus (Grep : un seul Desired = dans le bloc de trace).
  • Logique : le tracé est non dégénéré quand il touche (une trace de longueur nulle ne touche pas), donc Desired - Target n'est pas nul et Axis est un vecteur unitaire de la cible vers l'ancienne position visée : la caméra reste du côté de la cible d'où vient Desired. Reach = Max(distance au point d'impact − 20 cm, 50 cm) : jamais moins de 50 cm de la cible (établi par la forme de l'expression). La caméra ne peut plus passer de l'autre côté de la cible (défaut de N2). N2 levé (PASS-STATIC).
  • N2b, compromis non bloquant : si l'impact est à moins de 50 cm de la cible, la caméra est posée à 50 cm, donc au-delà de l'obstacle, de 50 − d cm (d = distance cible-impact, d < 50). Pour d entre 50 et 70 cm elle reste devant l'obstacle (d − 20 < 50 ≤ d). Si la distance visée initiale est elle-même inférieure à 50 cm (pièce très étroite, bornes F1), Reach peut dépasser la distance visée. Les deux cas sont rares (obstacle collé au couple, hors couple et caméras) et l'exigence « au moins 50 cm » les accepte ; le journal l'écrit (rapprochee devant un obstacle si l'impact existe). Aucun test en jeu rejoué.
  • Ligne de journal et rotation : Hit.bBlockingHit et FVector::Dist(Desired, Target) inchangés ; la distance écrite est désormais celle de la position finale.

R4. Ombrage C4456 à C4459 pour Axis et Reach

Méthode : Grep de \b(Axis|Reach)\b dans Source/LifePlay (*.cpp, *.h), lecture des portées, lecture de l'unité unifiée générée.

  • Portée : Axis et Reach sont déclarés dans le bloc if du tracé (l. 929-930) de RecenterSceneCamera. Paramètres de la fonction : GI, Room, SceneCamera, Couple, Pivot, Reason ; locales englobantes : World, LowestRoot, Target, Flat, Desired, Interior, Query, Hit : aucun Axis ni Reach : C4456 et C4457 exclus. Membres du .h (l. 49-140) : aucun Axis ni Reach : C4458 exclu.
  • Axis : les autres occurrences sont FName Axis en paramètre de fonction de LPCameraModeLocal::AxisValue (.cpp l. 241, 251), FName Axis de AxisValue dans LPDirectControlSubsystem.cpp:168, 178, une boucle int32 Axis locale (LPDirectControlSubsystem.cpp:1154) et FVector Axis locale (LPOutdoorRoomSubsystem.cpp:433-438). Ce sont des paramètres ou des locales, pas des noms de portée globale ni d'espace de noms : aucun C4459 pour Axis (confirmé). La preuve par l'existant : LPOutdoorRoomSubsystem.cpp:433 déclare FVector Axis en local dans la même unité unifiée sous V7 sans conflit, donc aucun Axis global n'existe dans les en-têtes de l'unité.
  • Reach : point d'attention. LPDirectControlSubsystem.cpp:58 déclare const float Reach = 150.f; dans un espace de noms anonyme (l. 53). La nouvelle locale const double Reach (l. 930) a le même nom. Établi : Module.LifePlay.cpp (unité unifiée, Intermediate/Build/Win64/x64/UnrealEditor/Development/LifePlay/) inclut les sources dans l'ordre alphabétique insensible à la casse (l. 31-57 : LifePlay.cpp, LifePlayGameModeBase.cpp, LPAssetRegistryHelpers.cpp, LPColorGradeSubsystem.cpp, … LPDirectControlSubsystem.cpp l. 37, … ) ; LPCameraModeSubsystem.cpp est absent de la copie générée (compilé seul, [NoUba], comme dans STG-COMMUN11-BUILD.md:21), mais à l'ordre alphabétique il se placerait avant LPDirectControlSubsystem.cpp. Une déclaration locale ne peut masquer qu'un nom déjà déclaré à ce point de l'unité : ici l'anonyme Reach n'est pas encore vu, donc pas de C4459 dans l'ordre actuel (estimé : l'ordre alphabétique est constaté dans le fichier généré, pas garanti par le moteur ; la sensibilité exacte de C4459 aux noms d'un espace anonyme n'a pas été rejouée, faute de compilation). Une autre combinaison d'unités (ordre différent, fichier d'une unité à part qui inclurait LPDirectControlSubsystem.cpp avant, ou renommage de fichiers) pourrait en faire une erreur sous /we4459. N7 (non bloquant). Remède sans risque, à l'appréciation de l'orchestrateur : renommer la locale Reach en un nom unique (par exemple ArmLength) ; non fait par la VAL. Sinon BUILD le tranche (compilation de l'unité complète, pas seulement du fichier [NoUba]).
  • Autres noms globaux possibles : Reach ne figure dans aucun autre fichier de Source ; pas de lecture des en-têtes moteur pour un Reach global (incertain, faible probabilité).

R5. Verdict final et suite

  • PASS-STATIC (N1 et N2 levés par lecture ; aucun défaut bloquant ; N7 non bloquant nouveau ; N3 reste ouvert comme avant). Non couverts : compilation (BLOCKED-compilation, BUILD), comportement en jeu (V7).
  • Cases à jour : voir § 6. L'IMPL doit être mise à jour (MD5 A5F041FF…, 1575 lignes, correction N1/N2) ; la ligne « Fichiers à commiter » de l'IMPL ne change pas.