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

30 KiB
Raw Blame History

CAM-COMMUN6-DIAG : caméra immobile en mode Scene pendant le sexe (lecture seule, sans UE, 2026-10-14)

Origine : essai COMMUN-6 (relais de Big Boss). L'utilisateur : « J'ai perdu à nouveau le droit de déplacer la caméra dans la scène de sexe ». Journal lu sans le modifier : D:\Game\LifePlay_Build_5.8_COMMUN-6_Development\Windows\LifePlay\Saved\Logs\LifePlay.log (05:11 à 05:18 UTC). Sources : dumps de la VAL B2kmn (Room CAM-B2kmn-VAL_LPDumpFunction_20261014-011706.txt, PC …-011816.txt), état actuel (Room 3A451FDD, PC F45BE57A). Aucune écriture, aucun appel MCP.

0. Ordre de traitement (priorité de l'utilisateur : « injouable à cause de C1 »)

Rang Défaut Section Cause Correctif Fiche Avant ORG4 ?
1 Après CharGen : C bloqué, souris C1 sans effet § 10 établie pour SpaceOn (GI ShowRender → 233 ; CloseCharGen en cours de partie ne le remet pas) ; souris : H-M (MainMenuOn resté vrai), à prouver B2r : C++ CAM, remet SpaceOn (et MainMenuOn si le menu est fermé) à la sortie de CharGen ; témoins étendus CAM-B2r-FICHE.md oui : environ 45 min, plus 10 min de compilation, sans éditeur
2 C1 à côté de la tête après un lever ; à-coup à la sortie de CharGen § 9, § 10.3 orientation de C1 figée à l'entrée (la minuterie B2k tourne et ne recale que la position) ; pose prise une image avant le retour de la vue B2s : P5 (orientation à chaque passage) + P6 (pose avant 434) CAM-B2s-FICHE.md oui : environ 35 min d'éditeur, plus 15 min de VAL
3 Vue C1 au plafond au retour d'une scène § 7 même cause que 2 (pose prise sur la tête couchée de fin de scène) B2s, P5 CAM-B2s-FICHE.md avec 2
4 Maj + souris pendant une scène lancée depuis C1 § 6 pas de défaut de code trouvé ; POV hors voie Room par conception ; MoveCamera requis B2p, P2 (flèches sans Maj) + témoin CAM-B2p-SPEC.md non
5 Personnages assis éliminés par le rendu ; caméra immobile en Scene ; ZQSD et flèches § 8, § 2-3, SPEC bornes de pose de référence ; Q8 + B2i B2q (lot à attribuer) ; B2p (P1, P2, P3) CAM-B2p-SPEC.md non

B2r et B2s se suivent dans un seul jeton UE : compilation C++ (B2r), puis éditeur MCP (B2s), puis VAL. Les fichiers touchés sont distincts : LPCameraModeSubsystem.* et Room.uasset.

1. Ce que dit le journal

Heure Ligne Lecture
05:12:30 C : C2 → C3, vue CameraScene CameraScene = ChargenCameras[0] = Room.SceneCamera (C3 « old »)
05:12:33 C : C3 → C1, CamC1On=1, C1Hidden=1 vue PlayerCharacter à l'image suivante
05:12:39 « Camera: Scene » entrée en sexe depuis C1 ; aucun SetViewTarget à l'entrée (CAM-COMMUN4-DIAG.md § 2) : la vue reste PlayerCharacter
05:12:43 C → POV, vue PlayerCharacter POV de SexPersons[0] (le MC)
05:12:53 C → POV, vue Person_C_2147481881 POV du partenaire
05:13:04 C → Scene, vue CameraActor_2147480656, C1Hidden 1 → 0 CameraActor_… = CamRoom, la caméra C2 (même acteur qu'à 05:12:08 en C2 ; cf. CAM-COMMUN5-DIAG.md § 0)
05:13:18 à 05:15:11 même cycle à chaque retour en Scene : CamRoom
05:18:57 « Camera: C1 First person » après la scène, C1 revient (fin de sexe, crochet B1-11 de GI : 2699 → ApplyRoomCamera, SexOn faux)

En mode Scene, la vue n'est donc jamais SceneCamera : d'abord PlayerCharacter (vue C1 conservée à l'entrée), puis CamRoom après chaque passage POV → Scene.

2. Cause (prouvée par les dumps)

Deux choses s'additionnent.

a) La vue pendant le sexe est la caméra C2 (décision Q8, B1-2 et B1-8).

  • PC, retour en Scene : VariableSet_12 (SexCameraIndex = 0) → 1800 → 353 SetViewTargetWithBlend(ChargenCameras[0]) → 140 Room.ApplyRoomCamera (B1-8).
  • Room, ApplyRoomCamera (CustomEvent_43) → 77 (CamC1On) vrai → 78 (SexOn) vrai → VariableSet_63 (C2Scripted = faux) → 465 PlaceC2 → 466 SetViewTargetWithBlend(CamRoom) → 94 RestoreC1 (d'où C1Hidden = 0) → 329 minuterie. Si CamC1On est faux et CamC3On faux (mode C2), 79.else mène au même VariableSet_63. Seul le mode C3 prend 79.then → 394 SetViewTargetWithBlend(ChargenCameras[0]).
  • Minuterie UpdateRoomCam : 91 (vue = CamRoom) vrai → 92 (CamC3On faux) → 93 (C2Scripted faux) → 467 : CamRoom suit la tête du joueur, sans garde SexOn.

b) La souris sans Maj fait tourner SceneCamera, qui n'est pas en vue (B2i).

  • PC, souris X pendant le sexe : InputAxisEvent_0 → 244 → VariableSet_27 (leftright) → 73 (MoveCamera) → 37 → 101 (SexOn) vrai → 75 (SexCameraIndex = 0) vrai → 71 (Room.bHidden) faux → 499 Birdview.CircleCamera(leftright).
  • Room, CircleCamera (CustomEvent_1) → 96 : condition AND_61 = (OR_60 (vue = CamRoom OU CamC1On)) ET NOT SexOn (453, B2i). Pendant le sexe, 96 est toujours faux → 26 → 19 (MoveObject, Maj) faux → 149 SetActorRotation(SceneCamera) → B2n (orbite de SceneCamera). La même garde ferme 97 (AND_61) et 98, 99, 100 (AND_62).
  • Avant B2i, avec la vue sur CamRoom, 96 était vrai et la souris faisait tourner CamRoom (C2) : la caméra bougeait, mais Maj ne déplaçait plus les personnages (défaut corrigé par B2i). B2i a fermé la voie C2 pendant le sexe, alors que Q8 garde la vue sur la caméra C2. Rien de visible ne répond plus à la souris en mode Scene.

Pourquoi COMMUN-5 n'a rien montré : le cas H1 portait sur Maj + souris (personnages), qui marche. La scène lancée depuis C2 a exactement le même défaut (vue CamRoom, souris vers SceneCamera). Le départ depuis C1 n'est pas la cause : il change seulement la vue de la première phase (PlayerCharacter au lieu de CamRoom).

Hors de cause :

  • STG : LPDirectControlSubsystem s'arrête pendant le sexe (ScenePlaying, .cpp:759 et :889) ;
  • B2m : le POV passe par 75.else → 136 → 204, sans lien avec le mode Scene ;
  • B2k : sa branche est derrière 81 (SexOn faux) ;
  • B2n : il est correct, mais il fait tourner une caméra qui n'est pas en vue.

3. Correctif proposé (Room seul, fiche lp-mcp-bp « B2p ») : option A recommandée

Option A : pendant le sexe, la vue Scene est SceneCamera, comme dans le jeu d'origine. Cela revient sur Q8 (décision de l'utilisateur : « étape hors POV = caméra de pièce, C2 si le mode est C1 »). C'est donc à l'utilisateur de décider. L'option A rend B2n utile (orbite autour du couple) et règle aussi la limite B2o (vue de la première phase).

  • E1 : ApplyRoomCamera : Branche S1 (VariableGet_258.SexOn) entre CustomEvent_43.then et 77. S1.then → 394 (SetViewTargetWithBlend(ChargenCameras[0]) → 303 RestoreC1, nœuds existants), S1.else → 77. Le nœud 78 devient inatteignable quand SexOn est vrai (laissé en place).
  • E2 : UpdateRoomCam : Branche S2 (258.SexOn) entre CustomEvent_45.then et 91. S2.then → 394, S2.else → 91. À l'entrée du sexe, la minuterie (qui tourne en C1 et en C2) pose SceneCamera dans les 16 ms.
  • E3 : 303.then → 441 (K2_ClearAndInvalidateTimerHandle(RoomCamTimer), existant) : la minuterie s'arrête après la pose. Sans cela, elle reprendrait la vue à chaque passage en POV. Hors sexe, en C3, elle s'arrêtait déjà au passage suivant (82.else → 440 → 441) : le résultat est le même.
  • Fin du sexe : inchangée. Le crochet de fin de sexe (B1-11, GI 2699 → ApplyRoomCamera) rappelle ApplyRoomCamera, qui rend C1 ou C2 et relance la minuterie (vu à 05:18:57).
  • Volume : 2 nœuds (Branche), 9 liens, environ 30 appels MCP. Barème : a0 b1 c1 d0 e1 f1 = 4 → N1, ajusté à N3 ; S = C3 1 + f 1 = 2 (moins de 3) : une fiche. Case C9a.
  • Garde Accessed None : ChargenCameras[0] est déjà lu sans garde par 394 et par le PC 353. SexOn est lu sur 258, comme 78 et 81.
  • Risque : le cadrage de SceneCamera en début de scène est celui que le jeu d'origine pose (pickPreset, moveToBedIfPossible). Non revu depuis la migration : à vérifier en V7.

Option B : garder Q8 (vue CamRoom) et rendre la souris à C2 hors Maj. Le NOT de 453 lirait SexOn ET MoveObject au lieu de SexOn (1 nœud AND, 3 liens). La caméra tournerait alors autour de la tête du joueur, à 1 m (cadrage C2), et non autour du couple. B2n resterait sans effet (sauf départ en C3). La première phase depuis C1 tournerait le regard C1. C'est moins proche du jeu d'origine : non recommandée.

4. Cas de test (à ajouter, V7)

N° Action Attendu
CAM-41 Lancer une scène depuis C1, puis depuis C2, puis depuis C3. Souris sans Maj dès le début. Vue de scène (SceneCamera) dès l'entrée ; la caméra tourne autour du couple.
CAM-42 C : POV, POV, Scene. Souris sans Maj. Même orbite ; aucune reprise de la vue par C2.
CAM-43 Maj + souris, flèches. Les personnages tournent et se déplacent (B2i).
CAM-44 Fin de la scène. Retour au mode d'avant (C1, C2 ou C3), minuterie relancée en C1 et C2.

5. Estimation et ordre

  • Rédaction de la fiche et instantané : 15 min. Passage UE (lp-mcp-bp, puis VAL V5 et V6 par lp-validator) : environ 45 min d'éditeur, plus 1 min de compilation stricte et 1 à 2 min par commandlet.
  • Room seul. Aucun recouvrement avec STE (ORG4) tant que STE ne touche pas Room. Pas besoin de passer avant ORG4 : le défaut ne bloque pas l'essai en cours (POV et Maj marchent), et il entre au prochain passage UE.
  • Décision attendue de l'utilisateur : option A (revenir sur Q8 pendant le sexe) ou option B.

6. Complément : KO « souris + déplacement » = Maj + souris pour déplacer le couple (précision de l'utilisateur)

Question de Big Boss : B2n (après 149) ou B2k (voie C1 de la minuterie) prennent-ils ou coupent-ils la voie Maj quand CamC1On est vrai pendant le sexe ? Non, ni l'un ni l'autre (établi par les dumps).

  • B2n est sur 19.else (sans Maj), après 149. La voie Maj est 19.then → 29 (SexOn) → ForEach SexPersons → SetActorRotation : B2n ne la touche pas.
  • B2k est sur 83.then, derrière 81 (SexOn faux). Pendant le sexe, la minuterie s'arrête à 81.then, qui est vide.
  • CamC1On n'entre dans la voie de la souris que par OR_60, qui est masqué par NOT SexOn (B2i) dans AND_61. Pendant le sexe, 96 et 97 sont faux quel que soit CamC1On. Les flèches (97 → 9 → 14 → 27 → 139) suivent la même garde.
  • STG est coupé pendant le sexe (LPDirectControlSubsystem.cpp:759). Aucun C++ ne consomme Maj, la souris ou les flèches (pas d'InputComponent, seulement des lectures d'état).
  • Preuve au journal que la voie couple marche : les relevés « LPDebug persons » montrent le couple déplacé ensemble entre 05:13:17 et 05:13:27 (MC et partenaire : (-3131513, -14967009) → (-3131471, -14967049) → (-3131558, -14967097)). Cela se passe en mode Scene, dans une scène lancée depuis C1. Ensuite, la position ne change plus jusqu'à la fin (05:18:57). Le journal ne dit pas si l'utilisateur a essayé.

Pistes qui expliqueraient le ressenti sans défaut de code (à confirmer avec l'utilisateur) :

  1. En POV, Maj + souris n'a jamais tourné le couple, même dans le jeu d'origine : la souris va à 75.else → 136 → 204 (regard), et Room n'est pas appelée. La scène a passé environ 52 s en POV (05:12:43-05:13:04, 05:13:18-19, 05:14:19-05:15:11). Le cas CAM-38 (« en POV : Maj + souris ») attendait à tort une rotation.
  2. La rotation demande aussi MoveCamera (PC 73, InputAction_50 : estimé bouton du milieu ou Alt). Sans ce bouton, la souris va vers 247/246 (vitesse d'animation avec Manual).
  3. Le déplacement par flèches demande Maj (14/17).

Proposition : pas de correctif propre à ce KO. CAM-B2p-SPEC.md le couvre :

  • P2 : les flèches déplacent le couple sans Maj pendant le sexe ;
  • P3 : un témoin « Sexe : touches … » journalise Maj, MoveObject, MoveCamera et la vue si le cas revient.

7. Complément : après la scène, la vue C1 regarde le plafond (cinéma, assis, fnt_new047, dès 05:23)

Journal : C1 rétabli à 05:18:57 (fin de scène). Aucune ligne caméra jusqu'à 05:26:00 : C passe de C1 à C2, puis RestoreC1 remet C1Hidden à 0. L'utilisateur dit qu'un passage en C2 rétablit la vue.

Cause (établie par le dump de Room ; seul le moment de la pose est estimé). En C1, l'orientation de la vue est celle du composant caméra FirstPersonSM (classe FirstPerson_C, une UCameraComponent). Elle est figée une fois pour toutes à l'entrée en C1, sur la pose de la tête à cet instant :

  • voie C1 d'ApplyRoomCamera, seulement si C1Hidden est faux (80.else) :
    • 430 : AttachToComponent(Mesh, head, KeepRelative) ;
    • 431 : SetRelativeTransform à la rotation (90, 90, 0) par rapport à l'os head ;
    • 446 : SetWorldLocation au milieu des yeux ;
    • 433 : AttachToComponent(Mesh, KeepWorld). À partir de là, la caméra suit le corps (lacet de l'acteur), mais son tangage reste celui que la tête avait à l'instant de la pose ;
  • si C1Hidden est déjà vrai (80.then), on passe directement à 434 SetViewTarget, sans aucune remise ;
  • minuterie C1 : B2k (512) recale la position à chaque passage, jamais l'orientation ;
  • en C1, hors sexe, aucune voie ne règle le tangage de FirstPersonSM : la souris Y va vers RotateCamera → 99, qui est faux hors CamRoom ; 316 n'est que dans la voie sexe.

Enchaînement probable (estimé) :

  • pendant la scène, chaque retour en Scene passe par RestoreC1, qui met C1Hidden à 0 (05:13:04, 05:15:11) ;
  • à la fin de la scène, ApplyRoomCamera (crochet B1-11 de GI) prend 80.else et pose FirstPersonSM sur la tête alors que le corps est encore dans la pose de fin de scène (SF_ft04_0, couché au relevé de 05:18:47). La tête regarde vers le haut, et la vue est figée vers le plafond ;
  • C1Hidden passe à vrai : les ApplyRoomCamera suivants (cinéma) prennent 80.then, sans nouvelle pose ;
  • le passage en C2 (05:26:00) refait RestoreC1. Au retour en C1, la pose est refaite sur une tête droite : la vue revient.
  • Ce n'est pas B2m : 204 et 316 tournent le FirstPersonSM de SexPersons[i], et 431 remplace ensuite toute la rotation relative. B2m n'agit que si 80.then est pris sans remise.
  • Le même mécanisme explique toute vue C1 penchée quand on entre en C1 dans une pose où la tête est inclinée (assis, allongé, animation de meuble).

Correctif (B2p, partie P5, Room, même passage que P1 et P2). Dans la minuterie C1, après 512 (B2k) : K2_SetWorldRotation(FirstPersonSM 265, MakeRotator(Pitch 0, Yaw ← 514.Yaw, Roll 0)), avec 514 = BreakRotator(GetControlRotation), qui existe déjà. La vue C1 est remise à l'horizontale, dans la direction de contrôle, à chaque passage (0,016 s), quelle que soit la pose de la tête. Effet de bord voulu : assis, la souris fait aussi tourner la vue (le lacet de contrôle est réglé par B2h). 2 nœuds, 4 liens. Le tangage à la souris en C1 n'est pas traité (ce serait une demande à part).

Déplacement impossible au même moment : piste STG, hors CAM. Le relevé de 05:19:02 montre que le joueur occupe de nouveau NODE_AddStaticMeshComponent-0_37 après la scène (OnSeat : la marche ne tourne pas le corps).

8. PRIORITAIRE (C5-2 bis) : personnages assis éliminés par le rendu (bar, C2, 05:28-05:29 et 05:31-05:32)

Cas 1 (05:28-05:29) : la PNJ assise en face disparaît selon l'angle. Cas 2 (05:31-05:32) : le joueur (Bridget, assise à la place _93, hidden=0, fnt_new048-f-0-sit, meshZ -80,4) disparaît quand la caméra bouge ; la chaise est vide au premier plan, les deux PNJ restent visibles.

Preuve que ce n'est pas un meuble entre la caméra et la tête (établi) : la vue est C2 sans interruption de 05:26:00 à 05:32:57 (lignes « Touche C »). T-occ trace de CamRoom vers la tête toutes les 0,5 s (canal Visibility, collision complexe, Room compris). Il n'a écrit qu'une ligne, à 05:26:03 (fnt_new163), puis rien jusqu'à 05:32:57. Le rayon caméra → tête était donc libre pendant les deux disparitions. C'est le rendu qui élimine le maillage : frustum ou requête d'occlusion, sur des bornes fausses. Cela touche tous les personnages assis (et sans doute aussi couchés ou en animation de meuble).

Ce qui est établi (lecture des assets, sans éditeur) :

  • bridget_gp.uasset et michael.uasset n'importent aucun asset physique (imports : matériaux, squelette, /Script/Engine.SkeletalMesh ; aucun PhysicsAsset en import). Sans asset physique, UE calcule les bornes d'un SkeletalMeshComponent à partir des bornes importées de la pose de référence (debout), décalées de la seule translation de l'os racine. Elles ne suivent pas la pose (moteur, USkinnedMeshComponent::CalcMeshBound) ;
  • Person.uasset contient BoundsScale (3 fois) et bUseAttachParentBound (2 fois) dans sa table des noms : un ou plusieurs composants de Person les modifient. Lesquels et avec quelles valeurs : c'est à lire par MCP ;
  • aucun C++ du projet ne règle les bornes ni le culling. r.AllowOcclusionQueries=True (DefaultEngine.ini:30).

Hypothèse (forte, pas encore prouvée) : la pose assise des meubles (fnt_*) déplace le corps par rapport à la racine (meshZ -80,4 au lieu de -90 et décalage du siège). Les bornes debout, centrées sur la racine, peuvent alors se retrouver derrière la table ou le dossier, alors que le corps est visible. La requête d'occlusion matérielle juge ces bornes cachées et élimine tout le maillage. C'est la « chaise vide » sous un angle voisin. Si le maillage du corps utilise en plus bUseAttachParentBound, ses bornes sont celles de la capsule, et l'écart est encore plus grand.

Preuve proposée, rapide et sans écriture : le build COMMUN-6 est en Development, donc la console est disponible. Au même endroit, avec le même angle :

  • r.AllowOcclusionQueries 0 : si la PNJ ne disparaît plus, c'est l'occlusion qui est en cause (la commande ne se sauvegarde pas) ;
  • show Bounds : affiche les bornes, on voit si elles sont décalées du corps.

Correctif proposé (CAM-B2q, C++, environ 40 lignes) : balayage léger toutes les 2 s des ACharacter de classe Person_C. Pour chacun, on ne retouche que si les valeurs diffèrent, ce qui couvre aussi le changement de maillage. Sur le SkeletalMeshComponent du corps et ses enfants maillés (vêtements, cheveux) :

  • bUseAttachParentBound = false pour le corps (les enfants gardent leur réglage) ;
  • BoundsScale = 3,0. BoundsScale agrandit la boîte autour de son centre, sans la déplacer : un facteur 2 (environ ±50 × ±25 × ±90 cm → ±100 × ±50 × ±180) peut ne pas couvrir un corps assis décalé de plus d'un mètre de la racine ; 3 laisse de la marge. À ajuster à l'essai avec show Bounds ;
  • UpdateBounds() et MarkRenderStateDirty().

Coût :

  • Écriture : 30 min, compilation 10 min (sous jeton UE), VAL statique 15 min, essai au build suivant.
  • En jeu : seuls les personnages de la pièce courante sont concernés (les autres sont cachés). Avec des bornes 3 fois plus grandes, un personnage juste hors écran ou juste caché est encore dessiné (ombres comprises). Pour une dizaine de maillages Daz, le surcoût GPU est faible ; à mesurer par stat unit au bar avant et après. Le jeu ne fait aucun travail CPU de plus par image.
  • Risque : aucun sur la collision et les tracés (les bornes de rendu ne servent pas aux collisions).

Alternatives :

  • Un asset physique pour bridget_gp et michael donnerait des bornes qui suivent les os. C'est plus juste, mais il faut modifier deux gros assets dans l'éditeur, et ses corps entreraient en jeu dans les tracés Visibility (sondes C2 de B2f, T-occ). Hors alpha.
  • r.AllowOcclusionQueries 0 dans DefaultEngine.ini supprimerait le cas d'occlusion pour tout le jeu, avec un coût de rendu global, et ne règle pas le cas frustum. Non recommandé.

Attention au périmètre : il ne s'agit pas d'éditer Person.uasset, mais de régler en jeu les composants des instances. Le domaine Person relève de STG/STE : Big Boss attribue le lot. Le même correctif couvrirait les disparitions du joueur assis dues aux bornes. Celles dues à un vrai meuble entre la caméra et le joueur (T-occ, 05:26:03, fnt_new163) relèvent de B2l, après l'alpha.

Témoin proposé avec B2q (T-cull) : toutes les 0,5 s, pour chaque Person à moins de 15 m dont le bassin est dans l'écran mais qui n'a pas été rendue depuis 0,2 s (WasRecentlyRendered(0.2) faux), écrire : Temoin culling : <Person> non rendue, bassin a %.0f cm du centre des bornes, bornes %.0f x %.0f x %.0f, BoundsScale %.1f, parent=%d. Le témoin peut entrer avant le correctif, dans le même build, pour chiffrer le décalage. Il touche le même fichier et prend 15 min de plus.

9. Complément (J5) : C1 décalée après le lever au bar (épaule et bras visibles, 05:33-05:34)

Journal :

  • 05:33:02 : passage en C1, assis à _93 (C1Hidden=1, indicateur « C1 First person ») ;
  • 05:33:04 : « Marche : le joueur quitte …_93 (unOccupyFnt) », puis la marche ;
  • aucune ligne d'indicateur avant le C de 05:34:22 : la vue est restée PlayerCharacter en C1 ;
  • après le cycle C2 → C3 → C1 (05:34:22-05:34:28), la vue est correcte.

La minuterie de B2k tourne (établi par le dump) :

  • elle part sur la voie C1 d'ApplyRoomCamera (434 → 329 SetTimerDelegate(UpdateRoomCam, 0,016, en boucle)) ;
  • elle ne s'arrête que par 441, après 440 RestoreC1, quand CamC1On est faux (82.else) ou que la vue n'est plus le joueur (83.else), ou par un passage en C2 ou C3 ;
  • ni le lever (unOccupyFnt, KO3c) ni la marche de STG n'y touchent : le C++ ne lit et n'écrit aucune variable de Room liée à la caméra. La vue n'a pas quitté le joueur (aucune ligne d'indicateur) ;
  • l'utilisateur voit la vue « à peu près à hauteur d'yeux » : le recalage de position fonctionne.

Cause : l'orientation, la même que § 7 (établi pour le mécanisme, estimé pour la pose) :

  • l'orientation de FirstPersonSM est posée une fois, à l'entrée en C1 (431, par rapport à l'os head). Ici, c'était en position assise, la tête tournée ou penchée selon fnt_new048 ;
  • ensuite, la caméra est accrochée au corps (433, KeepWorld) : elle suit le lacet de l'acteur, mais garde l'écart d'orientation de la tête assise ;
  • une fois debout, la marche de STG aligne le corps sur le lacet de contrôle (LPDirectControlSubsystem.cpp:1034), et la caméra garde l'écart. On voit son épaule et son bras gauches ;
  • le cycle C refait la pose sur une tête droite, ce qui corrige.
  • B2k ne recale que la position (512 SetWorldLocation), jamais l'orientation.

Correctif : P5 de CAM-B2p-SPEC.md (SetWorldRotation(0, lacet de contrôle, 0) après 512, à chaque passage). Il règle § 7 et § 9 d'un coup : 2 nœuds, 4 liens dans Room.

Remarque pour STG : currentAnimation reste fnt_new048-f-0-sit après le lever (relevés LPDebug). Si RestoreAnim rend l'Anim BP Daily_F_C, qui relit cette chaîne, le corps pourrait reprendre la pose assise à l'arrêt. Ce n'est pas de mon ressort ; à signaler à STG.

10. Complément : après un changement de personnage (CharGen) en C1, C bloqué et souris sans effet (05:35-05:37)

Journal :

  • 05:34:28 : C1 (SpaceOn=1) ;
  • 05:35:55-57 : menu principal, puis CharGen (LOAD ROOM chargenroom) ;
  • 05:36:09 : LOAD ROOM pub___vin01, puis GI FOLLOWUP ;
  • 05:37:28 : témoin « Touche C » : BuilderOn=0 SpaceOn=0 SexOn=0 CamC1On=1 C1Hidden=1 ; entree PC=1 regard ignore=0 viewport ignore=0 ; vue PlayerCharacter. H1 est établie (C bloqué par la garde SpaceOn du PC, B1-7, 131) ; H4 est écartée (entrées du PC intactes).

10.1 Qui met SpaceOn à faux, et pourquoi rien ne le remet (établi, dump GI EventGraph de B1-VAL)

Dump du 2026-10-13. Depuis, GI n'a été modifié que par STE-H3d (CallFunctionReturnBool), pas sur ces chaînes ; à redumper à la fiche.

  • Écritures de SpaceOn dans GI : 5 nœuds.
    • Vrai : 240 (OpenSpace), 296 (CreateCharGenWidget), 297 (CreateInventoryWidget).
    • Faux : 241 (CloseSpace), 233 (ShowRender).
  • Sortie de CharGen (CloseCharGen, CustomEvent_101) : … RevertStripAll → 4263 MovePlayerBack → 346 ShowUI → 347 ShowRender → 6009 ClearCharGenWidget → 1337 CharGenOn = faux → 1563 (GameStart) :
    • vrai (nouvelle partie) : InventoryInput → CreateInventoryWidget → SpaceOn = vrai (297) ;
    • faux (changement de personnage en cours de partie) : 167 DelayThenFollowUp, et rien ne remet SpaceOn.
  • ShowRender (CustomEvent_519) : 3014 → 213, et les deux branches de 213 mènent à 2920 → 2977 SetViewTargetWithBlend(PC, Birdview) → 233 SpaceOn = faux → 694 updateAllPersonButtons. ShowRender remet le jeu en mode carte de la ville. Or, ici, le joueur revient dans une pièce (MovePlayerBack, puis LOAD ROOM pub).
  • Le seul appel d'OpenSpace dans GI est 671, sur la chaîne de fin de scène (SceneOn, PregnancyBlocked, ConsolidateBeautyStandards). SpaceOn reste donc faux jusqu'à la prochaine fin de scène ou la prochaine ouverture de l'inventaire.
  • C'est un défaut latent du jeu d'origine. Il est devenu visible avec nos gardes SpaceOn : B1-7 (touche C) et B1-6 (LoadRoom n'appelle ApplyRoomCamera que si SpaceOn est vrai). Après CharGen, un changement de pièce ne réapplique donc plus le mode de caméra.

10.2 Souris sans effet en C1 : SpaceOn n'en est pas la cause ; hypothèse H-M (à prouver)

  • Le regard C1 passe par CircleCamera → 96.then (OR_60 contient CamC1On) → 516 SetControlRotation. Cette voie ne lit pas SpaceOn.
  • Côté PC, la souris n'appelle CircleCamera que si MoveCamera est vrai (73). MoveCamera passe à vrai par StartMoveCamera, sur appui d'un bouton de souris (InputKey_10 et _14 → 108 / 106). Sauf si JournalOn OU ContactsOn OU MainMenuOn est vrai (OR_33, OR_37 : la branche « vrai » est vide).
  • H-M : CharGen a été ouvert depuis le menu principal (menus=[MainMenu_C] à 05:35:57). MainMenuOn ne passe à faux que par CloseMainMenu (GI CustomEvent_904). Il est appelé par le PC (273) et par le widget MainMenu (le nom est dans MainMenu.uasset, mais ce widget n'a pas été dumpé). Si le bouton « changer de personnage » ne ferme pas le menu par CloseMainMenu, MainMenuOn reste vrai : les boutons de souris ne lancent plus MoveCamera, et aucune caméra ne répond plus à la souris, dans aucun mode.
  • Test sans écriture : après CharGen, ouvrir puis fermer le menu principal (Échap, Échap). Si la souris revient, H-M est établie. Le témoin « Touche C » pourrait aussi écrire MainMenuOn, JournalOn, ContactsOn et MoveCamera (environ 10 lignes, fichier CAM).

10.3 À-coup de la caméra à la sortie de CharGen (estimé)

Ordre probable :

  1. MovePlayerBack recharge la pièce (LOAD ROOM pub à 05:36:09.212, avant FOLLOWUP à .226). SpaceOn est encore vrai (posé par CreateCharGenWidget), donc B1-6 appelle ApplyRoomCamera. La voie C1 pose FirstPersonSM sur la tête (430 à 433), puis attend une image (449 DelayUntilNextFrame).
  2. Pendant cette image, ShowRender met la vue sur Birdview et SpaceOn à faux.
  3. À l'image suivante, 434 remet la vue sur le joueur et relance la minuterie. B2k recale la position 16 ms plus tard.

On voit donc une image de Birdview, puis une vue C1 posée sur une tête qui n'est pas encore à sa place (joueur déplacé, pose de CharGen), recalée au passage suivant : c'est l'à-coup.

  • Avec P5 (B2p), le recalage de l'orientation se fait aussi au premier passage.
  • Option P6 (Room, environ 3 nœuds), pour supprimer l'à-coup : recaler position et orientation entre 449 et 434, avant que la vue revienne.

10.4 Correctif proposé (CAM-B2r)

F-S (SpaceOn). Trois options :

  • (a) GI, dans CloseCharGen, branche GameStart faux : remettre SpaceOn à vrai, ou appeler HideRender et OpenSpace. C'est la réparation la plus juste, mais GI n'est pas un fichier CAM : propriétaire à désigner par Big Boss.
  • (b) C++ CAM (LPCameraModeSubsystem, environ 30 lignes) : sur le passage de GI.CharGenOn de vrai à faux, dès que la vue est de nouveau une caméra de pièce (le joueur avec CamC1On, CamRoom, ou SceneCamera avec CamC3On), si SpaceOn est faux : mise à vrai par réflexion, avec une ligne au journal (SpaceOn remis a vrai apres CharGen). Il agit seulement après CharGen, une fois. 30 min + compilation. Il écrit une variable de GI en jeu, ce qui relève du domaine GI : à valider par Big Boss.
  • (c) Retirer SpaceOn des gardes B1-6 et B1-7 (PC et Room). Non recommandé : ces gardes empêchent C et ApplyRoomCamera de jouer sur la carte de la ville.

Recommandation : (b) pour l'alpha, (a) ensuite.

H-M : correctif seulement si le test le prouve. Ce serait un appel CloseMainMenu sur la voie du bouton CharGen du widget MainMenu (fichier UI, propriétaire à désigner), ou la même méthode que (b) : MainMenuOn remis à faux à la sortie de CharGen si le widget MainMenu n'est plus à l'écran.

À-coup : P6, facultatif, avec B2p-R.

Estimation de B2r :

  • (b) et le témoin étendu : environ 45 min, plus la compilation, sous jeton UE ;
  • (a) et la correction de H-M : 30 à 45 min de MCP chacune, chez leur propriétaire.