12 KiB
Fiche ERR145-DIAG — Niveau N3− — Modèle prévu non précisé par la fiche | utilisé claude-opus-5-5 — Agent lp-diag — Skills sk-protocol, ue-cpp-build, lp-commandlets, lp-paths
ERR145-DIAG : C1 assis, le joueur se voit sans tête (2026-10-20)
Ordonnateur : Big Boss (local_104ff22d). Lecture seule : ni copie UE, ni MCP, ni compilation. Seul fichier écrit : ce livrable.
1. Symptôme et reproduction
- Symptôme (établi) : « bug à référencer en C1 quand assis. On se contemple décapité. à voir en build 2 »
(
etat/BOSS/BOSS-JOURNAL.md:872, 2026-10-20 13:08 ; capture non enregistrée,:873) ; ERR-145 (Docs/Registre_Erreurs.md:152) ;etat/BOSS/COMMUN-17-BILAN.md:65. - Journal de l'essai (établi, horodatage du journal en UTC, heure locale = UTC + 2 ; 13:08 locale = 11:08 UTC) :
D:\Game\LifePlay_Build_5.8_Development_COM17\Windows\LifePlay\Saved\Logs\LifePlay.log- l. 1869 (11:04:41) : touche C,
CamC1On=1 C1Hidden=1; l. 1874-1894 : C1 debout, souris, tangage jusqu'à -60 ; - l. 1898 (11:05:08) : sortie de C1 (tangage remis à 0) ; l. 1972 (11:05:33) : joueur assis au meuble
_3; l. 1975 : menuScene_C; l. 1978 (11:05:34) : « place_3libérée sans marche, joueur posé à gauche à 80 cm du siège » ; - l. 2111 (11:05:40.384) : fin du blocage par
Scene_C; l. 2329 (11:05:40.724) : retour en C1 (C1 souris,minuterie RoomCam active=1), aucune ligne « Touche C » entre l. 1869 et l. 2472 : la vue C1 est remise par le jeu à la fermeture du menu, joueur debout (posé à côté du siège) ; - l. 2387-2388 (11:05:42.397) : le joueur s'assoit au meuble
_20: « racine Genesis8Female à 128 cm du bassin, à 104 cm du composant » ; - l. 2390-2417 (11:05:43 → 11:07:56) : C1 assis, regard vers le bas (tangage -38 à -60) ; l. 2472 (11:08:12) : touche C vers C2. La plainte (13:08 locale) tombe dans cette fenêtre.
- l. 1869 (11:04:41) : touche C,
- Recette de reproduction proposée (non jouée) : en jeu, debout, touche C jusqu'à l'indicateur « C1 First person » ; aller s'asseoir sur une chaise (clic) sans quitter C1 ; regarder autour et vers le bas. Puis, toujours assis, C → C2 → C1. Puis se lever (Z) en C1.
2. Hypothèse testée (une seule)
H1 (reformulation précise de l'hypothèse de la fiche) : en C1, la tête est masquée par HideBoneByName(Mesh, 'neckLower')
et la caméra est recalée à chaque passage de la minuterie sur le milieu des os lEye / rEye. Or un os masqué sort des
os requis et n'est plus animé : lEye et rEye restent dans la pose (en espace composant) qu'ils avaient au moment
du masquage. Entré debout en C1 puis assis, le joueur garde une « tête fantôme » à la position debout, la caméra la suit,
et le corps assis (déplacé par l'animation de siège, racine à environ 1 m du composant) est vu de l'extérieur, sans tête.
3. Essai et résultat brut
Lectures (aucune exécution du jeu ; aucune commande ne modifie de fichier).
3.1 Ce que fait C1 (établi)
- Entrée en C1 (
Room.ApplyRoomCamera, voie C1,C1Hiddenfaux) : attacheFirstPersonSMàhead, transformée relative,SetWorldLocation(FirstPersonSM, 0,5 × (GetSocketLocation(Mesh,'lEye') + GetSocketLocation(Mesh,'rEye')))calculé avant le masquage, détache,HairSM.SetVisibility(faux),HideBoneByName(PlayerCharacter.Mesh,'neckLower',PBO_None),C1Hidden := vrai, une image d'attente,SetViewTargetWithBlend(etat/CAM/CAM-B1-IMPL.md:22,:87,:104-109). B1d note déjà : « un os masqué sort des os requis et ne bouge plus » (CAM-B1-IMPL.md:107). - Si
C1Hiddenest déjà vrai, la voie saute toute remise et va directement à la vue (etat/CAM/CAM-COMMUN6-DIAG.md:155). - Minuterie
UpdateRoomCam(0,016 s) : en C1 avec vue =PlayerCharacter,83.then → 512 K2_SetWorldLocation(FirstPersonSM, milieu de lEye/rEye) → 540 K2_SetWorldRotation(etat/CAM/CAM-B2s-IMPL.md:137,CAM-B2k-IMPL.md:75-76,:102). La position de la caméra suit donc en permanence les os des yeux… qui sont sousneckLower, donc masqués (lEye,rEyesoushead, sousneckLower:CAM-B1-IMPL.md:110-111). RestoreC1(sortie de C1 ou vue ≠ joueur) faitUnHideBoneByName(neckLower)etC1Hidden := faux(CAM-B1-IMPL.md:29-30,:87-88).- C++ :
LPCameraModeSubsystem.cppne masque rien (aucunHideBone,SetOwnerNoSee) ; il règle le tangage (TickC1Pitch,:770-806), remetC1Hiddenà faux en entrée de scène (:752-760) et écrit le témoin C1 (:1196-1231). GrepVisibilityBasedAnimTickOption|AlwaysTickPosesurSource: 0 résultat.
3.2 Moteur 5.8 installé (établi, D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime\Engine\Private\)
Components/SkeletalMeshComponent.cpp:2255-2258:ComputeRequiredBonesappelleExcludeHiddenBones(« Purge invisible bones and their children »).Components/SkeletalMeshComponent.cpp:2321-2332: un os n'est gardé que s'il estBVS_Visible, ou siVisibilityBasedAnimTickOption == AlwaysTickPoseAndRefreshBones; les autres sont retirés des os requis.Components/SkeletalMeshComponent.cpp:2271-2275puisSkinnedAsset.cpp:522-536: l'espace composant n'est recalculé que pour les os de cette liste : les os masqués gardent leur dernière transformée.Components/SkinnedMeshComponent.cpp:2433: une position d'os = transformée en espace composant ×LocalToWorld(branche avec leader ; branche sans leader estimée identique) :GetSocketLocation('lEye')= pose figée × position actuelle du composant.Character.cpp:125:ACharacterrègleMesh->VisibilityBasedAnimTickOption = AlwaysTickPose, pas…AndRefreshBones.- Surcharge dans Person :
rg -a -c 'AlwaysTickPose|VisibilityBasedAnimTickOption'surContent/BPs/Person.uasset,Room.uasset,FirstPerson.uasset→0,0,0(sortie :Person.uasset: 0/Room.uasset: 0/FirstPerson.uasset: 0).
3.3 Le témoin C1 ne peut pas voir le défaut (établi)
WitnessC1 mesure la distance caméra ↔ milieu de LeftEyeSocket / RightEyeSocket du même Mesh
(LPCameraModeSubsystem.cpp:1214-1215) : les mêmes os figés que ceux sur lesquels la minuterie pose la caméra. La distance
reste proche de 0 même quand la caméra est à 1 m du corps. Grep Temoin C1 sur les journaux COM16 à COM23 : 0 ligne
(seules lignes trouvées : unOccupyFnt, C1 : tangage … remis a 0, pile PlayFntAnim). L'absence de ligne ne prouve donc rien.
3.4 Même mécanisme déjà vu, attribué à autre chose (établi pour les constats, rapprochement estimé)
- ERR-106 (C5-3) : « après être entré assis puis changé de pose, la caméra est dans le buste » ; résidu après B2k « décalage
après un lever » (
Registre_Erreurs.md:113). - ERR-111 : « C1 à côté de la tête après un lever … un cycle C corrige » (
Registre_Erreurs.md:118). Un cycle C passe parRestoreC1(démasque) puis remasque dans la pose nouvelle : c'est exactement ce que prédit H1. B2k (recalage de position) et B2s (orientation) ne pouvaient pas corriger le fond, puisqu'ils relisent les os figés. - ERR-145 est le cas inverse : entré debout, puis assis.
4. Verdict : confirmée sur le mécanisme (estimé fort), non prouvée en jeu
- Établi : la caméra C1 suit
lEye/rEyeà chaque passage (§ 3.1) ; ces os sont masqués en C1 ; le moteur 5.8 retire les os masqués des os requis et ne recalcule plus leur pose saufAlwaysTickPoseAndRefreshBones(§ 3.2) ; le maillage du joueur est enAlwaysTickPose(défautACharacter, aucune surcharge lue dans les assets) ; à 11:05:40 le joueur revient en C1 debout puis s'assoit à 11:05:42 avec une racine à 104 cm du composant (journal COM17 l. 1978, 2329, 2388). - Estimé : la tête (et donc la caméra) reste dans la pose debout en espace composant ; le corps, déplacé par l'animation de siège, est vu de dehors et sans tête ; le témoin est aveugle (§ 3.3). Cause d'ERR-145 = os masqués figés.
- Inconnu : le moment exact de l'entrée en C1 à 11:05:40 (pas de ligne « Touche C », remise par le jeu à la fermeture de
Scene_C, voie exacte non lue) ; la capture (absente) ; la distance réelle caméra ↔ corps ; l'effet du double tampon des transformées (bDoubleBufferedComponentSpaceTransforms) sur la valeur figée (sans effet sur la conclusion, estimé). - Hypothèse concurrente non exclue, H2 : caméra bien aux yeux, mais le tangage vers le bas (nouveau depuis A1, journal l. 2390-2417
jusqu'à -60) montre le cou coupé à
neckLower. Essai qui départage : § 5, point 2 (cycle C assis).
Endroit du correctif
C++ (Source/LifePlay/LPCameraModeSubsystem.cpp), recommandé. Ni GI, ni PC, ni Person : la révision unique de GI n'est pas concernée.
Correctifs proposés
| Option | Constat traité | Coût | Risque |
|---|---|---|---|
A (recommandée) C++ : quand la vue est C1 (LabelC1), mettre PlayerCharacter.Mesh.VisibilityBasedAnimTickOption = AlwaysTickPoseAndRefreshBones (valeur d'origine gardée, remise en sortie de C1, même endroit que ResetC1PitchOnExit, :811-827) ; les os masqués restent requis et animés (SkeletalMeshComponent.cpp:2322), le masquage visuel reste par l'état de visibilité des os |
ERR-145, et le fond d'ERR-106 / ERR-111 (lever, rasseoir) | 15 à 25 lignes, une compilation, aucune écriture d'asset | à confirmer en SPEC : le changement d'option doit forcer un recalcul des os requis (RecalcRequiredBones ou équivalent, API 5.8 à lire) ; une image de retard possible au passage ; un os masqué non requis est-il bien rendu masqué quand il redevient requis (SkeletalRender.cpp:477-487, cité par CAM-B1e-CART.md:68-72) |
B Room (MCP) : dans UpdateRoomCam, poser la caméra depuis un os visible (parent de neckLower) plus un décalage |
ERR-145 en partie | ~5 nœuds, instantané Room, VAL BP | décalage homme / femme à calibrer ; regard qui ne suit plus les mouvements de tête de l'animation |
C C++ ou PC : au changement d'OccupyingFnt en C1, rejouer RestoreC1 puis ApplyRoomCamera (le cycle C qui corrige déjà) |
assis / levé seulement | ~20 lignes C++ (appel d'événements par nom) | une image de transition ; ne couvre pas les autres changements de pose (fin de scène, CharGen) |
Ajout à toute option : corriger le témoin C1 pour qu'il mesure la caméra contre un os non masqué (ex. distance caméra ↔
os parent de neckLower, attendu 15 à 40 cm), sinon il reste aveugle.
5. Hypothèse suivante proposée, et à quel niveau
- Preuve en jeu attendue (build suivant, C++ seulement, avant ou avec l'option A) : ligne de témoin nouvelle, une par
seconde en C1 : distance caméra ↔ os visible (
neckLowerparent),assis=, racine ↔ composant. Avant correctif, après la recette du § 1 : distance > 60 cm assis (attendu 15-40 cm debout) ; après correctif A : 15-40 cm dans tous les états. - Essai qui départage H1 / H2 (sans build, par l'utilisateur) : debout → C1 → s'asseoir : vue décapitée de l'extérieur ; puis, assis, C → C2 → C1 : si la vue revient aux yeux et que le corps n'est plus vu « de dehors », H1 ; si la coupe du cou reste visible en regardant vers le bas, H2 (alors masquer en plus le haut du buste ou borner le tangage bas, N2).
- SPEC de l'option A : N3 (
lp-impl), lecture de l'API 5.8 de recalcul des os requis avant écriture.
Tours : 21 / 30
Constats
FIX1-F7-PRE.md(currentAnimation non vidé au lever) : sans lien établi avec ERR-145 (concerne l'animation « look » etmeshZau lever,FIX1-F7-PRE.md:10,:18).KEY2-CART-02.md: aucune mention de C1 ni de la tête (GrepC1|tête|neckLower: 0 ligne).- Dumps
L6f_dump_*/CAM-B1-PRE_LPDumpFunction_*non relus directement : les nœuds de C1 sont cités depuis les livrables CAM qui les ont relus (CAM-B1-IMPL.md,CAM-B2k-IMPL.md,CAM-B2s-IMPL.md,CAM-B1e-CART.md:61-63). - Fichiers à commiter :
A Docs/Migration/etat/REL/ERR145/ERR145-DIAG.md(dépôt du jeu,ue5.8).