35 KiB
Fiche ERR145-SPEC — Niveau N3 — Modèle prévu non précisé par la fiche | utilisé claude-opus-5-5 — Agent lp-impl — Skills sk-protocol, ue-cpp-build, lp-build, lp-ue-compat
ERR145-SPEC : C1 assis, os masqués figés — option A, témoin C1 et réglages de vue (2026-10-21)
Ordonnateur : Big Boss (local_104ff22d). Étude seule : aucun code modifié, aucune compilation, ni copie UE, ni MCP, ni commit.
Seul fichier écrit : ce livrable. Amont : ERR145-DIAG.md (H1), confirmation de l'utilisateur du 2026-10-21 07:11, puis
fait nouveau relayé par Big Boss (capture 34 : le cycle C assis ne corrige pas la vue).
1. Objectif mesurable
1.1 Ce qui est établi avant d'écrire
- H1 (mécanisme) : Room masque
neckLowersurPlayerCharacter.Mesh(etat/CAM/CAM-B1-IMPL.md:87) ; un os masqué sort des os requis sauf siVisibilityBasedAnimTickOption == AlwaysTickPoseAndRefreshBones(SkeletalMeshComponent.cpp:2321-2332) ;lEye/rEye(soushead, sousneckLower) gardent leur pose en espace composant ;UpdateRoomCampose la caméra au milieu de ces os à chaque passage (CAM-B2k-IMPL.md:75, nœudSWL_512). Confirmé en jeu par l'utilisateur à 07:11 (debout → C1 → assis). - Journal de l'essai du 2026-10-21 (
D:\Game\LifePlay_Build_5.8_Development_COM23\Windows\LifePlay\Saved\Logs\LifePlay.log, heure UTC, locale = UTC + 2) :- l. 1725 (05:08:56) : entrée en C1 debout ; l. 1736 : action « Sit » (
have_a_meal_in) ; l. 1755-1757 (05:09:13) : assis au meuble_196, « racine Genesis8Female à 77 cm du bassin, à 9 cm du composant » ; l. 1758-1768 : C1 assis, tangage -11 à -57, lacet de contrôle de -175 à +187 (vue décapitée, cas ERR-145) ; - l. 1781 (05:11:21) C1 → C2 (
C1Hidden=0, doncRestoreC1passé) ; l. 1784 (05:12:10) → C3 ; l. 1787-1790 (05:12:14.978) → C1, toujours assis (aucununOccupyFntentre l. 1757 et l. 1809) ; - l. 1791-1792 (05:12:15 à 05:12:16) : tangage -0,1 puis -2,1, lacet de contrôle +133 puis +86 ; plus aucune ligne « C1 souris »
jusqu'à la sortie de C1 en l. 1800 (05:14:01).
TickC1Pitchn'écrit qu'au mouvement vertical de la souris (LPCameraModeSubsystem.cpp:782-786) et c'est le seul code qui change le tangage hors sortie (:790,:811-828).
- l. 1725 (05:08:56) : entrée en C1 debout ; l. 1736 : action « Sit » (
- Capture 34 (description de Big Boss) : C1 assis après le cycle, surface de peau cylindrique collée à gauche, haut violet et collier vus de très près à droite. Elle tombe dans la fenêtre 05:12:15 → 05:14:01 (07:12 → 07:14 locales), seule fenêtre « C1 assis après un cycle » du journal (estimé : l'heure exacte de la capture n'est pas dans le journal).
1.2 Conséquences du fait nouveau (établi / estimé)
- Établi : après le cycle, les yeux ont été relus dans la pose assise du moment (os démasqués en C2 et C3, puis remasqués à
l'entrée en C1 ; la voie C1 lit les yeux avant le masquage,
CAM-B1-IMPL.md:104-107). C'est l'état que l'option A maintient en permanence pour une pose immobile. La vue reste mauvaise : l'option A seule n'est pas prouvée suffisante. - Établi : le tangage valait environ -2° pendant la capture. « Borner le tangage » ne traite pas ce cas, et l'hypothèse H2 du DIAG (regard vers le bas sur la coupe du cou) n'explique pas la capture 34.
- Estimé (géométrie) : la largeur horizontale vue à 90° (FOV de
FirstPersonSM,CAM-B1e-CART.md:79) ne couvre verticalement qu'environ ±29° en 16:9. Pour qu'une épaule ou un cou remplisse la gauche de l'image à un tangage de -2°, il faut que le point de vue soit à hauteur d'épaule ou de cou, pas à hauteur des yeux d'une tête droite. Deux lectures restent ouvertes :- L1, gel à une phase basse : la pose « repas » plonge peut-être la tête vers l'assiette en boucle. Le remasquage a figé les yeux à un instant où la tête était baissée. A corrige le gel, mais la caméra suivra alors la tête qui plonge et remonte.
- L2, point de vue trop près du corps visible, yeux vivants : tête très penchée, regard de côté (lacet de contrôle +86, orientation
du buste inconnue), ou sommets du cou à poids mixtes étirés (voir R8). Le tout avec un plan proche à 1 cm
(
Config/DefaultEngine.ini:50, contre 10 cm par défaut dansBaseEngine.ini:361) : toute peau à plus de 1 cm est dessinée.
- Inconnu : le contenu de l'animation
fnt_new017-f-0-use(l. 1748) ; l'orientation du buste pendant la capture ; la distance réelle entre la caméra et la peau.
1.3 Objectifs (critères chiffrés au § 7)
- A : en C1, les os masqués du joueur restent requis et animés. Le témoin montre « yeux-buste » dans la plage debout, assis comme debout, et la recette du DIAG § 1 ne montre plus le corps décapité vu de dehors.
- Témoin : une ligne par seconde en C1. Elle mesure la vue contre un os visible (parent de
neckLower) et contre l'avant du buste. Elle donne aussi les yeux,assis=, l'écart racine ↔ composant, l'option d'animation et l'état du masquage. - Réglages de vue C1 (partie C, sans effet par défaut) : plan proche propre à la vue C1 et décalage du point de vue vers l'avant du buste, réglables à la console. Ils servent à trancher L1 / L2 dans la même build, sans second aller-retour.
- PNJ, POV de sexe, C2, C3, Overview, studio : comportement inchangé.
2. Fichiers touchés, ordre
Ordre : (1) LPCameraModeSubsystem.h, (2) LPCameraModeSubsystem.cpp, (3) LPRoomBoundsCameraModifier.h, (4)
LPRoomBoundsCameraModifier.cpp (tous sous D:\Game\LifePlay-master 5.8\Source\LifePlay\). Les parties 3 et 4 portent seulement la
partie C. Elles débordent du périmètre de la fiche, qui ne nommait que le sous-système (écart, voir Constats). Si l'orchestrateur ne
retient pas C, on retire § 2.4, les appels à SetC1View et CVarLPC1NearClip / CVarLPC1Forward ; le témoin écrit alors plan proche 0.0.
Volume estimé : partie A ≈ 45 lignes ; témoin ≈ 60 lignes, dont 36 remplacées ; partie C ≈ 35 lignes. Total ≈ 140 lignes touchées (≈ +115 / −30), aucune écriture d'asset, une compilation.
2.1 LPCameraModeSubsystem.h
- Ajouter
class USkeletalMeshComponent;aux déclarations avancées (l. 17-25). - Remplacer le commentaire du témoin C1 (l. 71) par : « Témoin C1 (ERR145) : une ligne par seconde en C1, vue mesurée contre le parent de neckLower (os visible) et l'avant du buste ».
- Après
float C1Cooldown = 0.f;(l. 74) :
/** ERR145 (option A) : en C1, les os masques par Room restent requis et animes sur le maillage du joueur
* (AlwaysTickPoseAndRefreshBones) ; option d'origine rendue des que le libelle quitte C1 ou que le joueur change.
* Transmet aussi les reglages de vue C1 au modificateur (partie C). Appele a chaque image depuis TickSwitchKey. */
void TickC1BoneRefresh(bool bC1Now);
/** Maillage dont l'option a ete changee, et sa valeur d'origine (EVisibilityBasedAnimTickOption). */
TWeakObjectPtr<USkeletalMeshComponent> C1RefreshMesh;
uint8 C1SavedTickOption = 0;
2.2 LPCameraModeSubsystem.cpp : partie A
- Include :
#include "HAL/IConsoleManager.h"(TAutoConsoleVariable,IConsoleManager.h:2028). - Dans
namespace LPCameraModeLocal, après la l. 108 :
// ERR145 : os masque par Room en C1 (CAM-B1c) ; son parent (chestUpper attendu sur Genesis 8, lu a l'execution) est le
// repere visible du temoin ; les clavicules donnent l'avant du buste.
const FName NeckLowerBone(TEXT("neckLower"));
const FName LeftCollarBone(TEXT("lCollar"));
const FName RightCollarBone(TEXT("rCollar"));
// Avant horizontal du buste : perpendiculaire a la ligne des clavicules (UE : X avant, Y droite, Z haut, donc
// (droite - gauche) x haut = avant) ; a defaut, avant de l'acteur. OutFrom nomme la source pour le temoin.
FVector BodyForward(const ACharacter* Player, const USkeletalMeshComponent* Mesh, const TCHAR*& OutFrom)
{
OutFrom = TEXT("acteur");
FVector Forward = Player->GetActorForwardVector();
if (Mesh->GetBoneIndex(LeftCollarBone) != INDEX_NONE && Mesh->GetBoneIndex(RightCollarBone) != INDEX_NONE)
{
const FVector Across = Mesh->GetSocketLocation(RightCollarBone) - Mesh->GetSocketLocation(LeftCollarBone);
const FVector Candidate = FVector::CrossProduct(Across, FVector::UpVector).GetSafeNormal2D();
if (!Candidate.IsNearlyZero())
{
Forward = Candidate;
OutFrom = TEXT("clavicules");
}
}
return Forward.GetSafeNormal2D();
}
BodyForward doit être défini après les includes de GameFramework/Character.h et Components/SkeletalMeshComponent.h
(déjà en l. 20 et 26). Il reste dans l'espace nommé, avec appel qualifié (règle C2374, lp-build § 8).
- Après la fermeture de l'espace de noms (l. 728) :
// ERR145 : bascules de l'essai en jeu (console, touche ² ou ~, DefaultInput.ini:166-168), sans recompiler.
static TAutoConsoleVariable<int32> CVarLPC1RefreshBones(TEXT("lp.C1RefreshBones"), 1,
TEXT("ERR145 : 1 = en C1, les os masques du joueur restent animes (option A) ; 0 = comportement d'avant (os figes)."));
static TAutoConsoleVariable<float> CVarLPC1NearClip(TEXT("lp.C1NearClip"), 0.f,
TEXT("ERR145 : plan proche de la vue C1, en cm (0 = plan global du projet, 1 cm)."));
static TAutoConsoleVariable<float> CVarLPC1Forward(TEXT("lp.C1Forward"), 0.f,
TEXT("ERR145 : avance du point de vue C1 vers l'avant du buste, en cm (0 = aucune)."));
- Nouvelle méthode, placée après
ResetC1PitchOnExit(l. 828) :
// ERR145 (option A, ERR145-SPEC § 2.2) : Room masque neckLower en C1 ; un os masque sort des os requis et sa pose reste
// figee (SkeletalMeshComponent.cpp:2321-2332), or UpdateRoomCam pose la camera sur lEye / rEye, sous neckLower. En C1,
// le maillage du joueur passe en AlwaysTickPoseAndRefreshBones : les os masques restent requis et animes, le rendu les
// garde masques (SkeletalRender.cpp:516-520). Le drapeau bRequiredBonesUpToDate = faux fait recalculer les os requis au
// prochain passage d'animation (SkeletalMeshComponent.cpp:1707-1712, 2879-2884), comme HideBone (:3950).
void ULPCameraModeSubsystem::TickC1BoneRefresh(bool bC1Now)
{
UGameInstance* GI = GetGameInstance();
ACharacter* Player = bC1Now ? Cast<ACharacter>(LPCameraModeLocal::ReadObject(GI, LPCameraModeLocal::GIPlayerCharacter)) : nullptr;
if (!IsValid(Player))
{
Player = nullptr;
}
USkeletalMeshComponent* Mesh = (Player && CVarLPC1RefreshBones.GetValueOnGameThread() != 0) ? Player->GetMesh() : nullptr;
USkeletalMeshComponent* Applied = C1RefreshMesh.Get();
if (Applied && Applied != Mesh)
{
Applied->VisibilityBasedAnimTickOption = static_cast<EVisibilityBasedAnimTickOption>(C1SavedTickOption);
Applied->bRequiredBonesUpToDate = false;
UE_LOG(LogLifePlay, Log, TEXT("C1 : os du joueur rendus a l'option %d (sortie de C1, scene, joueur change ou lp.C1RefreshBones 0)"), C1SavedTickOption);
Applied = nullptr;
}
if (!Applied)
{
C1RefreshMesh.Reset();
}
if (Mesh && !Applied)
{
C1SavedTickOption = static_cast<uint8>(Mesh->VisibilityBasedAnimTickOption);
Mesh->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::AlwaysTickPoseAndRefreshBones;
Mesh->bRequiredBonesUpToDate = false;
C1RefreshMesh = Mesh;
UE_LOG(LogLifePlay, Log, TEXT("C1 : os masques du joueur gardes animes (option %d -> %d ; neckLower masque=%d)"),
C1SavedTickOption, static_cast<int32>(EVisibilityBasedAnimTickOption::AlwaysTickPoseAndRefreshBones),
Mesh->IsBoneHiddenByName(LPCameraModeLocal::NeckLowerBone) ? 1 : 0);
}
// Partie C (§ 2.4) : reglages de vue C1 transmis au modificateur a chaque image ; nul hors C1.
UWorld* World = GI ? GI->GetWorld() : nullptr;
APlayerController* PC = World ? World->GetFirstPlayerController() : nullptr;
APlayerCameraManager* Manager = PC ? PC->PlayerCameraManager.Get() : nullptr;
ULPRoomBoundsCameraModifier* Modifier = Manager ? Cast<ULPRoomBoundsCameraModifier>(Manager->FindCameraModifierByClass(ULPRoomBoundsCameraModifier::StaticClass())) : nullptr;
if (Modifier)
{
FVector Offset = FVector::ZeroVector;
const float Forward = CVarLPC1Forward.GetValueOnGameThread();
if (Player && Player->GetMesh() && Forward != 0.f)
{
const TCHAR* From = nullptr;
Offset = LPCameraModeLocal::BodyForward(Player, Player->GetMesh(), From) * Forward;
}
Modifier->SetC1View(Player, CVarLPC1NearClip.GetValueOnGameThread(), Offset);
}
}
TickSwitchKey, après la l. 1264 (ResetC1PitchOnExit(...)) :TickC1BoneRefresh(FrameLabel == LPCameraModeLocal::LabelC1);. Le libellé est calculé à chaque image (l. 1261). Il vaut C1 seulement si la vue estPlayerCharacteretCamC1On(l. 2632). Pendant une scène, il vaut « Scene » ou « POV » (l. 2620-2627). L'entrée en scène rend donc l'option dans la même image ; aucun autre point d'appel n'est nécessaire. Un menu ouvert en C1 garde le libellé C1 et l'option : voulu, car la vue reste C1 derrière le menu.Deinitialize, avantRemoveViewBounds(l. 744) :TickC1BoneRefresh(false);(rend l'option si le jeu se ferme en C1).
2.3 LPCameraModeSubsystem.cpp : témoin C1 (remplace WitnessC1, l. 1196-1231)
// ERR145 : une ligne par seconde en C1, sans condition. L'ancien ecart aux yeux etait aveugle : Room pose la camera sur ces
// memes yeux (ERR145-DIAG § 3.3). Reperes : parent de neckLower (os visible) et avant du buste (clavicules).
void ULPCameraModeSubsystem::WitnessC1(float DeltaTime)
{
C1Cooldown = FMath::Max(0.f, C1Cooldown - DeltaTime);
if (C1Cooldown > 0.f)
{
return;
}
C1Cooldown = LPCameraModeLocal::C1LogInterval;
const UGameInstance* GI = GetGameInstance();
const UWorld* World = GI ? GI->GetWorld() : nullptr;
const APlayerController* PC = World ? World->GetFirstPlayerController() : nullptr;
const ACharacter* Player = Cast<ACharacter>(LPCameraModeLocal::ReadObject(GI, LPCameraModeLocal::GIPlayerCharacter));
USkeletalMeshComponent* Mesh = IsValid(Player) ? Player->GetMesh() : nullptr;
const USceneComponent* Camera = IsValid(Player) ? Cast<USceneComponent>(LPCameraModeLocal::ReadObject(Player, LPCameraModeLocal::PlayerFirstPersonSM)) : nullptr;
const FName Chest = Mesh ? Mesh->GetParentBone(LPCameraModeLocal::NeckLowerBone) : NAME_None;
if (!PC || !Mesh || !IsValid(Camera) || Chest.IsNone())
{
return;
}
const FVector ChestAt = Mesh->GetSocketLocation(Chest);
const FVector Eyes = (Mesh->GetSocketLocation(LPCameraModeLocal::LeftEyeSocket) + Mesh->GetSocketLocation(LPCameraModeLocal::RightEyeSocket)) * 0.5f;
const FVector CameraAt = Camera->GetComponentLocation();
const FVector ViewAt = PC->PlayerCameraManager ? PC->PlayerCameraManager->GetCameraLocation() : CameraAt;
const TCHAR* From = nullptr;
const FVector Forward = LPCameraModeLocal::BodyForward(Player, Mesh, From);
const FVector Right = FVector::CrossProduct(FVector::UpVector, Forward);
const FVector Rel = ViewAt - ChestAt;
const FRotator CameraRotation = Camera->GetComponentRotation();
const FVector Root = Mesh->GetBoneLocation(Mesh->GetBoneName(0));
const UObject* RoomObject = LPCameraModeLocal::ReadObject(GI, LPCameraModeLocal::GIRoom);
UE_LOG(LogLifePlay, Log, TEXT("Temoin C1 : vue-%s %.0f cm (avant %+.0f, droite %+.0f, haut %+.0f) ; yeux-buste %.0f cm ; camera-yeux %.0f cm ; vue-camera %.0f cm ; lacet vue-buste %+.0f (%s) ; tangage %+.0f ; assis=%d ; racine-composant %.0f cm ; option os %d ; cou masque=%d ; plan proche %.1f ; minuterie=%d"),
*Chest.ToString(), Rel.Size(), FVector::DotProduct(Rel, Forward), FVector::DotProduct(Rel, Right), Rel.Z,
FVector::Dist(Eyes, ChestAt), FVector::Dist(CameraAt, Eyes), FVector::Dist(ViewAt, CameraAt),
FRotator::NormalizeAxis(CameraRotation.Yaw - Forward.Rotation().Yaw), From, FRotator::NormalizeAxis(CameraRotation.Pitch),
IsValid(LPCameraModeLocal::ReadObject(Player, LPCameraModeLocal::PlayerOccupyingFnt)) ? 1 : 0,
FVector::Dist(Root, Mesh->GetComponentLocation()), static_cast<int32>(Mesh->VisibilityBasedAnimTickOption),
Mesh->IsBoneHiddenByName(LPCameraModeLocal::NeckLowerBone) ? 1 : 0, CVarLPC1NearClip.GetValueOnGameThread(),
LPCameraModeLocal::TimerActive(IsValid(RoomObject) ? RoomObject : nullptr, LPCameraModeLocal::RoomCamTimer, World) ? 1 : 0);
}
- Constantes devenues orphelines (
C1Interval,C1MaxEyeDistance,C1MaxPitch,C1MaxYawGap, l. 104-108) : l'IMPL les cherche par Grep et retire celles qui ne servent plus. Le préfixe « Temoin C1 : » est gardé pour les recherches de journal. - Plages attendues (estimées sur les proportions Genesis 8, non mesurées ; la VAL les cale sur la première ligne debout) :
| Champ | Debout, A actif | Assis, A actif | A coupé (lp.C1RefreshBones 0) après debout → assis |
|---|---|---|---|
yeux-buste |
20 à 35 cm (référence R0) | R0 ± 5 cm (la chaîne du cou est rigide) | s'écarte de R0 de plus de 10 cm (COM17 : racine à 104 cm du composant) |
camera-yeux |
0 à 3 cm (minuterie de 0,016 s, une image de retard) | 0 à 3 cm | 0 à 3 cm (la caméra suit les yeux figés : aveugle, comme l'ancien témoin) |
vue-buste haut / avant |
+15 à +30 / 0 à +15 cm | idem si la tête est droite ; haut < +10 = tête baissée (L1 / L2) | quelconque |
vue-camera |
0 (= lp.C1Forward si réglé) |
idem | idem |
lacet vue-buste |
libre (souris X) ; au-delà de ±90 = regard par-dessus l'épaule | idem | idem |
option os / cou masque |
0 / 1 | 0 / 1 | 1 / 1 (valeur d'ACharacter, Character.cpp:125 cité par le DIAG) |
2.4 Partie C : réglages de vue C1 (LPRoomBoundsCameraModifier.h/.cpp)
.h, section publique, à côté deSetBounds(l. 28) :
/** ERR145-SPEC § 2.4 : vue C1 (cible de vue = joueur) : plan proche propre en cm (0 = plan global) et decalage du point
* de vue en coordonnees monde. Joueur nul = rien. Rafraichi a chaque image par LPCameraModeSubsystem. */
void SetC1View(AActor* InPlayer, float InNearClip, const FVector& InOffset);
Section privée : TWeakObjectPtr<AActor> C1View; float C1NearClip = 0.f; FVector C1Offset = FVector::ZeroVector;
.cpp:SetC1Viewrecopie les trois valeurs. DansModifyCamera, après le bloc Studio (l. 42-47) et avant le filtre des l. 50-54 (qui rendfalsepour toute vue non bornée, C1 comprise) :
// ERR145 : vue C1 seulement (le joueur est aussi cible de vue en POV : SetC1View recoit nul hors C1).
const AActor* C1 = C1View.Get();
if (C1 && CameraOwner && CameraOwner->GetViewTarget() == C1)
{
if (C1NearClip > 0.f)
{
InOutPOV.PerspectiveNearClipPlane = C1NearClip;
}
InOutPOV.Location += C1Offset;
}
- Pourquoi un modificateur et pas
FirstPersonSM:UCameraComponentn'a pas de plan proche en perspective en 5.8, seulementOrthoNearClipPlane(CameraComponent.h:84-86). Son décalage additif (:221,:311,:330-334) resterait posé sur un composant partagé avec la POV de sexe (CAM-B1e-CART.md:89). Le champFMinimalViewInfo::PerspectiveNearClipPlane(CameraTypes.h:92-94) est pris par le calcul de projection (CameraStackTypes.cpp:342, appelé parLocalPlayer.cpp:1285). Il ne touche que la vue, pas le plan global. Le modificateur est toujours installé, quel que soit le libellé (LPCameraModeSubsystem.cpp:873-881).
3. Repli
- Sans build :
lp.C1RefreshBones 0à la console rend l'option d'origine dès l'image suivante (§ 2.2) ;lp.C1NearClip 0etlp.C1Forward 0(valeurs par défaut) annulent la partie C. En Shipping, sans console, ces variables gardent leur défaut. - Code : instantané pris par la session principale avant l'IMPL (
Tools\Snapshot.ps1 -Lot ERR145). Retour arrière seulement proposé (Tools\Restore.ps1 -Lot ERR145). Les quatre fichiers ne touchent ni Room, ni Person, ni aucun asset.
4. Risques R#, questions Q#
4.1 L'option A suffit-elle ? (réponse à Big Boss)
Non prouvé, et la capture 34 dit plutôt non pour la pose « repas ». A est nécessaire : le mécanisme est établi et confirmé en jeu, et c'est lui qui donne la vue décapitée de dehors (COM17 ; COM23, l. 1757-1768). Mais le cycle C a produit, pour cette pose, l'état que A maintient, et la vue est restée mauvaise à un tangage de -2°. Mesures proposées par Big Boss, jugées sur le code :
| Mesure | Effet sur la capture 34 | Verdict |
|---|---|---|
| Borner le tangage | aucun : tangage -0,1 / -2,1 (l. 1791-1792), borne actuelle -60 (LPCameraModeSubsystem.cpp:132) jamais atteinte dans la fenêtre |
écartée (et contraire à la décision A1-R « tous les axes », :766) |
Masquer plus haut (neckUpper) |
laisse neckLower visible, donc de la peau plus près des yeux |
écartée (aggrave, estimé) |
Masquer plus bas (chestUpper) |
masque aussi les clavicules et les bras, s'ils descendent de chestUpper (estimé, hiérarchie Genesis 8 non relue) ; c'est un changement de Room (Blueprint), pas du C++ |
écartée |
Plan proche propre à C1 (C, lp.C1NearClip) |
retire toute peau à moins de N cm du point de vue (projet : 1 cm, DefaultEngine.ini:50) ; ne déplace rien |
1er complément, réglage en jeu |
Décalage vers l'avant (C, lp.C1Forward) |
sort le point de vue du volume du buste quel que soit le lacet, s'il suit l'avant du buste ; s'il suivait le regard, il pousserait la vue dans l'épaule en regardant de côté | 2e complément, réglage en jeu |
Recommandation : livrer A, le témoin et C (C sans effet par défaut) dans la même build. On choisit les valeurs par défaut de C d'après les essais T0 (sans build) et T6 (§ 7.2). Décision par les chiffres du témoin, A actif, assis, vue mauvaise :
yeux-bustehors de R0 ± 5 → A n'agit pas (option non posée, voiroption os) : retour à l'IMPL ;haut< +10 (tête baissée, L1 ou L2) →lp.C1Forward10 à 20, ou plan proche 10 à 15 ;haut≥ +15 et |lacet| > 90 → regard par-dessus l'épaule : plan proche ; une borne de lacet relative au buste serait une mesure nouvelle, hors de cette fiche ;haut≥ +15, |lacet| ≤ 60, vue mauvaise → peau étirée du cou (R8) : plan proche ;- vue bonne → A suffit ; C reste à 0.
4.2 Risques
- R1 : A seul ne corrige pas la capture 34 (§ 4.1) ; couvert par C et le témoin.
- R2 : avec A, la caméra suit la tête animée : plongée vers l'assiette si la pose « repas » en a une (L1), petits mouvements de
tête des poses de repos. Gêne possible ; réponse par
lp.C1Forwardou plus tard un lissage (Q2). - R3 : 1 à 3 images de gel à l'entrée en C1. Room masque, attend une image, puis change la vue (
CAM-B1-IMPL.md:104-105) ; le libellé C1 n'est vu qu'après ce changement (l. 2632). Les yeux viennent d'être lus : écart négligeable (estimé). - R4 : A demande deux recalculs des os requis par passage en C1, un à l'entrée et un à la sortie, le même chemin que
HideBone/UnHideBone(SkeletalMeshComponent.cpp:3950,:3983).RecalcRequiredBonesremet la pose locale à la référence (:2369) avant l'évaluation de la même image (:1707-1712puis:2879-2884). Pas d'image en pose de référence attendue (estimé). C'est la raison pour laquelle on n'appelle pasRecalcRequiredBoneshors du passage d'animation. - R5 : coût : les os de la tête (visage compris) redeviennent évalués en C1, comme hors C1.
AlwaysTickPoseAndRefreshBonesrafraîchit même sans rendu (SkinnedMeshComponent.cpp:1805-1808), mais le corps est rendu en C1 : surcoût nul attendu (estimé). - R6 : avec un plan proche au-dessus de 1 cm, un mur à moins de N cm du point de vue est traversé du regard. Les passes qui liraient
GNearClippingPlanedirectement plutôt que la vue ne sont pas relues (incertain). Le risque n'existe que silp.C1NearClip> 0. - R7 : la POV de sexe n'est pas couverte. PC masque aussi
neckLowersurCurrentActor(CAM-B1e-CART.md:62-63) et y attacheFirstPersonSMàhead, un os sous l'os masqué : même gel possible (estimé, non observé). Voir Q3. - R8 : les sommets à poids mixtes du cou (
chestUpperetneckLower) sont dessinés entre leur place et l'origine de la matrice du parent. En effetApplyScale(0)=ScaleMatrix * Mannule les trois axes et garde la ligne de translation (Matrix.inl:897-905;SkeletalRender.cpp:486,:519). Ce point est solidaire dechestUpper, à l'endroit où se trouvait l'origine du modèle en pose de liaison, donc loin sous le buste. D'où un « tube » de peau étiré (les « lambeaux » deCAM-B1e-CART.md:72). A ne le change pas, C ne le cache que près de la vue. Candidat (estimé) pour la « surface de peau cylindrique » de la capture 34. - R9 : sans os
lCollar/rCollar,BodyForwardprend l'avant de l'acteur ; le témoin l'écrit (acteur). Une pose de meuble qui tourne le corps par rapport à l'acteur fausserait alorsavantetlacet: sans effet sur A. - R10 : compilation unitaire. Les noms neufs sont uniques (
CVarLPC1*) ou dans l'espace nomméLPCameraModeLocal(lp-build§ 8).
4.3 Questions pour l'utilisateur
- Q1 : quelles valeurs par défaut pour
lp.C1NearClipetlp.C1Forwarddans la build d'essai ? Proposition : 0 (aucun effet). Réglage à la console pendant T6, puis valeur retenue écrite dans le code, ou dans[SystemSettings]deDefaultEngine.ini, pour la Shipping. - Q2 : la vue C1 doit-elle suivre les mouvements de tête des animations, au risque de plonger avec la tête dans la pose « repas » ?
Si non, la position doit venir d'un os du buste plus un décalage fixe (option B du DIAG, voir
vue-bustedu témoin). - Q3 : étendre A à la POV de sexe (personne filmée = joueur ou PNJ) dans une fiche à part ?
5. Interdits
- Aucun changement de Room, PC, GI, Person ni d'asset ; aucun MCP ; aucun changement du plan proche global (
DefaultEngine.ini:50). - Ne toucher à aucun maillage autre que
GI.PlayerCharacter->GetMesh(). Les PNJ gardent leur option : la propriété est par instance de composant (SkinnedMeshComponent.h:793-794). - Pas d'appel direct à
RecalcRequiredBonesni àRecalcRequiredCurves(R4) ; pas de décalage additif surFirstPersonSM(§ 2.4). - Ne pas retirer ni modifier
LPStringCompat,LPMeshCompat,LPNavigationHelperset voisins (lp-ue-compat§ 4). - Pas de compilation par l'IMPL (fiche BUILD à
lp-builder), pas de commande git qui écrit, modification par Edit uniquement.
6. Valeurs moteur lues dans le moteur installé
Moteur D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime\ (préfixe omis : Engine/ sauf mention).
Classes/Components/SkinnedMeshComponent.h:94-113:EVisibilityBasedAnimTickOption;AlwaysTickPoseAndRefreshBones= « Always Tick and Refresh BoneTransforms whether rendered or not ». Lignes:787-794:VisibilityBasedAnimTickOptionest publique,UPROPERTY(… BlueprintReadWrite, Config …), sans setter (Grep surClasses/Components: seulement:794,:797-798, et des commentaires). Lignes:1156,:1165:GetBoneIndex,GetBoneName;:1263:GetParentBone;:2014:GetBoneLocation;:2153,:2161,:2171:HideBoneByName,UnHideBoneByName,IsBoneHiddenByName(non const) ;:1759-1772: états de visibilité.Classes/Components/SkeletalMeshComponent.h:894-896:bRequiredBonesUpToDateest public et transitoire ; à faux, les os requis sont recalculés au prochain passage. Lignes:1862-1868:RecalcRequiredBones(public) ;:1885:ExcludeHiddenBones;:1898:RecalcRequiredCurves. Aucune APIMarkRequiredCurvesDirtyà appeler :RecalcRequiredBonesmarque les courbes à jour (SkeletalMeshComponent.cpp:2400) après le recalcul des instances d'animation (:2383-2396).Private/Components/SkeletalMeshComponent.cpp:1707-1718(passage d'animation) et:2879-2890(RefreshBoneTransforms) : recalcul si le drapeau est faux. Lignes:2246-2258: les os demandés par les suiveurs sont ajoutés, puis les os masqués purgés. Lignes:2296-2336:ExcludeHiddenBonesgarde un os s'il estBVS_Visibleou si l'option vautAlwaysTickPoseAndRefreshBones(:2321-2322). Lignes:2358-2408:RecalcRequiredBones. Lignes:3933-3965et:3967-3983:HideBone/UnHideBonemettent le drapeau à faux, sauf sur un suiveur (:3942-3945,:3976-3979).Private/Components/SkinnedMeshComponent.cpp:2119-2123: un suiveur ne met pas à jour sa visibilité. Lignes:2124-2163: propagationBVS_HiddenByParent. Lignes:4424-4448,:4482-4500: masquage par nom et par index. Lignes:2413-2434et:2468-2474: position d'un os = transformée en espace composant (celle du leader pour un suiveur) ×LocalToWorld. Lignes:1805-1808,:1881-1897: rafraîchissement selon le rendu ou l'option.Private/SkeletalRender.cpp:453,:458-466: le rendu parcourt les os actifs de la LOD (ActiveBoneIndices), pas les os requis. Lignes:483-487(suiveur, états du leader) et:516-520(sans leader) : un os non visible reçoit la matrice du parent à l'échelle 0. Un os masqué mais requis reste invisible au rendu. Établi.Core/Public/Math/Matrix.inl:897-905:ApplyScale=ScaleMatrix * M; la ligne de translation de M est gardée (R8).Classes/GameFramework/Character.h:487:GetMesh() constrend un pointeur non const.Classes/Camera/PlayerCameraManager.h:705:GetCameraLocation() const.Classes/Camera/CameraModifier.h:95:ModifyCamera(float, FMinimalViewInfo&).Classes/Camera/CameraTypes.h:92-94,:178,:216-219:PerspectiveNearClipPlane(négatif = plan global, -1 par défaut).Private/Camera/CameraStackTypes.cpp:342(et:210) : pris par la projection.Private/LocalPlayer.cpp:1285: appel de la vue du joueur.Classes/Camera/CameraComponent.h:84-86: seul un plan proche orthographique existe sur la caméra ;Private/Camera/CameraComponent.cpp:448-461: décalage additif.Private/UnrealEngine.cpp:19090-19102: commande consoler.SetNearClipPlane <cm>, minimum 1, 20 sans argument, réglage global. Aucune garde de compilation lue entre les l. 19050 et 19102 ; présence en Development estimée.Core/Public/HAL/IConsoleManager.h:2028:TAutoConsoleVariable(const TCHAR* Name, const T& DefaultValue, const TCHAR* Help, uint32 Flags = ECVF_Default).- Configuration :
Engine/Config/BaseEngine.ini:361NearClipPlane=10.0; projetConfig/DefaultEngine.ini:50NearClipPlane=1.000000; consoleConfig/DefaultInput.ini:166-168(Tilde,²). - Leader et suiveurs dans le jeu : Room masque sur
PlayerCharacter.Mesh=CharacterMesh0(CAM-B1-IMPL.md:87;CAM-B1e-CART.md:48,:61). Ce maillage est le leader deHairSM,PenisSMet des tenues (SetLeaderPoseComponent(Mesh),CAM-B1e-CART.md:52-57). Les suiveurs lisent les transformées et la visibilité du leader : A ne demande rien sur eux.
7. Critères de VAL (commande → attendu)
7.1 Contrôles de code et de build (lp-validator, après BUILD par lp-builder)
git -C "D:\Game\LifePlay-master 5.8" diff --stat→ les 4 fichiers du § 2 seulement, ou 2 si C n'est pas retenue.- Grep
VisibilityBasedAnimTickOptionsurSource/→ occurrences dansLPCameraModeSubsystem.cppseulement. GrepRecalcRequiredBones|ApplyScale|AddAdditiveOffsetsurSource/LifePlay→ 0. .\Tools\Build.ps1 -Lot ERR145(LifePlayEditor) → code 0, moduleLifePlaycompilé, 0 C4996 nouveau, « autres lignes : 20 ». Puis build Development empaquetée neuve (BuildDebug.ps1,-ArchiveDirneuf) pour l'utilisateur.- Journal de l'essai :
.\Tools\LogStats.ps1 -Log <LifePlay.log> -Pattern 'Temoin C1', 'C1 : os'→ une ligne « gardes animes » par entrée en C1, une ligne « rendus » par sortie, et des lignes « Temoin C1 » à 1 s d'écart en C1.
7.2 Cahier de tests pour l'utilisateur
Essai dans une build donnée ; console = touche ² ; chaque ligne rapporte « vue correcte / non », plus une capture si non.
- T0, sans build, sur COM23, maintenant (oriente le défaut de C, Q1) :
- T0a : s'asseoir à la même chaise « repas », en C1. Faire trois cycles C (C2 → C3 → C1) espacés de 5 s, en regardant à l'horizontale. La vue change d'un cycle à l'autre (plus ou moins basse) → L1 (gel à une phase de la boucle) ; toujours la même → L2.
- T0b : dans la vue mauvaise,
r.SetNearClipPlane 10, puisr.SetNearClipPlane 20; noter si la peau proche disparaît. Remettre ensuiter.SetNearClipPlane 1(valeur du projet), ou relancer le jeu. - T0c : même essai sur une chaise sans repas (autre action « Sit »).
- T1, recette du DIAG § 1 (build ERR145) : debout → C1 → s'asseoir sans quitter C1 → regarder autour, en bas, à l'horizontale.
Attendu : vue depuis les yeux, plus de corps décapité vu de dehors. Témoin :
yeux-buste= R0 ± 5,option os 0,cou masque=1. - T2, preuve par bascule : même recette avec
lp.C1RefreshBones 0→ le défaut d'origine revient, etyeux-bustes'écarte de R0 de plus de 10 cm. Puislp.C1RefreshBones 1→ la vue revient aux yeux dans la seconde. - T3, lever en C1 (ERR-111, ERR-106) : assis en C1, se lever (Z) sans quitter C1. Attendu : caméra aux yeux, pas à côté de la tête ;
témoin :
yeux-buste= R0 ± 5 etassis=0. - T4, cycle C debout et assis : aucun écart ; une ligne « C1 : os … rendus » à chaque sortie de C1.
- T5, PNJ et scènes inchangés : en C2, PNJ assis et en marche (tête visible, aucun changement) ; lancer une scène depuis C1 (ligne « rendus » à l'entrée), POV sur le partenaire (tête normale), retour en C1 après la scène.
- T6, compléments (si T1 assis reste mauvais, pose « repas ») :
lp.C1NearClip 10, puis 15 ; remettre à 0 ;lp.C1Forward 10, puis 20 ; remettre à 0. Noter le réglage qui donne une vue correcte et recopier trois lignes « Temoin C1 » de chaque réglage.
7.3 Effet attendu sur ERR-106 et ERR-111
- ERR-111 (
Registre_Erreurs.md:118). Volet « C1 à côté de la tête après un lever … un cycle C corrige » : même mécanisme dans le sens inverse (yeux figés assis, corps levé). Corrigé par A (estimé fort), preuve par T3. Volets « vue au plafond au retour d'une scène » (orientation, B2s) et « à-coup à la sortie de CharGen » : sans effet attendu de A. - ERR-106 (
Registre_Erreurs.md:113). Résidu « décalage après un lever » : corrigé par A (estimé fort). Le recalage de B2k à chaque passage de minuterie lit enfin des yeux vivants. La partie principale (caméra posée une seule fois) était déjà corrigée par B2k. - ERR-145 (
Registre_Erreurs.md:152) : volet « entré debout puis assis » corrigé par A (T1, T2) ; volet « capture 34 » (assis, après cycle) : A plus un complément C, selon T0 et T6.
Fichiers à commiter :
A Docs/Migration/etat/REL/ERR145/ERR145-SPEC.md(dépôt du jeu, brancheue5.8)
Tours : 30 / 60
Constats
- Écart au périmètre de la fiche : la partie C touche
LPRoomBoundsCameraModifier.h/.cpp, non nommés par la fiche. Raison : aucun plan proche par vue n'existe surUCameraComponenten 5.8 (§ 2.4). Partie séparable, décision de l'orchestrateur. - Écart au DIAG : la plage du témoin « 15 à 40 cm » (
ERR145-DIAG.md:103) devient « R0 ± 5 », calée sur la première ligne debout de la build. Les 20 à 35 cm sont une estimation Genesis 8 non mesurée. - Le journal COM23 contient aussi, à l'action « Sit », trois
Divide by zero: Divide_DoubleDoublede GI (l. 1740-1745) : sans lien établi avec ERR-145. - Il reste à vérifier, par MCP en lecture (hors fiche) : la hiérarchie Genesis 8 sous
chestUpper(get_bone_children), et l'existence delCollar/rCollarsur Michael et Bridget. Le témoin écrit aussiclaviculesouacteur.