Add class-specific retargeter config

This commit is contained in:
2026-04-26 12:32:03 +08:00
parent 08182fcc15
commit 08e998324c
6 changed files with 45 additions and 11 deletions

View File

@@ -4,13 +4,13 @@ DefaultAIClassTag=(TagName="Class.Saber")
bApplyPlayerClassMeshToPlayers=True bApplyPlayerClassMeshToPlayers=True
bApplyPlayerClassMeshToAI=False bApplyPlayerClassMeshToAI=False
!PlayerClassMeshes=ClearArray !PlayerClassMeshes=ClearArray
+PlayerClassMeshes=(ClassTag=(TagName="Class.Saber"),PlayerMesh=None) +PlayerClassMeshes=(ClassTag=(TagName="Class.Saber"),PlayerMesh=None,PlayerRetargeter=)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Lancer"),PlayerMesh=None) +PlayerClassMeshes=(ClassTag=(TagName="Class.Lancer"),PlayerMesh=None,PlayerRetargeter=)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Archer"),PlayerMesh=None) +PlayerClassMeshes=(ClassTag=(TagName="Class.Archer"),PlayerMesh=None,PlayerRetargeter=)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Rider"),PlayerMesh=None) +PlayerClassMeshes=(ClassTag=(TagName="Class.Rider"),PlayerMesh=None,PlayerRetargeter=)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Caster"),PlayerMesh=None) +PlayerClassMeshes=(ClassTag=(TagName="Class.Caster"),PlayerMesh=None,PlayerRetargeter=)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Assassin"),PlayerMesh=None) +PlayerClassMeshes=(ClassTag=(TagName="Class.Assassin"),PlayerMesh=None,PlayerRetargeter=)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Berserker"),PlayerMesh=None) +PlayerClassMeshes=(ClassTag=(TagName="Class.Berserker"),PlayerMesh=None,PlayerRetargeter=)
!CoreAttributeAllocations=ClearArray !CoreAttributeAllocations=ClearArray
+CoreAttributeAllocations=(ClassTag=(TagName="Class.Saber"),Strength=12,Dexterity=10,Vitality=12,Intelligence=8,Spirit=8,Perception=10) +CoreAttributeAllocations=(ClassTag=(TagName="Class.Saber"),Strength=12,Dexterity=10,Vitality=12,Intelligence=8,Spirit=8,Perception=10)
+CoreAttributeAllocations=(ClassTag=(TagName="Class.Lancer"),Strength=10,Dexterity=13,Vitality=10,Intelligence=7,Spirit=8,Perception=12) +CoreAttributeAllocations=(ClassTag=(TagName="Class.Lancer"),Strength=10,Dexterity=13,Vitality=10,Intelligence=7,Spirit=8,Perception=12)

View File

@@ -93,7 +93,8 @@ bool UPHYCharacterMeshBridgeComponent::ApplyPlayerClassVisualFromClassComponent(
} }
USkeletalMesh* PlayerMesh = ClassComponent->GetConfiguredPlayerMesh(); USkeletalMesh* PlayerMesh = ClassComponent->GetConfiguredPlayerMesh();
return PlayerMesh ? ApplyDisplayMesh(PlayerMesh) : false; const FSoftObjectPath PlayerRetargeter = ClassComponent->GetConfiguredPlayerRetargeter();
return PlayerMesh ? ApplyDisplayMesh(PlayerMesh, nullptr, PlayerRetargeter) : false;
} }
void UPHYCharacterMeshBridgeComponent::RefreshFollowPoseMode() void UPHYCharacterMeshBridgeComponent::RefreshFollowPoseMode()

View File

@@ -92,6 +92,25 @@ USkeletalMesh* UPHYClassComponent::GetConfiguredPlayerMesh() const
return nullptr; return nullptr;
} }
FSoftObjectPath UPHYClassComponent::GetConfiguredPlayerRetargeter() const
{
const UPHYClassSettings* ClassSettings = GetDefault<UPHYClassSettings>();
if (!ClassSettings || !CurrentClassTag.IsValid())
{
return FSoftObjectPath();
}
for (const FPHYPlayerClassMeshConfig& MeshConfig : ClassSettings->PlayerClassMeshes)
{
if (MeshConfig.ClassTag == CurrentClassTag)
{
return MeshConfig.PlayerRetargeter;
}
}
return FSoftObjectPath();
}
bool UPHYClassComponent::ApplyConfiguredPlayerMeshIfAllowed(AActor* AvatarActor) const bool UPHYClassComponent::ApplyConfiguredPlayerMeshIfAllowed(AActor* AvatarActor) const
{ {
if (!IsPlayerClassMeshAllowed()) if (!IsPlayerClassMeshAllowed())
@@ -112,8 +131,9 @@ bool UPHYClassComponent::ApplyConfiguredPlayerMeshIfAllowed(AActor* AvatarActor)
return false; return false;
} }
const FSoftObjectPath PlayerRetargeter = GetConfiguredPlayerRetargeter();
UPHYCharacterMeshBridgeComponent* MeshBridge = Character->GetMeshBridgeComponent(); UPHYCharacterMeshBridgeComponent* MeshBridge = Character->GetMeshBridgeComponent();
return MeshBridge ? MeshBridge->ApplyDisplayMesh(PlayerMesh) : false; return MeshBridge ? MeshBridge->ApplyDisplayMesh(PlayerMesh, nullptr, PlayerRetargeter) : false;
} }
bool UPHYClassComponent::ApplyClassAttributesToAbilitySystem(UAbilitySystemComponent* AbilitySystemComponent) const bool UPHYClassComponent::ApplyClassAttributesToAbilitySystem(UAbilitySystemComponent* AbilitySystemComponent) const

View File

@@ -21,9 +21,11 @@ struct PHY_API FPHYRetargetPoseAnimInstanceProxy : public FAnimInstanceProxy
{ {
GENERATED_BODY() GENERATED_BODY()
friend class UPHYRetargetPoseAnimInstance;
FPHYRetargetPoseAnimInstanceProxy() = default; FPHYRetargetPoseAnimInstanceProxy() = default;
FPHYRetargetPoseAnimInstanceProxy(UAnimInstance* InAnimInstance, FAnimNode_RetargetPoseFromMesh* InRetargetNode); FPHYRetargetPoseAnimInstanceProxy(UAnimInstance* InAnimInstance, FAnimNode_RetargetPoseFromMesh* InRetargetNode);
protected:
virtual void Initialize(UAnimInstance* InAnimInstance) override; virtual void Initialize(UAnimInstance* InAnimInstance) override;
virtual void PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds) override; virtual void PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds) override;
virtual void CacheBones() override; virtual void CacheBones() override;
@@ -65,8 +67,9 @@ public:
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Animation") UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Animation")
FSoftObjectPath GetRetargeterPath() const { return RetargeterPath; } FSoftObjectPath GetRetargeterPath() const { return RetargeterPath; }
protected:
virtual void NativeInitializeAnimation() override; virtual void NativeInitializeAnimation() override;
protected:
virtual FAnimInstanceProxy* CreateAnimInstanceProxy() override; virtual FAnimInstanceProxy* CreateAnimInstanceProxy() override;
private: private:

View File

@@ -5,6 +5,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "Components/ActorComponent.h" #include "Components/ActorComponent.h"
#include "GameplayTagContainer.h" #include "GameplayTagContainer.h"
#include "UObject/SoftObjectPath.h"
#include "PHYClassComponent.generated.h" #include "PHYClassComponent.generated.h"
class USkeletalMesh; class USkeletalMesh;
@@ -43,6 +44,10 @@ public:
UFUNCTION(BlueprintCallable, Category="PHY|Class") UFUNCTION(BlueprintCallable, Category="PHY|Class")
USkeletalMesh* GetConfiguredPlayerMesh() const; USkeletalMesh* GetConfiguredPlayerMesh() const;
/** @brief 按当前职业读取配置的玩家 IK Retargeter 软路径,未配置时返回空路径。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Class")
FSoftObjectPath GetConfiguredPlayerRetargeter() const;
/** @brief 如果策略允许,则把当前职业配置的玩家 Mesh 应用到指定 Avatar 或组件所有者。 */ /** @brief 如果策略允许,则把当前职业配置的玩家 Mesh 应用到指定 Avatar 或组件所有者。 */
UFUNCTION(BlueprintCallable, Category="PHY|Class") UFUNCTION(BlueprintCallable, Category="PHY|Class")
bool ApplyConfiguredPlayerMeshIfAllowed(AActor* AvatarActor = nullptr) const; bool ApplyConfiguredPlayerMeshIfAllowed(AActor* AvatarActor = nullptr) const;

View File

@@ -5,6 +5,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "GameplayTagContainer.h" #include "GameplayTagContainer.h"
#include "UObject/Object.h" #include "UObject/Object.h"
#include "UObject/SoftObjectPath.h"
#include "UObject/SoftObjectPtr.h" #include "UObject/SoftObjectPtr.h"
#include "PHYClassSettings.generated.h" #include "PHYClassSettings.generated.h"
@@ -72,6 +73,10 @@ public:
/** @brief 玩家使用该职业时应用的专属 Skeletal Mesh 软引用。 */ /** @brief 玩家使用该职业时应用的专属 Skeletal Mesh 软引用。 */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class") UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class")
TSoftObjectPtr<USkeletalMesh> PlayerMesh; TSoftObjectPtr<USkeletalMesh> PlayerMesh;
/** @brief 玩家使用该职业时优先使用的 IK Retargeter 软路径,留空时回退动画默认配置。 */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class")
FSoftObjectPath PlayerRetargeter;
}; };
/** /**