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

232 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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`.