33 KiB
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.cpp03D85D28…sans avertissement et contient la chaîneShow 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 surchargeFQuat:Actor.h:1701-1702;(Target - Desired).Rotation()rendFRotator(Core/Public/Math/Vector.h:832) : pas d'ambiguïté.SetActorLocation(const FVector&, …):Actor.h:1636.FVector::Normalize(T Tolerance=UE_SMALL_NUMBER)rendbool:Vector.h:630.FVector::ForwardVectordéjà utilisé dans le module (LPSecondScreenSubsystem.cpp:101).FVector(0.0, 0.0, float): constructeur non gabaritTVector(T InX, T InY, T InZ)(Vector.h:168), conversionfloatversdoubleimplicite.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 queGrabCamera = View(déjà compilé) ;LastSexView.Get() != SceneCameracompareAActor*àAActor*;View == SceneCamera: deuxAActor*non const. DansComputeLabelles comparaisons existantes surconstrestent inchangées.FCollisionQueryParams(FName, bool, const AActor*),SCENE_QUERY_STAT(nom uniqueLPRecenter, une seule occurrence dansSource),AddIgnoredActors(const TArray<AActor*>&),AddIgnoredActor(const AActor*):Engine/Public/CollisionQueryParams.h:39, 231, 243, 257. Même forme que le code déjà compilé deTickCoupleMouse.Cast<AActor>(ReadObject(...))sans déréférence nulle.LineTraceSingleByChannelsurconst UWorld*: même usage que dansTickCoupleMouse(Worldconst, déjà compilé).FHitResult()appelleInit()qui faitMemzero(Engine/Classes/Engine/HitResult.h:148-176) :Hit.bBlockingHitvaut faux après une trace sans impact.FMath::Clamp: tous les arguments sontdouble(Desired.X/Y/Zdouble,Area.Min.X + Margindouble,Interior.FloorTop + 30.0double,FMath::Max(double, double)).FMath::Min(LowestRoot, cond ? double : double). Bornes duClampZ : minimumFloorTop+30, maximumMax(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 ; argumentsReason,FVector::Dist(double),Target.X/Y/Z(double), ternaireTEXT(…) : TEXT("")= 6.%sreçoit unconst 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 deTEXTetLeadMoved(double) = 2. Condition du ternaire : pointeurShowCouple, conversion contextuelle enbool.
- recadrage :
- Const-correctness :
ReadBool(const UObject*, FName)reçoitconst APlayerController* PCetAPlayerController* EndPC;WriteBool(UObject*, …)reçoitEndPCnon const.ReadObject(const UObject*, …)reçoitAActor* RoometUGameInstance* GI.ComputeRoomInterior(const AActor*, …)reçoitAActor* Room.GetFirstPlayerController()etGetViewTarget()appelables surconst(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,FollowMinStepdéfinies dans l'espace de noms. - Signatures :
.hl. 127 et 129 identiques aux définitions.cpp. Aucun paramètre ni variable inutilisé.GIn'est jamais nul dansTickSceneCamera(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 :
LastSexViewremis à zéro hors sexe ; en sexe,bOnScene && LastSexView.Get() != SceneCameradéclenche un recadrage une fois, puisLastSexView = View; plus de recadrage tant que la vue resteSceneCamera. 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 = bC3chaque image. Sans variableSexC3dans le PC,ReadBoolrend faux (FindPropertyByNamenul) :bC3faux, rien ne change ; l'étiquette reste « Camera: Scene ». Priorité du POV conservée dansComputeLabel. PASS-STATIC. - (c) G-c : condition
bOnScene && bHasLastPivot && CoupleGrabMode == 0 && !bGrabEnded; vecteurPivot - LastCouplePivot; appel avantLastCouplePivot = Pivot(pièce cachée et chemin normal) ;Pivotdéjà corrigé par E1 (Pivot += Correctionprécède l'appel du chemin normal). La distance de 900 cm est évaluée à chaque image du bloc. Enchaînement exclusif enelse if: une image recadrée ne suit pas. L'ordre de tick est établi :TickSwitchKey(intervalle 0) appelleTickSexCamera(qui contientTickCoupleMouse, doncbGrabJustEndedetCoupleGrabModede la même image) puisTickCoupleBounds. 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,WalletCamRoomignorés ; rapprochement devant l'obstacle (version corrigée : Reprise) ; rotation vers la cible. PASS-STATIC. - (e) Hors sexe :
PC.SexC3remis à faux s'il était vrai ;bSexC3WasetLastSexViewremis à zéro.WriteBoolne fait rien si la propriété est absente. PASS-STATIC. - (f) Témoin G3 :
LeadMovedcalculé une fois, condition> 1.0identique à l'ancienne ; une ligne par appel avec le texteappeleounon appele, sous-systeme absent; l'appelNotifyCoupleTeleportedreste conditionné parShowCouplenon nul. PASS-STATIC. - (g) Déréférences :
GInon nul ;Roomvalide ;PCtesté avantGetViewTarget;SceneCamerautilisé seulement sibOnScene(donc valide et non nul) ;Couple[0]aprèsCouple.Num() == 0;Meshtesté avant usage ;ReadBool(PC, …)acceptePCnul. PASS-STATIC.
G3 (contrôle 6)
KeepTogether(LPShowCoupleSubsystem.cpp:750-805) écritLastSafe = 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 casLastSafeégale déjàNowà la tolérance deIsNearlyZero). La formulation de l'IMPL « se termine à chaque image » est donc exacte à ce détail près ; le résultat est le même :LastSafesuit la place du meneur à chaque image où la scène est engagée.KeepTogetherest appelée l. 388 (parAfterActors, branché surFWorldDelegates::OnWorldPostActorTick, l. 275-285).NotifyCoupleTeleported(l. 807-824) : retour sans effet si!bEngaged || !Who || !Partner || !GetWorld()(l. 812-814) ;Show couple : place reprise apres teleportationn'est écrite (l. 819-820) que si!(Now - LastSafe).IsNearlyZero()(l. 817) ; l. 822-823 :LastSafe = NowetLastBlocker.Reset()toujours.- Ordre dans l'image (établi,
Launch/Private/LaunchEngineLoop.cpp5.8) :GEngine->Tick(l. 5859) précèdeFTSTicker::GetCoreTicker().Tick(l. 6103).OnWorldPostActorTick(doncKeepTogether) s'exécute dans le tick du monde ; les tickers deLPCameraModeSubsystem(FTSTicker, l. 527-529) s'exécutent après. Au relâcher, le meneur n'a donc pas bougé depuis le dernier passage deKeepTogether, qui avait déjà remisLastSafeà sa place :Now − LastSafeest 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:21compileLPShowCoupleSubsystem.cppetLPCameraModeSubsystem.cpp; l. 134 la chaîneShow couple : place reprisepasse de 0 à 1 ; l. 163 la CAM03D85D28…, état B2p-F + G3). Le critère d'essai deCOMMUN-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 siKeepTogethera laisséLastSafeen 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
SexC3et deux branches) laissée à MCP sous UE-GO ; le recadrage d'entrée en Scene remplace le cadragepickPresetà 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 contientmanifest.csv), « Ce qui n'a pas été fait » est fondu dans § 3 ; la ligneTours :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 »), etconst float RecenterMinDistance = 50.f;(cm). Utilisation inchangée :Moved.Size() > LPCameraModeLocal::FollowMinStep(doublecontrefloat, 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::DistrendT=double(Core/Public/Math/Vector.h:1015, 2464) ;Hit.Location(FVector_NetQuantize, dérivée deFVector) se lie àconst TVector<double>&par conversion vers la base (même emploi queLPDirectControlSubsystem.cpp:1582) ;double - floatdonnedouble;static_cast<double>(float): second argumentdouble;FMath::Max(double, double): même type, même forme qu'à.cppl. 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;Desirednon const. Aucune erreur de type. PASS-STATIC. - Ordre d'évaluation :
Axisest calculé avant l'affectation deDesiredet ne dépend que deDesiredd'avant ; la ligne remplacée de la VAL initiale n'existe plus (Grep: un seulDesired =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 - Targetn'est pas nul etAxisest un vecteur unitaire de la cible vers l'ancienne position visée : la caméra reste du côté de la cible d'où vientDesired.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),
Reachpeut 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 obstaclesi l'impact existe). Aucun test en jeu rejoué. - Ligne de journal et rotation :
Hit.bBlockingHitetFVector::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 :
AxisetReachsont déclarés dans le blocifdu tracé (l. 929-930) deRecenterSceneCamera. Paramètres de la fonction :GI,Room,SceneCamera,Couple,Pivot,Reason; locales englobantes :World,LowestRoot,Target,Flat,Desired,Interior,Query,Hit: aucunAxisniReach: C4456 et C4457 exclus. Membres du.h(l. 49-140) : aucunAxisniReach: C4458 exclu. Axis: les autres occurrences sontFName Axisen paramètre de fonction deLPCameraModeLocal::AxisValue(.cppl. 241, 251),FName AxisdeAxisValuedansLPDirectControlSubsystem.cpp:168, 178, une boucleint32 Axislocale (LPDirectControlSubsystem.cpp:1154) etFVector Axislocale (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 pourAxis(confirmé). La preuve par l'existant :LPOutdoorRoomSubsystem.cpp:433déclareFVector Axisen local dans la même unité unifiée sous V7 sans conflit, donc aucunAxisglobal n'existe dans les en-têtes de l'unité.Reach: point d'attention.LPDirectControlSubsystem.cpp:58déclareconst float Reach = 150.f;dans un espace de noms anonyme (l. 53). La nouvelle localeconst 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.cppl. 37, … ) ;LPCameraModeSubsystem.cppest absent de la copie générée (compilé seul,[NoUba], comme dansSTG-COMMUN11-BUILD.md:21), mais à l'ordre alphabétique il se placerait avantLPDirectControlSubsystem.cpp. Une déclaration locale ne peut masquer qu'un nom déjà déclaré à ce point de l'unité : ici l'anonymeReachn'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 incluraitLPDirectControlSubsystem.cppavant, 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 localeReachen un nom unique (par exempleArmLength) ; non fait par la VAL. Sinon BUILD le tranche (compilation de l'unité complète, pas seulement du fichier[NoUba]).- Autres noms globaux possibles :
Reachne figure dans aucun autre fichier deSource; pas de lecture des en-têtes moteur pour unReachglobal (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.