Add PHY class framework
This commit is contained in:
@@ -4,6 +4,8 @@
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYAICharacter)
|
||||
|
||||
#include "Class/PHYClassComponent.h"
|
||||
#include "Class/PHYClassSettings.h"
|
||||
#include "GGA_AbilitySystemComponent.h"
|
||||
|
||||
APHYAICharacter::APHYAICharacter(const FObjectInitializer& ObjectInitializer)
|
||||
@@ -12,6 +14,9 @@ APHYAICharacter::APHYAICharacter(const FObjectInitializer& ObjectInitializer)
|
||||
AbilitySystemComponent = CreateDefaultSubobject<UGGA_AbilitySystemComponent>(TEXT("AbilitySystemComponent"));
|
||||
AbilitySystemComponent->SetIsReplicated(true);
|
||||
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
|
||||
|
||||
ClassComponent = CreateDefaultSubobject<UPHYClassComponent>(TEXT("ClassComponent"));
|
||||
ClassComponent->SetClassTag(GetDefault<UPHYClassSettings>()->DefaultAIClassTag);
|
||||
}
|
||||
|
||||
void APHYAICharacter::BeginPlay()
|
||||
@@ -34,4 +39,9 @@ void APHYAICharacter::InitializeAIAbilitySystem()
|
||||
{
|
||||
InitializeAbilitySystem(AbilitySystemComponent, this);
|
||||
}
|
||||
|
||||
if (ClassComponent)
|
||||
{
|
||||
ClassComponent->ApplyConfiguredPlayerMeshIfAllowed(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "Camera/CameraComponent.h"
|
||||
#include "Camera/PHYUGCSpringArmComponent.h"
|
||||
#include "Class/PHYClassComponent.h"
|
||||
#include "GameFramework/SpringArmComponent.h"
|
||||
#include "GIPS_InputSystemComponent.h"
|
||||
#include "Player/PHYPlayerState.h"
|
||||
@@ -51,4 +52,9 @@ void APHYPlayerCharacter::InitializeAbilitySystemFromPlayerState()
|
||||
{
|
||||
InitializeAbilitySystem(PlayerASC, PHYPlayerState);
|
||||
}
|
||||
|
||||
if (UPHYClassComponent* ClassComponent = PHYPlayerState->GetClassComponent())
|
||||
{
|
||||
ClassComponent->ApplyConfiguredPlayerMeshIfAllowed(this);
|
||||
}
|
||||
}
|
||||
|
||||
112
Source/PHY/Private/Class/PHYClassComponent.cpp
Normal file
112
Source/PHY/Private/Class/PHYClassComponent.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "Class/PHYClassComponent.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYClassComponent)
|
||||
|
||||
#include "Characters/PHYAICharacter.h"
|
||||
#include "Characters/PHYPlayerCharacter.h"
|
||||
#include "Class/PHYClassSettings.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "PHYGameplayTags.h"
|
||||
#include "Player/PHYPlayerState.h"
|
||||
|
||||
UPHYClassComponent::UPHYClassComponent(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
SetIsReplicatedByDefault(true);
|
||||
|
||||
CurrentClassTag = PHYGameplayTags::Class_Saber;
|
||||
}
|
||||
|
||||
void UPHYClassComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME(ThisClass, CurrentClassTag);
|
||||
}
|
||||
|
||||
void UPHYClassComponent::SetClassTag(const FGameplayTag NewClassTag)
|
||||
{
|
||||
if (!NewClassTag.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AActor* OwnerActor = GetOwner())
|
||||
{
|
||||
if (!OwnerActor->HasAuthority())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CurrentClassTag = NewClassTag;
|
||||
}
|
||||
|
||||
bool UPHYClassComponent::IsPlayerClassMeshAllowed() const
|
||||
{
|
||||
const UPHYClassSettings* ClassSettings = GetDefault<UPHYClassSettings>();
|
||||
if (!ClassSettings)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const AActor* OwnerActor = GetOwner();
|
||||
if (OwnerActor && OwnerActor->IsA<APHYAICharacter>())
|
||||
{
|
||||
return ClassSettings->bApplyPlayerClassMeshToAI;
|
||||
}
|
||||
|
||||
if (OwnerActor && (OwnerActor->IsA<APHYPlayerState>() || OwnerActor->IsA<APHYPlayerCharacter>()))
|
||||
{
|
||||
return ClassSettings->bApplyPlayerClassMeshToPlayers;
|
||||
}
|
||||
|
||||
return ClassSettings->bApplyPlayerClassMeshToPlayers;
|
||||
}
|
||||
|
||||
USkeletalMesh* UPHYClassComponent::GetConfiguredPlayerMesh() const
|
||||
{
|
||||
const UPHYClassSettings* ClassSettings = GetDefault<UPHYClassSettings>();
|
||||
if (!ClassSettings || !CurrentClassTag.IsValid())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (const FPHYPlayerClassMeshConfig& MeshConfig : ClassSettings->PlayerClassMeshes)
|
||||
{
|
||||
if (MeshConfig.ClassTag == CurrentClassTag)
|
||||
{
|
||||
return MeshConfig.PlayerMesh.LoadSynchronous();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool UPHYClassComponent::ApplyConfiguredPlayerMeshIfAllowed(AActor* AvatarActor) const
|
||||
{
|
||||
if (!IsPlayerClassMeshAllowed())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AActor* MeshOwner = AvatarActor ? AvatarActor : GetOwner();
|
||||
ACharacter* Character = Cast<ACharacter>(MeshOwner);
|
||||
if (!Character)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
USkeletalMesh* PlayerMesh = GetConfiguredPlayerMesh();
|
||||
if (!PlayerMesh)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Character->GetMesh()->SetSkeletalMesh(PlayerMesh);
|
||||
return true;
|
||||
}
|
||||
24
Source/PHY/Private/Class/PHYClassSettings.cpp
Normal file
24
Source/PHY/Private/Class/PHYClassSettings.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "Class/PHYClassSettings.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYClassSettings)
|
||||
|
||||
#include "PHYGameplayTags.h"
|
||||
|
||||
UPHYClassSettings::UPHYClassSettings()
|
||||
{
|
||||
DefaultPlayerClassTag = PHYGameplayTags::Class_Saber;
|
||||
DefaultAIClassTag = PHYGameplayTags::Class_Saber;
|
||||
|
||||
PlayerClassMeshes =
|
||||
{
|
||||
{ PHYGameplayTags::Class_Saber, nullptr },
|
||||
{ PHYGameplayTags::Class_Lancer, nullptr },
|
||||
{ PHYGameplayTags::Class_Archer, nullptr },
|
||||
{ PHYGameplayTags::Class_Rider, nullptr },
|
||||
{ PHYGameplayTags::Class_Caster, nullptr },
|
||||
{ PHYGameplayTags::Class_Assassin, nullptr },
|
||||
{ PHYGameplayTags::Class_Berserker, nullptr }
|
||||
};
|
||||
}
|
||||
15
Source/PHY/Private/GameplayTags/PHYGameplayTags_Class.cpp
Normal file
15
Source/PHY/Private/GameplayTags/PHYGameplayTags_Class.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "GameplayTags/PHYGameplayTags_Class.h"
|
||||
|
||||
// 职业 Tag 由职业系统维护,玩家和 AI 共享分类语义,但 Mesh 应用策略由职业配置控制。
|
||||
namespace PHYGameplayTags
|
||||
{
|
||||
UE_DEFINE_GAMEPLAY_TAG(Class_Saber, "Class.Saber");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Class_Lancer, "Class.Lancer");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Class_Archer, "Class.Archer");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Class_Rider, "Class.Rider");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Class_Caster, "Class.Caster");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Class_Assassin, "Class.Assassin");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Class_Berserker, "Class.Berserker");
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYPlayerState)
|
||||
|
||||
#include "Class/PHYClassComponent.h"
|
||||
#include "Class/PHYClassSettings.h"
|
||||
#include "GGA_AbilitySystemComponent.h"
|
||||
|
||||
APHYPlayerState::APHYPlayerState(const FObjectInitializer& ObjectInitializer)
|
||||
@@ -14,6 +16,9 @@ APHYPlayerState::APHYPlayerState(const FObjectInitializer& ObjectInitializer)
|
||||
AbilitySystemComponent = CreateDefaultSubobject<UGGA_AbilitySystemComponent>(TEXT("AbilitySystemComponent"));
|
||||
AbilitySystemComponent->SetIsReplicated(true);
|
||||
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
|
||||
|
||||
ClassComponent = CreateDefaultSubobject<UPHYClassComponent>(TEXT("ClassComponent"));
|
||||
ClassComponent->SetClassTag(GetDefault<UPHYClassSettings>()->DefaultPlayerClassTag);
|
||||
}
|
||||
|
||||
UAbilitySystemComponent* APHYPlayerState::GetAbilitySystemComponent() const
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "PHYAICharacter.generated.h"
|
||||
|
||||
class UGGA_AbilitySystemComponent;
|
||||
class UPHYClassComponent;
|
||||
|
||||
/**
|
||||
* @brief PHY AI 角色。
|
||||
@@ -32,6 +33,10 @@ public:
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|AI")
|
||||
UGGA_AbilitySystemComponent* GetAIAbilitySystemComponent() const { return AbilitySystemComponent; }
|
||||
|
||||
/** @brief 获取 AI 职业分类组件。 */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Class")
|
||||
UPHYClassComponent* GetClassComponent() const { return ClassComponent; }
|
||||
|
||||
protected:
|
||||
/** @brief 初始化 AI 自身作为 Owner 和 Avatar 的 ASC。 */
|
||||
virtual void InitializeAIAbilitySystem();
|
||||
@@ -39,4 +44,8 @@ protected:
|
||||
/** @brief AI 自身 ASC。 */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|AI")
|
||||
TObjectPtr<UGGA_AbilitySystemComponent> AbilitySystemComponent;
|
||||
|
||||
/** @brief AI 职业分类组件,默认不应用玩家职业 Mesh。 */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|Class")
|
||||
TObjectPtr<UPHYClassComponent> ClassComponent;
|
||||
};
|
||||
|
||||
53
Source/PHY/Public/Class/PHYClassComponent.h
Normal file
53
Source/PHY/Public/Class/PHYClassComponent.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "PHYClassComponent.generated.h"
|
||||
|
||||
class USkeletalMesh;
|
||||
|
||||
/**
|
||||
* @brief PHY 职业组件。
|
||||
*
|
||||
* 组件复制当前职业分类;玩家可按配置把职业 Mesh 应用到 Avatar,AI 默认只保留分类不套用玩家 Mesh。
|
||||
*/
|
||||
UCLASS(BlueprintType, Blueprintable, meta=(BlueprintSpawnableComponent))
|
||||
class PHY_API UPHYClassComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** @brief 构造可复制的职业组件。 */
|
||||
UPHYClassComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
|
||||
|
||||
/** @brief 注册需要网络复制的职业字段。 */
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
/** @brief 设置当前职业 Tag,通常由服务器调用。 */
|
||||
UFUNCTION(BlueprintCallable, Category="PHY|Class")
|
||||
void SetClassTag(FGameplayTag NewClassTag);
|
||||
|
||||
/** @brief 获取当前职业 Tag。 */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Class")
|
||||
FGameplayTag GetClassTag() const { return CurrentClassTag; }
|
||||
|
||||
/** @brief 查询当前所有者是否允许应用玩家职业 Mesh。 */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Class")
|
||||
bool IsPlayerClassMeshAllowed() const;
|
||||
|
||||
/** @brief 按当前职业读取配置的玩家 Mesh,未配置时返回空。 */
|
||||
UFUNCTION(BlueprintCallable, Category="PHY|Class")
|
||||
USkeletalMesh* GetConfiguredPlayerMesh() const;
|
||||
|
||||
/** @brief 如果策略允许,则把当前职业配置的玩家 Mesh 应用到指定 Avatar 或组件所有者。 */
|
||||
UFUNCTION(BlueprintCallable, Category="PHY|Class")
|
||||
bool ApplyConfiguredPlayerMeshIfAllowed(AActor* AvatarActor = nullptr) const;
|
||||
|
||||
protected:
|
||||
/** @brief 当前职业 Tag,由服务器复制给客户端。 */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category="PHY|Class")
|
||||
FGameplayTag CurrentClassTag;
|
||||
};
|
||||
64
Source/PHY/Public/Class/PHYClassSettings.h
Normal file
64
Source/PHY/Public/Class/PHYClassSettings.h
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "UObject/SoftObjectPtr.h"
|
||||
#include "PHYClassSettings.generated.h"
|
||||
|
||||
class USkeletalMesh;
|
||||
|
||||
/**
|
||||
* @brief 玩家职业 Mesh 配置项。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct PHY_API FPHYPlayerClassMeshConfig
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** @brief 职业 Tag,必须来自 Class.* 原生 Gameplay Tag。 */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class")
|
||||
FGameplayTag ClassTag;
|
||||
|
||||
/** @brief 玩家使用该职业时应用的专属 Skeletal Mesh 软引用。 */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class")
|
||||
TSoftObjectPtr<USkeletalMesh> PlayerMesh;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief PHY 职业系统配置。
|
||||
*
|
||||
* 职业分类同时服务玩家和 AI;玩家 Mesh 仅表达玩家外观,AI 默认不复用这些 Mesh。
|
||||
*/
|
||||
UCLASS(Config=PHYClass, DefaultConfig)
|
||||
class PHY_API UPHYClassSettings : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** @brief 构造默认七职阶配置。 */
|
||||
UPHYClassSettings();
|
||||
|
||||
/** @brief 玩家默认职业。 */
|
||||
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Class")
|
||||
FGameplayTag DefaultPlayerClassTag;
|
||||
|
||||
/** @brief AI 默认职业分类。 */
|
||||
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Class")
|
||||
FGameplayTag DefaultAIClassTag;
|
||||
|
||||
/** @brief 玩家职业到玩家专属 Mesh 的配置表。 */
|
||||
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Class")
|
||||
TArray<FPHYPlayerClassMeshConfig> PlayerClassMeshes;
|
||||
|
||||
/** @brief 是否允许玩家 Avatar 应用玩家职业 Mesh。 */
|
||||
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Class")
|
||||
bool bApplyPlayerClassMeshToPlayers = true;
|
||||
|
||||
/** @brief 是否允许 AI 应用玩家职业 Mesh,默认关闭以保持 AI 外观独立。 */
|
||||
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Class")
|
||||
bool bApplyPlayerClassMeshToAI = false;
|
||||
};
|
||||
32
Source/PHY/Public/GameplayTags/PHYGameplayTags_Class.h
Normal file
32
Source/PHY/Public/GameplayTags/PHYGameplayTags_Class.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "NativeGameplayTags.h"
|
||||
|
||||
/**
|
||||
* @brief PHY 职业相关原生 Gameplay Tag。
|
||||
*/
|
||||
namespace PHYGameplayTags
|
||||
{
|
||||
/** @brief Saber 职阶。 */
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Class_Saber);
|
||||
|
||||
/** @brief Lancer 职阶。 */
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Class_Lancer);
|
||||
|
||||
/** @brief Archer 职阶。 */
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Class_Archer);
|
||||
|
||||
/** @brief Rider 职阶。 */
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Class_Rider);
|
||||
|
||||
/** @brief Caster 职阶。 */
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Class_Caster);
|
||||
|
||||
/** @brief Assassin 职阶。 */
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Class_Assassin);
|
||||
|
||||
/** @brief Berserker 职阶。 */
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Class_Berserker);
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "GameplayTags/PHYGameplayTags_Ability.h"
|
||||
#include "GameplayTags/PHYGameplayTags_Class.h"
|
||||
#include "GameplayTags/PHYGameplayTags_Effect.h"
|
||||
#include "GameplayTags/PHYGameplayTags_Event.h"
|
||||
#include "GameplayTags/PHYGameplayTags_Input.h"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "PHYPlayerState.generated.h"
|
||||
|
||||
class UGGA_AbilitySystemComponent;
|
||||
class UPHYClassComponent;
|
||||
|
||||
/**
|
||||
* @brief PHY 玩家状态。
|
||||
@@ -30,8 +31,16 @@ public:
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|PlayerState")
|
||||
UGGA_AbilitySystemComponent* GetGGAAbilitySystemComponent() const { return AbilitySystemComponent; }
|
||||
|
||||
/** @brief 获取玩家持久职业组件。 */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Class")
|
||||
UPHYClassComponent* GetClassComponent() const { return ClassComponent; }
|
||||
|
||||
protected:
|
||||
/** @brief 玩家持久 ASC。 */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|PlayerState")
|
||||
TObjectPtr<UGGA_AbilitySystemComponent> AbilitySystemComponent;
|
||||
|
||||
/** @brief 玩家持久职业组件,职业不保存在 PlayerCharacter 上。 */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|Class")
|
||||
TObjectPtr<UPHYClassComponent> ClassComponent;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user