Plugins/LPAnimCore (Runtime, PreDefault, toutes configurations) recoit la partie runtime de l'ancien LPAnimMeasure de l'addon. LifePlay.Build.cs : LPAnimMeasure -> LPAnimCore. LifePlayEditorTools depend encore de LPAnimMeasure jusqu'a DC2. Pin addon 3e9c32f. Validation : DC1-BUILD code 0, DC1-VAL PASS (tournees Mesure identiques a SB2-REF, controle visuel utilisateur marche et ShowCouple). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
215 lines
9.5 KiB
C++
215 lines
9.5 KiB
C++
// Geometrie commune du couple (lot S5-T2, voir Docs/Migration/etat/sandbox/S5/S5-CART.md).
|
|
// Une seule source de verite pour : la transformation mesh -> acteur, l'echantillonnage des poses,
|
|
// le padding et le recalage de la boite englobante, le placement des corps, et le glissement du couple.
|
|
//
|
|
// Deux appelants, qui partagent la geometrie mais gardent leurs reglages :
|
|
// - addon : ALPPairSandboxStage (LPSandbox/LPCoupleSim.cpp) -> FSweepOptions::Sandbox()
|
|
// - jeu : ULPShowCoupleSubsystem (LifePlay/LPShowCoupleSubsystem.cpp) -> FSweepOptions::Show()
|
|
//
|
|
// Tout ce qui differe d'un cote a l'autre passe par FBoxFitParams ou FSweepOptions : le code
|
|
// de mesure et de boite n'est plus duplique, mais aucune option n'est alignee d'office.
|
|
// Regle du module : aucune dependance vers LifePlay ni vers LPSandbox.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "Engine/EngineTypes.h"
|
|
#include "Templates/Function.h"
|
|
|
|
class AActor;
|
|
class ACharacter;
|
|
class UAnimSequence;
|
|
class UBoxComponent;
|
|
class UCharacterMovementComponent;
|
|
class UPrimitiveComponent;
|
|
class USkeletalMeshComponent;
|
|
class UWorld;
|
|
|
|
namespace LPCoupleSim
|
|
{
|
|
// Demi-hauteur de capsule par defaut (Person et sandbox ont la meme valeur nominale).
|
|
inline constexpr double DefaultCapsuleHalf = 88.0;
|
|
// Pas d'echantillonnage d'une AnimSequence (0, L/8, ... 7L/8).
|
|
inline constexpr int32 PoseSamples = 8;
|
|
// Normale au-dela de laquelle une surface est consideree comme un appui (marche, lit).
|
|
inline constexpr float WalkableNormalZ = 0.6f;
|
|
// Penetration en-dessous de laquelle on ne depenetre pas.
|
|
inline constexpr float MinPenetrationDepth = 1.f;
|
|
// Tolerance de comparaison de position avant teleportation.
|
|
inline constexpr float MoveEpsilon = 0.1f;
|
|
// Rotation du mesh squelettique sous l'acteur (lacet -90).
|
|
inline constexpr double MeshYawDegrees = -90.0;
|
|
|
|
// Parametres de mesure. Les valeurs par defaut sont celles du jeu ; la sandbox surcharge
|
|
// CapsuleHalf et FloorLocalZ (son sol est a z = +10, donc son plancher local vaut 18 - PlayerSpot.Z).
|
|
struct FBoxFitParams
|
|
{
|
|
double CapsuleHalf = DefaultCapsuleHalf;
|
|
|
|
// Voie "animations" : FitCoupleBox (sandbox) et FitBox (jeu).
|
|
FVector AnimPad = FVector(12.0, 12.0, 6.0);
|
|
double AnimMaxSize = 800.0;
|
|
|
|
// Voie "corps vivants" : FitToBodies (jeu).
|
|
FVector LivePad = FVector(4.0, 4.0, 4.0);
|
|
FVector LiveMinExtent = FVector(8.0, 8.0, 8.0);
|
|
double LiveMaxSize = 400.0;
|
|
double LiveAnchorRadius = 140.0;
|
|
|
|
// Fond de boite, en Z local de l'acteur : la plante des pieds est a -CapsuleHalf.
|
|
double FloorLocalZ = -DefaultCapsuleHalf;
|
|
double MinExtentZ = 20.0;
|
|
};
|
|
|
|
struct FBoxFit
|
|
{
|
|
FVector Center = FVector::ZeroVector;
|
|
FVector Extent = FVector::ZeroVector;
|
|
// false : mesure absente ou hors plafond ; l'appelant garde la boite precedente.
|
|
bool bValid = false;
|
|
};
|
|
|
|
// --- Niveau pur : aucun UObject, aucun UWorld. Testable sans monde. ---
|
|
|
|
// Transformation commune du repere du mesh vers celui de l'acteur.
|
|
LPANIMCORE_API FTransform MeshToActor(double CapsuleHalf);
|
|
|
|
// Espace composant a partir des transformations locales et de la table des parents
|
|
// (Parents[i] < i, ou INDEX_NONE pour la racine, comme dans un FReferenceSkeleton).
|
|
LPANIMCORE_API void ComposeComponentSpace(
|
|
const TArray<int32>& Parents, const TArray<FTransform>& Local, TArray<FTransform>& OutComponent);
|
|
|
|
// Ajoute la position de chaque os (espace composant) a la boite, via MeshToActor.
|
|
LPANIMCORE_API void AccumulatePoints(
|
|
const TArray<FTransform>& Component, const FTransform& ToActor, FBox& InOutBounds);
|
|
|
|
// Os ecartes de la mesure des corps vivants (extremites et accessoires : la boite suivrait une
|
|
// main tendue). La voie "animations" ne les ecarte pas : ecart E6 de la cartographie.
|
|
LPANIMCORE_API bool IsExcludedLiveBone(const FString& BoneName);
|
|
|
|
// Boite brute -> boite finale, voie "animations" : padding, plafond, recalage du fond sur FloorLocalZ.
|
|
LPANIMCORE_API FBoxFit FitFromAnimBounds(const FBox& Bounds, const FBoxFitParams& Params);
|
|
|
|
// Boite brute -> boite finale, voie "corps vivants" : padding, etendue minimale, plafond. Pas de recalage.
|
|
LPANIMCORE_API FBoxFit FitFromLiveBounds(const FBox& Bounds, const FBoxFitParams& Params);
|
|
|
|
// Glissement le long d'une surface. TimeFraction : 1 - Hit.Time pour la part de course restante.
|
|
// bFlattenZ : la sandbox annule la composante verticale (son Z est verrouille par ailleurs).
|
|
LPANIMCORE_API FVector SlideWish(
|
|
const FVector& Wish, const FVector& Normal, float TimeFraction, bool bFlattenZ);
|
|
|
|
// --- Niveau lecture : assets et composants, sans effet de bord. ---
|
|
|
|
// Demi-hauteur reelle du personnage, avec repli (une capsule non initialisee mesure 0).
|
|
LPANIMCORE_API double CapsuleHalfOf(const ACharacter* Who, double Fallback = DefaultCapsuleHalf);
|
|
|
|
// Echantillonne une AnimSequence et ajoute les os a la boite. Os animes seulement,
|
|
// GetBoneTransform en donnees compressees (raw = false), rejet des poses NaN.
|
|
LPANIMCORE_API bool AccumulateAnimBounds(
|
|
const UAnimSequence* Anim, const FTransform& ToActor, FBox& InOutBounds, int32 Samples = PoseSamples);
|
|
|
|
// Ajoute les os affiches d'un mesh squelettique a la boite : ancre pelvis / hip, rayon
|
|
// LiveAnchorRadius, os exclus. Le mesh doit etre a jour (appel apres le tick d'animation).
|
|
LPANIMCORE_API void AccumulateLiveBody(
|
|
const USkeletalMeshComponent* Mesh, const FBoxFitParams& Params, FBox& InOutBounds);
|
|
|
|
// --- Niveau effets de bord : boite, corps, balayage. ---
|
|
|
|
// Cree la boite si besoin (profil commun : objet Pawn, tout Ignore sauf WorldDynamic = Block,
|
|
// cachee, hors navigation) et applique l'etat de collision. Retourne la boite a stocker.
|
|
LPANIMCORE_API UBoxComponent* EnsureBox(
|
|
ACharacter* Lead, UBoxComponent* Existing, FName Name, bool bEnabled);
|
|
|
|
// Etendue, centre et rotation nulle. Sans effet si Fit n'est pas valide.
|
|
LPANIMCORE_API void ApplyBoxFit(UBoxComponent* Box, const FBoxFit& Fit);
|
|
|
|
// Place le mesh squelettique sous l'acteur (plante des pieds a -CapsuleHalf, lacet -90).
|
|
LPANIMCORE_API void PlaceBodyMesh(ACharacter* Body, double CapsuleHalf);
|
|
|
|
// --- Soudure du partenaire ---
|
|
|
|
// Etat de mouvement memorise avant gel, pour le rendre a la sortie du show.
|
|
struct FSavedMove
|
|
{
|
|
TWeakObjectPtr<UCharacterMovementComponent> Move;
|
|
TEnumAsByte<EMovementMode> Mode = MOVE_None;
|
|
float Gravity = 1.f;
|
|
bool bTick = true;
|
|
};
|
|
|
|
// Arrete le mouvement, le desactive et coupe son tick. Retourne l'etat d'avant.
|
|
LPANIMCORE_API FSavedMove SaveAndFreeze(ACharacter* Body);
|
|
// Rend l'etat memorise, puis vide la memoire.
|
|
LPANIMCORE_API void RestoreMove(FSavedMove& Saved);
|
|
|
|
// Attache le partenaire au porteur et le pose sur son point (relatif nul).
|
|
LPANIMCORE_API void AttachPartner(ACharacter* Lead, ACharacter* Partner);
|
|
// Detache en gardant la position monde.
|
|
LPANIMCORE_API void Unweld(ACharacter* Partner);
|
|
|
|
// --- Balayage du couple ---
|
|
|
|
// Tout ce qui differe entre la sandbox et le jeu. Partir de Sandbox() ou de Show().
|
|
struct FSweepOptions
|
|
{
|
|
// Nombre de passes de glissement.
|
|
int32 MaxIterations = 3;
|
|
// SweepMultiByChannel (jeu : lits et depenetration ont besoin de tous les contacts)
|
|
// ou SweepSingleByChannel (sandbox).
|
|
bool bMultiSweep = true;
|
|
// Annule la composante verticale du glissement (sandbox).
|
|
bool bFlattenZ = false;
|
|
// Un contact dont la normale depasse WalkableNormalZ laisse passer le pas entier (sandbox).
|
|
// Voie simple seulement.
|
|
bool bAcceptWalkableStep = false;
|
|
// Sortir des penetrations au lieu de s'arreter net. Voie multiple seulement.
|
|
bool bDepenetrate = true;
|
|
// Ignorer tous les ACharacter du monde (jeu : les PNJ ne bloquent pas le couple).
|
|
bool bIgnoreAllCharacters = true;
|
|
// Appui franchissable : le couple se pose dessus au lieu d'etre bloque (jeu : les lits).
|
|
TFunction<bool(const UPrimitiveComponent*)> IsWalkableSupport;
|
|
// Acteur a ignorer entierement (jeu : la coque de la chambre, nommee Wall / Wall_*).
|
|
TFunction<bool(const AActor*)> IsIgnoredShell;
|
|
// Appele pour chaque acteur ignore par IsIgnoredShell, afin que l'appelant journalise.
|
|
TFunction<void(const AActor*)> OnIgnoredShell;
|
|
|
|
// Reglages du jeu : 3 passes, balayage multiple, depenetration, PNJ et coque ignores.
|
|
static FSweepOptions Show()
|
|
{
|
|
FSweepOptions Out;
|
|
return Out;
|
|
}
|
|
|
|
// Reglages de la sandbox : 2 passes, balayage simple, arret net en penetration,
|
|
// marches franchissables, glissement a plat, aucun PNJ ignore.
|
|
static FSweepOptions Sandbox()
|
|
{
|
|
FSweepOptions Out;
|
|
Out.MaxIterations = 2;
|
|
Out.bMultiSweep = false;
|
|
Out.bFlattenZ = true;
|
|
Out.bAcceptWalkableStep = true;
|
|
Out.bDepenetrate = false;
|
|
Out.bIgnoreAllCharacters = false;
|
|
return Out;
|
|
}
|
|
};
|
|
|
|
struct FSweepResult
|
|
{
|
|
// Position d'acteur retenue (From si rien n'a bouge).
|
|
FVector Location = FVector::ZeroVector;
|
|
// Dernier bloqueur en penetration, "Acteur/Composant", pour le journal. Vide sinon.
|
|
FString Blocker;
|
|
FVector BlockerNormal = FVector::ZeroVector;
|
|
float BlockerDepth = 0.f;
|
|
bool bMoved = false;
|
|
};
|
|
|
|
// Resout un deplacement du couple : la boite glisse, From est la position d'acteur de depart
|
|
// (sandbox : la position courante du porteur ; jeu : la derniere position sure), Wish le
|
|
// deplacement voulu. La fonction ne deplace rien : l'appelant applique FSweepResult::Location.
|
|
LPANIMCORE_API FSweepResult ResolveCoupleMove(
|
|
UWorld* World, const UBoxComponent* Box, const ACharacter* Lead, const ACharacter* Partner,
|
|
const FVector& From, const FVector& Wish, const FSweepOptions& Options);
|
|
}
|