Add PHY character framework

This commit is contained in:
2026-04-26 00:33:28 +08:00
parent 5aacc79f03
commit acaa8eb83c
25 changed files with 1784 additions and 2 deletions

View File

@@ -0,0 +1,7 @@
[/Script/PHY.PHYCharacterSettings]
bEnableGenericGameInteraction=True
bEnableContextEffects=True
DefaultMaxWalkSpeed=500.000000
DefaultSprintSpeed=650.000000
DefaultMaxAcceleration=2048.000000
DefaultInteractionOption=0

View File

@@ -18,6 +18,14 @@
"Editor"
]
},
{
"Name": "GameplayAbilities",
"Enabled": true
},
{
"Name": "ModularGameplay",
"Enabled": true
},
{
"Name": "GenericCombatSystem",
"Enabled": true,

View File

@@ -8,7 +8,25 @@ public class PHY : ModuleRules
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput", "GameplayTags" });
PublicDependencyModuleNames.AddRange(new string[]
{
"Core",
"CoreUObject",
"Engine",
"InputCore",
"EnhancedInput",
"GameplayTags",
"GameplayAbilities",
"GameplayTasks",
"ModularGameplay",
"GenericGameplayAbilities",
"GenericCombatSystem",
"GenericInputSystem",
"GenericGameSystem",
"GenericEffectsSystem",
"AuroraDevs_UGC",
"AIModule"
});
PrivateDependencyModuleNames.AddRange(new string[] { });

View File

@@ -0,0 +1,41 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "AI/PHYAIController.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYAIController)
#include "AbilitySystemComponent.h"
#include "Characters/PHYAICharacter.h"
APHYAICharacter* APHYAIController::GetPHYAICharacter() const
{
return Cast<APHYAICharacter>(GetPawn());
}
bool APHYAIController::RequestInteraction(const int32 OptionIndex)
{
APHYAICharacter* PHYCharacter = GetPHYAICharacter();
return PHYCharacter ? PHYCharacter->RequestInteraction(OptionIndex) : false;
}
void APHYAIController::SetCombatTarget(AActor* TargetActor)
{
if (APHYAICharacter* PHYCharacter = GetPHYAICharacter())
{
PHYCharacter->SetCombatTargetActor(TargetActor);
}
}
bool APHYAIController::ActivateAbilityByTag(const FGameplayTag AbilityTag)
{
APHYAICharacter* PHYCharacter = GetPHYAICharacter();
UAbilitySystemComponent* AbilitySystemComponent = PHYCharacter ? PHYCharacter->GetAbilitySystemComponent() : nullptr;
if (!AbilitySystemComponent || !AbilityTag.IsValid())
{
return false;
}
FGameplayTagContainer AbilityTags;
AbilityTags.AddTag(AbilityTag);
return AbilitySystemComponent->TryActivateAbilitiesByTag(AbilityTags);
}

View File

@@ -0,0 +1,7 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Camera/PHYUGCSpringArmComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYUGCSpringArmComponent)
// 当前包装层只解除抽象限制,后续相机专家可在这里集中扩展 UGC 项目逻辑。

View File

@@ -0,0 +1,37 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Characters/PHYAICharacter.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYAICharacter)
#include "GGA_AbilitySystemComponent.h"
APHYAICharacter::APHYAICharacter(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
AbilitySystemComponent = CreateDefaultSubobject<UGGA_AbilitySystemComponent>(TEXT("AbilitySystemComponent"));
AbilitySystemComponent->SetIsReplicated(true);
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
}
void APHYAICharacter::BeginPlay()
{
Super::BeginPlay();
InitializeAIAbilitySystem();
}
void APHYAICharacter::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
InitializeAIAbilitySystem();
}
void APHYAICharacter::InitializeAIAbilitySystem()
{
if (AbilitySystemComponent)
{
InitializeAbilitySystem(AbilitySystemComponent, this);
}
}

View File

@@ -0,0 +1,580 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Characters/PHYCharacterBase.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYCharacterBase)
#include "AbilitySystemComponent.h"
#include "Camera/CameraComponent.h"
#include "Characters/PHYCharacterSettings.h"
#include "Characters/PHYCharacterStateComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/GameFrameworkComponentManager.h"
#include "Components/SkeletalMeshComponent.h"
#include "Feedback/GES_ContextEffectComponent.h"
#include "GCS_CombatSystemComponent.h"
#include "GGA_AbilitySystemComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Interaction/GGS_InteractionSystemComponent.h"
#include "Net/UnrealNetwork.h"
#include "PHYGameplayTags.h"
#include "Ragdoll/GGS_RagdollComponent.h"
#include "Targeting/GCS_TargetingSystemComponent.h"
#include "Team/GCS_CombatTeamAgentComponent.h"
APHYCharacterBase::APHYCharacterBase(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
bReplicates = true;
CharacterStateComponent = CreateDefaultSubobject<UPHYCharacterStateComponent>(TEXT("CharacterStateComponent"));
CombatSystemComponent = CreateDefaultSubobject<UGCS_CombatSystemComponent>(TEXT("CombatSystemComponent"));
TargetingSystemComponent = CreateDefaultSubobject<UGCS_TargetingSystemComponent>(TEXT("TargetingSystemComponent"));
CombatTeamAgentComponent = CreateDefaultSubobject<UGCS_CombatTeamAgentComponent>(TEXT("CombatTeamAgentComponent"));
InteractionSystemComponent = CreateDefaultSubobject<UGGS_InteractionSystemComponent>(TEXT("InteractionSystemComponent"));
RagdollComponent = CreateDefaultSubobject<UGGS_RagdollComponent>(TEXT("RagdollComponent"));
ContextEffectComponent = CreateDefaultSubobject<UGES_ContextEffectComponent>(TEXT("ContextEffectComponent"));
}
void APHYCharacterBase::PreInitializeComponents()
{
Super::PreInitializeComponents();
UGameFrameworkComponentManager::AddGameFrameworkComponentReceiver(this);
}
void APHYCharacterBase::BeginPlay()
{
Super::BeginPlay();
ApplyCharacterSettings();
if (RagdollComponent)
{
RagdollComponent->SetMeshComponent(GetMesh());
}
if (ContextEffectComponent)
{
ContextEffectComponent->SetGameplayTagsProvider(this);
}
UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(this, UGameFrameworkComponentManager::NAME_GameActorReady);
}
void APHYCharacterBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
UGameFrameworkComponentManager::RemoveGameFrameworkComponentReceiver(this);
Super::EndPlay(EndPlayReason);
}
void APHYCharacterBase::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
if (CharacterStateComponent)
{
CharacterStateComponent->SetMovementIntent(FVector::ZeroVector);
}
}
void APHYCharacterBase::OnRep_Controller()
{
Super::OnRep_Controller();
if (CharacterStateComponent)
{
CharacterStateComponent->SetMovementIntent(FVector::ZeroVector);
}
}
UAbilitySystemComponent* APHYCharacterBase::GetAbilitySystemComponent() const
{
return CachedAbilitySystemComponent;
}
void APHYCharacterBase::GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const
{
if (CachedAbilitySystemComponent)
{
CachedAbilitySystemComponent->GetOwnedGameplayTags(TagContainer);
}
if (CharacterStateComponent)
{
CharacterStateComponent->GetOwnedStateTags(TagContainer);
}
}
void APHYCharacterBase::InitializeAbilitySystem(UAbilitySystemComponent* NewAbilitySystemComponent, AActor* OwnerActor)
{
if (!NewAbilitySystemComponent || !OwnerActor)
{
return;
}
if (CachedAbilitySystemComponent == NewAbilitySystemComponent && bAbilitySystemReady)
{
return;
}
CachedAbilitySystemComponent = NewAbilitySystemComponent;
if (UGGA_AbilitySystemComponent* GGAAbilitySystemComponent = Cast<UGGA_AbilitySystemComponent>(NewAbilitySystemComponent))
{
GGAAbilitySystemComponent->InitializeAbilitySystem(OwnerActor, this);
}
else
{
NewAbilitySystemComponent->InitAbilityActorInfo(OwnerActor, this);
}
bAbilitySystemReady = true;
}
void APHYCharacterBase::ClearAbilitySystem()
{
if (UGGA_AbilitySystemComponent* GGAAbilitySystemComponent = Cast<UGGA_AbilitySystemComponent>(CachedAbilitySystemComponent))
{
GGAAbilitySystemComponent->UninitializeAbilitySystem();
}
CachedAbilitySystemComponent = nullptr;
bAbilitySystemReady = false;
}
bool APHYCharacterBase::HandleInputTag(const FGameplayTag InputTag, const ETriggerEvent TriggerEvent)
{
if (!InputTag.IsValid() || IGCS_CombatEntityInterface::Execute_IsDead(this))
{
return false;
}
const bool bPressed = TriggerEvent == ETriggerEvent::Started || TriggerEvent == ETriggerEvent::Triggered;
const bool bReleased = TriggerEvent == ETriggerEvent::Completed || TriggerEvent == ETriggerEvent::Canceled;
if (InputTag == PHYGameplayTags::Input_Jump)
{
if (bPressed)
{
Jump();
}
else if (bReleased)
{
StopJumping();
}
return true;
}
if (InputTag == PHYGameplayTags::Input_Sprint)
{
if (CharacterStateComponent)
{
CharacterStateComponent->SetMovementState(bPressed ? PHYGameplayTags::State_Movement_Sprint : PHYGameplayTags::State_Movement_Run);
}
if (UCharacterMovementComponent* MoveComponent = GetCharacterMovement())
{
const UPHYCharacterSettings* Settings = GetDefault<UPHYCharacterSettings>();
MoveComponent->MaxWalkSpeed = bPressed ? Settings->DefaultSprintSpeed : Settings->DefaultMaxWalkSpeed;
}
return true;
}
if (InputTag == PHYGameplayTags::Input_Aim)
{
if (CharacterStateComponent)
{
CharacterStateComponent->SetMovementSet(bPressed ? PHYGameplayTags::State_MovementSet_Aiming : PHYGameplayTags::State_MovementSet_Default);
CharacterStateComponent->SetRotationMode(bPressed ? PHYGameplayTags::State_Rotation_Strafe : PHYGameplayTags::State_Rotation_OrientToMovement);
}
return true;
}
if (InputTag == PHYGameplayTags::Input_LockOn && bPressed)
{
if (TargetingSystemComponent)
{
if (TargetingSystemComponent->GetTargetedActor())
{
SetCombatTargetActor(nullptr);
}
else
{
TargetingSystemComponent->SearchForActorToTarget();
SetCombatTargetActor(TargetingSystemComponent->GetTargetedActor());
}
}
return true;
}
if (InputTag == PHYGameplayTags::Input_Interact && bPressed)
{
const UPHYCharacterSettings* Settings = GetDefault<UPHYCharacterSettings>();
return RequestInteraction(Settings->DefaultInteractionOption);
}
const FGameplayTag AbilityTag = GetAbilityTagForInputTag(InputTag);
if (AbilityTag.IsValid() && bPressed && CachedAbilitySystemComponent)
{
FGameplayTagContainer AbilityTags;
AbilityTags.AddTag(AbilityTag);
return CachedAbilitySystemComponent->TryActivateAbilitiesByTag(AbilityTags);
}
return false;
}
void APHYCharacterBase::SetMovementIntent(const FVector NewMovementIntent)
{
const FVector ClampedIntent = NewMovementIntent.GetClampedToMaxSize(1.0f);
if (CharacterStateComponent)
{
CharacterStateComponent->SetMovementIntent(ClampedIntent);
}
if (!HasAuthority())
{
ServerSetMovementIntent(ClampedIntent);
}
}
void APHYCharacterBase::SetCombatTargetActor(AActor* NewCombatTargetActor)
{
if (CharacterStateComponent)
{
CharacterStateComponent->SetCombatTargetActor(NewCombatTargetActor);
}
if (TargetingSystemComponent)
{
TargetingSystemComponent->SetTargetedActor(NewCombatTargetActor);
}
if (!HasAuthority())
{
ServerSetCombatTargetActor(NewCombatTargetActor);
}
}
bool APHYCharacterBase::RequestInteraction(const int32 OptionIndex)
{
const UPHYCharacterSettings* Settings = GetDefault<UPHYCharacterSettings>();
if (!Settings->bEnableGenericGameInteraction || !InteractionSystemComponent)
{
return false;
}
if (!HasAuthority())
{
ServerRequestInteraction(OptionIndex);
return true;
}
InteractionSystemComponent->SearchInteractableActors();
InteractionSystemComponent->StartInteraction(OptionIndex);
return true;
}
bool APHYCharacterBase::RequestInstantInteraction(const int32 OptionIndex)
{
const UPHYCharacterSettings* Settings = GetDefault<UPHYCharacterSettings>();
if (!Settings->bEnableGenericGameInteraction || !InteractionSystemComponent)
{
return false;
}
if (!HasAuthority())
{
ServerRequestInstantInteraction(OptionIndex);
return true;
}
InteractionSystemComponent->SearchInteractableActors();
InteractionSystemComponent->InstantInteraction(OptionIndex);
return true;
}
void APHYCharacterBase::EndInteraction()
{
if (!InteractionSystemComponent)
{
return;
}
if (!HasAuthority())
{
ServerEndInteraction();
return;
}
InteractionSystemComponent->EndInteraction();
}
void APHYCharacterBase::CycleInteractionTarget(const bool bNext)
{
if (!InteractionSystemComponent)
{
return;
}
if (!HasAuthority())
{
ServerCycleInteractionTarget(bNext);
return;
}
InteractionSystemComponent->CycleInteractableActors(bNext);
}
AActor* APHYCharacterBase::GetCombatTargetActor_Implementation() const
{
if (CharacterStateComponent && CharacterStateComponent->GetCombatTargetActor())
{
return CharacterStateComponent->GetCombatTargetActor();
}
return TargetingSystemComponent ? TargetingSystemComponent->GetTargetedActor() : nullptr;
}
const UDataTable* APHYCharacterBase::GetComboDefinitionTable_Implementation() const
{
return nullptr;
}
USceneComponent* APHYCharacterBase::GetCombatTargetObject_Implementation() const
{
const AActor* TargetActor = IGCS_CombatEntityInterface::Execute_GetCombatTargetActor(this);
return TargetActor ? TargetActor->GetRootComponent() : nullptr;
}
bool APHYCharacterBase::QueryAbilityActions_Implementation(FGameplayTagContainer AbilityTags, FGameplayTagContainer SourceTags, FGameplayTagContainer TargetTags, TArray<FGCS_AbilityAction>& AbilityActions)
{
(void)AbilityTags;
(void)SourceTags;
(void)TargetTags;
AbilityActions.Reset();
return false;
}
bool APHYCharacterBase::QueryAbilityActionsByContext_Implementation(UObject* Context, FGameplayTagContainer AbilityTags, FGameplayTagContainer SourceTags, FGameplayTagContainer TargetTags, TArray<FGCS_AbilityAction>& AbilityActions)
{
(void)Context;
(void)AbilityTags;
(void)SourceTags;
(void)TargetTags;
AbilityActions.Reset();
return false;
}
UObject* APHYCharacterBase::QueryWeapon_Implementation(const FGameplayTagQuery& Query) const
{
UObject* Weapon = CharacterStateComponent ? CharacterStateComponent->GetCurrentWeapon() : nullptr;
if (!Weapon || Query.IsEmpty())
{
return Weapon;
}
if (const IGameplayTagAssetInterface* WeaponTagsProvider = Cast<IGameplayTagAssetInterface>(Weapon))
{
FGameplayTagContainer WeaponTags;
WeaponTagsProvider->GetOwnedGameplayTags(WeaponTags);
return Query.Matches(WeaponTags) ? Weapon : nullptr;
}
return nullptr;
}
void APHYCharacterBase::SetRotationMode_Implementation(const FGameplayTag NewRotationMode)
{
if (CharacterStateComponent)
{
CharacterStateComponent->SetRotationMode(NewRotationMode);
}
}
FGameplayTag APHYCharacterBase::GetRotationMode_Implementation() const
{
return CharacterStateComponent ? CharacterStateComponent->GetRotationMode() : FGameplayTag();
}
void APHYCharacterBase::SetMovementSet_Implementation(const FGameplayTag NewMovementSet)
{
if (CharacterStateComponent)
{
CharacterStateComponent->SetMovementSet(NewMovementSet);
}
}
FGameplayTag APHYCharacterBase::GetMovementSet_Implementation() const
{
return CharacterStateComponent ? CharacterStateComponent->GetMovementSet() : FGameplayTag();
}
void APHYCharacterBase::SetMovementState_Implementation(const FGameplayTag NewMovementState)
{
if (CharacterStateComponent)
{
CharacterStateComponent->SetMovementState(NewMovementState);
}
}
FGameplayTag APHYCharacterBase::GetMovementState_Implementation() const
{
return CharacterStateComponent ? CharacterStateComponent->GetMovementState() : FGameplayTag();
}
void APHYCharacterBase::StartDeath_Implementation()
{
if (CharacterStateComponent && CharacterStateComponent->IsDead())
{
return;
}
if (CharacterStateComponent)
{
CharacterStateComponent->SetDead(true);
}
if (CachedAbilitySystemComponent)
{
CachedAbilitySystemComponent->CancelAllAbilities();
}
if (UCharacterMovementComponent* MoveComponent = GetCharacterMovement())
{
MoveComponent->DisableMovement();
}
if (UCapsuleComponent* Capsule = GetCapsuleComponent())
{
Capsule->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
}
void APHYCharacterBase::FinishDeath_Implementation()
{
if (RagdollComponent && !RagdollComponent->IsRagdolling())
{
RagdollComponent->StartRagdoll();
}
}
bool APHYCharacterBase::IsDead_Implementation() const
{
return CharacterStateComponent && CharacterStateComponent->IsDead();
}
FVector APHYCharacterBase::GetMovementIntent_Implementation() const
{
return CharacterStateComponent ? CharacterStateComponent->GetMovementIntent() : FVector::ZeroVector;
}
UObject* APHYCharacterBase::GetCurrentWeapon_Implementation(UObject* Context) const
{
(void)Context;
return CharacterStateComponent ? CharacterStateComponent->GetCurrentWeapon() : nullptr;
}
void APHYCharacterBase::SetCurrentWeapon_Implementation(UObject* Weapon)
{
if (CharacterStateComponent)
{
CharacterStateComponent->SetCurrentWeapon(Weapon);
}
}
bool APHYCharacterBase::GetRelativeTransformToSocket_Implementation(const USkeletalMeshComponent* InSkeletalMeshComponent, const UStaticMesh* StaticMesh, const USkeletalMesh* SkeletalMesh, const FName SocketName, FTransform& OutTransform) const
{
(void)StaticMesh;
(void)SkeletalMesh;
if (!InSkeletalMeshComponent || SocketName.IsNone() || !InSkeletalMeshComponent->DoesSocketExist(SocketName))
{
OutTransform = FTransform::Identity;
return false;
}
OutTransform = InSkeletalMeshComponent->GetSocketTransform(SocketName, RTS_Component);
return true;
}
FGameplayTag APHYCharacterBase::GetAbilityTagForInputTag(const FGameplayTag InputTag) const
{
if (InputTag == PHYGameplayTags::Input_Attack_Primary)
{
return PHYGameplayTags::Ability_Attack_Primary;
}
if (InputTag == PHYGameplayTags::Input_Attack_Secondary)
{
return PHYGameplayTags::Ability_Attack_Secondary;
}
if (InputTag == PHYGameplayTags::Input_Jump)
{
return PHYGameplayTags::Ability_Jump;
}
if (InputTag == PHYGameplayTags::Input_Interact)
{
return PHYGameplayTags::Ability_Interact;
}
return FGameplayTag();
}
void APHYCharacterBase::ApplyCharacterSettings()
{
const UPHYCharacterSettings* Settings = GetDefault<UPHYCharacterSettings>();
if (UCharacterMovementComponent* MoveComponent = GetCharacterMovement())
{
MoveComponent->MaxWalkSpeed = Settings->DefaultMaxWalkSpeed;
MoveComponent->MaxAcceleration = Settings->DefaultMaxAcceleration;
}
if (CharacterStateComponent && GetCharacterMovement() && GetCharacterMovement()->IsFalling())
{
CharacterStateComponent->SetMovementState(PHYGameplayTags::State_Movement_Falling);
}
}
void APHYCharacterBase::ServerSetMovementIntent_Implementation(const FVector NewMovementIntent)
{
if (CharacterStateComponent)
{
CharacterStateComponent->SetMovementIntent(NewMovementIntent);
}
}
void APHYCharacterBase::ServerSetCombatTargetActor_Implementation(AActor* NewCombatTargetActor)
{
SetCombatTargetActor(NewCombatTargetActor);
}
void APHYCharacterBase::ServerRequestInteraction_Implementation(const int32 OptionIndex)
{
RequestInteraction(OptionIndex);
}
void APHYCharacterBase::ServerRequestInstantInteraction_Implementation(const int32 OptionIndex)
{
RequestInstantInteraction(OptionIndex);
}
void APHYCharacterBase::ServerEndInteraction_Implementation()
{
EndInteraction();
}
void APHYCharacterBase::ServerCycleInteractionTarget_Implementation(const bool bNext)
{
CycleInteractionTarget(bNext);
}

View File

@@ -0,0 +1,7 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Characters/PHYCharacterSettings.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYCharacterSettings)
// 角色配置只承载默认值,实际初始化由角色基类按需读取。

View File

@@ -0,0 +1,89 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Characters/PHYCharacterStateComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYCharacterStateComponent)
#include "Net/UnrealNetwork.h"
#include "PHYGameplayTags.h"
UPHYCharacterStateComponent::UPHYCharacterStateComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
SetIsReplicatedByDefault(true);
MovementState = PHYGameplayTags::State_Movement_Walk;
MovementSet = PHYGameplayTags::State_MovementSet_Default;
RotationMode = PHYGameplayTags::State_Rotation_OrientToMovement;
}
void UPHYCharacterStateComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ThisClass, bDead);
DOREPLIFETIME(ThisClass, CombatTargetActor);
DOREPLIFETIME(ThisClass, CurrentWeapon);
DOREPLIFETIME(ThisClass, MovementIntent);
DOREPLIFETIME(ThisClass, MovementState);
DOREPLIFETIME(ThisClass, MovementSet);
DOREPLIFETIME(ThisClass, RotationMode);
}
void UPHYCharacterStateComponent::SetDead(const bool bNewDead)
{
bDead = bNewDead;
}
void UPHYCharacterStateComponent::SetCombatTargetActor(AActor* NewCombatTargetActor)
{
CombatTargetActor = NewCombatTargetActor;
}
void UPHYCharacterStateComponent::SetCurrentWeapon(UObject* NewCurrentWeapon)
{
CurrentWeapon = NewCurrentWeapon;
}
void UPHYCharacterStateComponent::SetMovementIntent(const FVector NewMovementIntent)
{
MovementIntent = NewMovementIntent.GetClampedToMaxSize(1.0f);
}
void UPHYCharacterStateComponent::SetMovementState(const FGameplayTag NewMovementState)
{
MovementState = NewMovementState;
}
void UPHYCharacterStateComponent::SetMovementSet(const FGameplayTag NewMovementSet)
{
MovementSet = NewMovementSet;
}
void UPHYCharacterStateComponent::SetRotationMode(const FGameplayTag NewRotationMode)
{
RotationMode = NewRotationMode;
}
void UPHYCharacterStateComponent::GetOwnedStateTags(FGameplayTagContainer& OutTags) const
{
if (bDead)
{
OutTags.AddTag(PHYGameplayTags::State_Dead);
}
if (MovementState.IsValid())
{
OutTags.AddTag(MovementState);
}
if (MovementSet.IsValid())
{
OutTags.AddTag(MovementSet);
}
if (RotationMode.IsValid())
{
OutTags.AddTag(RotationMode);
}
}

View File

@@ -0,0 +1,51 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Characters/PHYPlayerCharacter.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYPlayerCharacter)
#include "Camera/CameraComponent.h"
#include "Camera/PHYUGCSpringArmComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "Player/PHYPlayerState.h"
APHYPlayerCharacter::APHYPlayerCharacter(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
CameraBoom = CreateDefaultSubobject<UPHYUGCSpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(GetRootComponent());
CameraBoom->TargetArmLength = 320.0f;
CameraBoom->bUsePawnControlRotation = true;
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
FollowCamera->bUsePawnControlRotation = false;
}
void APHYPlayerCharacter::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
InitializeAbilitySystemFromPlayerState();
}
void APHYPlayerCharacter::OnRep_PlayerState()
{
Super::OnRep_PlayerState();
InitializeAbilitySystemFromPlayerState();
}
void APHYPlayerCharacter::InitializeAbilitySystemFromPlayerState()
{
APHYPlayerState* PHYPlayerState = GetPlayerState<APHYPlayerState>();
if (!PHYPlayerState)
{
return;
}
if (UAbilitySystemComponent* PlayerASC = PHYPlayerState->GetAbilitySystemComponent())
{
InitializeAbilitySystem(PlayerASC, PHYPlayerState);
}
}

View File

@@ -10,4 +10,13 @@ namespace PHYGameplayTags
UE_DEFINE_GAMEPLAY_TAG(State_LockedOn, "State.LockedOn");
UE_DEFINE_GAMEPLAY_TAG(State_Interacting, "State.Interacting");
UE_DEFINE_GAMEPLAY_TAG(State_UI_Open, "State.UI.Open");
UE_DEFINE_GAMEPLAY_TAG(State_Dead, "State.Dead");
UE_DEFINE_GAMEPLAY_TAG(State_Movement_Walk, "State.Movement.Walk");
UE_DEFINE_GAMEPLAY_TAG(State_Movement_Run, "State.Movement.Run");
UE_DEFINE_GAMEPLAY_TAG(State_Movement_Sprint, "State.Movement.Sprint");
UE_DEFINE_GAMEPLAY_TAG(State_Movement_Falling, "State.Movement.Falling");
UE_DEFINE_GAMEPLAY_TAG(State_MovementSet_Default, "State.MovementSet.Default");
UE_DEFINE_GAMEPLAY_TAG(State_MovementSet_Aiming, "State.MovementSet.Aiming");
UE_DEFINE_GAMEPLAY_TAG(State_Rotation_OrientToMovement, "State.Rotation.OrientToMovement");
UE_DEFINE_GAMEPLAY_TAG(State_Rotation_Strafe, "State.Rotation.Strafe");
}

View File

@@ -0,0 +1,19 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Player/PHYPlayerCameraManager.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYPlayerCameraManager)
#include "Player/PHYPlayerController.h"
FRotator APHYPlayerCameraManager::GetRotationInput_Implementation() const
{
const APHYPlayerController* PHYController = Cast<APHYPlayerController>(PCOwner);
return PHYController ? PHYController->GetCachedRotationInput() : FRotator::ZeroRotator;
}
FVector APHYPlayerCameraManager::GetMovementControlInput_Implementation() const
{
const APHYPlayerController* PHYController = Cast<APHYPlayerController>(PCOwner);
return PHYController ? PHYController->GetCachedMovementControlInput() : FVector::ZeroVector;
}

View File

@@ -0,0 +1,138 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Player/PHYPlayerController.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYPlayerController)
#include "Characters/PHYPlayerCharacter.h"
#include "GIPS_InputSystemComponent.h"
#include "InputActionValue.h"
#include "Player/PHYPlayerCameraManager.h"
#include "PHYGameplayTags.h"
namespace
{
// 将 EnhancedInput 的多种值类型统一成二维输入,便于移动和视角路径共用。
FVector2D ExtractAxis2D(const FInputActionValue& Value)
{
switch (Value.GetValueType())
{
case EInputActionValueType::Axis1D:
return FVector2D(Value.Get<float>(), 0.0f);
case EInputActionValueType::Axis2D:
return Value.Get<FVector2D>();
case EInputActionValueType::Axis3D:
{
const FVector Axis3D = Value.Get<FVector>();
return FVector2D(Axis3D.X, Axis3D.Y);
}
default:
return FVector2D::ZeroVector;
}
}
bool IsInputReleased(const ETriggerEvent TriggerEvent)
{
return TriggerEvent == ETriggerEvent::Completed || TriggerEvent == ETriggerEvent::Canceled;
}
}
APHYPlayerController::APHYPlayerController(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
PlayerCameraManagerClass = APHYPlayerCameraManager::StaticClass();
InputSystemComponent = CreateDefaultSubobject<UGIPS_InputSystemComponent>(TEXT("InputSystemComponent"));
}
void APHYPlayerController::BeginPlay()
{
Super::BeginPlay();
BindInputEvents();
}
void APHYPlayerController::OnPossess(APawn* InPawn)
{
Super::OnPossess(InPawn);
BindInputEvents();
}
APHYPlayerCharacter* APHYPlayerController::GetPHYPlayerCharacter() const
{
return Cast<APHYPlayerCharacter>(GetPawn());
}
void APHYPlayerController::BindInputEvents()
{
if (!InputSystemComponent)
{
return;
}
InputSystemComponent->OnReceivedInput.RemoveDynamic(this, &ThisClass::OnReceivedInput);
InputSystemComponent->OnReceivedInput.AddDynamic(this, &ThisClass::OnReceivedInput);
}
void APHYPlayerController::OnReceivedInput(const FInputActionInstance& ActionData, const FGameplayTag& InputTag, const ETriggerEvent TriggerEvent)
{
if (InputTag == PHYGameplayTags::Input_Move)
{
HandleMoveInput(ActionData, TriggerEvent);
return;
}
if (InputTag == PHYGameplayTags::Input_Look)
{
HandleLookInput(ActionData, TriggerEvent);
return;
}
if (APHYPlayerCharacter* PHYCharacter = GetPHYPlayerCharacter())
{
PHYCharacter->HandleInputTag(InputTag, TriggerEvent);
}
}
bool APHYPlayerController::HandleMoveInput(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent)
{
APHYPlayerCharacter* PHYCharacter = GetPHYPlayerCharacter();
if (!PHYCharacter)
{
CachedMovementInput = FVector2D::ZeroVector;
return false;
}
CachedMovementInput = IsInputReleased(TriggerEvent) ? FVector2D::ZeroVector : ExtractAxis2D(ActionData.GetValue());
const FRotator CurrentControlRotation = GetControlRotation();
const FRotator YawRotation(0.0f, CurrentControlRotation.Yaw, 0.0f);
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
const FVector WorldIntent = (ForwardDirection * CachedMovementInput.Y) + (RightDirection * CachedMovementInput.X);
PHYCharacter->SetMovementIntent(WorldIntent);
if (!CachedMovementInput.IsNearlyZero())
{
PHYCharacter->AddMovementInput(ForwardDirection, CachedMovementInput.Y);
PHYCharacter->AddMovementInput(RightDirection, CachedMovementInput.X);
}
return true;
}
bool APHYPlayerController::HandleLookInput(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent)
{
CachedLookInput = IsInputReleased(TriggerEvent) ? FVector2D::ZeroVector : ExtractAxis2D(ActionData.GetValue());
CachedRotationInput = FRotator(CachedLookInput.Y, CachedLookInput.X, 0.0f);
if (!CachedLookInput.IsNearlyZero())
{
AddYawInput(CachedLookInput.X);
AddPitchInput(CachedLookInput.Y);
}
return true;
}

View File

@@ -0,0 +1,22 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Player/PHYPlayerState.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYPlayerState)
#include "GGA_AbilitySystemComponent.h"
APHYPlayerState::APHYPlayerState(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
SetNetUpdateFrequency(100.0f);
AbilitySystemComponent = CreateDefaultSubobject<UGGA_AbilitySystemComponent>(TEXT("AbilitySystemComponent"));
AbilitySystemComponent->SetIsReplicated(true);
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
}
UAbilitySystemComponent* APHYPlayerState::GetAbilitySystemComponent() const
{
return AbilitySystemComponent;
}

View File

@@ -0,0 +1,38 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "AIController.h"
#include "GameplayTagContainer.h"
#include "PHYAIController.generated.h"
class APHYAICharacter;
/**
* @brief PHY AI 控制器。
*
* AI 通过角色请求接口触发移动、目标、交互和 Ability不模拟玩家输入。
*/
UCLASS(BlueprintType, Blueprintable)
class PHY_API APHYAIController : public AAIController
{
GENERATED_BODY()
public:
/** @brief 获取当前控制的 PHY AI 角色。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|AI")
APHYAICharacter* GetPHYAICharacter() const;
/** @brief 请求 AI 角色开始交互。 */
UFUNCTION(BlueprintCallable, Category="PHY|AI")
bool RequestInteraction(int32 OptionIndex = 0);
/** @brief 设置 AI 角色的战斗目标。 */
UFUNCTION(BlueprintCallable, Category="PHY|AI")
void SetCombatTarget(AActor* TargetActor);
/** @brief 通过 Ability Tag 激活 AI 角色能力。 */
UFUNCTION(BlueprintCallable, Category="PHY|AI")
bool ActivateAbilityByTag(FGameplayTag AbilityTag);
};

View File

@@ -0,0 +1,18 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Camera/Components/UGC_SpringArmComponentBase.h"
#include "PHYUGCSpringArmComponent.generated.h"
/**
* @brief PHY 对 UGC SpringArm 的项目级可实例化包装。
*
* UGC 基类是抽象组件,项目通过该薄包装在 C++ 构造玩家相机臂。
*/
UCLASS(BlueprintType, Blueprintable, meta=(BlueprintSpawnableComponent))
class PHY_API UPHYUGCSpringArmComponent : public UUGC_SpringArmComponentBase
{
GENERATED_BODY()
};

View File

@@ -0,0 +1,42 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Characters/PHYCharacterBase.h"
#include "PHYAICharacter.generated.h"
class UGGA_AbilitySystemComponent;
/**
* @brief PHY AI 角色。
*
* AI ASC 创建在 AI Character 自身,不创建玩家相机,也不绑定玩家输入。
*/
UCLASS(BlueprintType, Blueprintable)
class PHY_API APHYAICharacter : public APHYCharacterBase
{
GENERATED_BODY()
public:
/** @brief 构造 AI 角色自有 ASC。 */
APHYAICharacter(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
/** @brief 开始游戏时初始化 AI 自身 ASC。 */
virtual void BeginPlay() override;
/** @brief AI 被控制器接管时初始化自身 ASC。 */
virtual void PossessedBy(AController* NewController) override;
/** @brief 获取 AI 自有 GGA ASC。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|AI")
UGGA_AbilitySystemComponent* GetAIAbilitySystemComponent() const { return AbilitySystemComponent; }
protected:
/** @brief 初始化 AI 自身作为 Owner 和 Avatar 的 ASC。 */
virtual void InitializeAIAbilitySystem();
/** @brief AI 自身 ASC。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|AI")
TObjectPtr<UGGA_AbilitySystemComponent> AbilitySystemComponent;
};

View File

@@ -0,0 +1,244 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "AbilitySystemInterface.h"
#include "GCS_CombatEntityInterface.h"
#include "GameplayTagAssetInterface.h"
#include "GameFramework/Character.h"
#include "InputTriggers.h"
#include "PHYCharacterBase.generated.h"
class UAbilitySystemComponent;
class UGCS_CombatSystemComponent;
class UGCS_CombatTeamAgentComponent;
class UGCS_TargetingSystemComponent;
class UGES_ContextEffectComponent;
class UGGS_InteractionSystemComponent;
class UGGS_RagdollComponent;
class UPHYCharacterStateComponent;
/**
* @brief PHY 玩家和 AI 共用角色基类。
*
* 基类直接继承 ACharacter并通过项目级组件和接口适配 GAS、GCS、GGS、UGC 与后续运动系统。
*/
UCLASS(Abstract, BlueprintType, Blueprintable)
class PHY_API APHYCharacterBase : public ACharacter, public IAbilitySystemInterface, public IGameplayTagAssetInterface, public IGCS_CombatEntityInterface
{
GENERATED_BODY()
public:
/** @brief 构造基础角色组件。 */
APHYCharacterBase(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
/** @brief 注册 ModularGameplay 组件接收者。 */
virtual void PreInitializeComponents() override;
/** @brief 初始化角色运行时组件。 */
virtual void BeginPlay() override;
/** @brief 移除 ModularGameplay 组件接收者。 */
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
/** @brief 控制器接管时刷新项目状态。 */
virtual void PossessedBy(AController* NewController) override;
/** @brief 控制器复制到客户端后刷新项目状态。 */
virtual void OnRep_Controller() override;
/** @brief 获取当前角色使用的 AbilitySystemComponent。 */
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
/** @brief 收集 ASC 与角色状态组件提供的 GameplayTag。 */
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override;
/** @brief 初始化当前 Avatar 对应的 ASC。 */
UFUNCTION(BlueprintCallable, Category="PHY|Character|Ability")
virtual void InitializeAbilitySystem(UAbilitySystemComponent* NewAbilitySystemComponent, AActor* OwnerActor);
/** @brief 清理角色缓存的 ASC 引用。 */
UFUNCTION(BlueprintCallable, Category="PHY|Character|Ability")
virtual void ClearAbilitySystem();
/** @brief 查询 ASC 是否已经完成项目级初始化。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Character|Ability")
bool IsAbilitySystemReady() const { return bAbilitySystemReady; }
/** @brief 接收输入 Tag 并路由到移动、交互或 GAS Ability。 */
UFUNCTION(BlueprintCallable, Category="PHY|Character|Input")
virtual bool HandleInputTag(FGameplayTag InputTag, ETriggerEvent TriggerEvent);
/** @brief 设置移动意图,并在客户端请求服务端同步。 */
UFUNCTION(BlueprintCallable, Category="PHY|Character|Movement")
virtual void SetMovementIntent(FVector NewMovementIntent);
/** @brief 设置项目级战斗目标。 */
UFUNCTION(BlueprintCallable, Category="PHY|Character|Combat")
virtual void SetCombatTargetActor(AActor* NewCombatTargetActor);
/** @brief 请求开始交互。 */
UFUNCTION(BlueprintCallable, Category="PHY|Character|Interaction")
virtual bool RequestInteraction(int32 OptionIndex = 0);
/** @brief 请求一次性即时交互。 */
UFUNCTION(BlueprintCallable, Category="PHY|Character|Interaction")
virtual bool RequestInstantInteraction(int32 OptionIndex = 0);
/** @brief 请求结束当前交互。 */
UFUNCTION(BlueprintCallable, Category="PHY|Character|Interaction")
virtual void EndInteraction();
/** @brief 请求切换当前可交互对象。 */
UFUNCTION(BlueprintCallable, Category="PHY|Character|Interaction")
virtual void CycleInteractionTarget(bool bNext);
/** @brief 获取项目角色状态组件。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Character")
UPHYCharacterStateComponent* GetCharacterStateComponent() const { return CharacterStateComponent; }
/** @brief 获取 GCS 战斗组件。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Character")
UGCS_CombatSystemComponent* GetCombatSystemComponent() const { return CombatSystemComponent; }
/** @brief 获取 GCS 目标组件。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Character")
UGCS_TargetingSystemComponent* GetTargetingSystemComponent() const { return TargetingSystemComponent; }
/** @brief 获取 GGS 交互组件。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Character")
UGGS_InteractionSystemComponent* GetInteractionSystemComponent() const { return InteractionSystemComponent; }
/** @brief 获取 GGS 布娃娃组件。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Character")
UGGS_RagdollComponent* GetRagdollComponent() const { return RagdollComponent; }
/** @brief 获取上下文效果组件。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Character")
UGES_ContextEffectComponent* GetContextEffectComponent() const { return ContextEffectComponent; }
/** @brief 获取当前战斗目标 Actor。 */
virtual AActor* GetCombatTargetActor_Implementation() const override;
/** @brief 获取连击定义表,首期骨架默认不绑定表。 */
virtual const UDataTable* GetComboDefinitionTable_Implementation() const override;
/** @brief 获取当前战斗目标场景对象。 */
virtual USceneComponent* GetCombatTargetObject_Implementation() const override;
/** @brief 查询能力动作,首期骨架默认返回空。 */
virtual bool QueryAbilityActions_Implementation(FGameplayTagContainer AbilityTags, FGameplayTagContainer SourceTags, FGameplayTagContainer TargetTags, TArray<FGCS_AbilityAction>& AbilityActions) override;
/** @brief 按上下文查询能力动作,首期骨架默认返回空。 */
virtual bool QueryAbilityActionsByContext_Implementation(UObject* Context, FGameplayTagContainer AbilityTags, FGameplayTagContainer SourceTags, FGameplayTagContainer TargetTags, TArray<FGCS_AbilityAction>& AbilityActions) override;
/** @brief 按 Tag 查询当前武器。 */
virtual UObject* QueryWeapon_Implementation(const FGameplayTagQuery& Query) const override;
/** @brief 设置旋转模式。 */
virtual void SetRotationMode_Implementation(FGameplayTag NewRotationMode) override;
/** @brief 获取旋转模式。 */
virtual FGameplayTag GetRotationMode_Implementation() const override;
/** @brief 设置移动集合。 */
virtual void SetMovementSet_Implementation(FGameplayTag NewMovementSet) override;
/** @brief 获取移动集合。 */
virtual FGameplayTag GetMovementSet_Implementation() const override;
/** @brief 设置移动状态。 */
virtual void SetMovementState_Implementation(FGameplayTag NewMovementState) override;
/** @brief 获取移动状态。 */
virtual FGameplayTag GetMovementState_Implementation() const override;
/** @brief 开始死亡流程。 */
virtual void StartDeath_Implementation() override;
/** @brief 完成死亡流程。 */
virtual void FinishDeath_Implementation() override;
/** @brief 查询角色是否死亡。 */
virtual bool IsDead_Implementation() const override;
/** @brief 获取移动意图。 */
virtual FVector GetMovementIntent_Implementation() const override;
/** @brief 获取当前武器。 */
virtual UObject* GetCurrentWeapon_Implementation(UObject* Context = nullptr) const override;
/** @brief 设置当前武器。 */
virtual void SetCurrentWeapon_Implementation(UObject* Weapon) override;
/** @brief 获取指定 Socket 的相对变换。 */
virtual bool GetRelativeTransformToSocket_Implementation(const USkeletalMeshComponent* InSkeletalMeshComponent, const UStaticMesh* StaticMesh, const USkeletalMesh* SkeletalMesh, FName SocketName, FTransform& OutTransform) const override;
protected:
/** @brief 将输入 Tag 映射为 Ability Tag。 */
virtual FGameplayTag GetAbilityTagForInputTag(FGameplayTag InputTag) const;
/** @brief 应用 Character 配置默认值。 */
virtual void ApplyCharacterSettings();
/** @brief 服务端同步移动意图。 */
UFUNCTION(Server, Unreliable)
void ServerSetMovementIntent(FVector NewMovementIntent);
/** @brief 服务端设置战斗目标。 */
UFUNCTION(Server, Reliable)
void ServerSetCombatTargetActor(AActor* NewCombatTargetActor);
/** @brief 服务端执行开始交互。 */
UFUNCTION(Server, Reliable)
void ServerRequestInteraction(int32 OptionIndex);
/** @brief 服务端执行即时交互。 */
UFUNCTION(Server, Reliable)
void ServerRequestInstantInteraction(int32 OptionIndex);
/** @brief 服务端结束交互。 */
UFUNCTION(Server, Reliable)
void ServerEndInteraction();
/** @brief 服务端切换交互目标。 */
UFUNCTION(Server, Reliable)
void ServerCycleInteractionTarget(bool bNext);
/** @brief 角色共享状态组件。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|Character")
TObjectPtr<UPHYCharacterStateComponent> CharacterStateComponent;
/** @brief GCS 战斗系统组件。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|Character")
TObjectPtr<UGCS_CombatSystemComponent> CombatSystemComponent;
/** @brief GCS 目标系统组件。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|Character")
TObjectPtr<UGCS_TargetingSystemComponent> TargetingSystemComponent;
/** @brief GCS 战斗队伍代理组件。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|Character")
TObjectPtr<UGCS_CombatTeamAgentComponent> CombatTeamAgentComponent;
/** @brief GGS 交互系统组件。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|Character")
TObjectPtr<UGGS_InteractionSystemComponent> InteractionSystemComponent;
/** @brief GGS 布娃娃组件。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|Character")
TObjectPtr<UGGS_RagdollComponent> RagdollComponent;
/** @brief GES 上下文效果组件。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|Character")
TObjectPtr<UGES_ContextEffectComponent> ContextEffectComponent;
/** @brief 当前 Avatar 缓存的 ASC玩家来自 PlayerStateAI 来自自身。 */
UPROPERTY(Transient)
TObjectPtr<UAbilitySystemComponent> CachedAbilitySystemComponent;
/** @brief ASC 是否已经初始化为当前 Avatar。 */
UPROPERTY(Transient)
bool bAbilitySystemReady = false;
};

View File

@@ -0,0 +1,75 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "UObject/SoftObjectPtr.h"
#include "PHYCharacterSettings.generated.h"
class APHYAICharacter;
class APHYAIController;
class APHYPlayerCameraManager;
class APHYPlayerCharacter;
class APHYPlayerController;
class UUGC_CameraDataAssetBase;
/**
* @brief PHY 角色系统配置。
*
* 角色默认参数独立放在 PHYCharacter 配置域,避免把玩法参数混入 Engine 或 Input 标准配置。
*/
UCLASS(Config=PHYCharacter, DefaultConfig)
class PHY_API UPHYCharacterSettings : public UObject
{
GENERATED_BODY()
public:
/** @brief 是否启用 GenericGameSystem 交互组件路径。 */
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Character")
bool bEnableGenericGameInteraction = true;
/** @brief 是否启用 GenericEffectsSystem 上下文效果组件。 */
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Character")
bool bEnableContextEffects = true;
/** @brief 默认最大行走速度。 */
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Character")
float DefaultMaxWalkSpeed = 500.0f;
/** @brief 默认冲刺速度。 */
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Character")
float DefaultSprintSpeed = 650.0f;
/** @brief 默认最大加速度。 */
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Character")
float DefaultMaxAcceleration = 2048.0f;
/** @brief 默认交互选项索引。 */
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Character")
int32 DefaultInteractionOption = 0;
/** @brief 玩家角色默认软类引用。 */
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Character")
TSoftClassPtr<APHYPlayerCharacter> DefaultPlayerCharacterClass;
/** @brief AI 角色默认软类引用。 */
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Character")
TSoftClassPtr<APHYAICharacter> DefaultAICharacterClass;
/** @brief 玩家控制器默认软类引用。 */
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Character")
TSoftClassPtr<APHYPlayerController> DefaultPlayerControllerClass;
/** @brief AI 控制器默认软类引用。 */
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Character")
TSoftClassPtr<APHYAIController> DefaultAIControllerClass;
/** @brief 玩家相机管理器默认软类引用。 */
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Character")
TSoftClassPtr<APHYPlayerCameraManager> DefaultPlayerCameraManagerClass;
/** @brief UGC 默认 CameraData 软引用,首期不强制制作资产。 */
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Character")
TSoftObjectPtr<UUGC_CameraDataAssetBase> DefaultUGCCameraDataAsset;
};

View File

@@ -0,0 +1,114 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "GameplayTagContainer.h"
#include "PHYCharacterStateComponent.generated.h"
/**
* @brief PHY 角色复制状态组件。
*
* 该组件集中保存 GCS、相机、输入和运动层共享的轻量状态避免玩法层直接散落插件状态字段。
*/
UCLASS(BlueprintType, Blueprintable, meta=(BlueprintSpawnableComponent))
class PHY_API UPHYCharacterStateComponent : public UActorComponent
{
GENERATED_BODY()
public:
/** @brief 构造默认复制状态。 */
UPHYCharacterStateComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
/** @brief 注册需要网络复制的角色状态字段。 */
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
/** @brief 设置死亡状态。 */
UFUNCTION(BlueprintCallable, Category="PHY|Character|State")
void SetDead(bool bNewDead);
/** @brief 查询角色是否已经死亡。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Character|State")
bool IsDead() const { return bDead; }
/** @brief 设置当前战斗目标 Actor。 */
UFUNCTION(BlueprintCallable, Category="PHY|Character|State")
void SetCombatTargetActor(AActor* NewCombatTargetActor);
/** @brief 获取当前战斗目标 Actor。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Character|State")
AActor* GetCombatTargetActor() const { return CombatTargetActor; }
/** @brief 设置当前武器对象。 */
UFUNCTION(BlueprintCallable, Category="PHY|Character|State")
void SetCurrentWeapon(UObject* NewCurrentWeapon);
/** @brief 获取当前武器对象。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Character|State")
UObject* GetCurrentWeapon() const { return CurrentWeapon; }
/** @brief 设置移动输入意图,通常由玩家控制器或 AI 控制器写入。 */
UFUNCTION(BlueprintCallable, Category="PHY|Character|State")
void SetMovementIntent(FVector NewMovementIntent);
/** @brief 获取当前移动输入意图。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Character|State")
FVector GetMovementIntent() const { return MovementIntent; }
/** @brief 设置当前移动状态 Tag。 */
UFUNCTION(BlueprintCallable, Category="PHY|Character|State")
void SetMovementState(FGameplayTag NewMovementState);
/** @brief 获取当前移动状态 Tag。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Character|State")
FGameplayTag GetMovementState() const { return MovementState; }
/** @brief 设置当前移动集合 Tag。 */
UFUNCTION(BlueprintCallable, Category="PHY|Character|State")
void SetMovementSet(FGameplayTag NewMovementSet);
/** @brief 获取当前移动集合 Tag。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Character|State")
FGameplayTag GetMovementSet() const { return MovementSet; }
/** @brief 设置当前旋转模式 Tag。 */
UFUNCTION(BlueprintCallable, Category="PHY|Character|State")
void SetRotationMode(FGameplayTag NewRotationMode);
/** @brief 获取当前旋转模式 Tag。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Character|State")
FGameplayTag GetRotationMode() const { return RotationMode; }
/** @brief 将状态组件拥有的 GameplayTag 写入输出容器。 */
void GetOwnedStateTags(FGameplayTagContainer& OutTags) const;
protected:
/** @brief 死亡状态,由服务端驱动复制。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category="PHY|Character|State")
bool bDead = false;
/** @brief 当前战斗目标 Actor。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category="PHY|Character|State")
TObjectPtr<AActor> CombatTargetActor;
/** @brief 当前武器对象。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category="PHY|Character|State")
TObjectPtr<UObject> CurrentWeapon;
/** @brief 移动输入意图。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category="PHY|Character|State")
FVector MovementIntent = FVector::ZeroVector;
/** @brief 当前移动状态。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category="PHY|Character|State")
FGameplayTag MovementState;
/** @brief 当前移动集合。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category="PHY|Character|State")
FGameplayTag MovementSet;
/** @brief 当前旋转模式。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category="PHY|Character|State")
FGameplayTag RotationMode;
};

View File

@@ -0,0 +1,51 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Characters/PHYCharacterBase.h"
#include "PHYPlayerCharacter.generated.h"
class UCameraComponent;
class UPHYUGCSpringArmComponent;
/**
* @brief PHY 玩家角色。
*
* 玩家角色从 PlayerState 获取 ASC并只持有相机组件不直接持有输入组件。
*/
UCLASS(BlueprintType, Blueprintable)
class PHY_API APHYPlayerCharacter : public APHYCharacterBase
{
GENERATED_BODY()
public:
/** @brief 构造玩家角色相机组件。 */
APHYPlayerCharacter(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
/** @brief 服务端 Possess 后从 PlayerState 初始化 ASC。 */
virtual void PossessedBy(AController* NewController) override;
/** @brief 客户端 PlayerState 复制后初始化 ASC。 */
virtual void OnRep_PlayerState() override;
/** @brief 获取 UGC 相机臂。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Camera")
UPHYUGCSpringArmComponent* GetCameraBoom() const { return CameraBoom; }
/** @brief 获取跟随相机。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Camera")
UCameraComponent* GetFollowCamera() const { return FollowCamera; }
protected:
/** @brief 根据当前 PlayerState 初始化 ASC。 */
virtual void InitializeAbilitySystemFromPlayerState();
/** @brief UGC 项目相机臂。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|Camera")
TObjectPtr<UPHYUGCSpringArmComponent> CameraBoom;
/** @brief 玩家跟随相机。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|Camera")
TObjectPtr<UCameraComponent> FollowCamera;
};

View File

@@ -23,4 +23,31 @@ namespace PHYGameplayTags
/** @brief UI 占用主要输入。 */
UE_DECLARE_GAMEPLAY_TAG_EXTERN(State_UI_Open);
/** @brief 角色已经进入死亡流程。 */
UE_DECLARE_GAMEPLAY_TAG_EXTERN(State_Dead);
/** @brief 角色处于行走移动状态。 */
UE_DECLARE_GAMEPLAY_TAG_EXTERN(State_Movement_Walk);
/** @brief 角色处于常规跑动移动状态。 */
UE_DECLARE_GAMEPLAY_TAG_EXTERN(State_Movement_Run);
/** @brief 角色处于冲刺移动状态。 */
UE_DECLARE_GAMEPLAY_TAG_EXTERN(State_Movement_Sprint);
/** @brief 角色处于下落移动状态。 */
UE_DECLARE_GAMEPLAY_TAG_EXTERN(State_Movement_Falling);
/** @brief 角色使用默认移动集合。 */
UE_DECLARE_GAMEPLAY_TAG_EXTERN(State_MovementSet_Default);
/** @brief 角色使用瞄准移动集合。 */
UE_DECLARE_GAMEPLAY_TAG_EXTERN(State_MovementSet_Aiming);
/** @brief 角色旋转朝向移动方向。 */
UE_DECLARE_GAMEPLAY_TAG_EXTERN(State_Rotation_OrientToMovement);
/** @brief 角色旋转使用战斗横移模式。 */
UE_DECLARE_GAMEPLAY_TAG_EXTERN(State_Rotation_Strafe);
}

View File

@@ -0,0 +1,25 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Camera/UGC_PlayerCameraManager.h"
#include "PHYPlayerCameraManager.generated.h"
/**
* @brief PHY 玩家相机管理器。
*
* 该类型从 PlayerController 的 C++ 缓存读取输入,不把 UGC 蓝图 Pawn Interface 作为主路径。
*/
UCLASS(BlueprintType, Blueprintable)
class PHY_API APHYPlayerCameraManager : public AUGC_PlayerCameraManager
{
GENERATED_BODY()
protected:
/** @brief 返回玩家视角输入。 */
virtual FRotator GetRotationInput_Implementation() const override;
/** @brief 返回玩家移动控制输入。 */
virtual FVector GetMovementControlInput_Implementation() const override;
};

View File

@@ -0,0 +1,78 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "GIPS_InputTypes.h"
#include "PHYPlayerController.generated.h"
class APHYPlayerCharacter;
class UGIPS_InputSystemComponent;
/**
* @brief PHY 玩家控制器。
*
* 控制器持有 GenericInputSystem 组件,并将 Input GameplayTag 路由到当前玩家角色、ASC、移动和相机输入缓存。
*/
UCLASS(BlueprintType, Blueprintable)
class PHY_API APHYPlayerController : public APlayerController
{
GENERATED_BODY()
public:
/** @brief 构造输入组件并设置 UGC 相机管理器。 */
APHYPlayerController(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
/** @brief 初始化输入委托。 */
virtual void BeginPlay() override;
/** @brief 接管 Pawn 时刷新输入委托。 */
virtual void OnPossess(APawn* InPawn) override;
/** @brief 获取 GenericInputSystem 组件。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Input")
UGIPS_InputSystemComponent* GetInputSystemComponent() const { return InputSystemComponent; }
/** @brief 获取当前 PHY 玩家角色。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Input")
APHYPlayerCharacter* GetPHYPlayerCharacter() const;
/** @brief 获取相机使用的旋转输入缓存。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Input")
FRotator GetCachedRotationInput() const { return CachedRotationInput; }
/** @brief 获取相机使用的移动控制输入缓存。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Input")
FVector GetCachedMovementControlInput() const { return FVector(CachedMovementInput.X, CachedMovementInput.Y, 0.0f); }
protected:
/** @brief 绑定 GenericInputSystem 输入委托。 */
virtual void BindInputEvents();
/** @brief 处理 GenericInputSystem 发出的输入事件。 */
UFUNCTION()
void OnReceivedInput(const FInputActionInstance& ActionData, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent);
/** @brief 处理移动输入。 */
virtual bool HandleMoveInput(const FInputActionInstance& ActionData, ETriggerEvent TriggerEvent);
/** @brief 处理视角输入。 */
virtual bool HandleLookInput(const FInputActionInstance& ActionData, ETriggerEvent TriggerEvent);
/** @brief GenericInputSystem 路由组件。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|Input")
TObjectPtr<UGIPS_InputSystemComponent> InputSystemComponent;
/** @brief 本地移动输入缓存。 */
UPROPERTY(Transient)
FVector2D CachedMovementInput = FVector2D::ZeroVector;
/** @brief 本地视角输入缓存。 */
UPROPERTY(Transient)
FVector2D CachedLookInput = FVector2D::ZeroVector;
/** @brief UGC 相机管理器读取的旋转输入缓存。 */
UPROPERTY(Transient)
FRotator CachedRotationInput = FRotator::ZeroRotator;
};

View File

@@ -0,0 +1,37 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "AbilitySystemInterface.h"
#include "GameFramework/PlayerState.h"
#include "PHYPlayerState.generated.h"
class UGGA_AbilitySystemComponent;
/**
* @brief PHY 玩家状态。
*
* 玩家 ASC 挂在 PlayerState 上,以支持重生、换 Pawn 和持久属性复制。
*/
UCLASS(BlueprintType, Blueprintable)
class PHY_API APHYPlayerState : public APlayerState, public IAbilitySystemInterface
{
GENERATED_BODY()
public:
/** @brief 构造玩家状态与 ASC。 */
APHYPlayerState(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
/** @brief 获取玩家持久 AbilitySystemComponent。 */
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
/** @brief 获取 GGA ASC 的强类型指针。 */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|PlayerState")
UGGA_AbilitySystemComponent* GetGGAAbilitySystemComponent() const { return AbilitySystemComponent; }
protected:
/** @brief 玩家持久 ASC。 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|PlayerState")
TObjectPtr<UGGA_AbilitySystemComponent> AbilitySystemComponent;
};