LifePlay-Revival/Docs/Migration/etat/CAM/CAM-SPEC.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

85 KiB
Raw Blame History

Fiche CAM-T5 — Niveau N3 — Modèle prévu opus (opus-xhigh) | utilisé claude-opus-5-5 — Agent opus-xhigh — Skills sk-protocol, lp-mcp, lp-lot (préchargé aussi : debug-verification) — amendée CAM-T5b (même agent, contexte gardé, 2026-10-11)

CAM-T5-SPEC : phase 2, réduction et repositionnement des caméras par Blueprints (voie B, MCP) (2026-10-11, amendée CAM-T5b)

Copie D:\Game\LifePlay-master 5.8, branche ue5.8, HEAD 4283c13. Lecture seule : aucun éditeur, MCP, commandlet ni build ; seul ce fichier est écrit. Les décisions qui font foi sont celles de CAM-CIBLE.md § 4b et § 4c (réponses de l'utilisateur aux Q1-Q20 de cette SPEC : « q4 lissage et évitement, le reste oui »). Amendement CAM-T5b : § 4c intégré. Sections modifiées : en-tête, § 1, § 2, § 4 (R1, R5, R6, R12, R16 ; R17-R19 nouveaux ; Q1-Q20 décidées), § 5, § 6, § 7, (a) B0 à B6, (b), (c), (d), (e) C2, (f), (g), Constats. Amendement CAM-B0a (session CAM, 2026-10-13, après la sonde B0, CAM-B0-IMPL.md) : R1, R2, B1-1, B1-13, B2-6, (c), (d) ; nouveau § « Identifiants de nœuds (B0a) » après (g) ; amendement B1b (après l'IMPL de B1, CAM-B1-IMPL.md § 3) : B1-1, B1-3, B1-12, § 7 V5 ; amendement B1c (essai en jeu de l'utilisateur : caméra dans la tête) : B1-3, B1-12 ; amendement B1d (2e essai : rémanence d'une image, vue aux yeux) : B1-3 réordonnée, B1-4 sortie inversée ; amendement B2d (maniement Third Person, CAM-B2d-TPS.md ; copie d'avant Backup_5.5\CAM\SPEC-avant-B2d.md) ; amendement B2b (essais de B2, exigence « caméra toujours dans la pièce », option 1 de pilotage ; CAM-B2b-AMENDEMENT.md) : nouvelle section CAM-B2b, copie d'avant Backup_5.5\CAM\SPEC-avant-B2b\ (ecefee2f) ; complément (avis de commit STE-H10) : § 7 V2 et V5, (f) B5 (MD5 de Person D1B4C63D, arrêt par handle). Aucun autre changement ; liste des points de GI inchangée. Copie d'avant : D:\Game\LifePlay_Backup_5.5\CAM\SPEC-avant-B0a\CAM-SPEC.md (MD5 3f0f59bf…). Abréviations (dumps texte ; « HEAD » = version citée par la fiche) :

  • GI = Docs/Migration/etat/ST/STE/dumps/GI__EventGraph_V5.txt ; GICF = .../ST/STE/dumps/GI__CallFunction_V5.txt ; ROOM = .../ST/STE/dumps/Room__EventGraph_V5.txt ; PC = .../ST/STE/dumps/PC__EventGraph.txt (tous avec broches).
  • BV = Docs/Migration/etat/CAM/CAM-T6_LPDumpFunction_20261011-013552.txt (Birdview, 4 graphes, 366 nœuds) ; CM = ...CAM-T6_LPDumpFunction_20261011-013659.txt (ContactMenu, 34 graphes, 1 430 nœuds) ; PERS = ...CAM-T6_LPDumpFunction_20261011-013811.txt (Person.EventGraph à HEAD, 3 684 nœuds) : redumps avec broches (CAM-T6, amendement CAM-T5b). BV4, CM4, PERS4 = les listes sans broches de CAM-T4 (CAM-T4_LPDumpFunction_20261011- 005305.txt, -005400.txt, -005458.txt, CAM-T4-BUILD.md:20-22), gardées seulement où le texte d'origine les cite.
  • HEAD au moment de l'amendement : c01b543 (commit U8 sur Docs seulement ; assets inchangés, mêmes MD5, d'après l'orchestrateur) ; les dumps V5 et CAM-T6 restent valables.
  • SMS = Docs/Migration/etat/ST/STB/dumps/StreetMap_Static__EventGraph.txt ; PCOMP = Docs/Migration/etat/CAM/CAM-T4_LPDumpComponents_20261011-004910.txt (Person) ; RCOMP = Docs/Migration/etat/post-v1/PawnDyn/PawnDyn_LPDumpComponents_20261004-223842.txt (Room, copie 5.5, 2026-10-04 : valeurs indicatives seulement).
  • TS = D:\Game\Launcher\Engine\UE_5.8\Engine\Plugins\Experimental\Toolsets\EditorToolset\Content\Python\editor_toolset\toolsets\ (outils Epic du MCP) ; ENG = D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime\Engine\Private\.
  • Marques : (N) = nœud nouveau ; « coupe » = break_pins ; « relie » = connect_pins ; « suppr. » = delete_node.

1. Objectif mesurable

Dans une pièce (GI.SpaceOn vrai, hors constructeur, hors CharGen et contacts), la vue est l'une de trois caméras qui suivent le MC, et CameraScene (ChargenCameras[0]) ne sert plus qu'au CharGen (et au constructeur, Q9) :

  • C1 : FirstPersonSM existant du MC (composant de Person), attaché à la tête comme la POV de sexe ; POV des partenaires inchangée pendant le sexe (décision Q5).
  • C2 : un CameraActor créé à l'exécution par Room (aucune écriture de .umap), non attaché au MC : posé à l'initialisation à 175 cm derrière le MC (horizontale), à la hauteur sol + taille du MC + 50 cm, regard vers la tête ; puis poursuite lissée avec évitement des murs par une minuterie de Room : cible recalculée à chaque passage, balayage en sphère (12 cm) sur le canal Camera depuis la tête, retrait de 12 cm sur impact, VInterpTo et RInterpTo vitesse 8 ; portage BP de FollowCamera (LPDirectControlSubsystem.cpp:929-940) ; la minuterie s'arrête d'elle-même quand la vue n'est plus une caméra de pièce (Q1-Q4 décidées, CAM-CIBLE.md § 4c).
  • C3 : le même CameraActor (Q19 décidée) placé sous le plafond au centre de la pièce ; en extérieur (échelle Z de la racine < 0,05), à 380 cm au-dessus du sol, au bout de l'axe long du lieu, regard dans l'axe (enfilade) (Q3 et Q1 décidées).
  • Touche C hors sexe : C1 → C2 → C3 → C1 ; pendant le sexe : cycle POV actuel (Q4 décidée) ; commandes de scène cameraMove / cameraFocus / cameraFocusPoint → C2 (Q8 décidée) ; Birdview, CameraFullBody, CameraPortrait inchangées (Q6 décidée) ; défauts du lot corrigés : pivot de l'acteur Room par CameraFocus, chaînes mortes, code FollowCamera (Q9 décidée).

Mesures de réussite (rejouées en VAL, § 7) : SetViewTargetWithBlend vivants dans les dumps : GI 4 → 4, PC 3 → 3, Room 0 → 3 (C1, C2, C3) ; MD5 de Birdview, ContactMenu, CentralLondon.umap et Person (Q18 décidée : non touchée) identiques aux valeurs de CAM-T4 ; compilation : 0 diagnostic, CompileAllBlueprints 0 erreur ; build Development code 0 ; essais en jeu de l'utilisateur § (f) tous PASS ; coût de la poursuite de C2 sous le seuil de V11 ; FollowCamera|FollowView|BirdView dans Source/ : 0 ligne. Sortes de caméras de vue : 6 avant (CAM-AUDIT § 2) ; 6 après (Q19 décidée : un seul acteur CamRoom ajouté, FollowView retirée). La réduction porte surtout sur les rôles : CameraScene passe de 4 rôles à 1 (+ constructeur).

2. Fichiers touchés, ordre

Sous-lots, un commit chacun, dans cet ordre (dépendances au § (a)) :

Ordre Sous-lot Fichiers (dépôt du jeu, branche ue5.8) Graphes
0 CAM-B0 sonde MCP (Q16 décidée) aucun fichier suivi ; BP jetable /Game/Dev/CAM_Probe (non commité, déplacé ensuite vers D:\Game\LifePlay_Backup_5.5\CAM-B0\) —
1 CAM-B1 C1 hors sexe, commutation C, reclassement des appelants de ChargenCameras[0] Content/BPs/Room.uasset, Content/BPs/PC.uasset, Content/BPs/GI.uasset Room.EventGraph ; PC.EventGraph ; GI.EventGraph
2 CAM-B2 C2 en poursuite lissée avec évitement des murs (Q4) Content/BPs/Room.uasset Room.EventGraph
3 CAM-B3 C3 intérieur, extérieur, enfilade Content/BPs/Room.uasset Room.EventGraph
4 CAM-B4 commandes de scène → C2, correctif CameraFocus Content/BPs/Room.uasset, Content/BPs/GI.uasset Room.EventGraph ; GI.CallFunction ; GI.EventGraph (Q12 décidée)
5 CAM-B5 chaînes mortes et suppression d'AutoCamera (Q14) Content/BPs/Room.uasset, Content/BPs/PC.uasset, Content/BPs/GI.uasset (Person non touchée, Q18) Room.EventGraph ; PC.EventGraph ; GI.EventGraph
6 CAM-B6 suppression du C++ FollowCamera (Q15), après la VAL de B2 (portage) Source/LifePlay/LPDirectControlSubsystem.h, .cpp —

Ordre dans un sous-lot MCP : Room d'abord (les événements appelés par PC et GI doivent exister et compiler), puis PC, puis GI ; compile_blueprint après chaque asset ; save_assets à la fin. Aucun fichier hors de cette liste : Birdview, ContactMenu, MainMenu, Person (Q18 décidée), CentralLondon.umap, Config/ ne sont pas modifiés.

3. Repli

  • Par sous-lot, éditeur fermé, proposé à l'utilisateur, jamais lancé sans accord (lp-lot) : .\Tools\Snapshot.ps1 -Lot CAM-Bn-retour -Path <mêmes chemins> puis .\Tools\Restore.ps1 -Lot CAM-Bn -WhatIf, puis .\Tools\Restore.ps1 -Lot CAM-Bn. Après commit : ticket RESTORE au gardien (git revert du commit du sous-lot).
  • Les sous-lots B1 → B5 s'enchaînent sur les mêmes assets binaires : le retour de Bn impose celui de Bn+1… d'abord (ordre inverse). B6 (C++) est indépendant. B0 ne laisse rien dans le dépôt.
  • Repli fonctionnel sans retour : B1 seul laisse le jeu au comportement actuel pour C2/C3 (repli ChargenCameras[0], § (a) ligne B1-4) ; on peut s'arrêter après n'importe quel sous-lot validé.
  • Un échec de compilation, un diagnostic ou un Accessed None nouveau : arrêt du sous-lot, aucun contournement, rapport (sk-protocol § 2).

4. Risques R#, questions Q#

Risques

  • R1 Capacités MCP non prouvées dans le projet. Prouvées par usage : find_nodes, get_connected_subgraph, get_node_infos, create_node (Branche, IsValid, appels Classe|<BP>|<Fonction>, Définir<Var>), connect_pins, break_pins, delete_node, set_pin_value, compile_blueprint, save_assets (Docs/29_Projet_Stabilisation_V2.md:153, :166-169, :262-264 ; lp-mcp § 3). Présentes dans le code Epic mais jamais utilisées ici : add_variable (TS blueprint.py:1154-1185), add_object_variable (:1224-1251), add_event (:324-366), add_function_graph (:287-320), création d'un nœud SpawnActorFromClass et réglage de sa broche Class (create_node générique :877-915 + set_pin_value :1091), VariableGet d'une variable d'un autre BP (contexte find_node_types context_pins, :752), ActorTools.add_component sur un Blueprint (SCS, TS actor.py:392-484), pose d'acteur dans la carte (TS scene.py:102, :445). S'ajoutent pour la poursuite de C2 (Q4) : nœuds SphereTraceSingle, BreakHitResult, VInterpTo, RInterpTo, GetWorldDeltaSeconds, K2_ClearTimerDelegate, et le rebranchement de la broche OutputDelegate d'un événement vers la broche Delegate d'une minuterie. Variante : sonde CAM-B0 (Q16 décidée) ; si une capacité échoue : arrêt et retour à l'utilisateur (Q20 décidée), aucune variante lancée d'office. B0a (établi par la sonde, CAM-B0-IMPL.md § 2) : add_variable, add_object_variable, add_struct_variable, add_event (personnalisé et surcharge), SpawnActorFromClass + Class, rebranchement OutputDelegate → Delegate, SphereTraceByChannel (TraceTypeQuery2), BreakHitResult, VInterpTo, RInterpTo, GetWorldDeltaSeconds, FindLookAtRotation, GetComponentBounds, GetActorBounds, GetSocketLocation, GetScaledCapsuleHalfHeight, affectation de PostProcessSettings, getter et appel d'un autre BP : faits. Échec : K2_ClearTimerDelegate (« ClearTimerbyEvent does not exist ») → remplacé par RoomCamTimer + ClearandInvalidateTimerbyHandle (B1-1, B1-13), prouvé par la sonde. VariableGet d'un autre BP : le type_id rendu par find_node_types(context_pins) (Variables|…) est refusé par create_node ; passer par Classe|<Classe>|Obtenir<Var> (§ « Identifiants de nœuds »). Non prouvé : ActorTools.add_component, pose d'acteur dans la carte (écartés, (c)). Réserve : la compilation des nœuds purs non câblés de la sonde n'est pas prouvée.
  • R2 Opérateurs et comparateurs (Less_DoubleDouble, opérations sur vecteurs, EqualEqual_ObjectObject) : lp-mcp § 3 signale que les comparateurs ne sortent pas de find_node_types. Ils existent dans le projet (ROOM:3431, BV:1320-1324, PC:1816) mais l'outil n'a pas de copie de nœud. Variante : recherche par nom de fonction KismetMathLibrary (create_node accepte Utilities|Operators|Add, TS blueprint.py:886-887) ; la commutation C1-C3 est conçue sans comparateur (deux booléens, § (a) B1). Si Less_DoubleDouble reste introuvable pour le test extérieur : retour à l'utilisateur (Q20 décidée). B0a (établi) : les opérateurs génériques Utilitaires|Opérateurs|Inférieurà(<), Égalà(==), Ajouter, Soustraire, Multiplier (K2Node_PromotableOperator) se créent et prennent leur type à la 1re liaison (float<float en double, Equal(Object), vector+vector, vector-vector ; Multiplier accepte vecteur × double). Relier l'entrée avant set_pin_value. R2 levé ; le repli WallT.bHiddenInGame n'est plus nécessaire.
  • R3 hyper_blueprint_debug_inspect peut manquer après relance de l'éditeur (mémoire du projet « unreal-mcp reconnect »). Variante : compile_blueprint sans erreur + CompileAllBlueprints éditeur fermé + redump LPDumpFunction.
  • R4 Ordre des appels de vue. La SPEC suppose que HideRender (GI:772-775) pose sa vue de façon synchrone avant de rendre la main (chemin lu sans nœud latent : GI:13526-13531, 782-787, 30049-30063, 4452-4466, 3117-3125, 17489-17492, 24207-24209 ; maillon CallFunction_38555 → CallFunction_2919 → CallFunction_5578 non lu). Si un nœud latent existait, HideRender reprendrait la vue après ApplyRoomCamera. Contrôle : get_connected_subgraph sur K2Node_CustomEvent_471 en début de B1. Variante : appeler ApplyRoomCamera au bout de la chaîne de HideRender (après K2Node_CallFunction_693, GI:24207-24209) sous garde CurrentCamera = 0 et SpaceOn (modifie HideRender).
  • R5 Évitement des murs (transformé par Q4) : l'attache rigide est abandonnée ; C2 fait le balayage de FollowCamera (LPDirectControlSubsystem.cpp:933-938). Reste : le balayage ne voit que ce qui bloque le canal Camera. Murs Wall1-4 de Room : profil Custom, QueryAndPhysics, tableau de réponses vide (= réponses par défaut, bloquer : estimé) ; plafond WallT : NoCollision, Camera ignoré (RCOMP:17-19, copie 5.5) : sans effet, C2 reste sous le plafond ; mobilier (acteurs d'objets de la pièce) : collision non lue. Contrôle en jeu § (f) (MC dos à un mur, puis près d'un meuble).
  • R6 Rendu : un CameraActor neuf a le FOV 90 (ENG Camera/CameraActor.cpp:25-26) mais aucun réglage de post-traitement ; les caméras du studio ont gamma 0,85, AO, DOF (CAM-AUDIT § 3b) ; le réglage DOF du menu n'écrit que ChargenCameras[0] (GI:21058-21106 cité par CAM-T2). Traité par B2-2 : copie du post-traitement de CameraScene à la création de CamRoom (Q17 décidée) ; un changement ultérieur du réglage DOF du menu ne suit pas CamRoom (constat, hors demande).
  • R7 Concurrence avec STE : GI (EventGraph, CallFunction) et Person sont des assets binaires partagés ; aucun nœud commun avec GI.destroyDummy n'est identifié ici (non lu), mais un même fichier ne se fusionne pas : passages l'un après l'autre, MD5 de départ relus à l'entrée de chaque sous-lot, noms de nœuds revérifiés par find_nodes (les noms K2Node_* de la SPEC sont ceux des dumps V5 à HEAD 4283c13). B5 retire un appel mort à Room.refreshIdleDummies (GI:24030-24035), domaine voisin des pantins de STE : à signaler à l'arbitrage.
  • R8 Point du studio : la base codée en dur (-3131543, -14967213, 75) n'apparaît qu'en ROOM:3576 dans les dumps V5 (Grep -313[0-9]{4}|-1496[0-9]{4} sur *V5.txt : 1 ligne) ; la position réelle de l'acteur Room n'est pas établie. Les formules § (e) n'en dépendent pas (bornes lues à l'exécution) ; AutoCamera la garde si elle est conservée (Q14).
  • R9 FirstPersonSM partagé entre C1 et la POV de sexe : chacun le rattache à la tête avant usage (PC:3364-3396 ; B1). SetViewTarget(Person) prend le premier composant caméra actif (ENG Actor.cpp:3688-3700, bFindCameraComponentWhenViewTarget = true :320) ; Person n'a qu'un composant caméra (8 SCS relevés, CAM-T4-BUILD.md:143) et bAutoActivate = True (CAM-T4-BUILD.md:123) : établi pour la classe, non vérifié par instance de PNJ.
  • R10 Vie de Room : si l'acteur Room est recréé (rechargement), CamRoom est perdue ; la création paresseuse sous IsValid (§ (a) B2) la recrée. Acteur orphelin possible : option ReceiveEndPlay → DestroyActor(CamRoom) (B2, à confirmer : Room n'a pas d'EndPlay dans ROOM, Grep « evenement ' » : seul ReceiveBeginPlay, ROOM:2).
  • R11 Commandes de scène sans contenu de test : aucun module livré n'utilise cameraMove / cameraFocus / cameraLock (CAM-AUDIT § 2) ; B4 sera PASS-STATIC sauf script de scène d'essai fourni (et Mods/ vidé avant tout build ou commit, lp-lot).
  • R12 Trous restants (réduits par les redumps CAM-T6 avec broches) : fermés : conditions du relais Birdview → Room (BV:124-180, 511-555, 1538-1577), index de ReturnFromChargen (CM:5116-5118, 5146-5173, 5321-5340), état mort de la chaîne de Person à HEAD (PERS:13468-13529) (Constats) ; restent : qui remplit GI.ChargenCameras (script de niveau, non dumpable), ProjectionMode non affiché, dump FirstPerson vide. Aucun ne bloque la SPEC.
  • R13 Build : tout sous-lot invalide le candidat Shipping d'U8 : rien avant la porte T11 (décision Q7).
  • R14 K2Node_InputAction_51 (PC:563-566, sans liens) : supprimer un nœud d'entrée peut changer la consommation de la touche ; gardé tant que son action n'est pas identifiée (get_node_infos).
  • R15 Événements sans appelant vu (CenterCameraToAction, ROOM:2102-2104) : des appelants hors des dumps lus (widgets) ne sont pas exclus ; gardé.
  • R16 Commandes manuelles : en pièce, flèches, molette et souris passent par Birdview vers Room.LeftRight, RotateCamera, CircleCamera, DownUp, ScrollDownUp (BV:1538-1577 ; relais pris sur la sortie « faux » de CheckIfCameraMovable, BV:511-555, qui rend faux dès que GI.SpaceOn est vrai : Moveable = ET(non ActionBox.HasAnyChildren, non ScrollBoxOn, non CharGenOn, TopRight visible, non SpaceOn), BV:124-180), qui déplacent SceneCamera (ROOM:394-910) : avec C1-C3, ces commandes n'auraient plus d'effet visible (perte du réglage manuel de la vue, y compris pendant le sexe) : accepté pour un premier passage (Q10 décidée), aucun nœud de déplacement modifié.
  • R17 Coût de la minuterie de pièce (nouveau, Q4) : UpdateRoomCam tourne à ≈ 60 Hz (Time 0,016, bMaxOncePerFrame vrai) tant qu'une caméra de pièce est la vue ; en C2, un SphereTraceSingle et ≈ 30 nœuds BP par passage (estimé : bien moins de 0,1 ms par image ; non mesuré). Bornes : arrêt automatique dès que la vue n'est plus une caméra de pièce (B1-13) ; aucun coût sur la carte ni en CharGen. Mesure : V11. Repli si le seuil est dépassé : Time 0,033 (30 Hz), sur décision.
  • R18 État masqué de C1 (nouveau, Q11) : C1 masque cheveux, soleil de scène et mur ; une sortie de C1 par un autre chemin qu'ApplyRoomCamera (contacts, CharGen, sortie de pièce : HideRender / ShowRender non modifiés) les laisserait masqués. Parade : drapeau C1Hidden et événement RestoreC1, appelés par les voies C2 / C3 et par la garde de la minuterie quand la vue n'est plus le MC (B1-12, B1-13). Pendant le sexe la garde n'agit pas : la POV du PC gère soleil et mur (PC:2343-2355, 1797-1809).
  • R19 Canal du balayage (nouveau) : en BP, SphereTraceSingle prend un ETraceTypeQuery (ENG KismetSystemLibrary.cpp:1965) ; TraceTypeQuery2 = Camera est déduit de l'ordre des canaux marqués TraceQuery="1" (D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime\Engine\Classes\Engine\EngineTypes.h:1104-1105, :1276-1277 ; ENG Collision/CollisionProfile.cpp:303-377) et d'aucune redéfinition dans Config/DefaultEngine.ini (Grep CollisionProfile|DefaultChannelResponses|TraceType : 0 ligne) : estimé. Contrôle : get_node_infos montre « Camera » sur la broche TraceChannel après set_pin_value.

Questions : toutes décidées (CAM-CIBLE.md § 4c, « q4 lissage et évitement, le reste oui »)

Q Décision Où dans la SPEC
Q1 décidée : derrière le MC, 175 cm à l'horizontale (e) C2
Q2 décidée : taille = Z(head) − Z(sol) + 10 cm (e) C2
Q3 décidée : C2 regarde la tête (e) C2
Q4 décidée autrement que proposé : poursuite lissée avec évitement des murs (portage BP de FollowCamera), pas d'attache § 1, (a) B1-13, B2, (e) C2, R5, R17
Q5 décidée : C3 intérieure fixe, rotation qui suit le MC (a) B3-2
Q6 décidée : enfilade au bout opposé au MC, lacet dans l'axe, tangage vers le MC (e) C3 extérieur
Q7 décidée : C2 à l'entrée ; mode gardé pendant la session, non sauvegardé (d)
Q8 décidée : étape hors POV du sexe = caméra de pièce (C2 si le mode est C1) (a) B1-2, B1-8
Q9 décidée : constructeur et édition de personnage gardent CameraScene (d)
Q10 décidée : flèches, molette, souris sans effet en pièce (premier passage) R16
Q11 décidée : C1 masque cheveux, soleil, mur ; pas de regard à la souris (a) B1-3, B1-12, R18
Q12 décidée : scène démarrée hors pièce → C2 (a) B4-6
Q13 décidée : cameraMove « détache » C2, c'est-à-dire suspend sa poursuite (drapeau C2Scripted) jusqu'au prochain changement de vue ; cameraLock / cameraUnlock inchangés (a) B2-6, B4-1
Q14 décidée : AutoCamera supprimée (a) B5-5
Q15 décidée : FollowCamera C++ supprimée, après le portage (a) B6
Q16 décidée : sonde B0 accordée (a) B0
Q17 décidée : post-traitement de CameraScene copié sur CamRoom (a) B2-2
Q18 décidée : Person non touchée § 2, (a) B5, V2
Q19 décidée : un seul CameraActor (CamRoom) pour C2 et C3 (a) B2-1
Q20 décidée : si le MCP échoue, retour à l'utilisateur (aucune variante lancée d'office) R1, R2, (c)

Texte d'origine des questions (CAM-T5), gardé pour la trace :

  • Q1 C2, 1,75 m : distance horizontale de 175 cm à l'axe du MC, derrière lui (sens opposé à son regard) ? (proposé ; la distance totale vaut alors ≈ 185 cm pour 60 cm d'écart de hauteur, calcul).
  • Q2 Taille du MC : Z(os head) − Z(sol) + 10 cm (proposé) ; la capsule ne convient pas (88 cm de demi-hauteur fixe pour toute Person, PCOMP:1767-1769) ; autre choix : bornes du maillage.
  • Q3 C2 regarde la tête du MC (proposé, comme AutoCamera, ROOM:3441-3447) ou le centre du corps ?
  • Q4 Suivi de C2 : attachée au MC, rigide, sans anti-collision (proposé, aucun Tick) ; ou poursuite lissée par minuterie avec balayage anti-murs (portage BP de FollowCamera, plus de nœuds) ?
  • Q5 C3 intérieure : position fixe au plafond, rotation qui suit le MC (proposé, d'après « 3 caméras suivent les mouvements du MC », CAM-CIBLE § 1) ou regard fixe vers le bas ?
  • Q6 Enfilade : caméra au bout de l'axe long opposé au MC, lacet fixe dans l'axe, tangage qui suit le MC (proposé) ; ou regard entièrement vers le MC ?
  • Q7 Caméra à l'entrée dans une pièce : C2 (proposé) ; mode gardé d'une pièce à l'autre pendant la session, non sauvegardé (proposé) ?
  • Q8 Sexe : le cycle POV actuel finit sur CameraScene (PC:1752-1760) que la décision Q6 réserve au CharGen ; l'étape « hors POV » devient la caméra de pièce du mode courant, C2 si le mode est C1 (proposé) ?
  • Q9 Constructeur de pièce (EditBedroom, ROOM:5-36) et édition de personnage : garder CameraScene (proposé) ?
  • Q10 Flèches, molette, souris en pièce (R16) : déplacer la caméra active (C2/C3 ; 8 nœuds SceneCamera à rebrancher dans ROOM:394-910) ou rien (proposé pour un premier passage) ?
  • Q11 C1 hors sexe : masquer les cheveux (proposé, comme PC:2290-2297), le soleil de scène et le mur comme en POV (PC:2346-2350, 1800-1804) ? Regard à la souris (non prévu : la souris pilote Birdview quand SexCameraIndex = 0, PC:1813-1830) ?
  • Q12 Scène scriptée démarrée hors pièce (SpaceOn faux → HideRender(0), GI:5866-5870, 23705-23709) : C2 ou CameraScene ?
  • Q13 cameraMove détache C2 du MC (la caméra reste où le script la pose jusqu'au prochain changement de vue, proposé) ; cameraLock / cameraUnlock (GICF:6730-6739, lus par Birdview BV4:480) : gardés tels quels (proposé) ?
  • Q14 Room.AutoCamera (ROOM:3406-3593, 3796-3872), rendue sans objet en pièce : supprimer ou garder ?
  • Q15 FollowCamera C++ : supprimer le code (proposé, B6) ou le garder mort ?
  • Q16 Sonde CAM-B0 sur un BP jetable (non commité, déplacé vers la sauvegarde après) : accord ?
  • Q17 Post-traitement : copier celui de CameraScene (gamma 0,85, AO, DOF) sur CamRoom à sa création ?
  • Q18 Chaîne morte de Person (suivi par SceneCamera, PERS4:2605, 2614 ; à HEAD avec broches : PERS:13468-13529 ; L6f PERS-L6f:34520-34580) : supprimer (touche Person, asset de STE-H4) ou laisser ?
  • Q19 Un seul CameraActor (CamRoom) pour C2 et C3 (proposé : un seul est actif à la fois) ou deux ?
  • Q20 Variante si SpawnActor ou le test extérieur ne se créent pas par MCP : (a) acteur posé dans CentralLondon.umap (écrit la carte, non prouvé) ; (b) petite fonction C++ BlueprintCallable (voie mixte) ; (c) arrêt.

5. Interdits

  • Aucune écriture de .umap, de Birdview, ContactMenu, MainMenu, Person (Q18 décidée), Config/.
  • Ne pas modifier HideRender, ShowRender, ZoomCharGen, OpenContacts, ResetSceneCamera, ResetCameraAndPlayer, secondScreen, ni destroyDummy (STE) ; ne pas toucher aux chaînes CharGen et édition de personnage (GI:750-752, 803-807, 3863-3866, 4397-4400, 22424-22427, 23975-23985).
  • Aucune écriture par HyperAIStudio (lp-mcp règle 1) ; aucun appel MCP chevauché ; aucune écriture sans instantané ni sans trace (fiche, CHG) ; gel de la copie annoncé à toutes les sessions (lp-mcp règle 7).
  • Aucun sous-lot avant la porte T11 d'U8 ; aucune écriture dans GI pendant une fenêtre de STE ; un sous-lot = un commit, jamais de regroupement ; aucune commande git qui écrit hors du gardien ; rien poussé.
  • Aucun IsValid hors des états « vide » normaux nommés ici (CamRoom avant sa création) ; cascades de branches, pas de AND / OR purs (lp-mcp règle 4).
  • Aucune suppression d'événement personnalisé dont un appelant hors dumps n'est pas exclu (R15) ; aucun retour arrière lancé sans accord.

6. Valeurs moteur lues dans le moteur installé (chemin:ligne)

Valeur Source
ACameraActor crée son CameraComponent, FOV 90 ENG Camera/CameraActor.cpp:25-26
UCameraComponent : FieldOfView 90, FirstPersonFieldOfView 90 ENG Camera/CameraComponent.cpp:78-79
FOV par défaut du gestionnaire de caméra : 90 ENG PlayerCameraManager.cpp:46
bFindCameraComponentWhenViewTarget = true ; CalcCamera prend le premier composant caméra actif ENG Actor.cpp:320 ; :3688-3700
Capsule par défaut d'un ACharacter : rayon 34, demi-hauteur 88 (Person : 20 / 88, PCOMP:1767-1769) ENG Character.cpp:78
APlayerController::SetViewTargetWithBlend ENG PlayerController.cpp:2819
USceneComponent::K2_AttachToComponent ; AActor::K2_AttachToComponent ENG Components/SceneComponent.cpp:2276 ; ENG Actor.cpp:2711
UKismetSystemLibrary::GetComponentBounds ENG KismetSystemLibrary.cpp:2591
UKismetSystemLibrary::SphereTraceSingle(WorldContext, Start, End, Radius, ETraceTypeQuery TraceChannel, bTraceComplex, ActorsToIgnore, DrawDebugType, OutHit, bIgnoreSelf, …) ENG KismetSystemLibrary.cpp:1965
UKismetSystemLibrary::K2_SetTimerDelegate(Delegate, Time, bLooping, bMaxOncePerFrame, …) ; K2_ClearTimerDelegate(Delegate) ; K2_ClearAndInvalidateTimerHandle ENG KismetSystemLibrary.cpp:735 ; :796 ; :828
UKismetMathLibrary::VInterpTo(Current, Target, DeltaTime, InterpSpeed) ; RInterpTo(Current, Target, DeltaTime, InterpSpeed) D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime\Engine\Classes\Kismet\KismetMathLibrary.inl:1705 ; :862
UGameplayStatics::GetWorldDeltaSeconds ENG GameplayStatics.cpp:2558
Canaux de requête : ECC_Visibility, ECC_Camera marqués TraceQuery="1" ; énumérés TraceTypeQuery1, TraceTypeQuery2 ; table remplie dans l'ordre des canaux (R19) …\Runtime\Engine\Classes\Engine\EngineTypes.h:1104-1105, :1276-1277 ; ENG Collision/CollisionProfile.cpp:303-377
Référence du portage FollowCamera : visée pawn + 50 cm, recul 340 cm, sphère 12 cm sur ECC_Camera en ignorant le pawn, retrait 12 cm le long de la normale, VInterpTo vitesse 8, rotation directe vers la visée Source/LifePlay/LPDirectControlSubsystem.cpp:929-940
Outils MCP Epic : create 177, compile_blueprint 199, add_function_graph 287-320, add_event 324-366, add_function_param 394, get_node_infos 597, find_nodes 681, find_node_types 752, create_node 877-915, delete_node 919, connect_pins 1058, break_pins 1074, set_pin_value 1091, add_variable 1154-1185 (types bool, int, float, Vector, Rotator, Transform…), add_object_variable 1224-1251, remove_variable 1372 TS blueprint.py
ActorTools.add_component sur Blueprint = SCS via SubobjectDataSubsystem, puis compilation ; remove_component TS actor.py:392-484 ; :488-538
SceneTools.add_to_scene_from_class ; save_actor TS scene.py:102 ; :445
Jeu : seuil extérieur 0,05 sur l'échelle Z relative de la racine de GI.Room, plafond WallT Source/LifePlay/LPOutdoorRoomSubsystem.cpp:13-16, :55
Jeu : touche C = SwitchCamera Config/DefaultInput.ini:118 (CAM-T3 § 3.4)

7. Critères de VAL (commande → attendu)

# Commande Attendu
V1 git -C "D:\Game\LifePlay-master 5.8" status --short -- Content Source seuls les fichiers du sous-lot (§ 2)
V2 Get-FileHash -Algorithm MD5 sur Content\BPs\Birdview.uasset, Content\UI\ContactMenu.uasset, Content\Scenes\CentralLondon.umap, Content\BPs\Person.uasset (Q18) AC4B231463C3FF61335A33B81300793C, 369088B422E1D4F43788D91068114A64, 6CE42F91DCAEE468E073A1CDE465C4B5, D1B4C63D47621B1191AD9352C80C99CD (Birdview, ContactMenu, carte : CAM-T4-BUILD.md:78-84 ; Person : après STE-H10, commit 5b58ffd, B0a)
V3 MCP : get_node_infos sur chaque nœud (N) et chaque nœud existant relié du § (a) liaisons exactement celles du tableau (a)
V4 MCP : compile_blueprint (Room, PC, GI) puis hyper_blueprint_debug_inspect (si présent, R3) 0 erreur, 0 diagnostic
V5 .\Tools\RunCommandlet.ps1 -Lot CAM-Bn-VAL -EtatDir "D:\Game\LifePlay-master 5.8\Docs\Migration\etat\CAM" -Commandlet LPDumpFunction -Args "-BP=Room -Fn=* -Pins -Exec" (idem -BP=PC -Fn=EventGraph, -BP=GI -Fn=EventGraph, -BP=GI -Fn=CallFunction) code 0 ; Grep `evenement personnalise '(ApplyRoomCamera
V6 CompileAllBlueprints éditeur fermé (RunCommandlet.ps1, lp-commandlets) code 0, 0 erreur, 0 avertissement nouveau
V7 build Development empaqueté (lp-build, Tools/Build.ps1) code 0, 0 avertissement nouveau (Build.ps1 -FromLog)
V8 journal du build empaqueté après les essais : Select-String -Path <journal> -Pattern "Accessed None" filtré sur `ApplyRoomCamera CycleRoomCamera
V9 B6 : Grep `FollowCamera FollowView
V10 essais en jeu de l'utilisateur (§ (f)) chaque case PASS
V11 build Development, console stat unit : temps du fil Game relevé 30 s en pièce en C2 (MC qui marche), puis 30 s sur la carte Birdview, même partie écart ≤ 0,5 ms (seuil proposé, R17) ; au-delà : repli Time 0,033 sur décision

Verdict attendu : PASS si V1-V10 ; PASS-STATIC pour B4 sans script de scène d'essai (R11).

(a) Modifications par asset et par graphe, en sous-lots commitables

Dépendances : B0 → B1 → B2 → B3 ; B4 après B2 ; B5 après B4 (B1 réemploie la minuterie morte de Room, que B5 ne supprime donc pas) ; B6 après la VAL de B2 (portage de FollowCamera validé, Q15) ; tout après la porte T11. Fenêtres GI pour l'arbitrage avec STE : B1, B4, B5.

CAM-B0 — sonde (Q16 décidée), aucun fichier suivi

BlueprintTools.create d'un Actor BP jetable /Game/Dev/CAM_Probe, puis dans l'ordre : add_variable (bool, Vector) ; add_object_variable (CameraActor) ; add_event (« ProbeEvent ») et surcharge ReceiveEndPlay ; create_node SpawnActorFromClass + set_pin_value Class = /Script/Engine.CameraActor ; minuterie par événement : nœud K2_SetTimerDelegate relié par ProbeEvent.OutputDelegate → Delegate, set_pin_value Time = 0,016 et bMaxOncePerFrame = vrai, puis K2_ClearTimerDelegate sur le même délégué ; SphereTraceSingle avec set_pin_value TraceChannel = Camera (TraceTypeQuery2, R19) et BreakHitResult ; VInterpTo, RInterpTo, GetWorldDeltaSeconds ; GetComponentBounds, GetActorBounds, FindLookAtRotation, GetSocketLocation, GetScaledCapsuleHalfHeight, Less_DoubleDouble, EqualEqual_ObjectObject, addition, soustraction et multiplication de vecteurs ; affectation d'un champ PostProcessSettings d'un CameraComponent ; dans un 2e BP jetable, VariableGet d'une variable du premier par find_node_types(context_pins) et appel de ProbeEvent ; compile_blueprint. Résultat attendu : chaque capacité de R1 et R2 = « fait » ou message d'erreur exact. Fichiers déplacés ensuite vers la sauvegarde (jamais supprimés).

CAM-B1 — C1 hors sexe, commutation C, reclassement (Room, PC, GI)

# Asset / graphe Nœud existant ciblé (preuve) Modification Variables / valeurs
B1-1 Room — add_variable ×4 ; add_struct_variable ×1 (B0a) CamC1On, CamC3On (bool, défaut faux : mode C2, Q7) ; C1Hidden (bool, Q11, R18) ; (B1b) WallWasHidden retirée : la lecture de bHidden d'un autre acteur n'est pas créable par MCP, le mur reprend la règle de PC (B1-12) ; RoomCamTimer (/Script/Engine.TimerHandle, B0a : remplace K2_ClearTimerDelegate, non créable)
B1-2 Room.EventGraph — (N) événement ApplyRoomCamera : Branche(CamC1On) vrai → Branche(GI.SexOn) vrai → voie C2, faux → voie C1 ; faux → Branche(CamC3On) vrai → voie C3, faux → voie C2 voie C2 pendant le sexe si mode C1 (Q8)
B1-3 Room.EventGraph copie des valeurs de PC:3378-3388, 3389-3396 + 3403-3408, 3364-3373, 1788-1796, 2290-2297 (N) voie C1 : GI.PlayerCharacter.FirstPersonSM → K2_AttachToComponent(Mesh, 'head', KeepRelative ×3) → K2_SetRelativeTransform(MakeTransform((0,0,0), (90,90,0), (1,1,1))) → K2_AttachToComponent(Mesh, None, KeepWorld ×3) → SetViewTargetWithBlend(GI.PC, GI.PlayerCharacter, 0, VTBlend_Linear) → Branche(C1Hidden) faux → HairSM.SetVisibility(false) (B1b : sans WallWasHidden) → GI.PlayerCharacter.Mesh.HideBoneByName('neckLower', PBO_None) (B1c, comme PC:2319-2324 ; omis par CAM-T2 : sans lui la caméra voit l'intérieur de la tête) → SetActorHiddenInGame(SceneSun, vrai) → SetActorHiddenInGame(GI.Wall, vrai) → C1Hidden := vrai ; puis (et si C1Hidden était vrai) → minuterie K2Node_CallFunction_329 (B1-13) Q11 décidée ; nœuds de même type dans PC : cheveux PC:2290-2297, soleil PC:2343-2355, mur PC:1797-1809
B1-3d Room.EventGraph (B1d, remplace l'ordre de B1-3) — voie C1 : Branche(C1Hidden) faux → attache head KeepRelative → MakeTransform((0,0,0),(90,90,0)) → (N) FirstPersonSM.SetWorldLocation(0,5 × (Mesh.GetSocketLocation('lEye') + Mesh.GetSocketLocation('rEye'))) (lu avant HideBoneByName : un os masqué sort des os requis et ne bouge plus, ENG SkeletalMeshComponent.cpp:2300-2330) → attache KeepWorld → cheveux, HideBoneByName('neckLower'), soleil, mur → C1Hidden := vrai → (N) DelayUntilNextFrame → SetViewTargetWithBlend(GI.PC, GI.PlayerCharacter) → 329 ; Branche vrai → SetViewTargetWithBlend → 329 os lEye, rEye (enfants de head) et neckLower présents sur Michael et Bridget (MCP get_bone_children)
B1-4d Room.EventGraph (B1d) — voies provisoires C2 et C3 : SetViewTargetWithBlend(ChargenCameras[0]) puis RestoreC1 (la tête ne revient qu'une fois la vue partie) —
B1-4 Room.EventGraph GI:3117-3125 (vue actuelle) (N) voies C2 et C3 provisoires : (N) appel RestoreC1 (B1-12) → SetViewTargetWithBlend(GI.PC, GI.ChargenCameras[0], 0) (comportement actuel) ; remplacées en B2 / B3 —
B1-5 Room.EventGraph — (N) événement CycleRoomCamera : Branche(CamC1On) vrai → CamC1On:=faux ; faux → Branche(CamC3On) vrai → CamC3On:=faux → CamC1On:=vrai ; faux → CamC3On:=vrai ; puis → appel ApplyRoomCamera C1 → C2 → C3 → C1, sans comparateur (R2)
B1-6 Room.EventGraph (LoadRoom) K2Node_CallFunction_290 MoveAllToCenterRoom then -> K2Node_IfThenElse_63 (ROOM:3330-3332, 4068-4069) coupe 290.then → 63.execute ; relie 290.then → (N) Branche(GI.BuilderOn) vrai → 63 ; faux → (N) Branche(GI.SexOn) vrai → 63 ; faux → (N) Branche(GI.SpaceOn) vrai → (N) ApplyRoomCamera → 63 ; faux → 63 replace C2/C3 après un changement de pièce
B1-7 PC.EventGraph (touche C) K2Node_IfThenElse_23 (SexOn) else (vide) (PC:1723-1727), amont InputAction_12 → IfThenElse_65 (BuilderOn) → PlayClick (PC:1713-1716, 3876-3880, 4430-4432) relie 23.else → (N) Branche(GI.SpaceOn) vrai → (N) appel Room_C::CycleRoomCamera (cible GI.Room) hors pièce : rien (comme aujourd'hui)
B1-8 PC.EventGraph (fin du cycle POV, ResetCamera) K2Node_CallFunction_353 SetViewTargetWithBlend(ChargenCameras[0]) then -> K2Node_CallFunction_194 (PC:1752-1760, 1805-1807 ; entrée par CallFunction_1800, PC:2351-2355, ResetCamera PC:3344-3346) coupe 353.then → 194.execute ; relie 353.then → (N) appel Room_C::ApplyRoomCamera (cible GI.Room) → 194.execute Q8
B1-9 GI.EventGraph (OpenSpace) K2Node_CallFunction_30 HideRender(0) then -> K2Node_CallFunction_645 (GI:23596-23600, 23624-23628 ; chaîne CustomEvent_66 → … → SpaceOn:=vrai → Exit Action Menu, GI:23571-23595) coupe 30.then → 645.execute ; relie 30.then → (N) VariableGet Room + (N) appel Room_C::ApplyRoomCamera → 645.execute entrée dans une pièce
B1-10 GI.EventGraph (CloseContacts, NPCSelector vrai) K2Node_CallFunction_3168 HideRender(0) then -> K2Node_CallFunction_1039 (GI:17470-17474, 17481-17485, 18982-18986) idem B1-9 entre 3168 et 1039 retour des contacts en pièce
B1-11 GI.EventGraph (fin de sexe) K2Node_CallFunction_2699 SetViewTargetWithBlend(ChargenCameras[0]) then -> K2Node_MacroInstance_3053 (GI:14498-14513, 6793-6796) idem B1-9 entre 2699 et 3053 —
B1-12 Room.EventGraph — (N) événement RestoreC1 : Branche(C1Hidden) vrai → HairSM.SetVisibility(true) → Mesh.UnHideBoneByName('neckLower') (B1c, comme PC:2328-2340) → SetActorHiddenInGame(SceneSun, faux) → SetActorHiddenInGame(GI.Wall, NOT(Room.bHidden)) (B1b, décision de l'utilisateur : règle de PC, PC:194 ← 152) → C1Hidden := faux ; faux → fin R18
B1-13 Room.EventGraph (minuterie de pièce) K2Node_CallFunction_329 K2_SetTimerDelegate mort (execute (vide), Delegate ← K2Node_CustomEvent_32.OutputDelegate d'AutoCamera, Time 1,0, bLooping vrai, bMaxOncePerFrame faux, ROOM:3643-3653) (N) événement UpdateRoomCam ; coupe 32.OutputDelegate → 329.Delegate ; relie UpdateRoomCam.OutputDelegate → 329.Delegate ; set_pin_value Time = 0,016, bMaxOncePerFrame = vrai ; 329.execute ← voie C1 (B1), voie C2 (B2), voie C3 (B3) ; (B0a) 329.then (vide, ROOM:3645) → (N) Définir RoomCamTimer ← 329.ReturnValue (vide, ROOM:3653). Corps en B1 (garde) : Branche(GI.SexOn) vrai → fin ; faux → Branche(CamC1On) vrai → Branche(GetViewTarget(GI.PC) = GI.PlayerCharacter) vrai → fin ; faux → RestoreC1 → (N) ClearandInvalidateTimerbyHandle(RoomCamTimer) ; CamC1On faux → RestoreC1 → ClearandInvalidateTimerbyHandle(RoomCamTimer) (B0a : K2_ClearTimerDelegate non créable par MCP, CAM-B0-IMPL § 2 n° 9) ≈ 60 Hz ; arrêt automatique dès que la vue n'est plus une caméra de pièce (R17, R18) ; réemploi d'une chaîne morte ; un nouvel appel de 329 avec le même délégué remplace la minuterie en cours et rend un nouveau handle ; ENG KismetSystemLibrary.cpp:735, :828
Comportement attendu après B1 : identique à aujourd'hui en mode C2/C3 (repli B1-4), plus C1 hors sexe par la touche C.

CAM-B2 — C2 en poursuite lissée avec évitement des murs (Room seul ; Q4 décidée)

Portage BP de FollowCamera (LPDirectControlSubsystem.cpp:929-940) avec les valeurs décidées : cible derrière le MC (Q1-Q3) au lieu de la rotation de contrôle, même balayage (sphère 12 cm, canal Camera, retrait 12 cm), même vitesse 8. Pas de Tick : Room n'a aucun événement Tick (ROOM : seul ReceiveBeginPlay, ROOM:2) ; la minuterie de B1-13 (≈ 60 Hz, bMaxOncePerFrame) se démarre et s'arrête explicitement.

# Graphe Nœud existant ciblé (preuve) Modification Variables / valeurs
B2-1 Room — add_object_variable ; add_variable ×2 CamRoom : CameraActor (Q19) ; C2Target : Vector ; C2Scripted : bool, défaut faux (Q13)
B2-2 Room.EventGraph modèle : K2Node_SpawnActorFromClass_1 déjà présent dans Room (ROOM:248, SelectUnderCursor ROOM:191-198) ; post-traitement de CameraScene (CAM-AUDIT § 3b) (N) événement EnsureCamRoom : IsValid(CamRoom) non valide → (N) SpawnActorFromClass(CameraActor, transform du MC, AlwaysSpawn) → CamRoom := → CamRoom.CameraComponent.PostProcessSettings := GI.ChargenCameras[0].CameraComponent.PostProcessSettings (Q17) ; valide → fin « vide » normal avant la 1re pièce (garde écrite au CHG)
B2-3 Room.EventGraph référence : LPDirectControlSubsystem.cpp:931-938 (N) événement ComputeC2Target : calcul § (e) de Desired (MC, capsule, os head, avant à plat) → (N) SphereTraceSingle(Start = tête, End = Desired, Radius = 12, TraceChannel = Camera (TraceTypeQuery2, R19), bTraceComplex = faux, ActorsToIgnore = [GI.PlayerCharacter, CamRoom], DrawDebugType = None, bIgnoreSelf = vrai) → Branche(ReturnValue) vrai → (N) BreakHitResult → C2Target := Location + Normal × 12 ; faux → C2Target := Desired 175 cm ; +50 cm ; 12 cm ; ENG KismetSystemLibrary.cpp:1965
B2-4 Room.EventGraph — (N) événement PlaceC2 (initialisation, sans lissage) : EnsureCamRoom → ComputeC2Target → K2_SetActorLocationAndRotation(CamRoom, C2Target, FindLookAtRotation(C2Target, tête)) « lors de l'initialisation, à 1,75 m »
B2-5 Room.EventGraph voie C2 provisoire de B1-4 coupe et suppr. le SetViewTargetWithBlend(ChargenCameras[0]) de la voie C2 ; relie RestoreC1 → (N) C2Scripted := faux → (N) PlaceC2 → (N) SetViewTargetWithBlend(GI.PC, CamRoom, 0) → K2Node_CallFunction_329 (minuterie, B1-13) le repli reste pour C3 jusqu'à B3
B2-6 Room.EventGraph (UpdateRoomCam, B1-13) entrée de UpdateRoomCam insère en tête : (N) Branche(GetViewTarget(GI.PC) = CamRoom) vrai → Branche(CamC3On) faux → Branche(C2Scripted) vrai → fin ; faux → ComputeC2Target → (N) Next = VInterpTo(CamRoom.K2_GetActorLocation, C2Target, GetWorldDeltaSeconds, 8) → (N) Rot = RInterpTo(CamRoom.K2_GetActorRotation, FindLookAtRotation(Next, tête), GetWorldDeltaSeconds, 8) → K2_SetActorLocationAndRotation(CamRoom, Next, Rot) ; CamC3On vrai → voie de B3 ; vue ≠ CamRoom → garde de B1-13, inchangée (elle arrête la minuterie par ClearandInvalidateTimerbyHandle(RoomCamTimer), B0a) vitesse 8 (.cpp:939) ; RInterpTo ajouté (le C++ tourne directement, .cpp:940) ; KismetMathLibrary.inl:1705, :862 ; ENG GameplayStatics.cpp:2558
B2-7 Room.EventGraph aucun EndPlay dans Room (ROOM:2) (N) surcharge ReceiveEndPlay → IsValid(CamRoom) vrai → K2_DestroyActor(CamRoom) R10
Arrêt de la minuterie : par la garde de B1-13 dès que la vue n'est ni CamRoom ni le MC en C1 (hors sexe) ; pendant une
commande de scène (C2Scripted vrai) la minuterie tourne sans déplacer C2.

CAM-B2b — confinement de C2 dans la pièce, repli, trajectoire protégée, pilotage (option 1) (amendement B2b)

Origine : essais de B2 (« caméra trop proche près d'un mur », « perte de la commande de la caméra ») ; exigence de l'utilisateur « la caméra doit toujours être placée dans la pièce et jamais dehors » ; option 1 choisie par l'utilisateur. Justification et faits : CAM-B2b-AMENDEMENT.md (§ 2). Room seul. Identifiants : § « Identifiants de nœuds (B0a) » et CAM-B2-journal.txt (B2).

# Graphe Nœud existant ciblé Modification Variables / valeurs
B2b-0 Room — add_variable ×5 C2BoxMin, C2BoxMax : Vector ; C2Yaw : float (0) ; C2Height : float (0) ; C2Dist : float (0 ; décalage, distance = 175 + C2Dist, car add_variable ne règle pas le défaut)
B2b-1 Room.EventGraph — (N) événement ComputeRoomBox : sol = StaticMeshComponent.GetWorldLocation, R = GetActorLocation(self), Ex = Max(Abs(Wall2.X − sol.X) − 30, 0), Ey = Max(Abs(Wall4.Y − sol.Y) − 30, 0) (formule de isPointInsideRoom, marge 30) ; ZTop = SelectFloat(1000000, WallT.Z − 25, StaticMeshComponent.RelativeScale3D.Z < 0,05) ; C2BoxMin := (R.X − Ex, R.Y − Ey, sol.Z + 50) ; C2BoxMax := (R.X + Ex, R.Y + Ey, ZTop) extérieur : Z sans borne haute ; boîte alignée sur les axes du monde (comme isPointInsideRoom)
B2b-2 Room.EventGraph (ComputeC2Target, CE47) 47.then → 459 ; 458 → 459.End et → 62 ; 459.Radius 12 ; PO6 (12,12,12) 47.then → (N) appel ComputeRoomBox → 459 ; Desired borné : (N) VectorBoundedToBox(458, C2BoxMin, C2BoxMax) → 459.End et 62 ; 459.Radius = 20 ; PO6 = (20,20,20) ; 61 reçoit VectorBoundedToBox(PO7, Min, Max) rayon 20 ; retrait 20
B2b-3 Room.EventGraph (ComputeC2Target) sorties 61 et 62 (N) après 61 et 62 : Branche(Distance(C2Target, tête) < 80) vrai → Desired2 = VectorBoundedToBox(tête + (0,0,100) + Bdir × 60, Min, Max) → (N) SphereTraceByChannel(tête → Desired2, 20, TraceTypeQuery2, ignorer [MC, CamRoom]) → C2Target := VectorBoundedToBox(SelectVector(Hit.Location + Hit.Normal × 20, Desired2, ReturnValue), Min, Max) ; faux → fin repli vers le haut et vers l'intérieur ; Bdir = direction arrière de B2b-5 normalisée
B2b-4 Room.EventGraph (poursuite, UpdateRoomCam) 467.then → 474 ; 468 → 474.NewLocation et 473.Start 467.then → (N) SphereTraceByChannel(tête → 468, 20, TraceTypeQuery2, ignorer [MC, CamRoom]) → 474 ; (N) Safe = VectorBoundedToBox(SelectVector(Hit.Location + Hit.Normal × 20, 468, ReturnValue), Min, Max) → 474.NewLocation et 473.Start le lissage ne sort jamais de la boîte et ne passe pas derrière un obstacle Camera
B2b-5 Room.EventGraph (ComputeC2Target) PO3 (−175 ×3) ; 457 → PO5 (+60) Bdir = (N) RotateVectorAroundAxis(Normalize2D(Fwd) × −1, C2Yaw, (0,0,1)) ; PO3 := Bdir × (175 + C2Dist) (remplace −175) ; Z := tête.Z + 60 + C2Height option 1 ; à C2Yaw = 0, C2Height = 0, C2Dist = 0 : identique à B2 (correction E3 de F3)
B2b-6 Room.EventGraph (5 événements de déplacement) CircleCamera CE1 → IfThenElse_26 ; LeftRight CE4 → IfThenElse_9 ; RotateCamera CE19 → IfThenElse_25 ; DownUp CE8 → IfThenElse_8 ; ScrollDownUp CE55 → IfThenElse_10 pour chacun : coupe CE.then → branche existante ; (N) Branche(GetViewTarget(GI.PC) = CamRoom) vrai → réglage ; faux → branche existante (inchangée). Réglages : CircleCamera C2Yaw += D ; LeftRight C2Yaw += AxisValue × 2 ; RotateCamera C2Height := Clamp(C2Height + D × 3, −40, 150) ; DownUp C2Height := Clamp(C2Height + AxisValue × 3, −40, 150) ; ScrollDownUp C2Dist := Clamp(C2Dist − Values × 15, −75, 125) (distance 100 à 300 cm) signes et gains ajustables à l'essai ; hors C2, comportement inchangé
Règle : toute position posée par CAM sur CamRoom (initialisation PlaceC2, poursuite, repli) passe par VectorBoundedToBox(…, C2BoxMin, C2BoxMax) ; PlaceC2 pose sans lissage avant la bascule de vue (466), donc la première image de C2 est dans la pièce. C3 provisoire (ChargenCameras[0]) n'est pas couverte (ancienne vue) : B3 la remplace et doit utiliser la même boîte (B3-1 : ComputeRoomBox au lieu de GetActorBounds).
V5 après B2b : SphereTraceSingle 3 ; Vector_BoundedToBox ≥ 4 ; RotateAngleAxis 1 ; Vector_Distance 1 ; événement ComputeRoomBox 1 ; SetViewTargetWithBlend 3 ; 5 événements de déplacement : then → nouvelle branche, else → branche d'origine.

CAM-B2d — C2 sur la rotation de contrôle, comme le modèle Third Person d'Epic (amendement B2d)

Origine : demande directe de l'utilisateur (« le personnage est difficile à manier »), analyse CAM-B2d-TPS.md, GO de Big Boss (2026-10-13). Room seul ; LPDirectControlSubsystem et Person non touchés (la marche lit déjà GetControlRotation().Yaw, LPDirectControlSubsystem.cpp:887).

# Graphe Nœud existant ciblé Modification Valeurs
B2d-1 Room.EventGraph (ComputeC2Target) 453 GetActorForwardVector(MC) → 454.A coupe 453 → 454 ; (N) GetControlRotation(GI.PC 262) → (N) GetForwardVector(rotateur) → 454.A ; suppr. 453 la direction arrière de C2 suit le lacet de contrôle (454 met à plat)
B2d-2 Room.EventGraph (CircleCamera, F6a) 96.then → 67 Définir C2Yaw ← PO28 (292 + DeltaSeconds) (N) BreakRotator(GetControlRotation) → Yaw → PO28.A (au lieu de 292) ; (N) MakeRotator(Roll 0, Pitch 0, Yaw ← PO28) → (N) SetControlRotation(GI.PC 262) ; 96.then → ce nœud ; suppr. 67 et 292 lacet += delta souris
B2d-3 Room.EventGraph (LeftRight, F6a) 97.then → 68 ← PO30 (293 + AxisValue × 2) idem : Yaw du BreakRotator → PO30.A ; MakeRotator(0, 0, PO30) → (N) SetControlRotation(262) ; 97.then → ce nœud ; suppr. 68 et 293 lacet += flèche × 2
B2d-4 Room.EventGraph (PlaceC2) CE48.then → 461 EnsureCamRoom coupe ; CE48.then → (N) SetControlRotation(262, MakeRotator(0, 0, BreakRotator(GetActorRotation(MC 261)).Yaw)) → 461 à l'entrée en C2, la caméra part derrière le personnage
C2Yaw (288 → 497.AngleDeg) n'est plus écrit et reste à 0 : 497 garde sa place (aucun effet). Un seul GetControlRotation et un seul BreakRotator (purs) peuvent servir B2d-1 à B2d-3.
V5 après B2d : GetControlRotation ≥ 1 ; SetControlRotation 3 ; GetActorForwardVector absent de ComputeC2Target ; variable C2Yaw écrite 0 fois ; critères de B2b inchangés.

CAM-B3 — C3 intérieur, extérieur, enfilade (Room seul)

# Graphe Nœud existant ciblé (preuve) Modification Variables / valeurs
B3-1 Room.EventGraph StaticMeshComponent relu en ROOM:4050-4052 ; WallT en ROOM:4655 ; seuil LPOutdoorRoomSubsystem.cpp:14, 55 (N) événement PlaceC3 : EnsureCamRoom → Branche(Less(StaticMeshComponent.RelativeScale3D.Z, 0.05)) faux → position intérieure ; vrai → position extérieure (§ (e)) → K2_SetActorLocationAndRotation (C2 n'étant plus attachée, aucun détachement) 0,05 ; 380 cm ; marge 5 cm / 50 cm
B3-2 Room.EventGraph (UpdateRoomCam) branche CamC3On vrai de B2-6 relie → (N) K2_SetActorRotation(CamRoom, Rc3) (Rc3 § (e) : intérieur, regard vers la tête ; extérieur, lacet dans l'axe, tangage vers la tête) Q5, Q6 décidées ; sans lissage (non demandé)
B3-3 Room.EventGraph voie C3 provisoire de B1-4 coupe et suppr. le repli ; relie RestoreC1 → (N) PlaceC3 → (N) SetViewTargetWithBlend(GI.PC, CamRoom, 0) → K2Node_CallFunction_329 (minuterie de B1-13, déjà en place) Room : 3 SetViewTargetWithBlend

CAM-B4 — commandes de scène → C2, correctif CameraFocus (Room, GI)

# Asset / graphe Nœud existant ciblé (preuve) Modification Valeurs
B4-1 Room.EventGraph — (N) événement UseC2 : CamC1On := faux → CamC3On := faux → ApplyRoomCamera ; (N) événement ScriptC2 : UseC2 → C2Scripted := vrai (la poursuite de C2 est suspendue jusqu'au prochain ApplyRoomCamera, qui remet C2Scripted à faux, B2-5) Q13 décidée ; variable de B2-1
B4-2 Room.EventGraph (CameraFocus) K2Node_CustomEvent_40 then -> K2Node_CallFunction_320 ; 320 = K2_SetActorRotation, self (vide) = acteur Room (ROOM:4396-4399, 4435-4441) coupe 40.then → 320.execute ; relie 40.then → (N) ScriptC2 → 320.execute ; relie (N) VariableGet CamRoom → 320.self corrige le pivot de Room
B4-3 Room.EventGraph (CameraFocus, point de départ du regard) K2Node_CallFunction_406 K2_GetActorLocation self ← K2Node_VariableGet_133.SceneCamera (ROOM:4416-4418 ; 133 en ROOM:2115) coupe 133 → 406.self ; relie VariableGet CamRoom → 406.self —
B4-4 Room.EventGraph (CameraFocusPoint) K2Node_CustomEvent_41 then -> K2Node_CallFunction_404 ; 404 = K2_SetActorRotation(SceneCamera) (ROOM:4404-4410, 4431-4434) coupe 41.then → 404.execute ; relie 41.then → (N) ScriptC2 → 404.execute ; coupe 133 → 404.self ; relie VariableGet CamRoom → 404.self —
B4-5 GI.CallFunction (cameraMove) K2Node_SwitchString_22.cameraMove -> K2Node_CallFunction_242 K2_SetActorTransform (GICF:13, 209, 6185-6193) ; self ← K2Node_VariableGet_177.SceneCamera (GICF:6182-6184), cible K2Node_VariableGet_176.Room (GICF:6179-6181) coupe 22.cameraMove → 242.execute ; relie 22.cameraMove → (N) appel Room_C::ScriptC2 (self ← 176) → 242.execute ; coupe 177 → 242.self ; relie (N) VariableGet CamRoom (cible 176) → 242.self ; suppr. 177 (orphelin) UpdateDOF (GICF:6200-6203) inchangé
B4-6 GI.EventGraph (Q12 décidée : C2) K2Node_CallFunction_637 HideRender(0) then -> K2Node_CallFunction_6127 (GI:5866-5870 ; amont IfThenElse_117 SpaceOn faux, GI:23705-23709) coupe 637.then → 6127.execute ; relie 637.then → (N) VariableGet Room + (N) appel Room_C::UseC2 → 6127.execute (comme B1-9 ; C2 suit le MC tant qu'aucune commande de scène ne la fige) —
— GI.CallFunction cameraFocus → IfThenElse_10 → Room.CameraFocus / CameraFocusPoint (GICF:210, 6418-6422, 6292-6299, 6466-6473) ; cameraLock / cameraUnlock (GICF:218-219, 6730-6739) inchangés (le correctif est dans Room) —

CAM-B5 — chaînes mortes et AutoCamera (CAM-T2 § 3.4 ; tête morte = execute (vide) ; Q14 et Q18 décidées)

Règle : supprimer la tête morte, puis chaque nœud aval resté sans entrée d'exécution et chaque VariableGet resté sans sortie, après relecture get_node_infos (aucune suppression d'un nœud encore relié à un nœud vivant).

# Asset / graphe Nœud (preuve) Modification
B5-1 Room.EventGraph K2Node_CustomEvent_33 'TestAutoCamera' vide (ROOM:3453-3455) et son appel K2Node_CallFunction_307 (execute -> VariableSet_5.then, then (vide), ROOM:3456-3459) suppr. 307 puis 33
B5-2 Room.EventGraph K2Node_CallFunction_26 DownUp, execute (vide) (ROOM:561-565) suppr.
B5-3 Room.EventGraph K2Node_IfThenElse_1 (BuilderOn), execute (vide) (ROOM:389-393) suppr. (+ aval resté mort, à relire : CallFunction_23, ROOM:417-418)
B5-4 Room.EventGraph K2Node_CallFunction_333 DrawDebugBox, execute (vide) (ROOM:3674-3677) suppr.
B5-5 Room.EventGraph (Q14 décidée : supprimer) AutoCamera : K2Node_CustomEvent_32 et sa chaîne (ROOM:3406-3593, 3796-3872) ; appel K2Node_CallFunction_363 (execute -> IfThenElse_63.else, ROOM:3873-3876) ; MacroInstance_5.Exec ← 363.then et 63.then (ROOM:1135-1136) relie IfThenElse_63.else → MacroInstance_5.Exec ; suppr. 363 puis la chaîne ; la minuterie 329, réemployée par B1-13 (son lien de délégué vers CustomEvent_32 y est déjà coupé), n'est pas supprimée
B5-6 PC.EventGraph K2Node_CallFunction_1077 SetRelativeRotation(FirstPersonSM, 90,90,0), execute (vide) (PC:2055-2062) suppr.
B5-7 PC.EventGraph K2Node_CallFunction_204 AddWorldRotation(FirstPersonSM), execute (vide) (PC:1888-1895) suppr.
B5-8 PC.EventGraph K2Node_CallFunction_61 Delay 1, execute (vide) (PC:1696-1702) suppr. (aval CallFunction_173 à relire)
B5-9 GI.EventGraph K2Node_CallFunction_682 Room_C::refreshIdleDummies, execute (vide), then -> CallFunction_2699 (GI:24030-24035) suppr. (R7)
B5-10 GI.EventGraph K2Node_CallFunction_370 StreetMap_Static_C::UpdateWall, execute (vide) (GI:20939-20943) ; VariableGet_269 (GI:20936-20938) suppr. 370 ; 269 si orphelin ; VariableGet_270 à relire
B5-11 GI.EventGraph K2Node_CallFunction_107 SetActorHiddenInGame(Skylight), execute (vide) (GI:21049-21053) ; VariableGet_279 (GI:21046-21048) suppr. 107, puis 279 si orphelin
B5-12 Person.EventGraph chaîne morte établie à HEAD : K2Node_IfThenElse_70 execute (vide) → K2Node_IfThenElse_71 (GetViewTarget = Room.SceneCamera, CallFunction_296 / _481) → K2Node_CallFunction_478 K2_SetActorRotation(SceneCamera) (PERS:13468-13472, 13482-13484, 13503-13529) aucune : Person non touchée (Q18 décidée) ; chaîne laissée en place, inerte
gardés Room, PC CenterCameraToAction (ROOM:2102-2104, R15) ; K2Node_InputAction_51 (PC:563-566, R14) aucun

CAM-B6 — suppression du C++ FollowCamera (Q15 décidée), après la VAL de B2 (portage en BP validé)

# Fichier Lignes (preuve) Modification
B6-1 Source/LifePlay/LPDirectControlSubsystem.h :12 (« La camera suiveuse n'est pas branchee »), :19 include Camera/CameraActor.h, :43 déclaration, :63-67 membres FollowView, BirdView retirer :43, :63-67, :19 si plus utilisé ; :12 réécrite (« la caméra suiveuse est portée en Blueprint dans Room, lot CAM »)
B6-2 Source/LifePlay/LPDirectControlSubsystem.cpp :573-577 destruction de FollowView ; :897-941 définition ; includes :15-16 retirer ; includes retirés seulement si Grep `CameraActor
Vérification : V9 ; aucun appelant à retirer (FollowCamera : 2 lignes, déclaration et définition, CAM-AUDIT § 5).

(b) Fonctions et graphes touchés, par asset (arbitrage avec STE)

Asset Graphe Sous-lot Nœuds existants touchés (liens coupés ou relus) Nœuds (N)
GI EventGraph, chaîne OpenSpace (K2Node_CustomEvent_66) B1 K2Node_CallFunction_30 → K2Node_CallFunction_645 (GI:23596-23600, 23624-23628) VariableGet Room, appel Room_C::ApplyRoomCamera
GI EventGraph, chaîne CloseContacts (K2Node_CustomEvent_734) B1 K2Node_CallFunction_3168 → K2Node_CallFunction_1039 (GI:17470-17474, 18982-18986) idem
GI EventGraph, fin de sexe B1 K2Node_CallFunction_2699 → K2Node_MacroInstance_3053 (GI:14505-14513, 6793-6796) idem
GI CallFunction (fonction), SwitchString_22.cameraMove B4 K2Node_CallFunction_242, K2Node_VariableGet_177 (suppr.), K2Node_VariableGet_176 (GICF:6179-6193) appel Room_C::ScriptC2, VariableGet CamRoom
GI EventGraph, démarrage de scène hors pièce B4 (Q12 décidée) K2Node_CallFunction_637 → K2Node_CallFunction_6127 (GI:5866-5870) VariableGet Room, appel Room_C::UseC2 (seul changement de GI dû à l'amendement : le nom de l'événement appelé ; la liste des points de GI est inchangée)
GI EventGraph, nœuds morts B5 suppr. K2Node_CallFunction_682 (GI:24030-24035), K2Node_CallFunction_370 (+ VariableGet_269) (GI:20936-20943), K2Node_CallFunction_107 (+ VariableGet_279) (GI:21046-21053) —
GI non touchés — HideRender (CustomEvent_471), ShowRender (519), ZoomCharGen, OpenContacts (531), édition de personnage, ResetCameraAndPlayer, secondScreen, Sex, PanToBuilding, destroyDummy, réglage DOF, variables de GI (aucune variable ajoutée à GI) —
Room EventGraph B1-B5 K2Node_CallFunction_290, K2Node_IfThenElse_63 (LoadRoom) ; K2Node_CallFunction_329 (minuterie) ; K2Node_CustomEvent_40, _41, K2Node_CallFunction_320, _404, _406, K2Node_VariableGet_133 ; morts B5-1 à B5-5 événements ApplyRoomCamera, CycleRoomCamera, RestoreC1, UpdateRoomCam (B1) ; EnsureCamRoom, ComputeC2Target, PlaceC2, surcharge ReceiveEndPlay (B2) ; PlaceC3 (B3) ; UseC2, ScriptC2 (B4) ; variables CamC1On, CamC3On, C1Hidden, RoomCamTimer (B1 ; WallWasHidden retirée, B1b), CamRoom, C2Target, C2Scripted (B2) ; minuterie K2Node_CallFunction_329 réemployée dès B1
PC EventGraph B1, B5 K2Node_IfThenElse_23.else ; K2Node_CallFunction_353 → _194 ; morts _1077, _204, _61 Branche(SpaceOn), 2 appels Room
Person — aucun (Q18 décidée) chaîne morte établie à HEAD (PERS:13468-13529), laissée en place —
Birdview — aucun — —
ContactMenu, MainMenu — aucun — —
CentralLondon.umap — aucun — —

(c) Choix technique de chaque caméra, et preuve MCP

Caméra Choix proposé Ce que le MCP doit faire Preuve Variante sûre si non prouvé
C1 composant existant FirstPersonSM de Person (aucun changement de Person) ; attache et vue posées par Room create_node d'appels (K2_AttachToComponent, K2_SetRelativeTransform, SetViewTargetWithBlend, SetVisibility), connect_pins, set_pin_value établi : mêmes familles d'outils utilisées en STB (29_Projet_Stabilisation_V2.md:262-264) ; nœuds de même type déjà dans PC (PC:3364-3396, 1788-1796) —
C2 / C3 un CameraActor créé à l'exécution par Room (SpawnActorFromClass paresseux sous IsValid), tenu par la variable CamRoom, en poursuite du MC (C2, non attaché, Q4) ou posé au plafond (C3) ; aucune .umap écrite add_object_variable, add_variable, add_event, create_node SpawnActorFromClass + broche Class établi par B0a (CAM-B0-IMPL.md § 2 n° 2-7 ; CamRoom.CameraComponent par Classe|Acteurdecaméra|ObtenirCameraComponent, n° 22) ; avant B0 : non prouvé dans le projet (R1) ; code présent (TS blueprint.py:1224-1251, :1154-1185, :324-366, :877-915, :1091) ; un SpawnActorFromClass compile déjà dans Room (ROOM:248) CAM-B0 ; si échec : retour à l'utilisateur (Q20 décidée)
Poursuite de C2 (Q4) minuterie de Room (nœud mort K2Node_CallFunction_329 réemployé, Time 0,016, bMaxOncePerFrame) + SphereTraceSingle sur le canal Camera + VInterpTo / RInterpTo vitesse 8 ; pas de Tick (Room n'en a pas, ROOM:2) connect_pins d'une broche OutputDelegate vers Delegate, set_pin_value (Time, bMaxOncePerFrame, TraceChannel), create_node des fonctions moteur fonctions établies dans le moteur (ENG KismetSystemLibrary.cpp:735, :796, :1965 ; KismetMathLibrary.inl:862, :1705 ; ENG GameplayStatics.cpp:2558) ; un K2_SetTimerDelegate relié au délégué d'un événement existe déjà dans Room (ROOM:3643-3653) ; création et rebranchement par MCP établis par B0a (n° 8-14), arrêt par handle au lieu de K2_ClearTimerDelegate (n° 9, 9b) CAM-B0 ; si échec : retour à l'utilisateur (Q20 décidée)
(écartée) composant ajouté à Person ActorTools.add_component(Person, CameraComponent) code présent (TS actor.py:392-484, SCS par SubobjectDataSubsystem), non prouvé une instance par Person (même défaut que FirstPersonSM, CAM-AUDIT § 4) ; ne convient pas à C3 ; Person est l'asset de STE-H4
(écartée) acteurs posés dans la carte SceneTools.add_to_scene_from_class + enregistrement code présent (TS scene.py:102, :445), non prouvé écrit CentralLondon.umap (interdit § 5) ; références à poser par le script de niveau (non dumpable, R12)
Commutation deux booléens CamC1On, CamC3On (C2 = les deux faux) Branche, Définir<Var> établi (lp-mcp § 3) —
Test extérieur Less_DoubleDouble(échelle Z, 0,05) création d'un comparateur établi par B0a (Inférieurà(<) → float<float, n° 18) ; avant B0 : non prouvé (R2) lecture de WallT.bHiddenInGame (masqué par le C++ en extérieur, LPOutdoorRoomSubsystem.cpp:55-60, mais avec un décalage d'une image possible), seulement sur accord ; sinon retour à l'utilisateur (Q20 décidée)

(d) Règle de priorité de vue et caméra par défaut

Le moteur garde le dernier SetViewTargetWithBlend (ENG PlayerController.cpp:2819) ; la SPEC place l'appel de la caméra de pièce après les appels existants, dans les seuls contextes de pièce.

Contexte (déclencheur) Vue résultante Mécanisme Qui a le dernier mot
Chargement, sortie de pièce, fin du constructeur (BeginAutoLoad, ShowRender) Birdview inchangé (PC:521-529, GI:3144-3152) Birdview hors pièce
Entrée en pièce (OpenSpace → HideRender(0)) caméra de pièce du mode courant (C2 par défaut, Q7) B1-9 ApplyRoomCamera après HideRender
LoadRoom en pièce, hors constructeur et hors sexe idem (replacée) B1-6 idem
Touche C hors sexe, en pièce C1 → C2 → C3 B1-7 CycleRoomCamera
Touche C pendant le sexe cycle POV actuel ; étape « hors POV » = caméra de pièce (C2 si mode C1) PC inchangé + B1-8 (Q8) PC
ResetCamera (PC) caméra de pièce B1-8 (même nœud 353) —
Fin de sexe (ChargenCameras[0], GI:14505) caméra de pièce B1-11 ApplyRoomCamera
Contacts (HideRender(1), GI:6230-6234) ChargenCameras[1] inchangé menus
Fermeture des contacts avec NPCSelector caméra de pièce B1-10 ApplyRoomCamera
CharGen, édition de personnage, ZoomCharGen, ContactMenu ReturnFromChargen → SelectCamera (CM:5116-5118, 5334-5340) ChargenCameras[0..2] ; ContactMenu : index 2 pour une créature, sinon 1 (SelectInt(2, 1, GI.isCreature(DummyPerson)), CM:5321-5333, 5146-5173) inchangé menus
Constructeur (EditBedroom → HideRender(0), ROOM:5-36) CameraScene inchangé (Q9) ; B1-6 ne s'applique pas sous BuilderOn constructeur
cameraMove, cameraFocus, cameraFocusPoint C2 (forcée, poursuite suspendue, Q13) B4 (ScriptC2) commande de scène
Scène démarrée hors pièce (GI:5866) C2 (Q12 décidée) B4-6 (UseC2) —
ResetSceneCamera CameraScene replacée, vue inchangée inchangé (ROOM:1250-1264) —
Toute vue posée hors des caméras de pièce (menus, carte, CharGen) pendant que la minuterie tourne celle du menu garde de UpdateRoomCam : RestoreC1 puis ClearandInvalidateTimerbyHandle(RoomCamTimer) (hors sexe) (B1-13, B0a) le menu ; la minuterie s'efface
Caméra par défaut à l'entrée : C2 (deux booléens faux), mode gardé tant que l'acteur Room vit, non sauvegardé (Q7 décidée).

(e) Formules

C2 (Q1-Q4 décidées, CAM-CIBLE.md § 4c : 175 cm derrière, hauteur = taille + 50 cm, regard vers la tête, poursuite lissée avec évitement des murs) :

  • Pmc = GI.PlayerCharacter.K2_GetActorLocation() (centre de capsule) ; HH = CapsuleComponent.GetScaledCapsuleHalfHeight() (capsule de Person : 88, PCOMP:1767-1768) ; Zsol = Pmc.Z − HH.
  • Ztete = Mesh.GetSocketLocation('head').Z (socket déjà lu par Room, ROOM:3441-3447) ; taille T = Ztete − Zsol + 10 (Q2 : +10 cm = os de la tête au sommet du crâne, estimé).
  • F = GetActorForwardVector() mis à plat (Z = 0, normalisé ; le MC s'oriente dans le sens de sa marche, LPDirectControlSubsystem.cpp:868-871 cité par CAM-T3).
  • Desired = (Pmc.X − 175·F.X, Pmc.Y − 175·F.Y, Zsol + T + 50) (Q1, Q2) ; tête = Mesh.GetSocketLocation('head').
  • Évitement (Q4, portage de LPDirectControlSubsystem.cpp:933-938) : SphereTraceSingle(tête → Desired, rayon 12, canal Camera, en ignorant le MC et CamRoom) ; impact → C2Target = Hit.Location + Hit.Normal·12 ; sinon C2Target = Desired. Le balayage part de la tête (le C++ partait du pawn + 50 cm, .cpp:931).
  • Initialisation (PlaceC2) : CamRoom posée directement en C2Target, rotation FindLookAtRotation(C2Target, tête) (Q3).
  • Poursuite, à chaque passage de la minuterie (Δt = GetWorldDeltaSeconds, ≈ 1/60 s) : Next = VInterpTo(Pcam, C2Target, Δt, 8) ; Rot = RInterpTo(Rcam, FindLookAtRotation(Next, tête), Δt, 8) ; K2_SetActorLocationAndRotation(Next, Rot). Le C++ applique la rotation directement (.cpp:940) ; RInterpTo est ajouté par l'amendement (vitesse 8, ajustable à l'essai).
  • Loi de lissage : chaque passage parcourt la fraction min(Δt·8, 1) de l'écart (loi lue pour l'interpolation d'un scalaire, D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime\Core\Public\Math\UnrealMathUtility.h:1530 ; même loi supposée pour VInterpTo, définie ailleurs : estimé) ; à 60 Hz : 13,3 % de l'écart par image, 95 % rattrapé en ≈ 21 images, ≈ 0,35 s (calcul).
  • Écarts au C++ : recul 175 cm (C++ : 340), cible derrière l'avant du MC mis à plat (C++ : rotation de contrôle, tangage borné −55 / −12°, .cpp:929-930), hauteur sol + T + 50 (C++ : déduite du tangage), visée sur la tête (C++ : pawn + 50 cm).
  • Exemple (calcul) : MC de 170 cm, os de la tête à 160 cm → caméra à 220 cm du sol, 175 cm derrière, tangage −atan(60/175) ≈ −19° (sans impact).

C3 intérieur (échelle Z de StaticMeshComponent ≥ 0,05) :

  • (O, E) = GetActorBounds(Room, false) ; (Ot, Et) = GetComponentBounds(WallT) (ENG KismetSystemLibrary.cpp:2591).
  • Pc3 = (O.X, O.Y, Ot.Z − Et.Z − 5) : centre de la pièce, 5 cm sous la sous-face du plafond (« collée au plafond »).
  • Rc3 = FindLookAtRotation(Pc3, tête du MC), recalculée toutes les 0,05 s tant que la vue est C3 (Q5).
  • Ordres de grandeur (indicatifs, RCOMP:24, :30, copie 5.5) : WallT à Z relatif 2040, échelle relative (8, 8, 0,2) (attribution de cette échelle à WallT estimée) ; plafond 408-816 cm au-dessus de la racine pour une échelle Z de 0,2-0,4 (CAM-T3 § 3.5, calcul).

C3 extérieur = caméra de rue en enfilade (échelle Z < 0,05, comme LPOutdoorRoomSubsystem.cpp:55) :

  • (O, E) = GetActorBounds(Room, false) (en extérieur, murs et plafond sont écrasés à ≈ 1 % de leur hauteur : les bornes sont celles du sol, estimé) ; axe A = X si E.X ≥ E.Y, sinon Y.
  • s = −signe(Pmc.A − O.A) (bout opposé au MC ; −1 si égal) ; Pc3 = O + s·(E.A − 50)·Â, Z = Zsol + 380.
  • Rc3 : lacet fixe le long de l'axe, vers O (enfilade) ; tangage vers la tête du MC, recalculé par la minuterie (Q6).
  • Proposition alternative argumentée (Q6) : si un lieu extérieur est presque carré (E.X ≈ E.Y), l'axe est arbitraire ; on peut alors viser le MC entièrement.

(f) Instantanés, MD5, compilation, build, essais

Pour chaque sous-lot MCP, dans cet ordre (lp-lot, lp-mcp) : entrée git status --short -- <chemins> vide pour le sous-lot ; ticket TAG CAM-avant au gardien avant B1 ; gel annoncé ; instantané ; MD5 de départ ; éditeur headless (-ModelContextProtocolStartServer, attente Wait-HyperAIStudioMCP.ps1 -TimeoutSeconds 540) ; AssetTools.load_asset ; find_nodes de chaque nœud existant du § (a) (noms revérifiés, R7) ; modifications ; V3 ; V4 ; save_assets ; Stop-Process de l'éditeur ; Tools/UeState.ps1 = 0 ; V5, V6 ; build Development (V7) ; essais en jeu ; DOC (CHG) ; ticket COMMIT.

Sous-lot Instantané MD5 de départ attendus
B1 .\Tools\Snapshot.ps1 -Lot CAM-B1 -Path Content\BPs\Room.uasset, Content\BPs\PC.uasset, Content\BPs\GI.uasset Room E8F58B2A4A79DA0D62D8FACC18411CE8, PC 09F54974D093893EA43AE10056B37220, GI 16394D438788E986AC23FAEB427402B8 (CAM-T4-BUILD.md:79-81) ; si STE a commité GI entre-temps, MD5 du commit de STE (R7)
B2, B3 -Lot CAM-B2 (puis CAM-B3) -Path Content\BPs\Room.uasset MD5 de Room après le commit du sous-lot précédent (relevé dans son IMPL)
B4 -Lot CAM-B4 -Path Content\BPs\Room.uasset, Content\BPs\GI.uasset idem
B5 -Lot CAM-B5 -Path Content\BPs\Room.uasset, Content\BPs\PC.uasset, Content\BPs\GI.uasset (Person non touchée, Q18 ; son MD5 D1B4C63D47621B1191AD9352C80C99CD (après STE-H10, B0a) est contrôlé par V2) idem
B6 -Lot CAM-B6 -Path Source\LifePlay\LPDirectControlSubsystem.h, Source\LifePlay\LPDirectControlSubsystem.cpp git HEAD
(Paramètres de Snapshot.ps1 : -Lot, -Path tableau, -Recurse, sauvegarde D:\Game\LifePlay_Backup_5.5, Tools/Snapshot.ps1:17-22.)

Essais en jeu de l'utilisateur (build Development ; cases reprises par la VAL) :

  • Nouvelle partie (liste des villes) et Continuer ; Birdview au chargement, inchangée (flèches, molette, survol).
  • Pièce intérieure : entrée en C2 (B2+) ; C → C1 (vue de la tête ; cheveux, soleil et mur masqués), C → C2 (cheveux, soleil et mur rendus), C → C3 (plafond, B3+), C → C1 ; marche du MC : C2 suit en douceur (rattrapage ≈ 0,35 s, sans à-coup), C3 le regarde.
  • Évitement (Q4) : MC dos à un mur, puis près d'un meuble : C2 se rapproche du MC au lieu de passer derrière le mur ; aucun mur entre C2 et le MC.
  • C1, puis contacts (ou CharGen), puis retour en pièce : cheveux, soleil et mur rendus (R18).
  • Performance : V11.
  • Lieu extérieur (un des 7) : C3 à ≈ 3,80 m, dans l'axe long, au bout opposé au MC.
  • Sortie de pièce → Birdview ; réentrée → mode gardé (Q7).
  • Contacts ouverts puis fermés (avec et sans NPCSelector) ; CharGen et zoom (ZoomCharGen 0 → 1 → 2) : caméras du studio inchangées ; retour en pièce sur la caméra de pièce.
  • Constructeur de pièce : CameraScene (Q9) ; sortie → Birdview.
  • Sexe : C parcourt les POV du MC et des partenaires puis l'étape hors POV (Q8) ; ResetCamera ; fin de sexe → caméra de pièce.
  • Scène scriptée avec cameraMove / cameraFocus / cameraFocusPoint (si un script d'essai existe, R11) : C2 ; cameraFocus ne fait plus pivoter la pièce ; C2 reste où le script la pose et ne reprend sa poursuite qu'au changement de vue suivant (Q13) ; scène démarrée hors pièce : C2 qui suit le MC (Q12).
  • Sauvegarde puis rechargement : pas d'erreur, CamRoom recréée à la première pièce.
  • Journal : V8 = 0 ligne.

(g) Durée de copie UE par sous-lot (pour la BUILD-DEMANDE à Big Boss)

Bases mesurées : démarrage de l'éditeur ≈ 1 min et correctif STB de ~30 appels MCP ≈ 5 min (29_Projet_Stabilisation_V2.md:264) ; commandlet 41-56 s (CAM-T4-BUILD.md:29-39) ; compilation C++ 55-62 s (U8-T5b-BUILD.md:22, U8-T2r-BUILD.md:10) ; cuisson 2 min 17 s à 2 min 47 s (U8-T3r-BUILD.md:40, U8-T3-BUILD.md:51) ; mise en scène et archive 49 s (U8-T4r-BUILD.md:63). Estimé : ≈ 1 min d'appels MCP par nœud créé et relié, relecture comprise.

Sous-lot Nœuds (N) estimés MCP Commandlets (V5, V6) Build Development Total copie UE (estimé)
B0 ≈ 25 (jetables : minuterie, balayage, interpolations en plus) 30-40 min — — ≈ 40 min
B1 ≈ 70 (Room ≈ 60 dont RestoreC1 et la garde de UpdateRoomCam, PC 4, GI 6) 70-85 min ≈ 5 min ≈ 5 min ≈ 1 h 35
B2 ≈ 70 (ComputeC2Target ≈ 30, poursuite ≈ 15, EnsureCamRoom et post-traitement ≈ 10, PlaceC2, voie, EndPlay ≈ 15) 70-85 min ≈ 3 min ≈ 5 min ≈ 1 h 30
B3 ≈ 45 45-55 min ≈ 3 min ≈ 5 min ≈ 1 h
B4 ≈ 18 (Room 12, GI 6) 20-25 min ≈ 5 min ≈ 5 min ≈ 35 min
B5 0 créé, ≈ 65 supprimés (chaîne AutoCamera ≈ 50, Q14) 30-40 min ≈ 5 min ≈ 5 min ≈ 50 min
B6 — (C++) — — ≈ 6 min (compilation + paquet) ≈ 10 min
Total ≈ 6 h 20 de copie UE (estimé ; ≈ 4 h 45 avant l'amendement), hors essais en jeu de l'utilisateur et mesure V11 ;
GI est ouvert pendant B1, B4 et B5 seulement (inchangé).

Identifiants de nœuds (B0a)

Relevés par la sonde B0 dans l'éditeur 5.8 en français (CAM-B0-IMPL.md § 2). Les noms de catégorie sont localisés : relire par find_node_types (filtre court, sans context_pins) au début de chaque sous-lot ; un identifiant rendu avec context_pins peut être refusé par create_node (« does not exist », même avec declaring_class).

Besoin dans la SPEC type_id à passer à create_node Remarque
membre d'un autre BP (GI.SexOn, GI.Room, GI.PC, GI.Wall, GI.PlayerCharacter, GI.BuilderOn, GI.SpaceOn) Classe|<Classe affichée>|Obtenir<Var> / Définir<Var> prouvé sur Classe|CAMProbe|ObtenirProbeBool ; nom affiché sans _ ni _C ; broche self (0) ← référence
appel d'un événement ou d'une fonction d'un autre BP (Room_C::ApplyRoomCamera, CycleRoomCamera, ScriptC2, UseC2) Classe|<Classe affichée>|<Fonction> prouvé sur Classe|CAMProbe|ProbeEvent (self 1) ; R1 citait déjà Classe|<BP>|<Fonction> en STB
CamRoom.CameraComponent (B2-2) Classe|Acteurdecaméra|ObtenirCameraComponent repli : Acteur|GetComponentbyClass + ComponentClass = /Script/Engine.CameraComponent (sortie typée)
PostProcessSettings (B2-2) Classe|CameraComponent|ObtenirPostProcessSettings / DéfinirPostProcessSettings K2Node_VariableSet : exécution 0, valeur 1, self 2
SpawnActorFromClass (B2-2) Jeu|Faireapparaîtreunacteuràpartird'uneclasse Class (1) = /Script/Engine.CameraActor ; ReturnValue (1) typé caméra
minuterie (B1-13) nœud existant K2Node_CallFunction_329 ; neuf : Utilitaires|Temps|SetTimerbyEvent Delegate 1, Time 2, bLooping 3, bMaxOncePerFrame 4 ; ReturnValue 1 = handle
arrêt de la minuterie (B1-13, B2-6) Utilitaires|Temps|ClearandInvalidateTimerbyHandle Handle (1, par référence) ← Obtenir RoomCamTimer ; K2_ClearTimerDelegate n'existe pas pour create_node
RoomCamTimer (B1-1) add_struct_variable(struct_type = /Script/Engine.TimerHandle) ; getters Variables|Pardéfaut|Obtenir/DéfinirRoomCamTimer —
SphereTraceSingle (B2-3) Collision|SphereTraceByChannel Radius 3, TraceChannel 4 = TraceTypeQuery2, ActorsToIgnore 6, DrawDebugType 7, bIgnoreSelf 8 ; OutHit 1, ReturnValue 2
BreakHitResult Collision|BreakHitResult entrée 0
VInterpTo, RInterpTo (B2-6) Math|Interpolation|VInterpTo / RInterpTo Current 0, Target 1, DeltaTime 2, InterpSpeed 3
GetWorldDeltaSeconds Utilitaires|Temps|GetWorldDeltaSeconds sortie double, acceptée sur DeltaTime float
FindLookAtRotation Math|Rotateur|FindLookatRotation —
GetComponentBounds, GetActorBounds Collision|GetComponentBounds, Collision|GetActorBounds broches non relues (nœuds non câblés en B0)
GetSocketLocation (os head) Transformation|GetSocketLocation idem
GetScaledCapsuleHalfHeight Composants|Capsule|GetScaledCapsuleHalfHeight self 0, sortie float
Less_DoubleDouble, EqualEqual_ObjectObject, + − × de vecteurs Utilitaires|Opérateurs|Inférieurà(<), Égalà(==), Ajouter, Soustraire, Multiplier typés à la 1re liaison (R2)
surcharge ReceiveEndPlay (B2-7) add_event(event_name = "ReceiveEndPlay") rend un K2Node_Event
Branche Utilitaires|ContrôleDeFlux|Branche exécution 0, Condition 1 ; then 0, else 1 (lp-mcp § 3)

Complément B1b : identifiants relevés pendant B1 (attache, transform, visibilité, GetViewTarget, tableau, appels Classe|Room|… depuis PC et GI) dans CAM-B1-IMPL.md § 2 ; Variables|Rendu|ObtenirActorHiddenInGame ne lit que l'acteur lui-même (aucune broche self), même avec declaring_class.

Fichiers à commiter

  • A Docs/Migration/etat/CAM/CAM-SPEC.md (dépôt du jeu, branche ue5.8, avec les livrables du lot, sur ticket du gardien). Aucun autre fichier écrit.

Tours : 29 / 60 (CAM-T5) ; CAM-T5b : 20 / 25

Constats

  • Trous de CAM-T2 § 3.5 fermés ou réduits :
    1. Déclencheur de OpenSpace / CloseSpace : le BP StreetMap_Static (GI_C::OpenSpace depuis IfThenElse_32.else et après PlayerMoving := faux ; GI_C::CloseSpace après CallFunction_71, SMS:5709-5719, 5743-5749), plus un appel interne de GI (K2Node_CallFunction_671, GI:23971-23974). Établi (dump STB, version non comparée à HEAD).
    2. Amont de Room.AutoCamera : LoadRoom (K2Node_CustomEvent_14, ROOM:1099-1102) → … → currentRoom → clearObjects → previousScale := échelle → CallFunction_186 → murs (ROOM:4652-4665) → MoveAllToCenterRoom (ROOM:3330-3332) → IfThenElse_63 « échelle inchangée » ; else → AutoCamera (ROOM:4059-4072, 3873-3876). Établi pour les maillons lus ; CallFunction_377 → VariableSet_6 et le contenu de CallFunction_186 non lus (estimé : AutoCamera ne joue que si l'échelle de la pièce change).
    3. Birdview relaie vers Room quand CheckIfCameraMovable rend faux : appels Room_C::LeftRight, RotateCamera, CircleCamera, DownUp, ScrollDownUp sur la sortie « faux » des branches IfThenElse_24, _25, _26, _1, _0 (BV:511-555, 1538-1577) ; Moveable = ET(non ActionBox.HasAnyChildren, non ScrollBoxOn, non CharGenOn, TopRight visible, non SpaceOn) (BV:124-180) : en pièce, toujours relayé ; Room ne déplace alors que SceneCamera, invisible en C1-C3 : la décision Q10 (« rien ») ne demande aucun nœud (établi). Birdview écrit GI.Birdview à son BeginPlay : établi (VariableSet_1937 GI → VariableSet_1 'Birdview' := Self, BV:436-446). Birdview lit PreventCameraMove (BV4:480).
    4. Écriture de ChargenCameras : non fermé (script du niveau ; aucun VariableSet ChargenCameras dans les dumps).
    5. ContactMenu : ReturnFromChargen → SelectCamera → CurrentCamera := SelectInt(2, 1, GI.isCreature(DummyPerson)) → SetViewTargetWithBlend(GI.ChargenCameras[CurrentCamera]) (CM:5116-5118, 5334-5340, 5321-5333, 5168-5173, 5146-5154) : index 2 pour une créature, sinon 1, jamais 0 (établi) ; rôle CharGen, inchangé par la SPEC.
    6. ProjectionMode non affiché, dump FirstPerson vide (CAM-T4) : sans effet (C1 garde le composant tel quel, FOV 90).
  • Reclassement des 4 rôles de ChargenCameras[0] : pièce (GI:23596, 17470 ; ROOM LoadRoom) → caméra de pièce (B1) ; scène (GICF:6182 ; ROOM CameraFocus* ; GI:5866) → C2 (B4, Q12) ; sexe (GI:14505 ; PC:1752) → caméra de pièce (B1, Q8) ; CharGen (GI:803-807, 3132-3140, 22424-22427 ; CM:5146-5173 ; ResetSceneCamera) → inchangé ; 5e rôle trouvé : constructeur (ROOM:28-36, déplacements ROOM:394-910) → inchangé (Q9, Q10).
  • Aucune décision du § 4b n'est contredite par un fait établi. Deux tensions, posées en questions : le cycle POV gardé finit aujourd'hui sur CameraScene, réservée au CharGen (Q8) ; « taille du MC » n'est portée par aucune donnée directe (capsule fixe de 88 cm de demi-hauteur pour toute Person, PCOMP:1768) (Q2).
  • Le but « moins de caméras » reste à 6 sortes avant comme après (Q19 décidée : un seul acteur ; FollowView retirée, CamRoom ajoutée). Les décisions Q6 (garder Birdview et les 3 caméras du studio) expliquent ce plancher.
  • Redumps avec broches CAM-T6 (Birdview, ContactMenu, Person) lus par l'amendement : R12 réduit (items 3 et 5 ci-dessus) ; chaîne de Person morte à HEAD (IfThenElse_70 execute (vide), PERS:13468-13472), laissée en place (Q18).
  • Amendement CAM-T5b : C2 n'est plus attachée ; la minuterie de Room (nœud mort réemployé) sert dès B1 de garde pour C1 (R18) puis de moteur de la poursuite de C2 (B2) et du regard de C3 (B3) ; 7 nouveaux nœuds de fonctions moteur à prouver par la sonde B0 (R1) ; durée de copie UE ≈ 6 h 20 au lieu de ≈ 4 h 45 ; liste des points de GI inchangée.
  • RCOMP (composants de Room) vient de la copie 5.5 (2026-10-04) : un LPDumpComponents -BP=Room -All sur la copie 5.8 confirmerait Box, WallT et l'échelle de la racine (valeurs non utilisées comme constantes par la SPEC).
  • Méthode : lecture seule (Read, Grep, Glob) ; un Grep lancé avec un chemin erroné (D:\Game\Launcher\..\LifePlay-master 5.8\...) a rendu 0 résultat, refait avec le bon chemin (7 lignes PreventCameraMove).