201 lines
14 KiB
Markdown
201 lines
14 KiB
Markdown
|
|
Fiche CAM-B2b-F6a — 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-B2b-F6a-IMPL : pilotage de C2, `CircleCamera` et `LeftRight` (B2b-6, partie 1) (2026-10-13)
|
|||
|
|
|
|||
|
|
Consignes : `CAM-B2b-FICHES.md` § « Rubriques communes » (l. 23-63) et § « F6 » (l. 127-132), réduit par la fiche
|
|||
|
|
F6a à `CircleCamera` et `LeftRight` (`ScrollDownUp` passe en F6b). SPEC : `CAM-SPEC.md` § « CAM-B2b », ligne B2b-6
|
|||
|
|
(l. 380). BUILD-GO / UE-GO de Big Boss B2b du 2026-10-13 (cité par l'orchestrateur). Éditeur headless PID 10708, port
|
|||
|
|
8000, connexion `unreal-mcp` partagée (MCPA-T1). Room chargé, F1 à F5 en mémoire, non enregistrés. Un seul `call_tool`
|
|||
|
|
par message. **31 appels MCP** : 9 `create_node`, 2 `break_pins`, 15 `connect_pins`, 1 `set_pin_value`,
|
|||
|
|
3 `get_node_infos`, 1 `compile_blueprint`. Aucun `delete_node`, `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é (pas de `save_assets`
|
|||
|
|
avant F7). Preuve au § 3 : le MD5 du disque après F6a est celui de l'instantané.
|
|||
|
|
- Graphe `/Game/BPs/Room.Room:EventGraph` : événement `CircleCamera` (CE1, x 5296, y 4688) et événement `LeftRight`
|
|||
|
|
(CE4, x 5072, y 3168). Nœuds neufs en x 5650-6050, y 4250-4450 (CircleCamera) et x 5250-5850, y 2750-2950 (LeftRight).
|
|||
|
|
|
|||
|
|
### 1.1 Lecture avant écriture (`get_node_infos` groupé, 5 nœuds existants)
|
|||
|
|
|
|||
|
|
Lecture conforme aux faits de la fiche :
|
|||
|
|
- `K2Node_CustomEvent_1` (`AddEvent|Custom|CircleCamera`) : `then` out 1 → `IfThenElse_26`.execute 0 ; `DeltaSeconds`
|
|||
|
|
out 2 (`Float (double-précision)`) → `CommutativeAssociativeBinaryOperator_26`.1, `_16`.1, `_27`.1.
|
|||
|
|
- `K2Node_CustomEvent_4` (`AddEvent|Custom|LeftRight`) : `then` out 1 → `IfThenElse_9`.execute 0 ; `AxisValue` out 2
|
|||
|
|
(`Float (double-précision)`) → `CommutativeAssociativeBinaryOperator_47`.0, `_13`.0, `_24`.0.
|
|||
|
|
- `K2Node_IfThenElse_26` : execute ← CE1.1 ; Condition ← `CommutativeAssociativeBinaryOperator_19`.0 ; then 0 →
|
|||
|
|
`IfThenElse_19`.0 ; else 1 libre.
|
|||
|
|
- `K2Node_IfThenElse_9` : execute ← CE4.1 ; Condition ← `CommutativeAssociativeBinaryOperator_17`.0 ; then 0 →
|
|||
|
|
`IfThenElse_14`.0 ; else 1 libre.
|
|||
|
|
- `K2Node_PromotableOperator_8` (`Utilitaires|Equal(Object)`) : A ← `CallFunction_439`.0, B ← `VariableGet_268`.0 ;
|
|||
|
|
out 0 (`Booléen`) → `IfThenElse_91`.1.
|
|||
|
|
|
|||
|
|
Les deux paramètres d'événement sont en **double précision** (la fiche prévoyait peut-être de la simple) : aucune
|
|||
|
|
conversion implicite n'est nécessaire.
|
|||
|
|
|
|||
|
|
### 1.2 Nœuds créés (préfixe `refPath` : `/Game/BPs/Room.Room:EventGraph.`)
|
|||
|
|
|
|||
|
|
| Événement | Rôle | `refPath` (suffixe) | `type_id` demandé → relu | Broches relues | Position |
|
|||
|
|
|---|---|---|---|---|---|
|
|||
|
|
| CircleCamera | **Branche** C2 | `K2Node_IfThenElse_96` | `Utilitaires\|ContrôleDeFlux\|Branche` → idem | execute 0, Condition 1 ; then 0, else 1 | 5650, 4450 |
|
|||
|
|
| CircleCamera | Obtenir C2Yaw | `K2Node_VariableGet_292` | `Variables\|Pardéfaut\|ObtenirC2Yaw` → `\|GetC2Yaw` | C2Yaw out 0 (double) | 5650, 4250 |
|
|||
|
|
| CircleCamera | C2Yaw + DeltaSeconds | `K2Node_PromotableOperator_28` | `Utilitaires\|Opérateurs\|Ajouter` → `Math\|Flottant\|float+float` | A 0, B 1 ; out 0 (double) | 5850, 4250 |
|
|||
|
|
| CircleCamera | **Définir C2Yaw** | `K2Node_VariableSet_67` | `Variables\|Pardéfaut\|DéfinirC2Yaw` → `\|SetC2Yaw` | execute 0, C2Yaw 1 ; then 0, Output_Get 1 | 6050, 4400 |
|
|||
|
|
| LeftRight | **Branche** C2 | `K2Node_IfThenElse_97` | `Utilitaires\|ContrôleDeFlux\|Branche` → idem | execute 0, Condition 1 ; then 0, else 1 | 5250, 2950 |
|
|||
|
|
| LeftRight | Obtenir C2Yaw | `K2Node_VariableGet_293` | `Variables\|Pardéfaut\|ObtenirC2Yaw` → `\|GetC2Yaw` | C2Yaw out 0 (double) | 5250, 2750 |
|
|||
|
|
| LeftRight | AxisValue × 2 | `K2Node_PromotableOperator_29` | `Utilitaires\|Opérateurs\|Multiplier` → `Math\|Flottant\|float*float` | A 0, B 1 ; out 0 (double) | 5450, 2850 |
|
|||
|
|
| LeftRight | C2Yaw + produit | `K2Node_PromotableOperator_30` | `Utilitaires\|Opérateurs\|Ajouter` → `Math\|Flottant\|float+float` | A 0, B 1 ; out 0 (double) | 5650, 2750 |
|
|||
|
|
| LeftRight | **Définir C2Yaw** | `K2Node_VariableSet_68` | `Variables\|Pardéfaut\|DéfinirC2Yaw` → `\|SetC2Yaw` | execute 0, C2Yaw 1 ; then 0, Output_Get 1 | 5850, 2900 |
|
|||
|
|
|
|||
|
|
À la création, les opérateurs portent le `type_id` trompeur déjà vu en F3 à F5 : `Utilitaires|Gestiondutemps|
|
|||
|
|
FrameNumber+Int` pour PO28 et PO30, `…|Seconds*FrameRate` pour PO29, avec des broches `Caractère de remplacement`. À la
|
|||
|
|
relecture finale, ils sont typés `float+float` / `float*float` en double précision. PO8 est réutilisé sans copie (nœud pur).
|
|||
|
|
|
|||
|
|
### 1.3 Liens coupés (`break_pins`, 2, tous `{"returnValue":null}`)
|
|||
|
|
|
|||
|
|
| # | Sortie | Entrée |
|
|||
|
|
|---|---|---|
|
|||
|
|
| C1 | CustomEvent_1.then 1 | IfThenElse_26.execute 0 |
|
|||
|
|
| C2 | CustomEvent_4.then 1 | IfThenElse_9.execute 0 |
|
|||
|
|
|
|||
|
|
Aucun autre lien coupé ; dans ces deux événements, rien d'autre n'a été touché.
|
|||
|
|
|
|||
|
|
### 1.4 Liens faits (`connect_pins`, 15, tous `{"returnValue":null}`)
|
|||
|
|
|
|||
|
|
| # | Sortie | Entrée | Événement |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| L1 | CustomEvent_1.then 1 | IfThenElse_96.execute 0 | CircleCamera |
|
|||
|
|
| L2 | IfThenElse_96.else 1 | IfThenElse_26.execute 0 | CircleCamera |
|
|||
|
|
| L3 | PromotableOperator_8.0 | IfThenElse_96.Condition 1 | CircleCamera |
|
|||
|
|
| L4 | VariableGet_292.0 (`C2Yaw`) | PromotableOperator_28.A 0 | CircleCamera |
|
|||
|
|
| L5 | CustomEvent_1.DeltaSeconds 2 | PromotableOperator_28.B 1 | CircleCamera |
|
|||
|
|
| L6 | PromotableOperator_28.0 | VariableSet_67.C2Yaw 1 | CircleCamera |
|
|||
|
|
| L7 | IfThenElse_96.then 0 | VariableSet_67.execute 0 | CircleCamera |
|
|||
|
|
| L8 | CustomEvent_4.then 1 | IfThenElse_97.execute 0 | LeftRight |
|
|||
|
|
| L9 | IfThenElse_97.else 1 | IfThenElse_9.execute 0 | LeftRight |
|
|||
|
|
| L10 | PromotableOperator_8.0 | IfThenElse_97.Condition 1 | LeftRight |
|
|||
|
|
| L11 | CustomEvent_4.AxisValue 2 | PromotableOperator_29.A 0 | LeftRight |
|
|||
|
|
| L12 | VariableGet_293.0 (`C2Yaw`) | PromotableOperator_30.A 0 | LeftRight |
|
|||
|
|
| L13 | PromotableOperator_29.0 | PromotableOperator_30.B 1 | LeftRight |
|
|||
|
|
| L14 | PromotableOperator_30.0 | VariableSet_68.C2Yaw 1 | LeftRight |
|
|||
|
|
| L15 | IfThenElse_97.then 0 | VariableSet_68.execute 0 | LeftRight |
|
|||
|
|
|
|||
|
|
`VariableSet_67.then` et `VariableSet_68.then` : laissés libres (fin de l'événement côté C2, comme le prescrit la fiche).
|
|||
|
|
|
|||
|
|
### 1.5 Valeurs (`set_pin_value`, 1, `{"returnValue":null}`, relue)
|
|||
|
|
|
|||
|
|
| Broche | Avant (relu à la création) | Après (relu) |
|
|||
|
|
|---|---|---|
|
|||
|
|
| PromotableOperator_29.B 1 | (neuf, `""`, joker) | `2.0` (`Float (double-précision)`) |
|
|||
|
|
|
|||
|
|
Le `set_pin_value` a été posé juste après L11, la liaison de A qui type l'opérateur. Les conditions des Branches 96 et 97
|
|||
|
|
gardent la valeur par défaut `true`, sans effet puisque la broche est liée (L3, L10).
|
|||
|
|
|
|||
|
|
### 1.6 Relecture et chaîne obtenue
|
|||
|
|
|
|||
|
|
`get_node_infos` final groupé sur 15 nœuds : les 9 créés, plus CE1, CE4, IfThenElse_26, IfThenElse_9, PO8 et
|
|||
|
|
`CallFunction_439` (amont de PO8, lu pour la garde). Les 15 liens apparaissent des deux côtés ; la valeur 2.0 est relue.
|
|||
|
|
Liens non touchés, relus intacts :
|
|||
|
|
- CE1.DeltaSeconds → `CommutativeAssociativeBinaryOperator_26`, `_16`, `_27` (plus PO28) ;
|
|||
|
|
- CE4.AxisValue → `CommutativeAssociativeBinaryOperator_47`, `_13`, `_24` (plus PO29) ;
|
|||
|
|
- IfThenElse_26 : Condition ← `CommutativeAssociativeBinaryOperator_19`, then → `IfThenElse_19`, else libre ;
|
|||
|
|
- IfThenElse_9 : Condition ← `CommutativeAssociativeBinaryOperator_17`, then → `IfThenElse_14`, else libre ;
|
|||
|
|
- PO8 : A ← 439, B ← `VariableGet_268`, sortie → `IfThenElse_91`.1 (plus 96.1 et 97.1) ;
|
|||
|
|
- `CallFunction_439` (`Pion|GetViewTarget`) : self ← `VariableGet_262` (`Controller`), sortie → `PromotableOperator_0`.0
|
|||
|
|
et PO8.0.
|
|||
|
|
|
|||
|
|
Aucune divergence.
|
|||
|
|
|
|||
|
|
Chaîne obtenue :
|
|||
|
|
- `CircleCamera` (CE1) → Branche 96 (`GetViewTarget(PC) == CamRoom`). Si vrai : `Définir C2Yaw` (67) := `C2Yaw +
|
|||
|
|
DeltaSeconds`, puis fin. Si faux : `IfThenElse_26`, chaîne d'origine inchangée.
|
|||
|
|
- `LeftRight` (CE4) → Branche 97 (même condition). Si vrai : `Définir C2Yaw` (68) := `C2Yaw + AxisValue × 2`, puis
|
|||
|
|
fin. Si faux : `IfThenElse_9`, chaîne d'origine inchangée.
|
|||
|
|
|
|||
|
|
### 1.7 Compilation
|
|||
|
|
|
|||
|
|
`compile_blueprint {blueprint:{refPath:"/Game/BPs/Room.Room"}, warnings_as_errors:true}` → `{"returnValue":null}`,
|
|||
|
|
aucune exception (critère de la rubrique « Fin de chaque fiche »). `hyper_blueprint_debug_inspect` n'a pas été appelé :
|
|||
|
|
il était absent du serveur le 2026-10-10 (mémoire `unreal-mcp-reconnect`, non revérifié). Le journal de l'éditeur n'a
|
|||
|
|
pas été lu : son chemin `-abslog` n'a pas été fourni. Pas de `save_assets` (réservé à F7).
|
|||
|
|
|
|||
|
|
## 2. Écarts à la SPEC
|
|||
|
|
|
|||
|
|
- Aucun écart de câblage : la ligne B2b-6 (`CAM-SPEC.md:380`), pour `CircleCamera` et `LeftRight`, et la fiche F6
|
|||
|
|
(`CAM-B2b-FICHES.md:127-131`) sont réalisées telles quelles. Seuls les liens CE.then → branche d'origine ont été coupés.
|
|||
|
|
- Fait différent de la fiche, sans effet : `DeltaSeconds` et `AxisValue` sont en double précision, pas en simple. Il n'y
|
|||
|
|
a donc aucune conversion implicite.
|
|||
|
|
- Deux `Obtenir C2Yaw` distincts (292, 293), un par événement, comme le prescrit la fiche (« (N) Obtenir C2Yaw ») ;
|
|||
|
|
PO8 est partagé par trois Branches (91, 96, 97).
|
|||
|
|
- Garde `Accessed None` : aucune `IsValid` ajoutée, car ni la fiche ni la SPEC n'en demandent. Les nœuds neufs ne lisent
|
|||
|
|
que des flottants. En revanche, PO8 (→ 439 `GetViewTarget`, self ← `VariableGet_262` ; → `VariableGet_268`
|
|||
|
|
`CamRoom`) est **désormais évalué à chaque appel** de `CircleCamera` et de `LeftRight`, ce qui n'était pas le cas
|
|||
|
|
avant. Si la cible de 262 (le contrôleur) est nulle à ce moment, un `Accessed None` apparaît là où il n'y en avait
|
|||
|
|
pas. Voir le § 5, points 3 et 4.
|
|||
|
|
- Comportement : identique à l'ancien quand C2 n'est pas la vue (sortie `else` vers la branche d'origine). Quand C2 est
|
|||
|
|
la vue, la chaîne d'origine **n'est plus exécutée** : c'est le changement voulu par la SPEC (« vrai → réglage ; faux
|
|||
|
|
→ branche existante »).
|
|||
|
|
- Positions des nœuds : choisies près des événements (cosmétique, non prescrit). Elles peuvent chevaucher visuellement
|
|||
|
|
des nœuds existants proches, sans effet sur le graphe.
|
|||
|
|
|
|||
|
|
## 3. Instantané
|
|||
|
|
|
|||
|
|
Fait par l'orchestrateur avant F1 (non refait, consigne). Manifeste relu et disque mesuré après F6a :
|
|||
|
|
```
|
|||
|
|
date -Iseconds; md5sum "/d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset"; grep -i "Room.uasset" "/d/Game/LifePlay_Backup_5.5/CAM-B2b/manifest.csv"
|
|||
|
|
2026-10-13T16:01:23+02:00
|
|||
|
|
a5fa970e821dc2d0d9bf220fd43b9de6 */d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset
|
|||
|
|
"Content\BPs\Room.uasset","True","6060745","A5FA970E821DC2D0D9BF220FD43B9DE6","2026-10-13T15:25:45"
|
|||
|
|
```
|
|||
|
|
Le MD5 du disque est celui de l'instantané : rien n'a été enregistré. Retour arrière avant F7 : fermer l'éditeur sans
|
|||
|
|
enregistrer, ce qui perd F1 à F6a. Après F7 : `Tools\Restore.ps1 -Lot CAM-B2b -Only Content\BPs\Room.uasset`, éditeur
|
|||
|
|
fermé, sur décision de l'orchestrateur (jamais lancé ici).
|
|||
|
|
|
|||
|
|
## 4. Ce qui n'a pas été fait
|
|||
|
|
|
|||
|
|
- `ScrollDownUp` (CE55 → IfThenElse_10, `Clamp` de `C2Dist`) : renvoyé à F6b par la fiche.
|
|||
|
|
- F7 (`RotateCamera`, `DownUp`, relecture finale, enregistrement) : hors périmètre.
|
|||
|
|
- `hyper_blueprint_debug_inspect`, journal de l'éditeur : non consultés (§ 1.7).
|
|||
|
|
- `save_assets` : interdit avant F7. F1 à F6a vivent en mémoire de l'éditeur PID 10708 : une fermeture ou un plantage
|
|||
|
|
avant F7 les perd, l'instantané restant intact.
|
|||
|
|
- Comportement à l'exécution : non mesuré (aucune PIE, aucun build) ; seulement déduit du câblage.
|
|||
|
|
- Preuve en jeu (build empaqueté + `LogStats`) : hors rôle.
|
|||
|
|
|
|||
|
|
## 5. Points à confirmer en VAL
|
|||
|
|
|
|||
|
|
1. Vue autre que C2 : `CircleCamera` et `LeftRight` prennent `else` → `IfThenElse_26` / `IfThenElse_9`, chaînes
|
|||
|
|
d'origine inchangées. Établi par le câblage ; à confirmer en jeu (pilotage de l'ancienne caméra identique).
|
|||
|
|
2. Vue C2 : `CircleCamera` ajoute `DeltaSeconds` à `C2Yaw`, et `LeftRight` ajoute `AxisValue × 2`. L'effet sur C2 passe
|
|||
|
|
par `Bdir` (F3, 497) au prochain `ComputeC2Target`. Signes et gains à juger à l'essai (SPEC : « ajustables à
|
|||
|
|
l'essai »).
|
|||
|
|
3. `LogStats` : aucun `Accessed None` nouveau sur `CallFunction_439` / `VariableGet_262`. PO8 est maintenant évalué à
|
|||
|
|
chaque `LeftRight`, événement d'axe probablement appelé à chaque image (estimé, non lu), et à chaque `CircleCamera`.
|
|||
|
|
4. Cas limite (estimé) : si `GetViewTarget` rend `None` et que `CamRoom` est aussi `None` (avant sa création), PO8 vaut
|
|||
|
|
`true` (None == None). Les deux événements écrivent alors `C2Yaw` au lieu de piloter l'ancienne caméra. À vérifier :
|
|||
|
|
`CamRoom` existe-t-elle toujours avant le premier appel possible de ces événements ?
|
|||
|
|
5. `C2Yaw` n'est pas borné (la SPEC ne le borne pas) : il croît sans fin à force de tourner. `RotateVectorAroundAxis`
|
|||
|
|
accepte tout angle ; la précision double suffit (estimé).
|
|||
|
|
6. V5 (`CAM-SPEC.md:382`) pour ces deux événements : `then` → nouvelle branche (96, 97) ; `else` → branche d'origine
|
|||
|
|
(26, 9).
|
|||
|
|
7. Valeur sérialisée après `save_assets` (F7), par dump (`LPDumpFunction`) : `PO29.B = 2.0`.
|
|||
|
|
8. Compilation stricte jugée sur l'absence d'exception seulement ; à recontrôler en F7 et par `CompileAllBlueprints`.
|
|||
|
|
|
|||
|
|
## 6. Fichiers à commiter
|
|||
|
|
|
|||
|
|
- `A Docs/Migration/etat/CAM/CAM-B2b-IMPL-F6a.md` (dépôt du jeu, branche `ue5.8`).
|
|||
|
|
- `Content/BPs/Room.uasset` : rien à commiter pour F6a (non enregistré) ; il le sera après F7 et la VAL.
|
|||
|
|
|
|||
|
|
Tours : 38 / 55 (ce Write et la remise du compte rendu compris ; 31 appels MCP, un par message)
|
|||
|
|
|
|||
|
|
## Constats
|
|||
|
|
|
|||
|
|
- `DeltaSeconds` (CE1) et `AxisValue` (CE4) sont en `Float (double-précision)` : relier A d'un `Ajouter` ou d'un
|
|||
|
|
`Multiplier` à ces broches le type directement en `float+float` / `float*float`, sans conversion.
|
|||
|
|
- `Variables|Pardéfaut|ObtenirC2Yaw` → `|GetC2Yaw` (sortie 0) ; `DéfinirC2Yaw` → `|SetC2Yaw` (execute 0, C2Yaw 1 ;
|
|||
|
|
then 0, Output_Get 1).
|
|||
|
|
- `CallFunction_439` = `Pion|GetViewTarget`, self ← `VariableGet_262` (`Controller`), sortie aussi vers
|
|||
|
|
`PromotableOperator_0`.
|
|||
|
|
- Pour F6b et F7, le même schéma coûte 2 + 4 à 5 créations + 1 lecture + 1 coupure + 7 à 9 liaisons + 0 à 3 valeurs par
|
|||
|
|
événement. Ordre économe : créer tous les nœuds, faire une lecture groupée, puis couper chaque lien et le remplacer
|
|||
|
|
aussitôt.
|