LyraAbilitySystemComponent.h 分析
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Abilities/LyraGameplayAbility.h"
#include "AbilitySystemComponent.h"
#include "NativeGameplayTags.h"
#include "LyraAbilitySystemComponent.generated.h"
class AActor;
class UGameplayAbility;
class ULyraAbilityTagRelationshipMapping;
class UObject;
struct FFrame;
struct FGameplayAbilityTargetDataHandle;
// 声明游戏标签 TAG_Gameplay_AbilityInputBlocked 的外部定义
LYRAGAME_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Gameplay_AbilityInputBlocked);
/**
* ULyraAbilitySystemComponent
*
* Base ability system component class used by this project.
*/
UCLASS()
class LYRAGAME_API ULyraAbilitySystemComponent : public UAbilitySystemComponent
{
GENERATED_BODY()
public:
// 构造函数
ULyraAbilitySystemComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
//~UActorComponent interface
// 组件结束播放时的处理
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
//~End of UActorComponent interface
// 初始化能力执行者信息
virtual void InitAbilityActorInfo(AActor* InOwnerActor, AActor* InAvatarActor) override;
// 定义取消能力的函数类型
typedef TFunctionRef<bool(const ULyraGameplayAbility* LyraAbility, FGameplayAbilitySpecHandle Handle)> TShouldCancelAbilityFunc;
// 根据条件函数取消能力
void CancelAbilitiesByFunc(TShouldCancelAbilityFunc ShouldCancelFunc, bool bReplicateCancelAbility);
// 取消输入激活的能力
void CancelInputActivatedAbilities(bool bReplicateCancelAbility);
// 输入标签按下处理
void AbilityInputTagPressed(const FGameplayTag& InputTag);
// 输入标签释放处理
void AbilityInputTagReleased(const FGameplayTag& InputTag);
// 处理能力输入
void ProcessAbilityInput(float DeltaTime, bool bGamePaused);
// 清空能力输入
void ClearAbilityInput();
// 检查激活组是否被阻塞
bool IsActivationGroupBlocked(ELyraAbilityActivationGroup Group) const;
// 添加能力到激活组
void AddAbilityToActivationGroup(ELyraAbilityActivationGroup Group, ULyraGameplayAbility* LyraAbility);
// 从激活组移除能力
void RemoveAbilityFromActivationGroup(ELyraAbilityActivationGroup Group, ULyraGameplayAbility* LyraAbility);
// 取消激活组中的能力
void CancelActivationGroupAbilities(ELyraAbilityActivationGroup Group, ULyraGameplayAbility* IgnoreLyraAbility, bool bReplicateCancelAbility);
// 使用游戏效果添加动态标签
void AddDynamicTagGameplayEffect(const FGameplayTag& Tag);
// 移除动态标签的游戏效果
void RemoveDynamicTagGameplayEffect(const FGameplayTag& Tag);
/** 获取与给定能力句柄和激活信息关联的能力目标数据 */
void GetAbilityTargetData(const FGameplayAbilitySpecHandle AbilityHandle, FGameplayAbilityActivationInfo ActivationInfo, FGameplayAbilityTargetDataHandle& OutTargetDataHandle);
/** 设置当前的标签关系映射,如果为null则清除 */
void SetTagRelationshipMapping(ULyraAbilityTagRelationshipMapping* NewMapping);
/** 查看能力标签并收集额外的需求和阻塞标签 */
void GetAdditionalActivationTagRequirements(const FGameplayTagContainer& AbilityTags, FGameplayTagContainer& OutActivationRequired, FGameplayTagContainer& OutActivationBlocked) const;
protected:
// 尝试在生成时激活能力
void TryActivateAbilitiesOnSpawn();
// 能力输入按下处理
virtual void AbilitySpecInputPressed(FGameplayAbilitySpec& Spec) override;
// 能力输入释放处理
virtual void AbilitySpecInputReleased(FGameplayAbilitySpec& Spec) override;
// 通知能力激活
virtual void NotifyAbilityActivated(const FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability) override;
// 通知能力激活失败
virtual void NotifyAbilityFailed(const FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason) override;
// 通知能力结束
virtual void NotifyAbilityEnded(FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability, bool bWasCancelled) override;
// 应用能力阻塞和取消标签
virtual void ApplyAbilityBlockAndCancelTags(const FGameplayTagContainer& AbilityTags, UGameplayAbility* RequestingAbility, bool bEnableBlockTags, const FGameplayTagContainer& BlockTags, bool bExecuteCancelTags, const FGameplayTagContainer& CancelTags) override;
// 处理能力可取消状态变化
virtual void HandleChangeAbilityCanBeCanceled(const FGameplayTagContainer& AbilityTags, UGameplayAbility* RequestingAbility, bool bCanBeCanceled) override;
/** 通知客户端能力激活失败 */
UFUNCTION(Client, Unreliable)
void ClientNotifyAbilityFailed(const UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason);
// 处理能力失败
void HandleAbilityFailed(const UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason);
protected:
// 如果设置,此表用于查找激活和取消的标签关系
UPROPERTY()
TObjectPtr<ULyraAbilityTagRelationshipMapping> TagRelationshipMapping;
// 这一帧输入被按下的能力句柄
TArray<FGameplayAbilitySpecHandle> InputPressedSpecHandles;
// 这一帧输入被释放的能力句柄
TArray<FGameplayAbilitySpecHandle> InputReleasedSpecHandles;
// 输入被保持的能力句柄
TArray<FGameplayAbilitySpecHandle> InputHeldSpecHandles;
// 每个激活组中运行的能力数量
int32 ActivationGroupCounts[(uint8)ELyraAbilityActivationGroup::MAX];
};
LyraAbilitySystemComponent.cpp 分析
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraAbilitySystemComponent.h"
#include "AbilitySystem/Abilities/LyraGameplayAbility.h"
#include "AbilitySystem/LyraAbilityTagRelationshipMapping.h"
#include "Animation/LyraAnimInstance.h"
#include "Engine/World.h"
#include "GameFramework/Pawn.h"
#include "LyraGlobalAbilitySystem.h"
#include "LyraLogChannels.h"
#include "System/LyraAssetManager.h"
#include "System/LyraGameData.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraAbilitySystemComponent)
// 定义游戏标签 TAG_Gameplay_AbilityInputBlocked
UE_DEFINE_GAMEPLAY_TAG(TAG_Gameplay_AbilityInputBlocked, "Gameplay.AbilityInputBlocked");
// 构造函数
ULyraAbilitySystemComponent::ULyraAbilitySystemComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
// 重置输入相关的句柄数组
InputPressedSpecHandles.Reset();
InputReleasedSpecHandles.Reset();
InputHeldSpecHandles.Reset();
// 初始化激活组计数为0
FMemory::Memset(ActivationGroupCounts, 0, sizeof(ActivationGroupCounts));
}
// 组件结束播放时的处理
void ULyraAbilitySystemComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
// 从全局能力系统中注销
if (ULyraGlobalAbilitySystem* GlobalAbilitySystem = UWorld::GetSubsystem<ULyraGlobalAbilitySystem>(GetWorld()))
{
GlobalAbilitySystem->UnregisterASC(this);
}
Super::EndPlay(EndPlayReason);
}
// 初始化能力执行者信息
void ULyraAbilitySystemComponent::InitAbilityActorInfo(AActor* InOwnerActor, AActor* InAvatarActor)
{
FGameplayAbilityActorInfo* ActorInfo = AbilityActorInfo.Get();
check(ActorInfo);
check(InOwnerActor);
// 检查是否有新的Pawn头像
const bool bHasNewPawnAvatar = Cast<APawn>(InAvatarActor) && (InAvatarActor != ActorInfo->AvatarActor);
// 调用父类实现
Super::InitAbilityActorInfo(InOwnerActor, InAvatarActor);
if (bHasNewPawnAvatar)
{
// 通知所有能力设置了新的Pawn头像
for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items)
{
ULyraGameplayAbility* LyraAbilityCDO = CastChecked<ULyraGameplayAbility>(AbilitySpec.Ability);
// 处理实例化和非实例化能力
if (LyraAbilityCDO->GetInstancingPolicy() != EGameplayAbilityInstancingPolicy::NonInstanced)
{
TArray<UGameplayAbility*> Instances = AbilitySpec.GetAbilityInstances();
for (UGameplayAbility* AbilityInstance : Instances)
{
ULyraGameplayAbility* LyraAbilityInstance = CastChecked<ULyraGameplayAbility>(AbilityInstance);
LyraAbilityInstance->OnPawnAvatarSet();
}
}
else
{
LyraAbilityCDO->OnPawnAvatarSet();
}
}
// 注册到全局系统
if (ULyraGlobalAbilitySystem* GlobalAbilitySystem = UWorld::GetSubsystem<ULyraGlobalAbilitySystem>(GetWorld()))
{
GlobalAbilitySystem->RegisterASC(this);
}
// 初始化动画实例
if (ULyraAnimInstance* LyraAnimInst = Cast<ULyraAnimInstance>(ActorInfo->GetAnimInstance()))
{
LyraAnimInst->InitializeWithAbilitySystem(this);
}
// 尝试在生成时激活能力
TryActivateAbilitiesOnSpawn();
}
}
// 尝试在生成时激活能力
void ULyraAbilitySystemComponent::TryActivateAbilitiesOnSpawn()
{
// 能力列表作用域锁
ABILITYLIST_SCOPE_LOCK();
for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items)
{
const ULyraGameplayAbility* LyraAbilityCDO = CastChecked<ULyraGameplayAbility>(AbilitySpec.Ability);
LyraAbilityCDO->TryActivateAbilityOnSpawn(AbilityActorInfo.Get(), AbilitySpec);
}
}
// 根据条件函数取消能力
void ULyraAbilitySystemComponent::CancelAbilitiesByFunc(TShouldCancelAbilityFunc ShouldCancelFunc, bool bReplicateCancelAbility)
{
ABILITYLIST_SCOPE_LOCK();
for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items)
{
if (!AbilitySpec.IsActive())
{
continue;
}
ULyraGameplayAbility* LyraAbilityCDO = CastChecked<ULyraGameplayAbility>(AbilitySpec.Ability);
// 处理实例化和非实例化能力
if (LyraAbilityCDO->GetInstancingPolicy() != EGameplayAbilityInstancingPolicy::NonInstanced)
{
// 取消所有生成的实例,而不是CDO
TArray<UGameplayAbility*> Instances = AbilitySpec.GetAbilityInstances();
for (UGameplayAbility* AbilityInstance : Instances)
{
ULyraGameplayAbility* LyraAbilityInstance = CastChecked<ULyraGameplayAbility>(AbilityInstance);
if (ShouldCancelFunc(LyraAbilityInstance, AbilitySpec.Handle))
{
if (LyraAbilityInstance->CanBeCanceled())
{
LyraAbilityInstance->CancelAbility(AbilitySpec.Handle, AbilityActorInfo.Get(), LyraAbilityInstance->GetCurrentActivationInfo(), bReplicateCancelAbility);
}
else
{
UE_LOG(LogLyraAbilitySystem, Error, TEXT("CancelAbilitiesByFunc: Can't cancel ability [%s] because CanBeCanceled is false."), *LyraAbilityInstance->GetName());
}
}
}
}
else
{
// 取消非实例化能力CDO
if (ShouldCancelFunc(LyraAbilityCDO, AbilitySpec.Handle))
{
// 非实例化能力总是可以取消
check(LyraAbilityCDO->CanBeCanceled());
LyraAbilityCDO->CancelAbility(AbilitySpec.Handle, AbilityActorInfo.Get(), FGameplayAbilityActivationInfo(), bReplicateCancelAbility);
}
}
}
}
// 取消输入激活的能力
void ULyraAbilitySystemComponent::CancelInputActivatedAbilities(bool bReplicateCancelAbility)
{
// 定义取消条件:输入触发或输入保持激活的能力
auto ShouldCancelFunc = [this](const ULyraGameplayAbility* LyraAbility, FGameplayAbilitySpecHandle Handle)
{
const ELyraAbilityActivationPolicy ActivationPolicy = LyraAbility->GetActivationPolicy();
return ((ActivationPolicy == ELyraAbilityActivationPolicy::OnInputTriggered) || (ActivationPolicy == ELyraAbilityActivationPolicy::WhileInputActive));
};
CancelAbilitiesByFunc(ShouldCancelFunc, bReplicateCancelAbility);
}
// 能力输入按下处理
void ULyraAbilitySystemComponent::AbilitySpecInputPressed(FGameplayAbilitySpec& Spec)
{
Super::AbilitySpecInputPressed(Spec);
// 不支持 UGameplayAbility::bReplicateInputDirectly
// 使用复制事件代替,以便 WaitInputPress 能力任务工作
if (Spec.IsActive())
{
// 调用 InputPressed 事件,这里不复制
InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputPressed, Spec.Handle, Spec.ActivationInfo.GetActivationPredictionKey());
}
}
// 能力输入释放处理
void ULyraAbilitySystemComponent::AbilitySpecInputReleased(FGameplayAbilitySpec& Spec)
{
Super::AbilitySpecInputReleased(Spec);
// 不支持 UGameplayAbility::bReplicateInputDirectly
// 使用复制事件代替,以便 WaitInputRelease 能力任务工作
if (Spec.IsActive())
{
// 调用 InputReleased 事件,这里不复制
InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputReleased, Spec.Handle, Spec.ActivationInfo.GetActivationPredictionKey());
}
}
// 输入标签按下处理
void ULyraAbilitySystemComponent::AbilityInputTagPressed(const FGameplayTag& InputTag)
{
if (InputTag.IsValid())
{
// 遍历所有可激活能力,找到匹配输入标签的
for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items)
{
if (AbilitySpec.Ability && (AbilitySpec.DynamicAbilityTags.HasTagExact(InputTag)))
{
InputPressedSpecHandles.AddUnique(AbilitySpec.Handle);
InputHeldSpecHandles.AddUnique(AbilitySpec.Handle);
}
}
}
}
// 输入标签释放处理
void ULyraAbilitySystemComponent::AbilityInputTagReleased(const FGameplayTag& InputTag)
{
if (InputTag.IsValid())
{
// 遍历所有可激活能力,找到匹配输入标签的
for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items)
{
if (AbilitySpec.Ability && (AbilitySpec.DynamicAbilityTags.HasTagExact(InputTag)))
{
InputReleasedSpecHandles.AddUnique(AbilitySpec.Handle);
InputHeldSpecHandles.Remove(AbilitySpec.Handle);
}
}
}
}
// 处理能力输入
void ULyraAbilitySystemComponent::ProcessAbilityInput(float DeltaTime, bool bGamePaused)
{
// 如果能力输入被阻塞,清空输入
if (HasMatchingGameplayTag(TAG_Gameplay_AbilityInputBlocked))
{
ClearAbilityInput();
return;
}
static TArray<FGameplayAbilitySpecHandle> AbilitiesToActivate;
AbilitiesToActivate.Reset();
// 处理输入保持激活的能力
for (const FGameplayAbilitySpecHandle& SpecHandle : InputHeldSpecHandles)
{
if (const FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromHandle(SpecHandle))
{
if (AbilitySpec->Ability && !AbilitySpec->IsActive())
{
const ULyraGameplayAbility* LyraAbilityCDO = CastChecked<ULyraGameplayAbility>(AbilitySpec->Ability);
if (LyraAbilityCDO->GetActivationPolicy() == ELyraAbilityActivationPolicy::WhileInputActive)
{
AbilitiesToActivate.AddUnique(AbilitySpec->Handle);
}
}
}
}
// 处理输入按下的能力
for (const FGameplayAbilitySpecHandle& SpecHandle : InputPressedSpecHandles)
{
if (FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromHandle(SpecHandle))
{
if (AbilitySpec->Ability)
{
AbilitySpec->InputPressed = true;
if (AbilitySpec->IsActive())
{
// 能力已激活,传递输入事件
AbilitySpecInputPressed(*AbilitySpec);
}
else
{
const ULyraGameplayAbility* LyraAbilityCDO = CastChecked<ULyraGameplayAbility>(AbilitySpec->Ability);
if (LyraAbilityCDO->GetActivationPolicy() == ELyraAbilityActivationPolicy::OnInputTriggered)
{
AbilitiesToActivate.AddUnique(AbilitySpec->Handle);
}
}
}
}
}
// 尝试激活所有按下和保持输入的能力
for (const FGameplayAbilitySpecHandle& AbilitySpecHandle : AbilitiesToActivate)
{
TryActivateAbility(AbilitySpecHandle);
}
// 处理输入释放的能力
for (const FGameplayAbilitySpecHandle& SpecHandle : InputReleasedSpecHandles)
{
if (FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromHandle(SpecHandle))
{
if (AbilitySpec->Ability)
{
AbilitySpec->InputPressed = false;
if (AbilitySpec->IsActive())
{
// 能力已激活,传递输入事件
AbilitySpecInputReleased(*AbilitySpec);
}
}
}
}
// 清空缓存的输入句柄
InputPressedSpecHandles.Reset();
InputReleasedSpecHandles.Reset();
}
// 清空能力输入
void ULyraAbilitySystemComponent::ClearAbilityInput()
{
InputPressedSpecHandles.Reset();
InputReleasedSpecHandles.Reset();
InputHeldSpecHandles.Reset();
}
// 通知能力激活
void ULyraAbilitySystemComponent::NotifyAbilityActivated(const FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability)
{
Super::NotifyAbilityActivated(Handle, Ability);
ULyraGameplayAbility* LyraAbility = CastChecked<ULyraGameplayAbility>(Ability);
// 添加能力到激活组
AddAbilityToActivationGroup(LyraAbility->GetActivationGroup(), LyraAbility);
}
// 通知能力激活失败
void ULyraAbilitySystemComponent::NotifyAbilityFailed(const FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason)
{
Super::NotifyAbilityFailed(Handle, Ability, FailureReason);
// 如果是非本地控制的Pawn且能力支持网络,通知客户端
if (APawn* Avatar = Cast<APawn>(GetAvatarActor()))
{
if (!Avatar->IsLocallyControlled() && Ability->IsSupportedForNetworking())
{
ClientNotifyAbilityFailed(Ability, FailureReason);
return;
}
}
HandleAbilityFailed(Ability, FailureReason);
}
// 通知能力结束
void ULyraAbilitySystemComponent::NotifyAbilityEnded(FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability, bool bWasCancelled)
{
Super::NotifyAbilityEnded(Handle, Ability, bWasCancelled);
ULyraGameplayAbility* LyraAbility = CastChecked<ULyraGameplayAbility>(Ability);
// 从激活组移除能力
RemoveAbilityFromActivationGroup(LyraAbility->GetActivationGroup(), LyraAbility);
}
// 应用能力阻塞和取消标签
void ULyraAbilitySystemComponent::ApplyAbilityBlockAndCancelTags(const FGameplayTagContainer& AbilityTags, UGameplayAbility* RequestingAbility, bool bEnableBlockTags, const FGameplayTagContainer& BlockTags, bool bExecuteCancelTags, const FGameplayTagContainer& CancelTags)
{
FGameplayTagContainer ModifiedBlockTags = BlockTags;
FGameplayTagContainer ModifiedCancelTags = CancelTags;
// 使用标签关系映射扩展能力标签
if (TagRelationshipMapping)
{
TagRelationshipMapping->GetAbilityTagsToBlockAndCancel(AbilityTags, &ModifiedBlockTags, &ModifiedCancelTags);
}
Super::ApplyAbilityBlockAndCancelTags(AbilityTags, RequestingAbility, bEnableBlockTags, ModifiedBlockTags, bExecuteCancelTags, ModifiedCancelTags);
// 待办:应用特殊逻辑,如阻塞输入或移动
}
// 处理能力可取消状态变化
void ULyraAbilitySystemComponent::HandleChangeAbilityCanBeCanceled(const FGameplayTagContainer& AbilityTags, UGameplayAbility* RequestingAbility, bool bCanBeCanceled)
{
Super::HandleChangeAbilityCanBeCanceled(AbilityTags, RequestingAbility, bCanBeCanceled);
// 待办:应用特殊逻辑,如阻塞输入或移动
}
// 获取额外的激活标签需求
void ULyraAbilitySystemComponent::GetAdditionalActivationTagRequirements(const FGameplayTagContainer& AbilityTags, FGameplayTagContainer& OutActivationRequired, FGameplayTagContainer& OutActivationBlocked) const
{
if (TagRelationshipMapping)
{
TagRelationshipMapping->GetRequiredAndBlockedActivationTags(AbilityTags, &OutActivationRequired, &OutActivationBlocked);
}
}
// 设置标签关系映射
void ULyraAbilitySystemComponent::SetTagRelationshipMapping(ULyraAbilityTagRelationshipMapping* NewMapping)
{
TagRelationshipMapping = NewMapping;
}
// 客户端通知能力失败(RPC)
void ULyraAbilitySystemComponent::ClientNotifyAbilityFailed_Implementation(const UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason)
{
HandleAbilityFailed(Ability, FailureReason);
}
// 处理能力失败
void ULyraAbilitySystemComponent::HandleAbilityFailed(const UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason)
{
// 记录能力失败日志
//UE_LOG(LogLyraAbilitySystem, Warning, TEXT("Ability %s failed to activate (tags: %s)"), *GetPathNameSafe(Ability), *FailureReason.ToString());
// 调用能力的失败回调
if (const ULyraGameplayAbility* LyraAbility = Cast<const ULyraGameplayAbility>(Ability))
{
LyraAbility->OnAbilityFailedToActivate(FailureReason);
}
}
// 检查激活组是否被阻塞
bool ULyraAbilitySystemComponent::IsActivationGroupBlocked(ELyraAbilityActivationGroup Group) const
{
bool bBlocked = false;
switch (Group)
{
case ELyraAbilityActivationGroup::Independent:
// 独立能力永远不会被阻塞
bBlocked = false;
break;
case ELyraAbilityActivationGroup::Exclusive_Replaceable:
case ELyraAbilityActivationGroup::Exclusive_Blocking:
// 如果没有阻塞,独占能力可以激活
bBlocked = (ActivationGroupCounts[(uint8)ELyraAbilityActivationGroup::Exclusive_Blocking] > 0);
break;
default:
checkf(false, TEXT("IsActivationGroupBlocked: Invalid ActivationGroup [%d]\n"), (uint8)Group);
break;
}
return bBlocked;
}
// 添加能力到激活组
void ULyraAbilitySystemComponent::AddAbilityToActivationGroup(ELyraAbilityActivationGroup Group, ULyraGameplayAbility* LyraAbility)
{
check(LyraAbility);
check(ActivationGroupCounts[(uint8)Group] < INT32_MAX);
// 增加组计数
ActivationGroupCounts[(uint8)Group]++;
const bool bReplicateCancelAbility = false;
switch (Group)
{
case ELyraAbilityActivationGroup::Independent:
// 独立能力不取消任何其他能力
break;
case ELyraAbilityActivationGroup::Exclusive_Replaceable:
case ELyraAbilityActivationGroup::Exclusive_Blocking:
// 取消可替换的独占能力
CancelActivationGroupAbilities(ELyraAbilityActivationGroup::Exclusive_Replaceable, LyraAbility, bReplicateCancelAbility);
break;
default:
checkf(false, TEXT("AddAbilityToActivationGroup: Invalid ActivationGroup [%d]\n"), (uint8)Group);
break;
}
// 确保只有一个独占能力在运行
const int32 ExclusiveCount = ActivationGroupCounts[(uint8)ELyraAbilityActivationGroup::Exclusive_Replaceable] + ActivationGroupCounts[(uint8)ELyraAbilityActivationGroup::Exclusive_Blocking];
if (!ensure(ExclusiveCount <= 1))
{
UE_LOG(LogLyraAbilitySystem, Error, TEXT("AddAbilityToActivationGroup: Multiple exclusive abilities are running."));
}
}
// 从激活组移除能力
void ULyraAbilitySystemComponent::RemoveAbilityFromActivationGroup(ELyraAbilityActivationGroup Group, ULyraGameplayAbility* LyraAbility)
{
check(LyraAbility);
check(ActivationGroupCounts[(uint8)Group] > 0);
// 减少组计数
ActivationGroupCounts[(uint8)Group]--;
}
// 取消激活组中的能力
void ULyraAbilitySystemComponent::CancelActivationGroupAbilities(ELyraAbilityActivationGroup Group, ULyraGameplayAbility* IgnoreLyraAbility, bool bReplicateCancelAbility)
{
// 定义取消条件:指定组中且不是忽略的能力
auto ShouldCancelFunc = [this, Group, IgnoreLyraAbility](const ULyraGameplayAbility* LyraAbility, FGameplayAbilitySpecHandle Handle)
{
return ((LyraAbility->GetActivationGroup() == Group) && (LyraAbility != IgnoreLyraAbility));
};
CancelAbilitiesByFunc(ShouldCancelFunc, bReplicateCancelAbility);
}
// 添加动态标签游戏效果
void ULyraAbilitySystemComponent::AddDynamicTagGameplayEffect(const FGameplayTag& Tag)
{
// 获取动态标签游戏效果类
const TSubclassOf<UGameplayEffect> DynamicTagGE = ULyraAssetManager::GetSubclass(ULyraGameData::Get().DynamicTagGameplayEffect);
if (!DynamicTagGE)
{
UE_LOG(LogLyraAbilitySystem, Warning, TEXT("AddDynamicTagGameplayEffect: Unable to find DynamicTagGameplayEffect [%s]."), *ULyraGameData::Get().DynamicTagGameplayEffect.GetAssetName());
return;
}
// 创建游戏效果规格
const FGameplayEffectSpecHandle SpecHandle = MakeOutgoingSpec(DynamicTagGE, 1.0f, MakeEffectContext());
FGameplayEffectSpec* Spec = SpecHandle.Data.Get();
if (!Spec)
{
UE_LOG(LogLyraAbilitySystem, Warning, TEXT("AddDynamicTagGameplayEffect: Unable to make outgoing spec for [%s]."), *GetNameSafe(DynamicTagGE));
return;
}
// 添加动态授予标签
Spec->DynamicGrantedTags.AddTag(Tag);
// 应用游戏效果到自身
ApplyGameplayEffectSpecToSelf(*Spec);
}
// 移除动态标签游戏效果
void ULyraAbilitySystemComponent::RemoveDynamicTagGameplayEffect(const FGameplayTag& Tag)
{
// 获取动态标签游戏效果类
const TSubclassOf<UGameplayEffect> DynamicTagGE = ULyraAssetManager::GetSubclass(ULyraGameData::Get().DynamicTagGameplayEffect);
if (!DynamicTagGE)
{
UE_LOG(LogLyraAbilitySystem, Warning, TEXT("RemoveDynamicTagGameplayEffect: Unable to find gameplay effect [%s]."), *ULyraGameData::Get().DynamicTagGameplayEffect.GetAssetName());
return;
}
// 创建查询并移除活动效果
FGameplayEffectQuery Query = FGameplayEffectQuery::MakeQuery_MatchAnyOwningTags(FGameplayTagContainer(Tag));
Query.EffectDefinition = DynamicTagGE;
RemoveActiveEffects(Query);
}
// 获取能力目标数据
void ULyraAbilitySystemComponent::GetAbilityTargetData(const FGameplayAbilitySpecHandle AbilityHandle, FGameplayAbilityActivationInfo ActivationInfo, FGameplayAbilityTargetDataHandle& OutTargetDataHandle)
{
// 从能力目标数据映射中查找数据
TSharedPtr<FAbilityReplicatedDataCache> ReplicatedData = AbilityTargetDataMap.Find(FGameplayAbilitySpecHandleAndPredictionKey(AbilityHandle, ActivationInfo.GetActivationPredictionKey()));
if (ReplicatedData.IsValid())
{
OutTargetDataHandle = ReplicatedData->TargetData;
}
}
总结
这两个文件构成了Lyra游戏的能力系统核心组件:
主要功能:
-
能力管理 - 处理能力的激活、取消、输入响应
-
输入处理 - 管理基于输入标签的能力触发
-
激活组系统 - 通过分组管理能力的互斥关系
-
标签关系映射 - 使用标签定义能力间的阻塞和取消关系
-
动态标签 - 通过游戏效果动态添加/移除标签
-
网络同步 - 支持客户端-服务器能力状态同步
关键特性:
-
支持多种激活策略(输入触发、输入保持等)
-
提供能力激活组管理(独立、独占可替换、独占阻塞)
-
完善的输入处理机制
-
扩展的标签关系系统
-
动态标签管理
-
网络复制支持
这是一个高度定制化的能力系统组件,为复杂的游戏能力交互提供了强大的基础框架。
我们来深入分析 LyraAbilitySystemComponent 中各个方法在 LyraStarterGame 项目中的具体应用场景。
LyraAbilitySystemComponent.h & .cpp 方法应用分析
这个组件是 Lyra 游戏能力系统的核心,它扩展了 Epic 的 UAbilitySystemComponent,为 Lyra 提供了特定的游戏功能。
1. 核心初始化和生命周期
InitAbilityActorInfo
-
应用场景:在
ALyraPlayerState或ALyraCharacter中调用,当 Pawn 被分配或复活着陆时。 -
具体作用:
-
将 ASC 的 OwnerActor 设置为 PlayerState,AvatarActor 设置为 Pawn/Character。
-
当一个新的 Pawn Avatar 被设置时(例如角色复活、切换角色),它会遍历所有能力,调用
OnPawnAvatarSet。这允许能力在拥有新的物理代表时重新初始化(例如,将效果应用到新的网格体上)。 -
向
ULyraGlobalAbilitySystem注册,以便全局游戏效果可以应用到此 ASC。 -
初始化动画实例,将 ASC 链接到动画蓝图,从而可以通过
GameplayTag驱动动画状态机。
-
TryActivateAbilitiesOnSpawn
-
应用场景:在
InitAbilityActorInfo中,当新的 Avatar 设置后立即调用。 -
具体作用:遍历所有
ActivatableAbilities,寻找那些在ULyraGameplayAbility中设置了bActivateOnSpawn为 true 的能力,并尝试激活它们。这用于被动能力,例如:-
英雄的初始属性设置(如初始移动速度)。
-
持续存在的光环效果(如每秒生命恢复)。
-
EndPlay
-
应用场景:当角色死亡或被销毁时。
-
具体作用:从
ULyraGlobalAbilitySystem中注销,确保全局效果不再作用于已销毁的 ASC,防止内存泄漏和逻辑错误。
2. 输入处理系统
这是 Lyra 输入系统的核心,它将“输入动作”(如 IA_Ability1)映射到 GameplayTag(如 InputTag.Ability1),再映射到具体的能力。
AbilityInputTagPressed / AbilityInputTagReleased
-
应用场景:在玩家控制器(如
ALyraPlayerController)的输入处理函数中调用。当输入组件检测到绑定到InputTag的按键被按下或释放时,会调用这些函数。 -
具体作用:
-
Pressed:将对应能力的SpecHandle添加到InputPressedSpecHandles和InputHeldSpecHandles列表中。 -
Released:将SpecHandle添加到InputReleasedSpecHandles并从InputHeldSpecHandles中移除。 -
这为下一帧的
ProcessAbilityInput准备了数据。
-
ProcessAbilityInput
-
应用场景:在玩家控制器的
PlayerTick或游戏模式的每帧更新中被调用。 -
具体作用:这是输入驱动的能力激活的“大脑”。
-
检查阻塞:如果 ASC 拥有
TAG_Gameplay_AbilityInputBlocked标签(例如,角色正处于眩晕、击退或播放动画状态),则清除所有输入,不处理任何能力激活。 -
处理按住输入的能力:遍历
InputHeldSpecHandles,寻找激活策略为WhileInputActive且尚未激活的能力(例如持续射击、持续护盾),并计划激活它们。 -
处理按下输入的能力:遍历
InputPressedSpecHandles,如果能力已激活,则触发其InputPressed事件;如果未激活且策略为OnInputTriggered(例如单发射击、跳跃),则计划激活它们。 -
批量激活:一次性激活所有计划中的能力,避免重复触发。
-
处理释放输入:遍历
InputReleasedSpecHandles,向已激活的能力发送InputReleased事件(例如,用于蓄力射击:按下开始蓄力,释放时发射)。 -
清空帧缓存:重置
InputPressedSpecHandles和InputReleasedSpecHandles,为下一帧做准备。
-
ClearAbilityInput
-
应用场景:在
ProcessAbilityInput开始时如果发现输入被阻塞,或在某些需要强制中断输入的状态下(如角色死亡)手动调用。 -
具体作用:立即清空所有输入缓存,中断当前帧的所有输入处理。
3. 能力取消与管理
CancelAbilitiesByFunc
-
应用场景:一个通用的取消工具,被其他多个取消函数内部调用。
-
具体作用:接受一个 lambda 函数作为条件,遍历所有活跃能力,取消所有满足该条件的能力。它正确处理了实例化和非实例化能力。
CancelInputActivatedAbilities
-
应用场景:当需要取消所有由输入触发的能力时。例如,当玩家打开菜单、开始交互或进入一个禁止战斗的区域时。
-
具体作用:通过传入一个检查
ActivationPolicy是否为OnInputTriggered或WhileInputActive的 lambda 给CancelAbilitiesByFunc,来取消所有与输入相关的能力。
4. 激活组系统
这个系统用于管理能力之间的高级互斥关系,定义了哪些能力可以同时运行。
IsActivationGroupBlocked
-
应用场景:在
ULyraGameplayAbility::CanActivateAbility中调用,作为能力能否激活的检查条件之一。 -
具体作用:
-
Independent:永不阻塞(如被动回血)。 -
Exclusive_Replaceable/Exclusive_Blocking:如果已经有任何Exclusive_Blocking能力在运行,则被阻塞。 -
例子:如果角色正在播放一个
Exclusive_Blocking的终极技能动画,那么他将无法激活另一个Exclusive的技能。
-
AddAbilityToActivationGroup / RemoveAbilityFromActivationGroup
-
应用场景:在
NotifyAbilityActivated和NotifyAbilityEnded中自动调用。 -
具体作用:
-
当能力激活时,增加对应组的计数器。
-
当
Exclusive能力激活时,会自动取消所有当前运行的Exclusive_Replaceable能力(例如,开始一个新的独占技能会打断上一个可替换的独占技能)。 -
当能力结束时,减少计数器。
-
它确保了
Exclusive能力(包括可替换和阻塞)同时只能有一个在运行。
-
CancelActivationGroupAbilities
-
应用场景:在
AddAbilityToActivationGroup内部,当一个Exclusive能力激活时,用于取消现有的Exclusive_Replaceable能力。 -
具体作用:取消指定组中的所有能力(除了传入的
IgnoreLyraAbility,通常是新激活的那个能力)。
5. 标签关系映射
SetTagRelationshipMapping
-
应用场景:在游戏初始化时,例如在
ULyraGameData中加载并设置一个ULyraAbilityTagRelationshipMapping数据资产。 -
具体作用:提供一个中央配置点,定义能力标签之间的复杂关系。
GetAdditionalActivationTagRequirements
-
应用场景:在
ULyraGameplayAbility::CanActivateAbility中调用。 -
具体作用:根据数据资产的配置,查询激活一个拥有特定标签的能力,需要哪些标签存在,以及会被哪些标签阻塞。
-
例子:一个“火焰护盾”能力可能拥有
Ability.Type.FireShield标签。关系映射可以配置为:激活此能力需要State.Health.Above50标签,并且会被State.Stunned标签阻塞。
-
ApplyAbilityBlockAndCancelTags
-
应用场景:当能力被激活或结束时,由父类
ASC调用。 -
具体作用:利用关系映射,扩展传入的
BlockTags和CancelTags。例如,激活一个“沉默”效果(带有GameplayEffect.Silence标签)时,关系映射可以配置为自动阻塞所有带有Ability.Type.Spell标签的能力,并取消当前激活的此类能力。
6. 动态标签
AddDynamicTagGameplayEffect / RemoveDynamicTagGameplayEffect
-
应用场景:用于临时添加或移除标签,这些标签会影响游戏逻辑。例如,拾取一个临时增益道具。
-
具体作用:
-
Add:创建一个配置好的GameplayEffect(在LyraGameData中定义)的实例,并为其动态添加一个指定的标签,然后应用到自身。这会将该标签授予 ASC。 -
Remove:查找并移除所有通过该动态 GE 添加的、包含指定标签的活动 GE 实例。 -
例子:拾取“无敌”道具,调用
AddDynamicTagGameplayEffect(FGameplayTag::RequestGameplayTag("State.Invulnerable")),使角色获得无敌状态。效果结束后调用Remove。
-
7. 网络与失败处理
AbilitySpecInputPressed / AbilitySpecInputReleased (Override)
-
应用场景:在
ProcessAbilityInput中,当输入事件需要传递给一个已经激活的能力时调用。 -
具体作用:Lyra 重写了这些函数,不使用引擎原生的
bReplicateInputDirectly,而是通过InvokeReplicatedEvent来复制输入事件。这确保了像WaitInputPress和WaitInputRelease这样的能力任务在网络游戏中能在客户端和服务器上正确工作。
NotifyAbilityFailed & ClientNotifyAbilityFailed
-
应用场景:当服务器判定一个客户端请求的能力激活失败时(例如,因为冷却未结束、法力不足或被关系映射阻塞)。
-
具体作用:
-
服务器上的
NotifyAbilityFailed会检查 Avatar 是否为非本地控制,如果是,则通过 RPCClientNotifyAbilityFailed通知客户端。 -
客户端收到 RPC 后,调用
HandleAbilityFailed,最终调用ULyraGameplayAbility::OnAbilityFailedToActivate。 -
应用:这允许在客户端上播放一个失败的提示音效或显示“法力不足”的 UI 信息。
-
GetAbilityTargetData
-
应用场景:在服务器端进行目标验证或应用效果时。
-
具体作用:从复制的
AbilityTargetDataMap中检索客户端在激活能力时发送的目标数据(例如,子弹的命中位置、技能释放方向)。这对于需要精确客户端预测和服务器校正的技能(如射击、瞄准)至关重要。
总结
ULyraAbilitySystemComponent 是 LyraStarterGame 的游戏性交互枢纽。它将底层的 GAS 框架与 Lyra 特定的设计(如基于标签的输入、激活组、关系映射)紧密地结合在一起,为构建复杂、响应迅速且网络同步良好的英雄技能、物品系统和状态效果提供了坚实的基础。每一个方法都是为了解决特定的游戏设计需求而存在的。
我将分析每个方法在 LyraStarterGame 项目中的具体实现和应用代码。
LyraAbilitySystemComponent 方法在 LyraStarterGame 中的具体应用
1. 输入处理系统
在 LyraPlayerController 中的调用:
// LyraPlayerController.cpp
void ALyraPlayerController::Input_AbilityInputTagPressed(FGameplayTag InputTag)
{
// 获取 Lyra ASC 并调用输入按下处理
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent())
{
LyraASC->AbilityInputTagPressed(InputTag);
}
}
void ALyraPlayerController::Input_AbilityInputTagReleased(FGameplayTag InputTag)
{
// 获取 Lyra ASC 并调用输入释放处理
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent())
{
LyraASC->AbilityInputTagReleased(InputTag);
}
}
void ALyraPlayerController::ProcessPlayerInput(const float DeltaTime, const bool bGamePaused)
{
// 每帧处理能力输入
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent())
{
LyraASC->ProcessAbilityInput(DeltaTime, bGamePaused);
}
Super::ProcessPlayerInput(DeltaTime, bGamePaused);
}
输入绑定配置:
// LyraHeroComponent.cpp - 输入配置
void ULyraHeroComponent::InitializePlayerInput(UInputComponent* PlayerInputComponent)
{
// 绑定输入标签到具体的按键
BindInputActionToTag(InputConfig, "IA_Ability1", ETriggerEvent::Started, this, &ThisClass::Input_AbilityInputTagPressed, FGameplayTag::RequestGameplayTag("InputTag.Ability1"));
BindInputActionToTag(InputConfig, "IA_Ability1", ETriggerEvent::Completed, this, &ThisClass::Input_AbilityInputTagReleased, FGameplayTag::RequestGameplayTag("InputTag.Ability1"));
BindInputActionToTag(InputConfig, "IA_Ability2", ETriggerEvent::Started, this, &ThisClass::Input_AbilityInputTagPressed, FGameplayTag::RequestGameplayTag("InputTag.Ability2"));
BindInputActionToTag(InputConfig, "IA_Ability2", ETriggerEvent::Completed, this, &ThisClass::Input_AbilityInputTagReleased, FGameplayTag::RequestGameplayTag("InputTag.Ability2"));
}
2. 能力初始化和激活组系统
在 LyraGameplayAbility 中的激活组应用:
// LyraGameplayAbility.cpp
ELyraAbilityActivationGroup ULyraGameplayAbility::GetActivationGroup() const
{
// 根据能力类型返回不同的激活组
if (bIsPassiveAbility)
{
return ELyraAbilityActivationGroup::Independent; // 被动能力独立运行
}
else if (bIsUltimateAbility)
{
return ELyraAbilityActivationGroup::Exclusive_Blocking; // 终极技能阻塞其他能力
}
else
{
return ELyraAbilityActivationGroup::Exclusive_Replaceable; // 普通技能可被替换
}
}
bool ULyraGameplayAbility::CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags, FGameplayTagContainer* OptionalRelevantTags) const
{
// 检查激活组是否被阻塞
if (ULyraAbilitySystemComponent* LyraASC = Cast<ULyraAbilitySystemComponent>(ActorInfo->AbilitySystemComponent))
{
if (LyraASC->IsActivationGroupBlocked(GetActivationGroup()))
{
return false; // 如果激活组被阻塞,无法激活能力
}
}
return Super::CanActivateAbility(Handle, ActorInfo, SourceTags, TargetTags, OptionalRelevantTags);
}
3. 动态标签系统的具体应用
在游戏物品和效果中的使用:
// LyraPickupDefinition.cpp - 拾取物品应用动态标签
void ULyraPickupDefinition::GrantToAbilitySystem(ULyraAbilitySystemComponent* LyraASC) const
{
if (GrantedAbilityTags.IsValid())
{
// 为每个授予的标签添加动态效果
for (const FGameplayTag& Tag : GrantedAbilityTags)
{
LyraASC->AddDynamicTagGameplayEffect(Tag);
}
}
}
// LyraHealthComponent.cpp - 死亡时应用输入阻塞标签
void ULyraHealthComponent::HandleOutOfHealth(AActor* DamageInstigator, AActor* DamageCauser, const FGameplayEffectSpec* DamageEffectSpec, float DamageMagnitude, float OldValue, float NewValue)
{
// 角色死亡时阻塞能力输入
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent())
{
LyraASC->AddDynamicTagGameplayEffect(FGameplayTag::RequestGameplayTag("Gameplay.AbilityInputBlocked"));
}
OnDeath.Broadcast();
}
// LyraHealthComponent.cpp - 复活时移除输入阻塞
void ULyraHealthComponent::HandleRevive()
{
// 复活时移除能力输入阻塞
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent())
{
LyraASC->RemoveDynamicTagGameplayEffect(FGameplayTag::RequestGameplayTag("Gameplay.AbilityInputBlocked"));
}
}
4. 标签关系映射的具体配置
在 LyraGameData 中的关系映射设置:
// LyraGameData.cpp
ULyraGameData::ULyraGameData()
{
// 设置默认的标签关系映射
TagRelationshipMapping = TSoftObjectPtr<ULyraAbilityTagRelationshipMapping>(FSoftObjectPath("/Game/Lyra/AbilitySystem/DA_AbilityTagRelationshipMapping"));
}
// 在游戏初始化时设置关系映射
void ALyraGameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
{
Super::InitGame(MapName, Options, ErrorMessage);
// 为所有玩家状态设置标签关系映射
ForEachPlayerState([this](ALyraPlayerState* PS)
{
if (ULyraAbilitySystemComponent* LyraASC = PS->GetLyraAbilitySystemComponent())
{
LyraASC->SetTagRelationshipMapping(GetGameData()->TagRelationshipMapping.LoadSynchronous());
}
});
}
标签关系映射数据资产配置示例:
// DA_AbilityTagRelationshipMapping 数据资产配置
void ULyraAbilityTagRelationshipMapping::GetAbilityTagsToBlockAndCancel(const FGameplayTagContainer& AbilityTags, FGameplayTagContainer* OutTagsToBlock, FGameplayTagContainer* OutTagsToCancel) const
{
// 配置:当有沉默效果时,阻塞所有法术能力
if (AbilityTags.HasTag(FGameplayTag::RequestGameplayTag("Ability.Type.Spell")))
{
OutTagsToBlock->AddTag(FGameplayTag::RequestGameplayTag("State.Silenced"));
}
// 配置:当有眩晕效果时,取消所有激活的能力
if (AbilityTags.HasTag(FGameplayTag::RequestGameplayTag("State.Stunned")))
{
OutTagsToCancel->AddTag(FGameplayTag::RequestGameplayTag("Ability"));
}
}
5. 能力取消的具体应用
在 UI 系统和游戏状态中的调用:
// LyraHUD.cpp - 打开菜单时取消能力
void ALyraHUD::ShowMenu()
{
// 打开菜单时取消所有输入激活的能力
if (APlayerController* PC = GetOwningPlayerController())
{
if (ALyraPlayerState* PS = PC->GetPlayerState<ALyraPlayerState>())
{
if (ULyraAbilitySystemComponent* LyraASC = PS->GetLyraAbilitySystemComponent())
{
LyraASC->CancelInputActivatedAbilities(true); // 复制取消到服务器
}
}
}
// 显示菜单UI
// ...
}
// LyraCharacter.cpp - 角色交互时取消能力
void ALyraCharacter::StartInteraction()
{
// 开始交互时取消输入能力
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent())
{
LyraASC->CancelInputActivatedAbilities(false); // 本地取消,不复制
}
// 执行交互逻辑
// ...
}
6. 网络同步和失败处理
在 LyraGameplayAbility 中的具体实现:
// LyraGameplayAbility.cpp
void ULyraGameplayAbility::OnAbilityFailedToActivate_Implementation(const FGameplayTagContainer& FailureReason)
{
// 客户端收到能力激活失败通知时的处理
// 播放失败音效
if (FailureSound)
{
UGameplayStatics::PlaySoundAtLocation(this, FailureSound, GetAvatarActorFromActorInfo()->GetActorLocation());
}
// 显示失败UI提示
if (APlayerController* PC = GetPlayerControllerFromActorInfo())
{
if (ULyraUIComponent* UIComponent = PC->FindComponentByClass<ULyraUIComponent>())
{
if (FailureReason.HasTag(FGameplayTag::RequestGameplayTag("Ability.Failure.Cooldown")))
{
UIComponent->ShowErrorText("Ability is on cooldown!");
}
else if (FailureReason.HasTag(FGameplayTag::RequestGameplayTag("Ability.Failure.Cost")))
{
UIComponent->ShowErrorText("Not enough resources!");
}
}
}
}
// 服务器端能力激活验证
bool ULyraGameplayAbility::CheckCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, FGameplayTagContainer* OptionalRelevantTags) const
{
if (!Super::CheckCost(Handle, ActorInfo, OptionalRelevantTags))
{
// 如果消耗检查失败,构建失败原因标签
FGameplayTagContainer FailureTags;
FailureTags.AddTag(FGameplayTag::RequestGameplayTag("Ability.Failure.Cost"));
// 通知客户端激活失败
if (ULyraAbilitySystemComponent* LyraASC = Cast<ULyraAbilitySystemComponent>(ActorInfo->AbilitySystemComponent))
{
LyraASC->NotifyAbilityFailed(Handle, this, FailureTags);
}
return false;
}
return true;
}
7. 生成时激活能力的应用
被动能力的配置:
// GA_PassiveHealthRegen.cpp - 被动生命恢复能力
void UGA_PassiveHealthRegen::TryActivateAbilityOnSpawn(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) const
{
// 检查是否应该在生成时激活
if (bActivateOnSpawn)
{
// 激活被动能力
ActorInfo->AbilitySystemComponent->TryActivateAbility(Spec.Handle);
}
}
void UGA_PassiveHealthRegen::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
// 设置定时器,每秒恢复生命值
GetWorld()->GetTimerManager().SetTimer(RegenTimerHandle, this, &UGA_PassiveHealthRegen::ExecuteRegen, 1.0f, true);
}
void UGA_PassiveHealthRegen::ExecuteRegen()
{
// 应用生命恢复效果
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponentFromActorInfo())
{
FGameplayEffectContextHandle ContextHandle = LyraASC->MakeEffectContext();
LyraASC->ApplyGameplayEffectToSelf(HealthRegenEffect.GetDefaultObject(), 1.0f, ContextHandle);
}
}
8. 目标数据获取的应用
在射击能力中的使用:
// GA_WeaponShoot.cpp - 射击能力使用目标数据
void UGA_WeaponShoot::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
// 在客户端预测射击
if (ActorInfo->IsNetAuthority())
{
// 服务器端:验证客户端的目标数据
FGameplayAbilityTargetDataHandle TargetData;
GetLyraAbilitySystemComponentFromActorInfo()->GetAbilityTargetData(Handle, ActivationInfo, TargetData);
// 使用目标数据进行命中检测
ProcessTargetData(TargetData);
}
else
{
// 客户端:发送目标数据到服务器
FGameplayAbilityTargetDataHandle TargetData = CreateTargetData();
ServerSetReplicatedTargetData(Handle, ActivationInfo, TargetData);
// 本地预测执行
ProcessTargetData(TargetData);
}
}
9. 激活组在具体能力中的应用
终极技能的实现:
// GA_UltimateAbility.cpp - 终极技能使用 Exclusive_Blocking 激活组
ELyraAbilityActivationGroup UGA_UltimateAbility::GetActivationGroup() const
{
return ELyraAbilityActivationGroup::Exclusive_Blocking; // 阻塞所有其他能力
}
void UGA_UltimateAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
// 播放终极技能动画
PlayUltimateAnimation();
// 应用终极技能效果
ApplyUltimateEffect();
// 设置输入阻塞,防止在终极技能期间进行其他操作
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponentFromActorInfo())
{
LyraASC->AddDynamicTagGameplayEffect(FGameplayTag::RequestGameplayTag("Gameplay.AbilityInputBlocked"));
}
}
void UGA_UltimateAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled)
{
// 移除输入阻塞
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponentFromActorInfo())
{
LyraASC->RemoveDynamicTagGameplayEffect(FGameplayTag::RequestGameplayTag("Gameplay.AbilityInputBlocked"));
}
Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled);
}
10. 在 LyraPlayerState 中的集成
// LyraPlayerState.cpp
void ALyraPlayerState::SetPawn(APawn* InPawn)
{
Super::SetPawn(InPawn);
// 当 Pawn 设置时,初始化 ASC 的 Actor 信息
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent())
{
LyraASC->InitAbilityActorInfo(this, InPawn);
}
}
ULyraAbilitySystemComponent* ALyraPlayerState::GetLyraAbilitySystemComponent() const
{
return Cast<ULyraAbilitySystemComponent>(GetAbilitySystemComponent());
}
这些具体的实现代码展示了 ULyraAbilitySystemComponent 在 LyraStarterGame 项目中的广泛应用,从输入处理、能力管理到网络同步和状态管理,每个方法都在游戏的各个系统中发挥着关键作用。
841

被折叠的 条评论
为什么被折叠?



