From 08e998324cc0eeebdf32e3510ae228edc8115d52 Mon Sep 17 00:00:00 2001 From: cit110 <840418418@qq.com> Date: Sun, 26 Apr 2026 12:32:03 +0800 Subject: [PATCH] Add class-specific retargeter config --- Config/DefaultPHYClass.ini | 14 ++++++------ .../PHYCharacterMeshBridgeComponent.cpp | 3 ++- .../PHY/Private/Class/PHYClassComponent.cpp | 22 ++++++++++++++++++- .../Animation/PHYRetargetPoseAnimInstance.h | 7 ++++-- Source/PHY/Public/Class/PHYClassComponent.h | 5 +++++ Source/PHY/Public/Class/PHYClassSettings.h | 5 +++++ 6 files changed, 45 insertions(+), 11 deletions(-) diff --git a/Config/DefaultPHYClass.ini b/Config/DefaultPHYClass.ini index 8db70f7..cba96f8 100644 --- a/Config/DefaultPHYClass.ini +++ b/Config/DefaultPHYClass.ini @@ -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) diff --git a/Source/PHY/Private/Animation/PHYCharacterMeshBridgeComponent.cpp b/Source/PHY/Private/Animation/PHYCharacterMeshBridgeComponent.cpp index e972905..890776e 100644 --- a/Source/PHY/Private/Animation/PHYCharacterMeshBridgeComponent.cpp +++ b/Source/PHY/Private/Animation/PHYCharacterMeshBridgeComponent.cpp @@ -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() diff --git a/Source/PHY/Private/Class/PHYClassComponent.cpp b/Source/PHY/Private/Class/PHYClassComponent.cpp index acbf701..69b9455 100644 --- a/Source/PHY/Private/Class/PHYClassComponent.cpp +++ b/Source/PHY/Private/Class/PHYClassComponent.cpp @@ -92,6 +92,25 @@ USkeletalMesh* UPHYClassComponent::GetConfiguredPlayerMesh() const return nullptr; } +FSoftObjectPath UPHYClassComponent::GetConfiguredPlayerRetargeter() const +{ + const UPHYClassSettings* ClassSettings = GetDefault(); + 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 diff --git a/Source/PHY/Public/Animation/PHYRetargetPoseAnimInstance.h b/Source/PHY/Public/Animation/PHYRetargetPoseAnimInstance.h index 973ec1f..a587ee8 100644 --- a/Source/PHY/Public/Animation/PHYRetargetPoseAnimInstance.h +++ b/Source/PHY/Public/Animation/PHYRetargetPoseAnimInstance.h @@ -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: diff --git a/Source/PHY/Public/Class/PHYClassComponent.h b/Source/PHY/Public/Class/PHYClassComponent.h index b3137c3..209a661 100644 --- a/Source/PHY/Public/Class/PHYClassComponent.h +++ b/Source/PHY/Public/Class/PHYClassComponent.h @@ -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; diff --git a/Source/PHY/Public/Class/PHYClassSettings.h b/Source/PHY/Public/Class/PHYClassSettings.h index b9d2753..f7b232f 100644 --- a/Source/PHY/Public/Class/PHYClassSettings.h +++ b/Source/PHY/Public/Class/PHYClassSettings.h @@ -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 PlayerMesh; + + /** @brief 玩家使用该职业时优先使用的 IK Retargeter 软路径,留空时回退动画默认配置。 */ + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class") + FSoftObjectPath PlayerRetargeter; }; /**