143 lines
11 KiB
Markdown
143 lines
11 KiB
Markdown
|
|
# CAM-B2b : fiches lp-mcp-bp (2026-10-13)
|
|||
|
|
|
|||
|
|
GO d'écriture de Big Boss du 2026-10-13 : option 1, écritures par `lp-mcp-bp`, un appel MCP à la fois. SPEC :
|
|||
|
|
`CAM-SPEC.md` § « CAM-B2b » (amendement B2b, MD5 `0f801b71`). Room seul.
|
|||
|
|
|
|||
|
|
## Découpage et barème
|
|||
|
|
Budget : 75 tours par agent ; ≈ 3 appels par nœud. B2b compte ≈ 70 nœuds, soit ≈ 200 appels. Il est découpé en
|
|||
|
|
7 fiches d'environ 50 appels, exécutées l'une après l'autre dans le même éditeur. Chaque fiche :
|
|||
|
|
- grille a1 b1 c1 d1 e1 f1 = 6 → N2, ajustée à N3 (agent `lp-mcp-bp`, écriture MCP sous instantané) ;
|
|||
|
|
- S = C3 1 (`lp-mcp` § 3) + f 1 = 2 < 3 : pas de scission supplémentaire.
|
|||
|
|
|
|||
|
|
| Fiche | Contenu (SPEC B2b) | Appels estimés |
|
|||
|
|
|---|---|---|
|
|||
|
|
| F1 | B2b-0 (5 variables) + `ComputeRoomBox`, partie XY | ≈ 55 |
|
|||
|
|
| F2 | `ComputeRoomBox`, partie Z, extérieur et affectations | ≈ 45 |
|
|||
|
|
| F3 | B2b-2 (appel de la boîte, borne de `Desired`, rayon 20) + B2b-5 (lacet, distance, hauteur) | ≈ 50 |
|
|||
|
|
| F4 | B2b-4 (trajectoire protégée de la poursuite) | ≈ 40 |
|
|||
|
|
| F5 | B2b-3 (repli vers le haut) | ≈ 55 |
|
|||
|
|
| F6 | B2b-6 : `CircleCamera`, `LeftRight`, `ScrollDownUp` | ≈ 50 |
|
|||
|
|
| F7 | B2b-6 : `RotateCamera`, `DownUp` ; compilation stricte finale ; `save_assets` | ≈ 40 |
|
|||
|
|
Après F7, l'orchestrateur ferme l'éditeur, puis lance V5 et V6.
|
|||
|
|
|
|||
|
|
## Rubriques communes à toutes les fiches
|
|||
|
|
- **Préalables** : BUILD-GO de Big Boss (2026-10-13, conditions 1 à 6) ; instantané `Backup_5.5\CAM-B2b\` fait par
|
|||
|
|
l'orchestrateur (Room `A5FA970E`) ; éditeur headless **déjà ouvert** (port 8000) ; la session qui te lance est
|
|||
|
|
connectée à `unreal-mcp`. Charge `select:mcp__unreal-mcp__call_tool` par ToolSearch. Outil absent : `BLOCKED-MCP`.
|
|||
|
|
- **Règle absolue** : un seul appel `mcp__unreal-mcp__call_tool` par message, lectures comprises. Jamais deux.
|
|||
|
|
- **Graphe** : `{"refPath":"/Game/BPs/Room.Room:EventGraph"}` ; nœud : `/Game/BPs/Room.Room:EventGraph.K2Node_<Nom>` ;
|
|||
|
|
broche : `{"direction":"EGPD_Input|EGPD_Output","index_id":n,"node":{"refPath":…}}`.
|
|||
|
|
- **Outils** (toolset `editor_toolset.toolsets.blueprint.BlueprintTools`) : `create_node {graph, type_id, pos}`,
|
|||
|
|
`connect_pins {output_pin, input_pin}`, `break_pins`, `set_pin_value {pin, value}` (vecteur : "x,y,z"),
|
|||
|
|
`get_node_infos {nodes:[{refPath}]}`, `find_node_types {graph, type_id_filter, context_pins:[]}`,
|
|||
|
|
`add_variable {blueprint:{refPath:"/Game/BPs/Room.Room"}, name, type_name}`,
|
|||
|
|
`add_event {blueprint, event_name, position}`, `compile_blueprint {blueprint, warnings_as_errors}`.
|
|||
|
|
Toolset `editor_toolset.toolsets.asset.AssetTools` : `save_assets {asset_paths:["/Game/BPs/Room"]}`, F7 seulement.
|
|||
|
|
- **Identifiants connus** (éditeur en français) :
|
|||
|
|
- `Utilitaires|ContrôleDeFlux|Branche` (exécution 0, Condition 1 ; then 0, else 1) ;
|
|||
|
|
- `Utilitaires|Opérateurs|Ajouter` / `Soustraire` / `Multiplier` / `Inférieurà(<)` (typés à la 1re liaison) ;
|
|||
|
|
- `Math|Vecteur|BreakVector` (X 0, Y 1, Z 2) / `MakeVector` ;
|
|||
|
|
- `Math|Vecteur|VectorBoundedtoBox` / `SelectVector` / `RotateVectorAroundAxis` / `VectorGetAbs` / `Distance(Vector)` ;
|
|||
|
|
- `Transformation|GetWorldLocation` / `GetActorLocation` / `GetWorldScale` ;
|
|||
|
|
- `Collision|SphereTraceByChannel` (Start 1, End 2, Radius 3, TraceChannel 4, ActorsToIgnore 6 ; then 0, OutHit 1,
|
|||
|
|
ReturnValue 2) ; `Collision|BreakHitResult` (Location 4, Normal 6) ;
|
|||
|
|
- `Variables|Pardéfaut|Obtenir<Var>` / `Définir<Var>` ; `Appelerunefonction|<Événement>`.
|
|||
|
|
Un identifiant inconnu se cherche par `find_node_types` avec un filtre court (ex. « Clamp », « SelectFloat », « Abs »).
|
|||
|
|
Après chaque `create_node`, relire les index de broches par `get_node_infos` avant de câbler.
|
|||
|
|
- **Nœuds existants** (relus en B2) : 455 `GetSocketLocation(Mesh,'head')` (out 0) ; 458 `MakeVector` = `Desired`
|
|||
|
|
(out 0 → 459.End et 62.in1) ; 459 `SphereTraceByChannel` ; 460 `BreakHitResult` ; PO3 `vector*vector` (A ← 454
|
|||
|
|
`Normalize2D`, B = (−175,−175,−175), out → PO4.B) ; PO5 `float+float` (A ← 457.Z, B 60, out → 458.Z) ; PO6 (Normal ×
|
|||
|
|
(12,12,12)) ; PO7 (Location + PO6, out → 61.in1) ; 61 / 62 = `Définir C2Target` ; MakeArray_5 = [MC, CamRoom] ;
|
|||
|
|
CE47 `ComputeC2Target` (then 1 → 459) ; 467 appel `ComputeC2Target` (then → 474) ; 468 `VInterpTo` (out → 474.in2 et
|
|||
|
|
473.in0) ; 473 `FindLookAtRotation` ; 474 `SetActorLocationAndRotation` ; 268 `Obtenir CamRoom` ; 261 `PlayerCharacter` ;
|
|||
|
|
PO8 = `GetViewTarget(GI.PC) == CamRoom` (bool, réutilisable).
|
|||
|
|
- **Interdits** : aucun autre asset ; ne touche à aucun nœud hors fiche ; ni `delete_node` ni `break_pins` hors des
|
|||
|
|
liens nommés ; pas de `write_graph_dsl` ; pas de `ProgrammaticToolset` pour écrire ; pas de `save_assets` avant F7 ;
|
|||
|
|
pas de git ; tu ne fermes pas l'éditeur.
|
|||
|
|
- **Arrêt** : refus de permission, erreur inattendue, écart à la SPEC ou index de broche différent de la fiche →
|
|||
|
|
arrêt et compte rendu (état de reprise : nœuds créés, liens faits), sans contourner.
|
|||
|
|
- **Fin de chaque fiche** : `compile_blueprint {warnings_as_errors:true}` sur Room. Une exception → arrêt.
|
|||
|
|
Puis `get_node_infos` des nœuds créés.
|
|||
|
|
- **Livrable** : ajoute ta section à `Docs\Migration\etat\CAM\CAM-B2b-IMPL-F<n>.md` (un fichier par fiche, un seul
|
|||
|
|
Write) : nœuds créés (`refPath`, `type_id`), liens, valeurs, compilation, écarts, tours.
|
|||
|
|
- **Compte rendu** : court, en français, avec les noms des nœuds créés (utiles à la fiche suivante).
|
|||
|
|
|
|||
|
|
## F1 — variables et `ComputeRoomBox`, partie XY
|
|||
|
|
1. `add_variable` ×5 : `C2BoxMin` Vector ; `C2BoxMax` Vector ; `C2Yaw` float ; `C2Height` float ; `C2Dist` float.
|
|||
|
|
Le défaut de `C2Dist` (175) se règle en F3 par la valeur d'entrée des nœuds, pas par le CDO : voir F3.
|
|||
|
|
2. `add_event` `ComputeRoomBox` (position x 38000, y 0).
|
|||
|
|
3. Nœuds (positions x 38000-40000, y 0-800) :
|
|||
|
|
- getters `StaticMeshComponent`, `Wall2`, `Wall4` → `GetWorldLocation` ×3 (sol, W2, W4) ;
|
|||
|
|
- `GetActorLocation` (self vide = Room) → `R` ;
|
|||
|
|
- `Soustraire` vecteur `W2 − sol` → `BreakVector` → X ;
|
|||
|
|
- `Soustraire` vecteur `W4 − sol` → `BreakVector` → Y ;
|
|||
|
|
- `MakeVector(X, Y, 0)` → `VectorGetAbs` → `Soustraire` vecteur, B = (30,30,0) → `E` ;
|
|||
|
|
- `Min0` = `R − E` (Soustraire) ; `Max0` = `R + E` (Ajouter).
|
|||
|
|
4. Laisser `Min0` et `Max0` non affectés (F2 termine). Compilation stricte : `Min0` et `Max0` sans consommateur ne
|
|||
|
|
donnent aucun avertissement (nœuds purs).
|
|||
|
|
Compte rendu : noms des nœuds `sol` (`GetWorldLocation`), `Min0`, `Max0`, du `CustomEvent` de `ComputeRoomBox`.
|
|||
|
|
|
|||
|
|
## F2 — `ComputeRoomBox`, partie Z, extérieur et affectations
|
|||
|
|
1. `BreakVector(Min0)`, `BreakVector(Max0)`, `BreakVector(sol)` ; getter `WallT` → `GetWorldLocation` → `BreakVector`.
|
|||
|
|
2. `ZMin` = `sol.Z + 50` ; `ZTopIn` = `WallT.Z − 25`.
|
|||
|
|
3. Extérieur : `GetWorldScale(StaticMeshComponent)` → `BreakVector` → `Z < 0.05` (`Inférieurà(<)`) →
|
|||
|
|
`SelectFloat(A = 1000000, B = ZTopIn, bPickA = <résultat>)` → `ZTop`. Chercher `SelectFloat` par `find_node_types`.
|
|||
|
|
4. `MakeVector(Min0.X, Min0.Y, ZMin)` → `Définir C2BoxMin` ; `MakeVector(Max0.X, Max0.Y, ZTop)` → `Définir C2BoxMax`.
|
|||
|
|
5. Exécution : `ComputeRoomBox.then` → `Définir C2BoxMin` → `Définir C2BoxMax`.
|
|||
|
|
|
|||
|
|
## F3 — B2b-2 (appel de la boîte, borne de `Desired`) et B2b-5 (lacet, distance, hauteur)
|
|||
|
|
1. `break_pins` CE47.then (out 1) → 459.execute (in 0) ; (N) `Appelerunefonction|ComputeRoomBox` ; CE47.then → (N).
|
|||
|
|
Puis (N).then → 459.execute.
|
|||
|
|
2. (N) `Obtenir C2BoxMin`, `Obtenir C2BoxMax` ; (N) `VectorBoundedtoBox(InVect ← 458, Min, Max)` = `DesiredB`.
|
|||
|
|
- `break_pins` 458 → 459.End ; `DesiredB` → 459.End (in 2) ;
|
|||
|
|
- `break_pins` 458 → 62.in1 ; `DesiredB` → 62.in1.
|
|||
|
|
3. `set_pin_value` 459.Radius (in 3) = "20.0" ; PO6.B (in 1) = "20.0,20.0,20.0".
|
|||
|
|
4. (N) `VectorBoundedtoBox(PO7, Min, Max)` : `break_pins` PO7 → 61.in1, puis (N) → 61.in1.
|
|||
|
|
5. B2b-5 :
|
|||
|
|
- `break_pins` 454 → PO3.A ; (N) `Multiplier` vecteur (A ← 454, B = "-1,-1,-1") ;
|
|||
|
|
- (N) `RotateVectorAroundAxis(InVect ← ce produit, AngleDeg ← Obtenir C2Yaw, Axis = "0,0,1")` = `Bdir`
|
|||
|
|
→ PO3.A ;
|
|||
|
|
- PO3.B, aujourd'hui une constante (−175 ×3) : (N) `Obtenir C2Dist` → (N) `Ajouter` float (A ← C2Dist, B = 175)
|
|||
|
|
→ (N) `MakeVector(d, d, d)` → PO3.B. Comme le défaut de `C2Dist` vaut 0, la distance réelle est 175 + C2Dist ;
|
|||
|
|
les bornes deviennent C2Dist ∈ [−75, 125] (F6) ;
|
|||
|
|
- hauteur : `break_pins` PO5 → 458.Z (in 2) ; (N) `Ajouter` float (A ← PO5, B ← `Obtenir C2Height`) → 458.Z.
|
|||
|
|
6. Contrôle : à `C2Yaw` = 0, `C2Height` = 0, `C2Dist` = 0, `Desired` = la valeur de B2.
|
|||
|
|
|
|||
|
|
## F4 — B2b-4 (trajectoire protégée de la poursuite)
|
|||
|
|
1. `break_pins` 467.then (out 0) → 474.execute (in 0) ; (N) `SphereTraceByChannel` (Start ← 455, End ← 468,
|
|||
|
|
Radius 20, TraceChannel "TraceTypeQuery2", ActorsToIgnore ← MakeArray_5) ; 467.then → (N).exec ;
|
|||
|
|
(N).then → 474.exec.
|
|||
|
|
2. (N) `BreakHitResult` ← OutHit ; (N) `Multiplier` vecteur (Normal × "20,20,20") ; (N) `Ajouter` (Location +
|
|||
|
|
ce produit) ; (N) `SelectVector(A ← cette somme, B ← 468, bPickA ← trace.ReturnValue)` ;
|
|||
|
|
(N) `VectorBoundedtoBox(← SelectVector, C2BoxMin, C2BoxMax)` = `Safe`.
|
|||
|
|
3. `break_pins` 468 → 474.NewLocation (in 2) et 468 → 473.Start (in 0) ; `Safe` → 474.in2 et 473.in0.
|
|||
|
|
|
|||
|
|
## F5 — B2b-3 (repli vers le haut)
|
|||
|
|
1. Après 61 et 62, qui n'ont pas de `then` : (N) Branche ; 61.then → Branche.exec ; 62.then → Branche.exec.
|
|||
|
|
2. Condition : (N) `Obtenir C2Target` → (N) `Distance(Vector)` (← C2Target, ← 455) → (N) `Inférieurà(<)`, B = 80.
|
|||
|
|
3. Vrai :
|
|||
|
|
- `Desired2` = `VectorBoundedtoBox(455 + (0,0,100) + Bdir × (60,60,60), Min, Max)`, avec `Bdir` (nœud F3)
|
|||
|
|
multiplié par "60,60,60" ;
|
|||
|
|
- (N) `SphereTraceByChannel(Start ← 455, End ← Desired2, 20, TraceTypeQuery2, MakeArray_5)` ;
|
|||
|
|
- `BreakHitResult` ; `Location + Normal × (20,20,20)` ; `SelectVector(somme, Desired2, ReturnValue)` ;
|
|||
|
|
`VectorBoundedtoBox` ;
|
|||
|
|
- (N) `Définir C2Target`.
|
|||
|
|
Faux → fin.
|
|||
|
|
|
|||
|
|
## F6 — B2b-6 : `CircleCamera` (CE1 → IfThenElse_26), `LeftRight` (CE4 → IfThenElse_9), `ScrollDownUp` (CE55 → IfThenElse_10)
|
|||
|
|
Pour chacun : `break_pins` CE.then (out 1) → branche d'origine ; (N) Branche (Condition ← PO8) ; CE.then → (N) ;
|
|||
|
|
(N).else → branche d'origine ; (N).then → réglage :
|
|||
|
|
- `CircleCamera` : `Définir C2Yaw` ← `C2Yaw + DeltaSeconds` (paramètre de CE1, out 2) ;
|
|||
|
|
- `LeftRight` : `Définir C2Yaw` ← `C2Yaw + AxisValue × 2` ;
|
|||
|
|
- `ScrollDownUp` : `Définir C2Dist` ← `Clamp(C2Dist − Values × 15, −75, 125)` (nœud `Clamp` float : chercher « Clamp »).
|
|||
|
|
|
|||
|
|
## F7 — B2b-6 : `RotateCamera` (CE19 → IfThenElse_25), `DownUp` (CE8 → IfThenElse_8) ; fin
|
|||
|
|
Même schéma que F6 :
|
|||
|
|
- `RotateCamera` : `Définir C2Height` ← `Clamp(C2Height + DeltaSeconds × 3, −40, 150)` ;
|
|||
|
|
- `DownUp` : `Définir C2Height` ← `Clamp(C2Height + AxisValue × 3, −40, 150)`.
|
|||
|
|
Puis :
|
|||
|
|
- relecture `get_node_infos` : CE1, CE4, CE8, CE19, CE55, 459, 467, 474, 61, 62 ;
|
|||
|
|
- compilation stricte ;
|
|||
|
|
- `save_assets` `/Game/BPs/Room` seul.
|
|||
|
|
Fin : ne ferme pas l'éditeur.
|