17 KiB
Fiche CAM-B2b-F6b — 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-F6b-IMPL : pilotage de C2, ScrollDownUp et RotateCamera (B2b-6, partie 2) (2026-10-13)
Consignes : CAM-B2b-FICHES.md § « Rubriques communes » (l. 23-63), § « F6 » (l. 127-132, ScrollDownUp) et § « F7 »
(l. 134-136, RotateCamera seulement). DownUp et la fin de F7 passent à la fiche suivante. SPEC : CAM-SPEC.md § « CAM-B2b »,
ligne B2b-6 (l. 380). Modèle suivi : CAM-B2b-IMPL-F6a.md et son ordre économe : créer tous les nœuds, faire une lecture
groupée, puis couper chaque lien et le remplacer aussitôt.
Contexte : 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 à F6a en mémoire, non enregistrés. Un seul call_tool par
message. 43 appels MCP : 3 get_node_infos, 1 find_node_types, 12 create_node, 2 break_pins,
18 connect_pins, 6 set_pin_value et 1 compile_blueprint. Aucun delete_node, save_assets, write_graph_dsl
ni ProgrammaticToolset. Aucune écriture par HyperAIStudio.
1. Fichiers modifiés
Content/BPs/Room.uassetest modifié en mémoire de l'éditeur seulement, sans enregistrement (pas desave_assetsavant F7). Le § 3 le prouve : le MD5 du disque après F6b est celui de l'instantané.- Graphe
/Game/BPs/Room.Room:EventGraph:- événement
ScrollDownUp(CE55, x 4896, y 2544) : nœuds neufs en x 5100-5950, y 2150-2350 ; - événement
RotateCamera(CE19, x 5184, y 5232) : nœuds neufs en x 5400-6250, y 5550-5800.
- événement
1.1 Lecture avant écriture (get_node_infos groupé, 5 nœuds existants)
La lecture est conforme aux faits de la fiche :
K2Node_CustomEvent_55(AddEvent|Custom|ScrollDownUp) :thenout 1 →IfThenElse_10.execute 0 ;Valuesout 2 (Float (double-précision)) →CommutativeAssociativeBinaryOperator_2.0,CallFunction_26.2,CommutativeAssociativeBinaryOperator_20.0,_10.0 et_22.0.
K2Node_CustomEvent_19(AddEvent|Custom|RotateCamera) :thenout 1 →IfThenElse_25.execute 0 ;DeltaSecondsout 2 (Float (double-précision)) →CommutativeAssociativeBinaryOperator_45.1,_12.1 et_15.1.
K2Node_IfThenElse_10: execute ← CE55.1 ; Condition ←CommutativeAssociativeBinaryOperator_17.0 ; then 0 →IfThenElse_12.0 ; else 1 libre.K2Node_IfThenElse_25: execute ← CE19.1 ; Condition ←CommutativeAssociativeBinaryOperator_19.0 ; then 0 →IfThenElse_18.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,_96.1 et_97.1.
Recherche du Clamp : find_node_types {type_id_filter:"Clamp"} sur le graphe. Le résultat flottant est
Math|Flottant|Clamp(Float), à côté de Math|Integer|Clamp(Integer), Math|Integer64|Clamp(Integer64), Math|Flottant| ClampAngle, MapRangeClamped et d'autres.
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 |
|---|---|---|---|---|---|
| ScrollDownUp | Branche C2 | K2Node_IfThenElse_98 |
Utilitaires|ContrôleDeFlux|Branche → idem |
execute 0, Condition 1 ; then 0, else 1 | 5100, 2350 |
| ScrollDownUp | Obtenir C2Dist | K2Node_VariableGet_294 |
Variables|Pardéfaut|ObtenirC2Dist → |GetC2Dist |
C2Dist out 0 (double) | 5100, 2150 |
| ScrollDownUp | Values × 15 | K2Node_PromotableOperator_31 |
Utilitaires|Opérateurs|Multiplier → Math|Flottant|float*float |
A 0, B 1 ; out 0 (double) | 5300, 2250 |
| ScrollDownUp | C2Dist − produit | K2Node_PromotableOperator_32 |
Utilitaires|Opérateurs|Soustraire → Math|Flottant|float-float |
A 0, B 1 ; out 0 (double) | 5500, 2150 |
| ScrollDownUp | Clamp | K2Node_CallFunction_509 |
Math|Flottant|Clamp(Float) → idem |
Value 0, Min 1, Max 2 ; ReturnValue out 0 (double) | 5700, 2200 |
| ScrollDownUp | Définir C2Dist | K2Node_VariableSet_69 |
Variables|Pardéfaut|DéfinirC2Dist → |SetC2Dist |
execute 0, C2Dist 1 ; then 0, Output_Get 1 | 5950, 2300 |
| RotateCamera | Branche C2 | K2Node_IfThenElse_99 |
Utilitaires|ContrôleDeFlux|Branche → idem |
execute 0, Condition 1 ; then 0, else 1 | 5450, 5800 |
| RotateCamera | Obtenir C2Height | K2Node_VariableGet_295 |
Variables|Pardéfaut|ObtenirC2Height → |GetC2Height |
C2Height out 0 (double) | 5400, 5550 |
| RotateCamera | DeltaSeconds × 3 | K2Node_PromotableOperator_33 |
Utilitaires|Opérateurs|Multiplier → Math|Flottant|float*float |
A 0, B 1 ; out 0 (double) | 5600, 5650 |
| RotateCamera | C2Height + produit | K2Node_PromotableOperator_34 |
Utilitaires|Opérateurs|Ajouter → Math|Flottant|float+float |
A 0, B 1 ; out 0 (double) | 5800, 5550 |
| RotateCamera | Clamp | K2Node_CallFunction_510 |
Math|Flottant|Clamp(Float) → idem |
Value 0, Min 1, Max 2 ; ReturnValue out 0 (double) | 6000, 5600 |
| RotateCamera | Définir C2Height | K2Node_VariableSet_70 |
Variables|Pardéfaut|DéfinirC2Height → |SetC2Height |
execute 0, C2Height 1 ; then 0, Output_Get 1 | 6250, 5700 |
À la création, les opérateurs portent le type_id trompeur déjà vu en F3 à F6a, avec des broches Caractère de remplacement :
Utilitaires|Gestiondutemps|Seconds*FrameRatepour PO31 et PO33 ;…|FrameNumber-Intpour PO32 ;…|FrameNumber+Intpour PO34.
À la relecture finale, ils sont typés float*float, float-float et float+float, en double précision. À la création,
le Clamp a pour défauts Min 0.0 et Max 1.0, remplacés ensuite (§ 1.5). 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_55.then 1 | IfThenElse_10.execute 0 |
| C2 | CustomEvent_19.then 1 | IfThenElse_25.execute 0 |
Aucun autre lien n'a été coupé, et rien d'autre n'a été touché dans ces deux événements.
1.4 Liens faits (connect_pins, 18, tous {"returnValue":null})
| # | Sortie | Entrée | Événement |
|---|---|---|---|
| L1 | CustomEvent_55.then 1 | IfThenElse_98.execute 0 | ScrollDownUp |
| L2 | IfThenElse_98.else 1 | IfThenElse_10.execute 0 | ScrollDownUp |
| L3 | PromotableOperator_8.0 | IfThenElse_98.Condition 1 | ScrollDownUp |
| L4 | CustomEvent_55.Values 2 | PromotableOperator_31.A 0 | ScrollDownUp |
| L5 | VariableGet_294.0 (C2Dist) |
PromotableOperator_32.A 0 | ScrollDownUp |
| L6 | PromotableOperator_31.0 | PromotableOperator_32.B 1 | ScrollDownUp |
| L7 | PromotableOperator_32.0 | CallFunction_509.Value 0 | ScrollDownUp |
| L8 | CallFunction_509.0 | VariableSet_69.C2Dist 1 | ScrollDownUp |
| L9 | IfThenElse_98.then 0 | VariableSet_69.execute 0 | ScrollDownUp |
| L10 | CustomEvent_19.then 1 | IfThenElse_99.execute 0 | RotateCamera |
| L11 | IfThenElse_99.else 1 | IfThenElse_25.execute 0 | RotateCamera |
| L12 | PromotableOperator_8.0 | IfThenElse_99.Condition 1 | RotateCamera |
| L13 | CustomEvent_19.DeltaSeconds 2 | PromotableOperator_33.A 0 | RotateCamera |
| L14 | VariableGet_295.0 (C2Height) |
PromotableOperator_34.A 0 | RotateCamera |
| L15 | PromotableOperator_33.0 | PromotableOperator_34.B 1 | RotateCamera |
| L16 | PromotableOperator_34.0 | CallFunction_510.Value 0 | RotateCamera |
| L17 | CallFunction_510.0 | VariableSet_70.C2Height 1 | RotateCamera |
| L18 | IfThenElse_99.then 0 | VariableSet_70.execute 0 | RotateCamera |
Les sorties VariableSet_69.then et VariableSet_70.then restent libres : elles marquent la fin de l'événement côté C2,
comme le schéma de la fiche (identique à F6a).
1.5 Valeurs (set_pin_value, 6, tous {"returnValue":null}, relues)
| Broche | Avant (relu à la création) | Après (relu) |
|---|---|---|
| PromotableOperator_31.B 1 | (neuf, "", joker) |
15.0 (Float (double-précision)) |
| CallFunction_509.Min 1 | 0.0 |
-75.0 |
| CallFunction_509.Max 2 | 1.0 |
125.0 |
| PromotableOperator_33.B 1 | (neuf, "", joker) |
3.0 (Float (double-précision)) |
| CallFunction_510.Min 1 | 0.0 |
-40.0 |
| CallFunction_510.Max 2 | 1.0 |
150.0 |
Chaque set_pin_value sur un opérateur a été posé juste après la liaison de A, qui type l'opérateur (L4, L13). Les
conditions des Branches 98 et 99 gardent leur valeur par défaut true, sans effet puisque la broche est liée (L3, L12).
1.6 Relecture et chaîne obtenue
get_node_infos final groupé sur 17 nœuds : les 12 créés, plus CE55, CE19, IfThenElse_10, IfThenElse_25 et PO8. Les
18 liens apparaissent des deux côtés et les 6 valeurs sont relues. Liens non touchés, relus intacts :
- CE55.Values →
CommutativeAssociativeBinaryOperator_2.0,CallFunction_26.2,_20.0,_10.0,_22.0 (plus PO31.0) ; - CE19.DeltaSeconds →
CommutativeAssociativeBinaryOperator_45.1,_12.1,_15.1 (plus PO33.0) ; - IfThenElse_10 : Condition ←
CommutativeAssociativeBinaryOperator_17, then →IfThenElse_12, else libre ; - IfThenElse_25 : Condition ←
CommutativeAssociativeBinaryOperator_19, then →IfThenElse_18, else libre ; - PO8 : A ← 439, B ←
VariableGet_268, sortie →IfThenElse_91,_96,_97(plus 98.1 et 99.1).
Aucune divergence.
Chaîne obtenue :
ScrollDownUp(CE55) → Branche 98 (GetViewTarget(PC) == CamRoom).- Vrai :
Définir C2Dist(69) :=Clamp(C2Dist − Values × 15, −75, 125), puis fin. - Faux :
IfThenElse_10, chaîne d'origine inchangée.
- Vrai :
RotateCamera(CE19) → Branche 99 (même condition).- Vrai :
Définir C2Height(70) :=Clamp(C2Height + DeltaSeconds × 3, −40, 150), puis fin. - Faux :
IfThenElse_25, chaîne d'origine inchangée.
- Vrai :
1.7 Compilation
compile_blueprint {blueprint:{refPath:"/Game/BPs/Room.Room"}, warnings_as_errors:true} a renvoyé
{"returnValue":null}, sans exception : c'est le critère de la rubrique « Fin de chaque fiche ».
hyper_blueprint_debug_inspect n'a pas été appelé, car 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, faute du chemin -abslog. Pas de
save_assets : il est réservé à F7.
2. Écarts à la SPEC
- Aucun écart de câblage. La ligne B2b-6 (
CAM-SPEC.md:380) et la fiche (CAM-B2b-FICHES.md:132et:136) sont réalisées telles quelles pourScrollDownUpetRotateCamera. Seuls les liens CE.then → branche d'origine ont été coupés. - Formules :
C2Dist := Clamp(C2Dist − Values × 15, −75, 125)etC2Height := Clamp(C2Height + D × 3, −40, 150). Les opérandes ne sont pas inversés : A de PO32 = C2Dist, B = produit, relu. - Typage :
ValuesetDeltaSecondssont en double précision, commeClamp(Float)(Value, Min, Max et sortie en double) : aucune conversion implicite. - Nœuds par événement : un
Obteniret unClampdistincts dans chacun, comme le prescrit la fiche (« (N) »). PO8 est désormais partagé par cinq Branches (91, 96, 97, 98, 99). - Garde
Accessed None: aucuneIsValidajoutée, car ni la fiche ni la SPEC n'en demandent, et les nœuds neufs ne lisent que des flottants. En revanche, PO8 (→ 439GetViewTarget, self ←VariableGet_262; →VariableGet_268CamRoom) est désormais évalué à chaque appel deScrollDownUpet deRotateCamera, ce qui n'était pas le cas avant. Même réserve qu'en F6a (§ 5, points 3 et 4). - Comportement : identique à l'ancien quand C2 n'est pas la vue (sortie
else→ 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 proches : nœuds
LeftRightde F6a (x 5250-5850, y 2750-2950),IfThenElse_25(5888, 5328). Sans effet sur le graphe.
3. Instantané
Fait par l'orchestrateur avant F1 (non refait, consigne). Manifeste relu et disque mesuré après F6b :
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:06:13+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 à F6b ;
- 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
DownUp(CE8 → IfThenElse_8,Clamp(C2Height + AxisValue × 3, −40, 150)) : renvoyé à la fiche suivante (F7).- Fin de F7 : relecture de CE1, CE4, CE8, CE19, CE55, 459, 467, 474, 61 et 62, compilation stricte, puis
save_assetsde/Game/BPs/Roomseul. Hors périmètre. hyper_blueprint_debug_inspectet journal de l'éditeur : non consultés (§ 1.7).save_assets: interdit avant F7. F1 à F6b 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
- Vue autre que C2 :
ScrollDownUpetRotateCameraprennentelse→IfThenElse_10/IfThenElse_25, avec leurs chaînes d'origine inchangées. Établi par le câblage ; à confirmer en jeu (zoom et tangage de l'ancienne caméra identiques). - Vue C2 :
ScrollDownUpretireValues × 15àC2Dist, borné à [−75, 125] (distance 100 à 300 cm selon la SPEC) ;RotateCameraajouteDeltaSeconds × 3àC2Height, borné à [−40, 150]. L'effet passe parComputeC2Target(F3) au prochain appel. Signes et gains sont à juger à l'essai (SPEC : « ajustables à l'essai »).
LogStats: aucunAccessed Nonenouveau surCallFunction_439/VariableGet_262. PO8 est maintenant évalué à chaqueScrollDownUpet à chaqueRotateCamera; ce dernier est probablement appelé à chaque image pendant la rotation (estimé, appelant non lu).- Cas limite (estimé, déjà noté en F6a) : si
GetViewTargetrendNoneet queCamRoomest aussiNone, PO8 vauttrue(None == None), et ces événements écrivent alorsC2Dist/C2Heightau lieu de piloter l'ancienne caméra. RotateCamera.DeltaSecondsest un delta de tangage selon la fiche, pas un temps (nom hérité). Si l'appelant passe une valeur par image, le gain ×3 dépend de la fréquence d'image. Estimé, à juger à l'essai.- V5 (
CAM-SPEC.md:382) pour ces deux événements :then→ nouvelle branche (98, 99) ;else→ branche d'origine (10, 25). - Valeurs sérialisées après
save_assets(F7), à relire par dump (LPDumpFunction) : PO31.B = 15.0 ; 509 Min −75.0, Max 125.0 ; PO33.B = 3.0 ; 510 Min −40.0, Max 150.0. - Compilation stricte jugée sur la seule absence d'exception ; à recontrôler en F7 et par
CompileAllBlueprints.
6. Fichiers à commiter
A Docs/Migration/etat/CAM/CAM-B2b-IMPL-F6b.md(dépôt du jeu, brancheue5.8).Content/BPs/Room.uasset: rien à commiter pour F6b (non enregistré) ; il le sera après F7 et la VAL.
Tours : 48 / 60 (ce Write et la remise du compte rendu compris ; 43 appels MCP, un par message)
Constats
Math|Flottant|Clamp(Float)crée unK2Node_CallFunction. Broches : Value 0 (défaut 0.0), Min 1 (défaut 0.0), Max 2 (défaut 1.0), ReturnValue out 0, toutes enFloat (double-précision)dès la création (pas de joker).- Correspondances relevées :
Variables|Pardéfaut|ObtenirC2Dist→|GetC2Dist;ObtenirC2Height→|GetC2Height;DéfinirC2Dist→|SetC2Dist;DéfinirC2Height→|SetC2Height; broches commeSetC2Yaw(execute 0, valeur 1 ; then 0, Output_Get 1).
Utilitaires|Opérateurs|Soustrairenaît enUtilitaires|Gestiondutemps|FrameNumber-Int. Lier A à un double le type enMath|Flottant|float-float(A − B, A index 0).DownUp(F7), dans le même schéma : 6 créations, 1 coupure, 9 liaisons, 3 valeurs, soit 19 appels plus 1 lecture groupée. Le Clamp se crée directement parMath|Flottant|Clamp(Float), sansfind_node_types.- À verser : lp-mcp § 3 —
type_idMath|Flottant|Clamp(Float)et index Value 0 / Min 1 / Max 2 / sortie 0 ;Soustrairenaît enFrameNumber-Intpuis devientfloat-floatà la liaison de A.