Use blueprint retarget pose graph

This commit is contained in:
2026-04-26 17:28:37 +08:00
parent cd7444f4d2
commit 3499cf363c
8 changed files with 54 additions and 147 deletions

View File

@@ -4,7 +4,7 @@ DefaultAIClassTag=(TagName="Class.Saber")
bApplyPlayerClassMeshToPlayers=True
bApplyPlayerClassMeshToAI=False
!PlayerClassMeshes=ClearArray
+PlayerClassMeshes=(ClassTag=(TagName="Class.Saber"),PlayerMesh="/Game/AGame/Character/Ida/SK_MMO_ADVANCED_F_01_AGame.SK_MMO_ADVANCED_F_01_AGame",PlayerRetargeter="/Game/AGame/Animation/Retargeters/RTG_Manny_To_Ida.RTG_Manny_To_Ida")
+PlayerClassMeshes=(ClassTag=(TagName="Class.Saber"),PlayerMesh="/Game/AGame/Character/Ida/SK_MMO_ADVANCED_F_01_AGame.SK_MMO_ADVANCED_F_01_AGame",PlayerDisplayAnimClass="/Game/AGame/Animation/Retargeters/ABP_Retargeter.ABP_Retargeter_C",PlayerRetargeter="/Game/AGame/Animation/Retargeters/RTG_Manny_To_Ida.RTG_Manny_To_Ida")
+PlayerClassMeshes=(ClassTag=(TagName="Class.Lancer"),PlayerMesh=None,PlayerRetargeter=)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Archer"),PlayerMesh=None,PlayerRetargeter=)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Rider"),PlayerMesh=None,PlayerRetargeter=)

View File

@@ -252,7 +252,10 @@ TSubclassOf<UAnimInstance> UPHYCharacterMeshBridgeComponent::ResolveRetargetDisp
if (UClass* CurrentAnimClass = DisplayMeshComponent->GetAnimClass())
{
return CurrentAnimClass;
if (CurrentAnimClass != UPHYRetargetPoseAnimInstance::StaticClass())
{
return CurrentAnimClass;
}
}
const UPHYAnimationSettings* Settings = GetDefault<UPHYAnimationSettings>();

View File

@@ -7,84 +7,7 @@
#include "Components/SkeletalMeshComponent.h"
#include "Retargeter/IKRetargeter.h"
FPHYRetargetPoseAnimInstanceProxy::FPHYRetargetPoseAnimInstanceProxy(UAnimInstance* InAnimInstance, FAnimNode_RetargetPoseFromMesh* InRetargetNode)
: FAnimInstanceProxy(InAnimInstance)
, RetargetNode(InRetargetNode)
{
}
void FPHYRetargetPoseAnimInstanceProxy::Initialize(UAnimInstance* InAnimInstance)
{
FAnimInstanceProxy::Initialize(InAnimInstance);
if (RetargetNode)
{
FAnimationInitializeContext InitContext(this);
RetargetNode->Initialize_AnyThread(InitContext);
}
}
void FPHYRetargetPoseAnimInstanceProxy::PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds)
{
FAnimInstanceProxy::PreUpdate(InAnimInstance, DeltaSeconds);
if (RetargetNode && RetargetNode->HasPreUpdate())
{
RetargetNode->PreUpdate(InAnimInstance);
}
}
void FPHYRetargetPoseAnimInstanceProxy::CacheBones()
{
if (bBoneCachesInvalidated && RetargetNode)
{
FAnimationCacheBonesContext Context(this);
RetargetNode->CacheBones_AnyThread(Context);
bBoneCachesInvalidated = false;
}
}
bool FPHYRetargetPoseAnimInstanceProxy::Evaluate(FPoseContext& Output)
{
if (!RetargetNode)
{
Output.ResetToRefPose();
return true;
}
RetargetNode->Evaluate_AnyThread(Output);
return true;
}
void FPHYRetargetPoseAnimInstanceProxy::UpdateAnimationNode(const FAnimationUpdateContext& InContext)
{
UpdateCounter.Increment();
if (RetargetNode)
{
RetargetNode->Update_AnyThread(InContext);
}
}
void FPHYRetargetPoseAnimInstanceProxy::ConfigureRetargetPose(UIKRetargeter* InRetargeter, USkeletalMeshComponent* InSourceMeshComponent)
{
if (!RetargetNode)
{
return;
}
RetargetNode->IKRetargeterAsset = InRetargeter;
RetargetNode->RetargetFrom = ERetargetSourceMode::CustomSkeletalMeshComponent;
RetargetNode->SourceMeshComponent = InSourceMeshComponent;
RetargetNode->bSuppressWarnings = false;
if (FIKRetargetProcessor* Processor = RetargetNode->GetRetargetProcessor())
{
Processor->SetNeedsInitialized();
}
}
bool UPHYRetargetPoseAnimInstance::ConfigureRetargetPoseFromMesh(USkeletalMeshComponent* InSourceMeshComponent, FSoftObjectPath InRetargeterPath)
bool UPHYRetargetPoseAnimInstance::ConfigureRetargetPoseFromMesh(USkeletalMeshComponent* InSourceMeshComponent, const FSoftObjectPath InRetargeterPath)
{
RetargetSourceMeshComponent = InSourceMeshComponent;
RetargeterPath = InRetargeterPath;
@@ -110,21 +33,6 @@ bool UPHYRetargetPoseAnimInstance::ConfigureRetargetPoseFromMesh(USkeletalMeshCo
return false;
}
FPHYRetargetPoseAnimInstanceProxy& Proxy = GetProxyOnGameThread<FPHYRetargetPoseAnimInstanceProxy>();
Proxy.ConfigureRetargetPose(LoadedRetargeter, RetargetSourceMeshComponent);
// AnimGraph 中的 Retarget Pose From Mesh 节点通过属性绑定读取 LoadedRetargeter。
return true;
}
void UPHYRetargetPoseAnimInstance::NativeInitializeAnimation()
{
Super::NativeInitializeAnimation();
FPHYRetargetPoseAnimInstanceProxy& Proxy = GetProxyOnGameThread<FPHYRetargetPoseAnimInstanceProxy>();
Proxy.Initialize(this);
Proxy.ConfigureRetargetPose(LoadedRetargeter, RetargetSourceMeshComponent);
}
FAnimInstanceProxy* UPHYRetargetPoseAnimInstance::CreateAnimInstanceProxy()
{
return new FPHYRetargetPoseAnimInstanceProxy(this, &RetargetNode);
}

View File

@@ -5,6 +5,7 @@
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYClassComponent)
#include "AbilitySystemComponent.h"
#include "Animation/AnimInstance.h"
#include "AbilitySystem/Attributes/PHYAttributeCalculationLibrary.h"
#include "AbilitySystem/Attributes/PHYCombatAttributeSet.h"
#include "AbilitySystem/Attributes/PHYCoreAttributeSet.h"
@@ -92,6 +93,25 @@ USkeletalMesh* UPHYClassComponent::GetConfiguredPlayerMesh() const
return nullptr;
}
TSubclassOf<UAnimInstance> UPHYClassComponent::GetConfiguredPlayerDisplayAnimClass() const
{
const UPHYClassSettings* ClassSettings = GetDefault<UPHYClassSettings>();
if (!ClassSettings || !CurrentClassTag.IsValid())
{
return nullptr;
}
for (const FPHYPlayerClassMeshConfig& MeshConfig : ClassSettings->PlayerClassMeshes)
{
if (MeshConfig.ClassTag == CurrentClassTag)
{
return MeshConfig.PlayerDisplayAnimClass.LoadSynchronous();
}
}
return nullptr;
}
FSoftObjectPath UPHYClassComponent::GetConfiguredPlayerRetargeter() const
{
const UPHYClassSettings* ClassSettings = GetDefault<UPHYClassSettings>();
@@ -139,7 +159,7 @@ bool UPHYClassComponent::ApplyConfiguredPlayerMeshIfAllowed(AActor* AvatarActor)
}
MeshBridge->InitializeMeshBridge(Character, Character->GetDisplayMeshComponent());
return MeshBridge->ApplyDisplayMesh(PlayerMesh, nullptr, PlayerRetargeter);
return MeshBridge->ApplyDisplayMesh(PlayerMesh, GetConfiguredPlayerDisplayAnimClass(), PlayerRetargeter);
}
bool UPHYClassComponent::ApplyClassAttributesToAbilitySystem(UAbilitySystemComponent* AbilitySystemComponent) const

View File

@@ -3,8 +3,6 @@
#pragma once
#include "Animation/AnimInstance.h"
#include "Animation/AnimInstanceProxy.h"
#include "AnimNodes/AnimNode_RetargetPoseFromMesh.h"
#include "UObject/SoftObjectPath.h"
#include "PHYRetargetPoseAnimInstance.generated.h"
@@ -12,46 +10,19 @@ class UIKRetargeter;
class USkeletalMeshComponent;
/**
* @brief 显示层 Retarget Pose From Mesh 动画实例代理
* @brief PHY 显示层 Retarget 动画实例基类
*
* 代理直接驱动 UE IKRig 的 FAnimNode_RetargetPoseFromMesh用于把 SourceMesh 的运动姿态重定向到显示层 Mesh。
* 该类只保存 SourceMesh 和 IK Retargeter 运行时参数,不再由 C++ 自动执行 AnimGraph 节点;
* 具体的 Retarget Pose From Mesh 节点必须在派生 AnimBlueprint 中显式连接。
*/
USTRUCT()
struct PHY_API FPHYRetargetPoseAnimInstanceProxy : public FAnimInstanceProxy
{
GENERATED_BODY()
friend class UPHYRetargetPoseAnimInstance;
FPHYRetargetPoseAnimInstanceProxy() = default;
FPHYRetargetPoseAnimInstanceProxy(UAnimInstance* InAnimInstance, FAnimNode_RetargetPoseFromMesh* InRetargetNode);
protected:
virtual void Initialize(UAnimInstance* InAnimInstance) override;
virtual void PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds) override;
virtual void CacheBones() override;
virtual bool Evaluate(FPoseContext& Output) override;
virtual void UpdateAnimationNode(const FAnimationUpdateContext& InContext) override;
/** @brief 配置 Retarget 节点使用的源 Mesh 与 IK Retargeter。 */
void ConfigureRetargetPose(UIKRetargeter* InRetargeter, USkeletalMeshComponent* InSourceMeshComponent);
private:
FAnimNode_RetargetPoseFromMesh* RetargetNode = nullptr;
};
/**
* @brief PHY 显示层原生 Retarget Pose 动画实例。
*
* MeshBridge 在异骨架显示层路径中使用该类,把 ACharacter::GetMesh() 的运动姿态通过 IK Retargeter 输出到 DisplayMesh。
*/
UCLASS(Transient, BlueprintType)
UCLASS(Transient, BlueprintType, Blueprintable)
class PHY_API UPHYRetargetPoseAnimInstance : public UAnimInstance
{
GENERATED_BODY()
public:
/**
* @brief 设置 Retarget Pose From Mesh 的运行时输入。
* @brief 设置 Retarget AnimBlueprint 读取的运行时输入。
* @param InSourceMeshComponent 提供运动姿态的源 Mesh 组件。
* @param InRetargeterPath IK Retargeter 资产软路径。
* @return SourceMesh 与 Retargeter 均有效时返回 true。
@@ -63,29 +34,24 @@ public:
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Animation")
USkeletalMeshComponent* GetRetargetSourceMeshComponent() const { return RetargetSourceMeshComponent; }
/** @brief 获取当前已加载的 IK Retargeter 资产,供 AnimBlueprint 节点绑定。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Animation")
UIKRetargeter* GetLoadedRetargeter() const { return LoadedRetargeter; }
/** @brief 获取当前 IK Retargeter 资产路径。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Animation")
FSoftObjectPath GetRetargeterPath() const { return RetargeterPath; }
virtual void NativeInitializeAnimation() override;
protected:
virtual FAnimInstanceProxy* CreateAnimInstanceProxy() override;
private:
/** @brief Retarget Pose From Mesh 运行时节点。 */
UPROPERTY()
FAnimNode_RetargetPoseFromMesh RetargetNode;
/** @brief 当前用于重定向的源 Mesh 组件。 */
UPROPERTY(Transient)
UPROPERTY(Transient, BlueprintReadOnly, Category="PHY|Animation")
TObjectPtr<USkeletalMeshComponent> RetargetSourceMeshComponent;
/** @brief 当前使用的 IK Retargeter 资产。 */
UPROPERTY(Transient)
/** @brief 当前使用的 IK Retargeter 资产AnimBlueprint 的节点应绑定到该属性或 Getter。 */
UPROPERTY(Transient, BlueprintReadOnly, Category="PHY|Animation")
TObjectPtr<UIKRetargeter> LoadedRetargeter;
/** @brief 当前 IK Retargeter 资产软路径。 */
UPROPERTY(Transient)
UPROPERTY(Transient, BlueprintReadOnly, Category="PHY|Animation")
FSoftObjectPath RetargeterPath;
};

View File

@@ -8,8 +8,9 @@
#include "UObject/SoftObjectPath.h"
#include "PHYClassComponent.generated.h"
class USkeletalMesh;
class UAbilitySystemComponent;
class UAnimInstance;
class USkeletalMesh;
/**
* @brief PHY 职业组件。
@@ -44,6 +45,10 @@ public:
UFUNCTION(BlueprintCallable, Category="PHY|Class")
USkeletalMesh* GetConfiguredPlayerMesh() const;
/** @brief 按当前职业读取配置的显示层 AnimClass未配置时返回空并交给动画默认配置。 */
UFUNCTION(BlueprintCallable, Category="PHY|Class")
TSubclassOf<UAnimInstance> GetConfiguredPlayerDisplayAnimClass() const;
/** @brief 按当前职业读取配置的玩家 IK Retargeter 软路径,未配置时返回空路径。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Class")
FSoftObjectPath GetConfiguredPlayerRetargeter() const;

View File

@@ -9,6 +9,7 @@
#include "UObject/SoftObjectPtr.h"
#include "PHYClassSettings.generated.h"
class UAnimInstance;
class USkeletalMesh;
/**
@@ -74,6 +75,10 @@ public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class")
TSoftObjectPtr<USkeletalMesh> PlayerMesh;
/** @brief 玩家使用该职业时优先使用的显示层 AnimClass留空时回退动画默认配置。 */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class")
TSoftClassPtr<UAnimInstance> PlayerDisplayAnimClass;
/** @brief 玩家使用该职业时优先使用的 IK Retargeter 软路径,留空时回退动画默认配置。 */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class")
FSoftObjectPath PlayerRetargeter;