# CAM-B2n : orbite de la caméra de scène autour du couple (Room, fiche lp-mcp-bp, 2026-10-14) Origine : `CAM-COMMUN5-DIAG.md` § 3. Défaut d'origine : en mode Scene, la souris fait tourner la caméra sur place au lieu de tourner autour du couple. Gel des scènes levé pour l'alpha. Room seul. ## Barème a0 b1 c1 d0 e1 f1 = 4 → N1, ajusté à N3 (MCP) ; S = C3 1 + f 1 (choix du pivot) = 2 < 3 : une fiche, sans SPEC séparée. 7 nœuds, environ 14 liens, environ 35 appels. Case C9a. ## État (dump Room `CAM-B2i-VAL_LPDumpFunction_20261013-225125.txt`, Room `18565365…` ou celui laissé par B2k) Chemin de la souris sans Maj : - `CircleCamera` (`CustomEvent_1`, sortie `DeltaSeconds` = lacet souris) → 96 (après B2i : faux pendant le sexe) → `IfThenElse_26` (`BuilderOn` OU `SexOn` OU `SpaceOn`) → `IfThenElse_19` (`MoveObject`) **faux** → `K2Node_CallFunction_149` = `SceneCamera.K2_SetActorRotation(148)` ; - 148 = `MakeRotator(Pitch ← 147.Pitch, Yaw ← CommutativeAssociativeBinaryOperator_26 = 147.Yaw + DeltaSeconds)` ; - 147 = `BreakRotator(146 = SceneCamera.GetActorRotation)` ; - **`149.then` vide**. Getters existants : - `VariableGet_102.SceneCamera` (self Room ; sorties 146, 149, 229, 228) ; - `VariableGet_86.SexOn` (self ← 103.GI ; après B2i : 19.B, 29, 28 et NOT 453) ; - `VariableGet_106.SexPersons` (self ← 105.GI ; sortie → `MacroInstance_13.Array`). ## Règle Pendant le sexe seulement (`SexOn` vrai), après la rotation de 149 : - Pivot P = `GetActorArrayAverageLocation(GI.SexPersons)`, la moyenne des personnes du sexe ; - `SceneCamera.SetActorLocation(P + RotateAngleAxis(SceneCamera.GetActorLocation − P, DeltaSeconds, (0, 0, 1)))`. La position tourne autour de P du même angle que le lacet : la caméra garde son cadrage sur le couple. Hors sexe (constructeur, ancienne vue C3), le comportement est inchangé. ## Écritures (préfixe `/Game/BPs/Room.Room:EventGraph.`) 1. `load_asset` `/Game/BPs/Room`. `get_node_infos` sur 149, 148, 147, 146, 26, 19, 102, 86, 106 et `CustomEvent_1`. Tout écart avec la section « État » : arrêt. Relever les PinID. 2. Types (`find_node_types`, puis confirmation par `get_node_infos` après création) : - Branche : `Utilitaires|ContrôleDeFlux|Branche` ; - `GameplayStatics::GetActorArrayAverageLocation` ; - `Actor::K2_GetActorLocation` ; - `KismetMathLibrary::Subtract_VectorVector` ; - `KismetMathLibrary::RotateAngleAxis` (déjà utilisé : 497) ; - `KismetMathLibrary::Add_VectorVector` ; - `Actor::K2_SetActorLocation`. 3. `create_node` des 7 nœuds, près de 149. 4. Liens : - L1 : `149.then` → Branche.execute ; - L2 : `86.SexOn` → Branche.Condition ; - L3 : `106.SexPersons` → `GetActorArrayAverageLocation.Actors`. Si le type est refusé (tableau de `Person` contre tableau d'`Actor`) : arrêt et compte rendu. Repli à décider : `SexPersons[0]`, avec un `IsValidIndex`. - L4 : `102.SceneCamera` → `GetActorLocation.self` ; - L5 : `GetActorLocation.ReturnValue` → `Subtract.A` ; L6 : `Average.ReturnValue` → `Subtract.B` ; - L7 : `Subtract.ReturnValue` → `RotateAngleAxis.InVect` ; L8 : `CustomEvent_1.DeltaSeconds` → `RotateAngleAxis.AngleDeg` ; `set_pin_value` `RotateAngleAxis.Axis` = `0,0,1` ; - L9 : `Average.ReturnValue` → `Add.A` ; L10 : `RotateAngleAxis.ReturnValue` → `Add.B` ; - L11 : `Branche.then` → `SetActorLocation.execute` ; L12 : `102.SceneCamera` → `SetActorLocation.self` ; L13 : `Add.ReturnValue` → `SetActorLocation.NewLocation` ; - `SetActorLocation` : `bSweep` et `bTeleport` faux ; Branche.else vide. 5. Relecture : - 149 then → Branche ; - 102 : sorties 146, 149, 229, 228, `GetActorLocation` et `SetActorLocation` ; - 86 : + Branche ; 106 : + Average ; - `CustomEvent_1.DeltaSeconds` : + `RotateAngleAxis` ; - aucun autre lien modifié (19, 26, 29, 96 et la boucle `SexPersons` 13 et 153 inchangés). 6. `is_dirty` = vrai ; compilation stricte ; `save_assets` de Room seul ; MD5. ## Garde `Accessed None` - `SceneCamera` est déjà lu sans garde par 146 et 149 sur ce même chemin : comportement inchangé. - `SexPersons` vide pendant `SexOn` : `GetActorArrayAverageLocation` rend (0, 0, 0), sans `Accessed None`. La caméra tournerait alors autour de l'origine. Cas non observé, à surveiller à l'essai. ## Limite connue (non traitée ici, à décider) Si la scène part de C2, la vue reste sur `CamRoom` jusqu'au premier C (`CAM-COMMUN4-DIAG.md` § 2). L'orbite ne se voit qu'après un premier passage Scene → POV → Scene, quand la vue est `SceneCamera`. Pour l'avoir dès le début, il faudrait poser `SceneCamera` en vue à l'entrée du sexe : branche `SexOn` dans la voie C2 de `UpdateRoomCam`, avec environ 4 nœuds. Ce serait une fiche B2o, à décider par l'utilisateur. ## VAL - V5 : dump Room ; seules différences : 7 nœuds et les liens ci-dessus ; EventGraph = nœuds avant + 7. - V6 : `CompileAllBlueprints` = référence. - V7 : voir `CAM-COMMUN-6-TESTS.md`. ## Retour arrière `.\Tools\Restore.ps1 -Lot CAM-B2n -Only Content\BPs\Room.uasset` (ou l'instantané commun du passage), éditeur fermé. ## Amendement B2n-a (2026-10-14, arrêt de `lp-mcp-bp` à l'étape 3, décision de l'orchestrateur) - **E1** : `GameplayStatics::GetActorArrayAverageLocation` est `BlueprintCallable`, pas pur (`GameplayStatics.h:76`). Il a execute et then. **Option A retenue** : le mettre dans le fil, après la Branche. - **L11a** : `Branche.then` → `GetActorArrayAverageLocation.execute` (`K2Node_CallFunction_535`, déjà créé) ; - **L11b** : `535.then` → `SetActorLocation.execute`. Le reste ne change pas : `SexPersons` n'est lu que si `SexOn` est vrai. - **E2** : la soustraction et l'addition de vecteurs se font par les opérateurs génériques `Utilitaires|Opérateurs| Soustraire` et `Ajouter`, typés vecteur au branchement. C'est la même fonction (`Subtract_VectorVector`, `Add_VectorVector`), dans une autre classe de nœud. - Nœuds déjà créés et gardés : Branche `K2Node_IfThenElse_101`, moyenne `K2Node_CallFunction_535`. - L3 (`SexPersons` vers `Actors`) reste la condition d'arrêt prévue : si le type est refusé, arrêt sans repli. - V5 : EventGraph = nœuds avant B2n + 7, avec un lien d'exécution de plus (L11a et L11b au lieu de L11).