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
bApplyPlayerClassMeshToAI=False
!PlayerClassMeshes=ClearArray
+PlayerClassMeshes=(ClassTag=(TagName="Class.Saber"),PlayerMesh=None)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Lancer"),PlayerMesh=None)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Archer"),PlayerMesh=None)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Rider"),PlayerMesh=None)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Caster"),PlayerMesh=None)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Assassin"),PlayerMesh=None)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Berserker"),PlayerMesh=None)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Saber"),PlayerMesh=None,PlayerRetargeter=)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Lancer"),PlayerMesh=None,PlayerRetargeter=)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Archer"),PlayerMesh=None,PlayerRetargeter=)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Rider"),PlayerMesh=None,PlayerRetargeter=)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Caster"),PlayerMesh=None,PlayerRetargeter=)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Assassin"),PlayerMesh=None,PlayerRetargeter=)
+PlayerClassMeshes=(ClassTag=(TagName="Class.Berserker"),PlayerMesh=None,PlayerRetargeter=)
!CoreAttributeAllocations=ClearArray
+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)

View File

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

View File

@@ -92,6 +92,25 @@ USkeletalMesh* UPHYClassComponent::GetConfiguredPlayerMesh() const
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
{
if (!IsPlayerClassMeshAllowed())
@@ -112,8 +131,9 @@ bool UPHYClassComponent::ApplyConfiguredPlayerMeshIfAllowed(AActor* AvatarActor)
return false;
}
const FSoftObjectPath PlayerRetargeter = GetConfiguredPlayerRetargeter();
UPHYCharacterMeshBridgeComponent* MeshBridge = Character->GetMeshBridgeComponent();
return MeshBridge ? MeshBridge->ApplyDisplayMesh(PlayerMesh) : false;
return MeshBridge ? MeshBridge->ApplyDisplayMesh(PlayerMesh, nullptr, PlayerRetargeter) : false;
}
bool UPHYClassComponent::ApplyClassAttributesToAbilitySystem(UAbilitySystemComponent* AbilitySystemComponent) const

View File

@@ -21,9 +21,11 @@ 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;
@@ -65,8 +67,9 @@ public:
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Animation")
FSoftObjectPath GetRetargeterPath() const { return RetargeterPath; }
protected:
virtual void NativeInitializeAnimation() override;
protected:
virtual FAnimInstanceProxy* CreateAnimInstanceProxy() override;
private:

View File

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

View File

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