LifePlay-Revival/Docs/Migration/etat/CAM/CAM-B2d-IMPL.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

18 KiB
Raw Blame History

Fiche CAM-B2d — Niveau N3 — Modèle prévu opus | utilisé claude-opus-5-5 (Opus 5.5) — Agent lp-mcp-bp — Skills sk-protocol, sk-change-safety, lp-mcp, lp-lot

CAM-B2d-IMPL : C2 sur la rotation de contrôle (B2d-1 à B2d-4) (2026-10-13)

Consignes : CAM-B2d-FICHE.md § « Consignes de l'agent » ; rubriques communes de CAM-B2b-FICHES.md (l. 23-63). SPEC : CAM-SPEC.md § « CAM-B2d » (l. 384-393). UE-GO de Big Boss pour le passage Room B2d + B2e (2026-10-13, cité par l'orchestrateur). Éditeur headless PID 22060, port 8000, connexion unreal-mcp partagée (MCPA-T1) ; Room chargé par la session (état B2b F1-F7 enregistré). Consigne de l'orchestrateur, différente de la fiche : pas de save_assets en B2d, B2e enregistre Room dans le même éditeur. Un seul call_tool par message. 53 appels MCP : 4 find_node_types, 13 create_node (dont 1 en erreur voulue, § 2 E1), 6 delete_node, 21 connect_pins, 1 break_pins, 5 get_node_infos (dont 1 en erreur), 2 find_nodes, 1 compile_blueprint. Aucun set_pin_value, save_assets, write_graph_dsl ni ProgrammaticToolset. Aucune écriture par HyperAIStudio.

1. Fichiers modifiés

  • Content/BPs/Room.uasset : modifié en mémoire de l'éditeur seulement, non enregistré (consigne : B2e enregistre). Le MD5 du disque est celui de l'instantané (§ 3).
  • Graphe /Game/BPs/Room.Room:EventGraph : ComputeC2Target (zone de 454, x 34450 y 1850), CircleCamera (96, x 5650 y 4450), LeftRight (97, x 5250 y 2950), PlaceC2 (CE48, x 34000 y 3000).

1.1 Identifiants trouvés (find_node_types)

  • ControlRotation, contexte = sortie 0 de VariableGet_262 : Pion|SetControlRotation, Pion|GetControlRotation.
  • Rotator : Math|Rotateur|BreakRotator, Math|Rotateur|MakeRotator.
  • GetForwardVector : Transformation|GetForwardVector, Math|Vecteur|GetForwardVector (version rotateur, relue : entrée InRot Rotator).
  • GetActorRotation : Transformation|GetActorRotation.

1.2 Lecture avant câblage (get_node_infos groupé, 11 créés + 15 existants), conforme à la fiche

  • 453 Transformation|GetActorForwardVector : self ← 261.0 ; out 0 → 454.A 0. 454 Normalize2D(Vector) : A 0, Tolerance 1 ; out 0 → PO17.A.
  • 262 |GetPC (out 0 PC Référence d'objet, self ← VariableGet_257 GI) → 394.1, 434.1, 439.0, 466.1.
  • 261 |GetPlayerCharacter (out 0 Person Référence d'objet) → 264, 265, 266, 434.2, PO0.1, 451, 452, 453, MakeArray_5.
  • 96 : execute ← CE1.1 ; Condition ← PO8 ; then 0 → 67.0 ; else 1 → IfThenElse_26. PO28 float+float : A ← 292, B ← CE1.2 (DeltaSeconds) ; out → 67.1. 67 |SetC2Yaw : then libre.
  • 97 : execute ← CE4.1 ; Condition ← PO8 ; then 0 → 68.0 ; else 1 → IfThenElse_9. PO30 : A ← 293, B ← PO29 ; out → 68.1. 68 : then libre.
  • CE48 AddEvent|Custom|PlaceC2 : then (out 1) → 461.execute 0. 461 |EnsureCamRoom : then → CallFunction_462.
  • K2Node_CallFunction_521 : nœud existant, MakeRotator (8240, 5872 ; sortie → CallFunction_90.2, Pitch ← CommutativeAssociativeBinaryOperator_12, Yaw ← CallFunction_84.2). Lu comme sonde (mon 3e MakeRotator a reçu le n° 522) ; non touché.

1.3 Nœuds créés (préfixe refPath : /Game/BPs/Room.Room:EventGraph.)

Rôle refPath (suffixe) type_id Broches relues (index) Position
CR = GetControlRotation (Controller) K2Node_CallFunction_524 Pion|GetControlRotation + declaring_class (§ 2 E1) self in 0 (Controller Référence d'objet) ; ReturnValue out 0 (Rotator) 33700, 2200
GetForwardVector(CR) K2Node_CallFunction_513 Math|Vecteur|GetForwardVector InRot in 0 ; ReturnValue out 0 (Vector) 34000, 2050
BR = BreakRotator(CR) (partagé) K2Node_CallFunction_514 Math|Rotateur|BreakRotator InRot in 0 ; Roll out 0, Pitch out 1, Yaw out 2 (simple précision) 34000, 1900
MakeRotator (CircleCamera) K2Node_CallFunction_515 Math|Rotateur|MakeRotator Roll in 0, Pitch in 1, Yaw in 2 (simple précision) ; out 0 6050, 4250
SetControlRotation (CircleCamera) K2Node_CallFunction_516 Pion|SetControlRotation execute in 0, self in 1 (Controller), NewRotation in 2 (Rotator par référence) ; then out 0 6300, 4400
MakeRotator (LeftRight) K2Node_CallFunction_517 Math|Rotateur|MakeRotator idem 515 5900, 2700
SetControlRotation (LeftRight) K2Node_CallFunction_518 Pion|SetControlRotation idem 516 6150, 2900
GetActorRotation(MC) (PlaceC2) K2Node_CallFunction_519 Transformation|GetActorRotation self in 0 (Acteur) ; ReturnValue out 0 (Rotator) 37000, 3000
BreakRotator (PlaceC2) K2Node_CallFunction_520 Math|Rotateur|BreakRotator idem 514 37250, 3000
MakeRotator (PlaceC2) K2Node_CallFunction_522 Math|Rotateur|MakeRotator idem 515 37500, 3000
SetControlRotation (PlaceC2) K2Node_CallFunction_523 Pion|SetControlRotation idem 516 37750, 3100

Roll et Pitch des trois MakeRotator : défaut 0.0, non liés (SPEC : MakeRotator(0, 0, Yaw)).

1.4 Nœuds supprimés (delete_node, 6, tous {"returnValue":null})

Nœud Rôle Ligne SPEC
K2Node_CallFunction_512 mon premier GetControlRotation, version Pawn (self Pion), jamais relié § 2 E1
K2Node_CallFunction_453 GetActorForwardVector(MC) B2d-1
K2Node_VariableSet_67 puis K2Node_VariableGet_292 Définir C2Yaw / Obtenir C2Yaw (CircleCamera) B2d-2
K2Node_VariableSet_68 puis K2Node_VariableGet_293 idem (LeftRight) B2d-3

1.5 Liens coupés

  • Par break_pins (1) : C1 CustomEvent_48.then 1 → CallFunction_461.execute 0 (B2d-4).
  • Par delete_node (§ 2 E2) : 453.0 → 454.A 0 et 261.0 → 453.self ; 292.0 → PO28.A 0 ; 96.then 0 → 67.execute 0 et PO28.0 → 67.C2Yaw 1 ; 293.0 → PO30.A 0 ; 97.then 0 → 68.execute 0 et PO30.0 → 68.C2Yaw 1.

1.6 Liens faits (connect_pins, 21, tous {"returnValue":null})

# Sortie Entrée Ligne SPEC
L1 VariableGet_262.0 (PC) CallFunction_524.self 0 B2d-1
L2 CallFunction_524.0 CallFunction_513.InRot 0 B2d-1
L3 CallFunction_513.0 CallFunction_454.A 0 B2d-1
L4 CallFunction_524.0 CallFunction_514.InRot 0 BR
L5 CallFunction_514.Yaw 2 PromotableOperator_28.A 0 B2d-2
L6 PromotableOperator_28.0 CallFunction_515.Yaw 2 B2d-2
L7 CallFunction_515.0 CallFunction_516.NewRotation 2 B2d-2
L8 VariableGet_262.0 CallFunction_516.self 1 B2d-2
L9 IfThenElse_96.then 0 CallFunction_516.execute 0 B2d-2
L10 CallFunction_514.Yaw 2 PromotableOperator_30.A 0 B2d-3
L11 PromotableOperator_30.0 CallFunction_517.Yaw 2 B2d-3
L12 CallFunction_517.0 CallFunction_518.NewRotation 2 B2d-3
L13 VariableGet_262.0 CallFunction_518.self 1 B2d-3
L14 IfThenElse_97.then 0 CallFunction_518.execute 0 B2d-3
L15 VariableGet_261.0 (MC) CallFunction_519.self 0 B2d-4
L16 CallFunction_519.0 CallFunction_520.InRot 0 B2d-4
L17 CallFunction_520.Yaw 2 CallFunction_522.Yaw 2 B2d-4
L18 CallFunction_522.0 CallFunction_523.NewRotation 2 B2d-4
L19 VariableGet_262.0 CallFunction_523.self 1 B2d-4
L20 CustomEvent_48.then 1 CallFunction_523.execute 0 B2d-4
L21 CallFunction_523.then 0 CallFunction_461.execute 0 B2d-4

516.then et 518.then laissés libres (fin de l'événement côté C2, comme 67 et 68 avant).

1.7 Relecture (get_node_infos groupé final, 20 nœuds : 11 créés + 454, PO28, PO30, 96, 97, CE48, 461, 262, 261)

Les 21 liens apparaissent des deux côtés. 454.A ← 513 seul ; PO28 : A ← 514.Yaw, B ← CE1.DeltaSeconds, out → 515 seul (float+float, double précision conservée) ; PO30 : A ← 514.Yaw, B ← PO29, out → 517 seul ; 96 : then → 516 seul, else → IfThenElse_26 (inchangé), Condition ← PO8 ; 97 : then → 518 seul, else → IfThenElse_9 (inchangé), Condition ← PO8 ; CE48.then → 523 seul ; 461 : execute ← 523, then → 462 (inchangé) ; 262 → 394, 434, 439, 466 (inchangés) + 524, 516, 518, 523 ; 261 → liens d'origine sans 453, + 519. Aucune divergence. Absence : find_nodes {title:"Forward"} → [K2Node_CallFunction_513] seul (témoin positif : la recherche par sous-chaîne marche ; 453 n'est plus dans le graphe). get_node_infos sur 512 → Parameter error: …K2Node_CallFunction_512 is not valid EdGraphNode. Voir § 2 E5 pour 453 et § 5 pour 67, 68, 292, 293.

Chaîne obtenue :

  • ComputeC2Target : direction arrière = Normalize2D(GetForwardVector(PC.GetControlRotation())) (au lieu de GetActorForwardVector(MC)), puis PO17 (× −1) → 497 (C2Yaw, désormais toujours 0) → PO3, inchangés.
  • CircleCamera en C2 : PC.SetControlRotation(0, 0, ControlYaw + DeltaSeconds), fin. Hors C2 : IfThenElse_26.
  • LeftRight en C2 : PC.SetControlRotation(0, 0, ControlYaw + AxisValue × 2), fin. Hors C2 : IfThenElse_9.
  • PlaceC2 : PC.SetControlRotation(0, 0, MC.GetActorRotation().Yaw) → EnsureCamRoom (461) → suite inchangée.

1.8 Compilation

compile_blueprint {blueprint:{refPath:"/Game/BPs/Room.Room"}, warnings_as_errors:true} → {"returnValue":null}, aucune exception. hyper_blueprint_debug_inspect non appelé : il était absent du serveur le 2026-10-10 (mémoire unreal-mcp-reconnect), non revérifié. Journal de l'éditeur non lu : chemin -abslog non fourni. Pas de save_assets (consigne de l'orchestrateur).

2. Écarts à la SPEC

  • E1 (identifiant, résolu ; méthode hors fiche) : create_node avec Pion|GetControlRotation a créé la version Pawn (512, self Pion Référence d'objet), qui n'accepte pas 262 (PC Référence d'objet). Le find_node_types avec contexte ne donnait qu'un seul type_id ; create_node ne prend pas ce contexte. Un appel create_node volontairement mal formé (pos:"schema") a renvoyé Function input params Json - {…,"declaring_class":null}. Ce retour révèle un paramètre caché. create_node {…, declaring_class:"/Script/Engine.Controller"} a créé 524, relu Controller Référence d'objet. 512, jamais relié, a été supprimé. La cible est conforme à la SPEC : la marche lit PC->GetControlRotation() (Source/LifePlay/LPDirectControlSubsystem.cpp:887). Pion|SetControlRotation a donné d'emblée la version Controller (516, 518, 523 : self Controller).
  • E2 (procédure, même résultat) : les 5 coupures nommées par la fiche (453 → 454.A, 292 → PO28.A, 96.then → 67, 293 → PO30.A, 97.then → 68) n'ont pas été faites par break_pins. Elles résultent du delete_node du nœud à retirer, fait avant de relier le remplaçant : un nœud supprimé perd tous ses liens. Motif : le budget de tours (60), qui n'aurait pas couvert 5 appels de plus. Le résultat est vérifié au § 1.7 : chaque entrée n'a qu'un lien, celui du remplaçant. Seule coupure explicite : CE48.then → 461, puisque 461 reste en place.
  • E3 (types) : BreakRotator.Yaw (simple précision) → PO28.A / PO30.A (double) et PO28 / PO30 (double) → MakeRotator.Yaw (simple) : conversions implicites. PO28 et PO30 restent float+float en double. La compilation stricte les accepte.
  • E4 (contrôle partagé) : un seul GetControlRotation (524) et un seul BreakRotator (514), purs, servent B2d-1 à B2d-3, comme le permet la SPEC (l. 392). Ils sont reliés de loin : ComputeC2Target est en x ≈ 34000, CircleCamera et LeftRight en x ≈ 5000-6000. Effet cosmétique seulement.
  • E5 (fait inattendu, sans effet sur le graphe) : après sa suppression, get_node_infos sur 453 rend encore un objet (broches vides), alors que 512 est refusé (not valid EdGraphNode). find_nodes établit que 453 n'est plus dans EventGraph. Hypothèse estimée : l'objet reste en mémoire, tenu par le tampon d'annulation, jusqu'au ramasse-miettes ou à l'enregistrement. À trancher par le dump après enregistrement (§ 5.1).
  • Positions (cosmétique, non prescrites) : les nœuds de B2d-4 sont en x 37000-37750, y 3000-3100. Ils sont donc loin de CE48 (34000, 3000) et de 461 (34300, 3000), qui n'étaient pas connus à la création.
  • Garde Accessed None : aucune IsValid ajoutée. « Vide » n'est pas un état normal de GI.PC (262) ni de GI.PlayerCharacter (261) en C2, et ni la SPEC ni la fiche n'en demandent. Les lectures nouvelles sont au § 5.3.
  • Comportement : hors C2, CircleCamera et LeftRight sont inchangés (else relus). En C2, le changement est voulu par la SPEC : la rotation de contrôle remplace C2Yaw. PlaceC2 pose désormais la rotation de contrôle avant EnsureCamRoom.

3. Instantané

Fait par l'orchestrateur (non refait, consigne) : D:\Game\LifePlay_Backup_5.5\CAM-B2d\. Relevé :

date -Iseconds; md5sum "/d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset"; grep -i "Room.uasset" "/d/Game/LifePlay_Backup_5.5/CAM-B2d/manifest.csv"; git -C "/d/Game/LifePlay-master 5.8" status --short -- Content/BPs/Room.uasset; git -C "/d/Game/LifePlay-master 5.8" rev-parse --short HEAD
2026-10-13T17:19:04+02:00
e0517c63541f249bd9dbda5c39815ba5 */d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset
"Content\BPs\Room.uasset","True","6318319","E0517C63541F249BD9DBDA5C39815BA5","2026-10-13T17:11:26"
 M Content/BPs/Room.uasset
0745917

Le MD5 du disque après B2d est celui de l'instantané : rien n'a été enregistré (attendu). Le M vient de B2b, déjà enregistré et non commité. Retour arrière avant l'enregistrement de B2e : fermer l'éditeur sans enregistrer, ce qui perd B2d. Après : Tools\Restore.ps1 -Lot CAM-B2d -Only Content\BPs\Room.uasset, éditeur fermé, sur décision de l'orchestrateur (jamais lancé ici).

4. Ce qui n'a pas été fait

  • save_assets : non fait, sur consigne de l'orchestrateur (B2e enregistre Room une fois). B2d vit en mémoire de l'éditeur PID 22060 : une fermeture ou un plantage avant l'enregistrement de B2e le perd ; l'instantané reste intact.
  • Mesures de la fiche « MD5 de Room après l'enregistrement (≠ E0517C63) » : sans objet ici, à prendre après B2e.
  • Absence de 67, 68, 292 et 293 dans le graphe : non prouvée par find_nodes, faute de tours. Elle est établie seulement par delete_node → null et par l'absence de leurs liens sur PO28, PO30, 96 et 97 (§ 1.7).
  • hyper_blueprint_debug_inspect et journal de l'éditeur : non consultés (§ 1.8).
  • Comportement à l'exécution : non mesuré (pas de PIE ni de build), seulement déduit du câblage. La preuve en jeu (build empaqueté + LogStats) n'est pas de mon rôle.

5. Points à confirmer en VAL

  1. V5 (CAM-SPEC.md:393), après l'enregistrement de B2e, par dump : GetControlRotation ≥ 1 (524, de classe Controller) ; SetControlRotation = 3 (516, 518, 523) ; aucun GetActorForwardVector dans ComputeC2Target (453 absent du fichier, cf. E5) ; C2Yaw écrite 0 fois (aucun SetC2Yaw : 67 et 68 absents) ; plus de VariableGet_292 ni _293 ; critères de B2b inchangés.
  2. Hors C2 : CircleCamera → IfThenElse_26 et LeftRight → IfThenElse_9 sont inchangés (relu). À confirmer en jeu.
  3. LogStats : aucun Accessed None nouveau sur VariableGet_262, _261, CallFunction_519, _523, _524. Lectures nouvelles : GI.PC (262) dans ComputeC2Target (524 ; avant, cette fonction lisait 261 par 453) et dans PlaceC2 avant EnsureCamRoom (523 ; 262 était déjà lu plus loin, par 466) ; GI.PlayerCharacter (261) dans PlaceC2 (519). Dans CircleCamera et LeftRight, 262 est déjà lu par la condition (PO8 → 439) avant 516 / 518 : aucun risque nouveau là.
  4. Essai B2d de la fiche (l. 49-53) : Z s'éloigne de la caméra ; souris et flèches tournent la caméra ; la caméra est derrière le personnage en entrant en C2. Estimé, non lu : si Person utilise la rotation de contrôle en lacet (bUseControllerRotationYaw), SetControlRotation fera aussi tourner le personnage. À observer.
  5. SetControlRotation(0, 0, Yaw) remet à 0 le tangage et le roulis de contrôle à chaque CircleCamera, LeftRight et PlaceC2. C'est voulu (SPEC : MakeRotator(0, 0, …)) ; la hauteur de C2 reste réglée par C2Height.
  6. Le lacet de contrôle n'est pas borné (somme libre). SetControlRotation ne normalise pas (estimé, non lu). Sans effet sur GetForwardVector.
  7. Compilation stricte jugée sur l'absence d'exception seulement : à recontrôler après B2e et par CompileAllBlueprints (V6).
  8. E5 : vérifier que l'enregistrement de B2e n'écrit pas 453 (dump : aucun GetActorForwardVector dans Room, sauf d'autres nœuds éventuels hors ComputeC2Target, à lister).

6. Fichiers à commiter

  • A Docs/Migration/etat/CAM/CAM-B2d-IMPL.md (dépôt du jeu, branche ue5.8).
  • Content/BPs/Room.uasset : rien pour B2d seul (non enregistré). Il sera enregistré par B2e, puis commité après l'essai commun (fiche, § « Après »).

Tours : 59 / 60

Constats

  • À verser : lp-mcp § 3. create_node accepte un paramètre caché declaring_class (ex. "/Script/Engine.Controller") qui choisit la classe quand deux fonctions ont le même type_id. Sans lui, Pion|GetControlRotation donne la version Pawn. On le voit en provoquant une erreur de conversion (pos en chaîne) : la liste des paramètres est rendue.
  • À verser : lp-mcp § 3. Index de broches relus :
    • GetControlRotation (Controller) : self in 0, out 0 ;
    • SetControlRotation : execute in 0, self in 1, NewRotation in 2, then out 0 ;
    • BreakRotator : InRot in 0, Roll out 0, Pitch out 1, Yaw out 2 (simple précision) ;
    • MakeRotator : Roll in 0, Pitch in 1, Yaw in 2, out 0 ;
    • Math|Vecteur|GetForwardVector : InRot in 0, out 0 ;
    • Transformation|GetActorRotation : self in 0, out 0.
  • À verser : lp-mcp § 3. Un nœud supprimé peut encore se lire par son refPath (broches vides). Son absence se prouve par find_nodes {title}, qui cherche par sous-chaîne, avec un témoin positif.
  • Les numéros de nœud ne sont pas consécutifs : CallFunction_521 existait déjà, et le MakeRotator suivant est 522. Toujours relire le refPath rendu par create_node.