LyraStarterGame/Source/LyraGame/AbilitySystem
AbilitySystem文件夹目录:

这些文件展示了Lyra游戏能力系统的核心架构:
🎯 核心架构总结
-
标签化系统:
LyraTaggedActor提供统一的标签管理接口 -
全局能力管理:
LyraGlobalAbilitySystem实现跨ASC的能力和效果管理 -
效果上下文扩展:
LyraGameplayEffectContext添加游戏特定的上下文数据 -
能力提示管理:
LyraGameplayCueManager优化游戏提示的加载和管理 -
目标数据扩展:
LyraGameplayAbilityTargetData_SingleTargetHit增强目标数据 -
标签关系映射:
LyraAbilityTagRelationshipMapping定义能力间的阻塞和取消关系 -
全局系统配置:
LyraAbilitySystemGlobals配置全局能力系统行为 -
能力组件扩展:
LyraAbilitySystemComponent增强标准ASC功能 -
能力源接口:
LyraAbilitySourceInterface定义能力计算源接口 -
能力集管理:
LyraAbilitySet提供批量授予能力、效果和属性集的功能
这个架构提供了高度模块化和可扩展的能力系统,支持复杂的游戏机制和交互。
我将为每个文件提供详细的行级代码分析,解释每行代码的意义和使用方法。
📁 LyraTaggedActor.h - 标签化Actor
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once // 头文件保护,防止重复包含
#include "GameFramework/Actor.h" // 包含Actor基类
#include "GameplayTagAssetInterface.h" // 包含游戏标签资产接口
#include "GameplayTagContainer.h" // 包含游戏标签容器
#include "LyraTaggedActor.generated.h" // 包含生成的UCLASS头文件
// 一个实现了游戏标签资产接口的Actor
UCLASS() // 声明为UCLASS,可在编辑器和蓝图中使用
class ALyraTaggedActor : public AActor, public IGameplayTagAssetInterface // 继承自Actor并实现标签接口
{
GENERATED_BODY() // UE宏,生成必要的反射代码
public:
// 构造函数,使用FObjectInitializer进行对象初始化
ALyraTaggedActor(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
//~IGameplayTagAssetInterface // 接口实现开始
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override; // 获取拥有的游戏标签
//~End of IGameplayTagAssetInterface // 接口实现结束
//~UObject interface // UObject接口开始
#if WITH_EDITOR // 仅在编辑器编译时包含
virtual bool CanEditChange(const FProperty* InProperty) const override; // 检查属性是否可以编辑
#endif
//~End of UObject interface // UObject接口结束
protected:
// 与此Actor关联的游戏相关标签
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Actor) // 可在任意地方编辑,蓝图只读,分类为Actor
FGameplayTagContainer StaticGameplayTags; // 静态游戏标签容器
};
使用方法:
// 创建带标签的Actor
ALyraTaggedActor* TaggedActor = GetWorld()->SpawnActor<ALyraTaggedActor>();
// 获取标签
FGameplayTagContainer Tags;
TaggedActor->GetOwnedGameplayTags(Tags);
// 检查标签
if (Tags.HasTag(FGameplayTag::RequestGameplayTag("Weapon.Rifle")))
{
// 处理步枪逻辑
}
📁 LyraTaggedActor.cpp - 标签化Actor实现
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraTaggedActor.h" // 包含头文件
#include "UObject/UnrealType.h" // 包含Unreal类型系统
#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraTaggedActor) // 包含生成的cpp代码
// 构造函数实现
ALyraTaggedActor::ALyraTaggedActor(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) // 调用父类构造函数
{
}
// 实现获取拥有游戏标签的方法
void ALyraTaggedActor::GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const
{
TagContainer.AppendTags(StaticGameplayTags); // 将静态标签添加到输出容器
}
#if WITH_EDITOR // 仅在编辑器编译时包含
// 实现检查属性是否可以编辑的方法
bool ALyraTaggedActor::CanEditChange(const FProperty* InProperty) const
{
// 防止编辑其他标签属性
if (InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(AActor, Tags)) // 检查是否是Actor的Tags属性
{
return false; // 返回false表示不能编辑
}
return Super::CanEditChange(InProperty); // 调用父类实现
}
#endif
📁 LyraGlobalAbilitySystem.h - 全局能力系统
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "ActiveGameplayEffectHandle.h" // 活跃游戏效果句柄
#include "Subsystems/WorldSubsystem.h" // 世界子系统
#include "GameplayAbilitySpecHandle.h" // 游戏能力规格句柄
#include "Templates/SubclassOf.h" // 类模板
#include "LyraGlobalAbilitySystem.generated.h" // 生成的UCLASS头文件
// 前向声明
class UGameplayAbility;
class UGameplayEffect;
class ULyraAbilitySystemComponent;
class UObject;
struct FActiveGameplayEffectHandle;
struct FFrame;
struct FGameplayAbilitySpecHandle;
// 全局应用能力列表结构体
USTRUCT()
struct FGlobalAppliedAbilityList
{
GENERATED_BODY() // UE结构体生成宏
UPROPERTY() // UPROPERTY标记,支持垃圾回收
TMap<TObjectPtr<ULyraAbilitySystemComponent>, FGameplayAbilitySpecHandle> Handles; // ASC到能力句柄的映射
// 向ASC添加能力
void AddToASC(TSubclassOf<UGameplayAbility> Ability, ULyraAbilitySystemComponent* ASC);
// 从ASC移除能力
void RemoveFromASC(ULyraAbilitySystemComponent* ASC);
// 从所有ASC移除
void RemoveFromAll();
};
// 全局应用效果列表结构体
USTRUCT()
struct FGlobalAppliedEffectList
{
GENERATED_BODY()
UPROPERTY()
TMap<TObjectPtr<ULyraAbilitySystemComponent>, FActiveGameplayEffectHandle> Handles; // ASC到效果句柄的映射
// 向ASC添加效果
void AddToASC(TSubclassOf<UGameplayEffect> Effect, ULyraAbilitySystemComponent* ASC);
// 从ASC移除效果
void RemoveFromASC(ULyraAbilitySystemComponent* ASC);
// 从所有ASC移除
void RemoveFromAll();
};
// 全局能力系统类,世界子系统
UCLASS()
class ULyraGlobalAbilitySystem : public UWorldSubsystem
{
GENERATED_BODY()
public:
ULyraGlobalAbilitySystem(); // 构造函数
// 对所有注册的ASC应用能力(蓝图可调用,仅权威端)
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category="Lyra")
void ApplyAbilityToAll(TSubclassOf<UGameplayAbility> Ability);
// 对所有注册的ASC应用效果
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category="Lyra")
void ApplyEffectToAll(TSubclassOf<UGameplayEffect> Effect);
// 从所有ASC移除能力
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Lyra")
void RemoveAbilityFromAll(TSubclassOf<UGameplayAbility> Ability);
// 从所有ASC移除效果
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Lyra")
void RemoveEffectFromAll(TSubclassOf<UGameplayEffect> Effect);
/** 注册ASC到全局系统并应用任何活跃的全局效果/能力 */
void RegisterASC(ULyraAbilitySystemComponent* ASC);
/** 从全局系统移除ASC,同时移除任何活跃的全局效果/能力 */
void UnregisterASC(ULyraAbilitySystemComponent* ASC);
private:
// 已应用的能力映射
UPROPERTY()
TMap<TSubclassOf<UGameplayAbility>, FGlobalAppliedAbilityList> AppliedAbilities;
// 已应用的效果映射
UPROPERTY()
TMap<TSubclassOf<UGameplayEffect>, FGlobalAppliedEffectList> AppliedEffects;
// 已注册的ASC列表
UPROPERTY()
TArray<TObjectPtr<ULyraAbilitySystemComponent>> RegisteredASCs;
};
使用方法:
// 获取全局能力系统
ULyraGlobalAbilitySystem* GlobalSystem = GetWorld()->GetSubsystem<ULyraGlobalAbilitySystem>();
// 应用全局能力
GlobalSystem->ApplyAbilityToAll(UGodModeAbility::StaticClass());
// 应用全局效果
GlobalSystem->ApplyEffectToAll(UInvincibilityEffect::StaticClass());
// 注册新的ASC
GlobalSystem->RegisterASC(MyAbilitySystemComponent);
📁 LyraGlobalAbilitySystem.cpp - 全局能力系统实现
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraGlobalAbilitySystem.h"
#include "AbilitySystem/LyraAbilitySystemComponent.h" // 包含Lyra ASC
#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraGlobalAbilitySystem) // 包含生成的代码
// 向ASC添加能力
void FGlobalAppliedAbilityList::AddToASC(TSubclassOf<UGameplayAbility> Ability, ULyraAbilitySystemComponent* ASC)
{
// 如果已经存在,先移除
if (FGameplayAbilitySpecHandle* SpecHandle = Handles.Find(ASC))
{
RemoveFromASC(ASC);
}
// 获取能力的CDO(Class Default Object)
UGameplayAbility* AbilityCDO = Ability->GetDefaultObject<UGameplayAbility>();
// 创建能力规格
FGameplayAbilitySpec AbilitySpec(AbilityCDO);
// 给予能力并获取句柄
const FGameplayAbilitySpecHandle AbilitySpecHandle = ASC->GiveAbility(AbilitySpec);
// 存储句柄
Handles.Add(ASC, AbilitySpecHandle);
}
// 从ASC移除能力
void FGlobalAppliedAbilityList::RemoveFromASC(ULyraAbilitySystemComponent* ASC)
{
// 查找能力句柄
if (FGameplayAbilitySpecHandle* SpecHandle = Handles.Find(ASC))
{
// 清除能力
ASC->ClearAbility(*SpecHandle);
// 从映射中移除
Handles.Remove(ASC);
}
}
// 从所有ASC移除能力
void FGlobalAppliedAbilityList::RemoveFromAll()
{
// 遍历所有句柄
for (auto& KVP : Handles)
{
// 检查ASC是否有效
if (KVP.Key != nullptr)
{
// 清除能力
KVP.Key->ClearAbility(KVP.Value);
}
}
// 清空映射
Handles.Empty();
}
// 向ASC添加效果
void FGlobalAppliedEffectList::AddToASC(TSubclassOf<UGameplayEffect> Effect, ULyraAbilitySystemComponent* ASC)
{
// 如果已经存在,先移除
if (FActiveGameplayEffectHandle* EffectHandle = Handles.Find(ASC))
{
RemoveFromASC(ASC);
}
// 获取效果的CDO
const UGameplayEffect* GameplayEffectCDO = Effect->GetDefaultObject<UGameplayEffect>();
// 应用效果到自身并获取句柄
const FActiveGameplayEffectHandle GameplayEffectHandle = ASC->ApplyGameplayEffectToSelf(GameplayEffectCDO, /*Level=*/ 1, ASC->MakeEffectContext());
// 存储句柄
Handles.Add(ASC, GameplayEffectHandle);
}
// 从ASC移除效果
void FGlobalAppliedEffectList::RemoveFromASC(ULyraAbilitySystemComponent* ASC)
{
// 查找效果句柄
if (FActiveGameplayEffectHandle* EffectHandle = Handles.Find(ASC))
{
// 移除活跃游戏效果
ASC->RemoveActiveGameplayEffect(*EffectHandle);
// 从映射中移除
Handles.Remove(ASC);
}
}
// 从所有ASC移除效果
void FGlobalAppliedEffectList::RemoveFromAll()
{
// 遍历所有句柄
for (auto& KVP : Handles)
{
// 检查ASC是否有效
if (KVP.Key != nullptr)
{
// 移除活跃游戏效果
KVP.Key->RemoveActiveGameplayEffect(KVP.Value);
}
}
// 清空映射
Handles.Empty();
}
// 构造函数
ULyraGlobalAbilitySystem::ULyraGlobalAbilitySystem()
{
}
// 对所有注册的ASC应用能力
void ULyraGlobalAbilitySystem::ApplyAbilityToAll(TSubclassOf<UGameplayAbility> Ability)
{
// 检查能力是否有效且未应用过
if ((Ability.Get() != nullptr) && (!AppliedAbilities.Contains(Ability)))
{
// 添加新的能力列表条目
FGlobalAppliedAbilityList& Entry = AppliedAbilities.Add(Ability);
// 对所有注册的ASC应用能力
for (ULyraAbilitySystemComponent* ASC : RegisteredASCs)
{
Entry.AddToASC(Ability, ASC);
}
}
}
// 对所有注册的ASC应用效果
void ULyraGlobalAbilitySystem::ApplyEffectToAll(TSubclassOf<UGameplayEffect> Effect)
{
// 检查效果是否有效且未应用过
if ((Effect.Get() != nullptr) && (!AppliedEffects.Contains(Effect)))
{
// 添加新的效果列表条目
FGlobalAppliedEffectList& Entry = AppliedEffects.Add(Effect);
// 对所有注册的ASC应用效果
for (ULyraAbilitySystemComponent* ASC : RegisteredASCs)
{
Entry.AddToASC(Effect, ASC);
}
}
}
// 从所有ASC移除能力
void ULyraGlobalAbilitySystem::RemoveAbilityFromAll(TSubclassOf<UGameplayAbility> Ability)
{
// 检查能力是否有效且已应用
if ((Ability.Get() != nullptr) && AppliedAbilities.Contains(Ability))
{
// 获取能力列表条目
FGlobalAppliedAbilityList& Entry = AppliedAbilities[Ability];
// 从所有ASC移除
Entry.RemoveFromAll();
// 从映射中移除
AppliedAbilities.Remove(Ability);
}
}
// 从所有ASC移除效果
void ULyraGlobalAbilitySystem::RemoveEffectFromAll(TSubclassOf<UGameplayEffect> Effect)
{
// 检查效果是否有效且已应用
if ((Effect.Get() != nullptr) && AppliedEffects.Contains(Effect))
{
// 获取效果列表条目
FGlobalAppliedEffectList& Entry = AppliedEffects[Effect];
// 从所有ASC移除
Entry.RemoveFromAll();
// 从映射中移除
AppliedEffects.Remove(Effect);
}
}
// 注册ASC到全局系统
void ULyraGlobalAbilitySystem::RegisterASC(ULyraAbilitySystemComponent* ASC)
{
check(ASC); // 确保ASC有效
// 应用所有已注册的能力
for (auto& Entry : AppliedAbilities)
{
Entry.Value.AddToASC(Entry.Key, ASC);
}
// 应用所有已注册的效果
for (auto& Entry : AppliedEffects)
{
Entry.Value.AddToASC(Entry.Key, ASC);
}
// 添加到注册列表(确保唯一)
RegisteredASCs.AddUnique(ASC);
}
// 从全局系统注销ASC
void ULyraGlobalAbilitySystem::UnregisterASC(ULyraAbilitySystemComponent* ASC)
{
check(ASC); // 确保ASC有效
// 移除所有能力
for (auto& Entry : AppliedAbilities)
{
Entry.Value.RemoveFromASC(ASC);
}
// 移除所有效果
for (auto& Entry : AppliedEffects)
{
Entry.Value.RemoveFromASC(ASC);
}
// 从注册列表中移除
RegisteredASCs.Remove(ASC);
}
📁 LyraAbilitySystemComponent.h - 能力系统组件扩展
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Abilities/LyraGameplayAbility.h" // 包含Lyra游戏能力
#include "AbilitySystemComponent.h" // 包含基础能力系统组件
#include "NativeGameplayTags.h" // 包含原生游戏标签
#include "LyraAbilitySystemComponent.generated.h" // 生成的UCLASS头文件
// 前向声明
class AActor;
class UGameplayAbility;
class ULyraAbilityTagRelationshipMapping;
class UObject;
struct FFrame;
struct FGameplayAbilityTargetDataHandle;
// 声明游戏能力输入阻塞标签
LYRAGAME_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Gameplay_AbilityInputBlocked);
/**
* ULyraAbilitySystemComponent
*
* 本项目使用的基础能力系统组件类。
*/
UCLASS()
class LYRAGAME_API ULyraAbilitySystemComponent : public UAbilitySystemComponent
{
GENERATED_BODY()
public:
// 构造函数
ULyraAbilitySystemComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
//~UActorComponent接口
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
//~End of UActorComponent接口
// 初始化能力Actor信息
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) // 客户端RPC,不可靠
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];
};
使用方法:
// 设置标签关系映射
ASC->SetTagRelationshipMapping(TagRelationshipMapping);
// 处理输入
ASC->AbilityInputTagPressed(InputTag);
ASC->AbilityInputTagReleased(InputTag);
ASC->ProcessAbilityInput(DeltaTime, bGamePaused);
// 管理激活组
ASC->AddAbilityToActivationGroup(ELyraAbilityActivationGroup::Exclusive_Blocking, Ability);
📁 LyraAbilitySystemComponent.cpp - 能力系统组件实现
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraAbilitySystemComponent.h"
#include "AbilitySystem/Abilities/LyraGameplayAbility.h" // 包含Lyra游戏能力
#include "AbilitySystem/LyraAbilityTagRelationshipMapping.h" // 包含标签关系映射
#include "Animation/LyraAnimInstance.h" // 包含动画实例
#include "Engine/World.h" // 包含世界
#include "GameFramework/Pawn.h" // 包含Pawn
#include "LyraGlobalAbilitySystem.h" // 包含全局能力系统
#include "LyraLogChannels.h" // 包含日志通道
#include "System/LyraAssetManager.h" // 包含资源管理器
#include "System/LyraGameData.h" // 包含游戏数据
#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraAbilitySystemComponent) // 包含生成的代码
// 定义游戏能力输入阻塞标签
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);
}
// 初始化能力Actor信息
void ULyraAbilitySystemComponent::InitAbilityActorInfo(AActor* InOwnerActor, AActor* InAvatarActor)
{
// 获取Actor信息并检查有效性
FGameplayAbilityActorInfo* ActorInfo = AbilityActorInfo.Get();
check(ActorInfo);
check(InOwnerActor);
// 检查是否有新的Pawn头像
const bool bHasNewPawnAvatar = Cast<APawn>(InAvatarActor) && (InAvatarActor != ActorInfo->AvatarActor);
// 调用父类实现
Super::InitAbilityActorInfo(InOwnerActor, InAvatarActor);
// 如果有新的Pawn头像
if (bHasNewPawnAvatar)
{
// 通知所有能力设置了新的Pawn头像
for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items)
{
// 获取能力的CDO
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
{
// 直接通知CDO
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)
{
// 获取能力的CDO
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;
}
// 获取能力的CDO
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事件。这里不复制。如果有人监听,他们可能将InputPressed事件复制到服务器
InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputPressed, Spec.Handle, Spec.ActivationInfo.GetActivationPredictionKey());
}
}
// 能力规格输入释放
void ULyraAbilitySystemComponent::AbilitySpecInputReleased(FGameplayAbilitySpec& Spec)
{
// 调用父类实现
Super::AbilitySpecInputReleased(Spec);
// 我们不支持UGameplayAbility::bReplicateInputDirectly
// 使用复制事件代替,以便WaitInputRelease能力任务工作
if (Spec.IsActive())
{
// 调用InputReleased事件。这里不复制。如果有人监听,他们可能将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();
//@TODO: 查看是否可以在某些循环中使用FScopedServerAbilityRPCBatcher
// 处理所有输入保持时激活的能力
for (const FGameplayAbilitySpecHandle& SpecHandle : InputHeldSpecHandles)
{
// 查找能力规格
if (const FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromHandle(SpecHandle))
{
// 检查能力是否有效且不活跃
if (AbilitySpec->Ability && !AbilitySpec->IsActive())
{
// 获取能力的CDO
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
{
// 获取能力的CDO
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);
// 转换为Lyra能力
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);
// 转换为Lyra能力
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);
//@TODO: 应用任何特殊逻辑,如阻塞输入或移动
}
// 处理能力可取消性变化
void ULyraAbilitySystemComponent::HandleChangeAbilityCanBeCanceled(const FGameplayTagContainer& AbilityTags, UGameplayAbility* RequestingAbility, bool bCanBeCanceled)
{
// 调用父类实现
Super::HandleChangeAbilityCanBeCanceled(AbilityTags, RequestingAbility, bCanBeCanceled);
//@TODO: 应用任何特殊逻辑,如阻塞输入或移动
}
// 获取额外的激活标签需求
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());
// 转换为Lyra能力并调用失败回调
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;
}
// 确保独占能力数量不超过1
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;
}
}
📁 LyraAbilitySet.h - 能力集管理
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "ActiveGameplayEffectHandle.h" // 活跃游戏效果句柄
#include "Engine/DataAsset.h" // 数据资产
#include "AttributeSet.h" // 属性集
#include "GameplayTagContainer.h" // 游戏标签容器
#include "GameplayAbilitySpecHandle.h" // 游戏能力规格句柄
#include "LyraAbilitySet.generated.h" // 生成的UCLASS头文件
// 前向声明
class UAttributeSet;
class UGameplayEffect;
class ULyraAbilitySystemComponent;
class ULyraGameplayAbility;
class UObject;
/**
* FLyraAbilitySet_GameplayAbility
*
* 能力集用于授予游戏能力的数据。
*/
USTRUCT(BlueprintType) // 蓝图类型结构体
struct FLyraAbilitySet_GameplayAbility
{
GENERATED_BODY()
public:
// 要授予的游戏能力。
UPROPERTY(EditDefaultsOnly) // 仅在编辑默认值时可编辑
TSubclassOf<ULyraGameplayAbility> Ability = nullptr;
// 要授予的能力等级。
UPROPERTY(EditDefaultsOnly)
int32 AbilityLevel = 1;
// 用于处理能力输入的标签。
UPROPERTY(EditDefaultsOnly, Meta = (Categories = "InputTag")) // 元数据指定分类
FGameplayTag InputTag;
};
/**
* FLyraAbilitySet_GameplayEffect
*
* 能力集用于授予游戏效果的数据。
*/
USTRUCT(BlueprintType)
struct FLyraAbilitySet_GameplayEffect
{
GENERATED_BODY()
public:
// 要授予的游戏效果。
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UGameplayEffect> GameplayEffect = nullptr;
// 要授予的效果等级。
UPROPERTY(EditDefaultsOnly)
float EffectLevel = 1.0f;
};
/**
* FLyraAbilitySet_AttributeSet
*
* 能力集用于授予属性集的数据。
*/
USTRUCT(BlueprintType)
struct FLyraAbilitySet_AttributeSet
{
GENERATED_BODY()
public:
// 要授予的属性集。
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAttributeSet> AttributeSet;
};
/**
* FLyraAbilitySet_GrantedHandles
*
* 用于存储能力集已授予内容的句柄的数据。
*/
USTRUCT(BlueprintType)
struct FLyraAbilitySet_GrantedHandles
{
GENERATED_BODY()
public:
// 添加能力规格句柄
void AddAbilitySpecHandle(const FGameplayAbilitySpecHandle& Handle);
// 添加游戏效果句柄
void AddGameplayEffectHandle(const FActiveGameplayEffectHandle& Handle);
// 添加属性集
void AddAttributeSet(UAttributeSet* Set);
// 从能力系统收回所有已授予的内容
void TakeFromAbilitySystem(ULyraAbilitySystemComponent* LyraASC);
protected:
// 已授予能力的句柄。
UPROPERTY()
TArray<FGameplayAbilitySpecHandle> AbilitySpecHandles;
// 已授予游戏效果的句柄。
UPROPERTY()
TArray<FActiveGameplayEffectHandle> GameplayEffectHandles;
// 指向已授予属性集的指针
UPROPERTY()
TArray<TObjectPtr<UAttributeSet>> GrantedAttributeSets;
};
/**
* ULyraAbilitySet
*
* 用于授予游戏能力和游戏效果的不可变数据资产。
*/
UCLASS(BlueprintType, Const) // 蓝图类型,常量
class ULyraAbilitySet : public UPrimaryDataAsset // 继承自主数据资产
{
GENERATED_BODY()
public:
// 构造函数
ULyraAbilitySet(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
// 将能力集授予指定的能力系统组件。
// 返回的句柄可用于稍后收回任何已授予的内容。
void GiveToAbilitySystem(ULyraAbilitySystemComponent* LyraASC, FLyraAbilitySet_GrantedHandles* OutGrantedHandles, UObject* SourceObject = nullptr) const;
protected:
// 当此能力集被授予时要授予的游戏能力。
UPROPERTY(EditDefaultsOnly, Category = "Gameplay Abilities", meta=(TitleProperty=Ability)) // 标题属性显示Ability
TArray<FLyraAbilitySet_GameplayAbility> GrantedGameplayAbilities;
// 当此能力集被授予时要授予的游戏效果。
UPROPERTY(EditDefaultsOnly, Category = "Gameplay Effects", meta=(TitleProperty=GameplayEffect))
TArray<FLyraAbilitySet_GameplayEffect> GrantedGameplayEffects;
// 当此能力集被授予时要授予的属性集。
UPROPERTY(EditDefaultsOnly, Category = "Attribute Sets", meta=(TitleProperty=AttributeSet))
TArray<FLyraAbilitySet_AttributeSet> GrantedAttributes;
};
使用方法:
// 创建能力集
ULyraAbilitySet* AbilitySet = LoadObject<ULyraAbilitySet>(...);
// 授予能力集
FLyraAbilitySet_GrantedHandles GrantedHandles;
AbilitySet->GiveToAbilitySystem(ASC, &GrantedHandles);
// 收回能力集
GrantedHandles.TakeFromAbilitySystem(ASC);
📁 LyraAbilitySet.cpp - 能力集实现
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraAbilitySet.h"
#include "AbilitySystem/Abilities/LyraGameplayAbility.h" // 包含Lyra游戏能力
#include "LyraAbilitySystemComponent.h" // 包含Lyra ASC
#include "LyraLogChannels.h" // 包含日志通道
#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraAbilitySet) // 包含生成的代码
// 添加能力规格句柄
void FLyraAbilitySet_GrantedHandles::AddAbilitySpecHandle(const FGameplayAbilitySpecHandle& Handle)
{
// 检查句柄是否有效
if (Handle.IsValid())
{
AbilitySpecHandles.Add(Handle);
}
}
// 添加游戏效果句柄
void FLyraAbilitySet_GrantedHandles::AddGameplayEffectHandle(const FActiveGameplayEffectHandle& Handle)
{
// 检查句柄是否有效
if (Handle.IsValid())
{
GameplayEffectHandles.Add(Handle);
}
}
// 添加属性集
void FLyraAbilitySet_GrantedHandles::AddAttributeSet(UAttributeSet* Set)
{
GrantedAttributeSets.Add(Set);
}
// 从能力系统收回所有已授予的内容
void FLyraAbilitySet_GrantedHandles::TakeFromAbilitySystem(ULyraAbilitySystemComponent* LyraASC)
{
// 检查ASC有效性
check(LyraASC);
// 检查是否具有权威性
if (!LyraASC->IsOwnerActorAuthoritative())
{
// 必须具有权威性才能给予或收回能力集
return;
}
// 清除所有能力
for (const FGameplayAbilitySpecHandle& Handle : AbilitySpecHandles)
{
if (Handle.IsValid())
{
LyraASC->ClearAbility(Handle);
}
}
// 移除所有游戏效果
for (const FActiveGameplayEffectHandle& Handle : GameplayEffectHandles)
{
if (Handle.IsValid())
{
LyraASC->RemoveActiveGameplayEffect(Handle);
}
}
// 移除所有属性集
for (UAttributeSet* Set : GrantedAttributeSets)
{
LyraASC->RemoveSpawnedAttribute(Set);
}
// 重置所有数组
AbilitySpecHandles.Reset();
GameplayEffectHandles.Reset();
GrantedAttributeSets.Reset();
}
// 构造函数
ULyraAbilitySet::ULyraAbilitySet(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) // 调用父类构造函数
{
}
// 将能力集授予能力系统组件
void ULyraAbilitySet::GiveToAbilitySystem(ULyraAbilitySystemComponent* LyraASC, FLyraAbilitySet_GrantedHandles* OutGrantedHandles, UObject* SourceObject) const
{
// 检查ASC有效性
check(LyraASC);
// 检查是否具有权威性
if (!LyraASC->IsOwnerActorAuthoritative())
{
// 必须具有权威性才能给予或收回能力集
return;
}
// 授予游戏能力
for (int32 AbilityIndex = 0; AbilityIndex < GrantedGameplayAbilities.Num(); ++AbilityIndex)
{
// 获取要授予的能力
const FLyraAbilitySet_GameplayAbility& AbilityToGrant = GrantedGameplayAbilities[AbilityIndex];
// 检查能力是否有效
if (!IsValid(AbilityToGrant.Ability))
{
UE_LOG(LogLyraAbilitySystem, Error, TEXT("GrantedGameplayAbilities[%d] on ability set [%s] is not valid."), AbilityIndex, *GetNameSafe(this));
continue;
}
// 获取能力的CDO
ULyraGameplayAbility* AbilityCDO = AbilityToGrant.Ability->GetDefaultObject<ULyraGameplayAbility>();
// 创建能力规格
FGameplayAbilitySpec AbilitySpec(AbilityCDO, AbilityToGrant.AbilityLevel);
AbilitySpec.SourceObject = SourceObject; // 设置源对象
AbilitySpec.DynamicAbilityTags.AddTag(AbilityToGrant.InputTag); // 添加输入标签
// 授予能力并获取句柄
const FGameplayAbilitySpecHandle AbilitySpecHandle = LyraASC->GiveAbility(AbilitySpec);
// 如果提供了输出句柄,存储句柄
if (OutGrantedHandles)
{
OutGrantedHandles->AddAbilitySpecHandle(AbilitySpecHandle);
}
}
// 授予游戏效果
for (int32 EffectIndex = 0; EffectIndex < GrantedGameplayEffects.Num(); ++EffectIndex)
{
// 获取要授予的效果
const FLyraAbilitySet_GameplayEffect& EffectToGrant = GrantedGameplayEffects[EffectIndex];
// 检查效果是否有效
if (!IsValid(EffectToGrant.GameplayEffect))
{
UE_LOG(LogLyraAbilitySystem, Error, TEXT("GrantedGameplayEffects[%d] on ability set [%s] is not valid"), EffectIndex, *GetNameSafe(this));
continue;
}
// 获取效果的CDO
const UGameplayEffect* GameplayEffect = EffectToGrant.GameplayEffect->GetDefaultObject<UGameplayEffect>();
// 应用游戏效果到自身并获取句柄
const FActiveGameplayEffectHandle GameplayEffectHandle = LyraASC->ApplyGameplayEffectToSelf(GameplayEffect, EffectToGrant.EffectLevel, LyraASC->MakeEffectContext());
// 如果提供了输出句柄,存储句柄
if (OutGrantedHandles)
{
OutGrantedHandles->AddGameplayEffectHandle(GameplayEffectHandle);
}
}
// 授予属性集
for (int32 SetIndex = 0; SetIndex < GrantedAttributes.Num(); ++SetIndex)
{
// 获取要授予的属性集
const FLyraAbilitySet_AttributeSet& SetToGrant = GrantedAttributes[SetIndex];
// 检查属性集是否有效
if (!IsValid(SetToGrant.AttributeSet))
{
UE_LOG(LogLyraAbilitySystem, Error, TEXT("GrantedAttributes[%d] on ability set [%s] is not valid"), SetIndex, *GetNameSafe(this));
continue;
}
// 创建新的属性集对象
UAttributeSet* NewSet = NewObject<UAttributeSet>(LyraASC->GetOwner(), SetToGrant.AttributeSet);
// 添加属性集子对象
LyraASC->AddAttributeSetSubobject(NewSet);
// 如果提供了输出句柄,存储属性集
if (OutGrantedHandles)
{
OutGrantedHandles->AddAttributeSet(NewSet);
}
}
}
📁 LyraAbilityTagRelationshipMapping.h - 能力标签关系映射
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Engine/DataAsset.h" // 数据资产
#include "GameplayTagContainer.h" // 游戏标签容器
#include "LyraAbilityTagRelationshipMapping.generated.h" // 生成的UCLASS头文件
class UObject;
/** 定义不同能力标签之间关系的结构体 */
USTRUCT()
struct FLyraAbilityTagRelationship
{
GENERATED_BODY()
/** 此容器关系所关于的标签。单个标签,但能力可以有多个这样的标签 */
UPROPERTY(EditAnywhere, Category = "Ability", meta = (Categories = "Gameplay.Action")) // 分类为Gameplay.Action
FGameplayTag AbilityTag;
/** 任何使用此标签的能力将阻塞的其他能力标签 */
UPROPERTY(EditAnywhere, Category = "Ability")
FGameplayTagContainer AbilityTagsToBlock;
/** 任何使用此标签的能力将取消的其他能力标签 */
UPROPERTY(EditAnywhere, Category = "Ability")
FGameplayTagContainer AbilityTagsToCancel;
/** 如果能力具有此标签,这会隐式添加到能力的激活需求标签中 */
UPROPERTY(EditAnywhere, Category = "Ability")
FGameplayTagContainer ActivationRequiredTags;
/** 如果能力具有此标签,这会隐式添加到能力的激活阻塞标签中 */
UPROPERTY(EditAnywhere, Category = "Ability")
FGameplayTagContainer ActivationBlockedTags;
};
/** 能力标签如何阻塞或取消其他能力的映射 */
UCLASS()
class ULyraAbilityTagRelationshipMapping : public UDataAsset // 继承自数据资产
{
GENERATED_BODY()
private:
/** 不同游戏标签之间关系的列表(哪些标签阻塞或取消其他标签) */
UPROPERTY(EditAnywhere, Category = "Ability", meta=(TitleProperty="AbilityTag")) // 标题属性显示AbilityTag
TArray<FLyraAbilityTagRelationship> AbilityTagRelationships;
public:
/** 给定一组能力标签,解析标签关系并填充要阻塞和取消的标签 */
void GetAbilityTagsToBlockAndCancel(const FGameplayTagContainer& AbilityTags, FGameplayTagContainer* OutTagsToBlock, FGameplayTagContainer* OutTagsToCancel) const;
/** 给定一组能力标签,添加额外的需求和阻塞标签 */
void GetRequiredAndBlockedActivationTags(const FGameplayTagContainer& AbilityTags, FGameplayTagContainer* OutActivationRequired, FGameplayTagContainer* OutActivationBlocked) const;
/** 返回指定的能力标签是否被传入的动作标签取消 */
bool IsAbilityCancelledByTag(const FGameplayTagContainer& AbilityTags, const FGameplayTag& ActionTag) const;
};
使用方法:
// 设置标签关系映射
AbilitySystemComponent->SetTagRelationshipMapping(TagRelationshipMapping);
// 获取阻塞和取消标签
FGameplayTagContainer TagsToBlock, TagsToCancel;
TagRelationshipMapping->GetAbilityTagsToBlockAndCancel(AbilityTags, &TagsToBlock, &TagsToCancel);
// 检查能力是否被取消
bool bCancelled = TagRelationshipMapping->IsAbilityCancelledByTag(AbilityTags, ActionTag);
📁 LyraAbilityTagRelationshipMapping.cpp - 标签关系映射实现
// Copyright Epic Games, Inc. All Rights Reserved.
#include "AbilitySystem/LyraAbilityTagRelationshipMapping.h" // 包含头文件
#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraAbilityTagRelationshipMapping) // 包含生成的代码
// 获取要阻塞和取消的能力标签
void ULyraAbilityTagRelationshipMapping::GetAbilityTagsToBlockAndCancel(const FGameplayTagContainer& AbilityTags, FGameplayTagContainer* OutTagsToBlock, FGameplayTagContainer* OutTagsToCancel) const
{
// 简单迭代处理
for (int32 i = 0; i < AbilityTagRelationships.Num(); i++)
{
// 获取标签关系
const FLyraAbilityTagRelationship& Tags = AbilityTagRelationships[i];
// 检查能力标签是否包含关系标签
if (AbilityTags.HasTag(Tags.AbilityTag))
{
// 添加要阻塞的标签
if (OutTagsToBlock)
{
OutTagsToBlock->AppendTags(Tags.AbilityTagsToBlock);
}
// 添加要取消的标签
if (OutTagsToCancel)
{
OutTagsToCancel->AppendTags(Tags.AbilityTagsToCancel);
}
}
}
}
// 获取需求和阻塞的激活标签
void ULyraAbilityTagRelationshipMapping::GetRequiredAndBlockedActivationTags(const FGameplayTagContainer& AbilityTags, FGameplayTagContainer* OutActivationRequired, FGameplayTagContainer* OutActivationBlocked) const
{
// 简单迭代处理
for (int32 i = 0; i < AbilityTagRelationships.Num(); i++)
{
// 获取标签关系
const FLyraAbilityTagRelationship& Tags = AbilityTagRelationships[i];
// 检查能力标签是否包含关系标签
if (AbilityTags.HasTag(Tags.AbilityTag))
{
// 添加激活需求标签
if (OutActivationRequired)
{
OutActivationRequired->AppendTags(Tags.ActivationRequiredTags);
}
// 添加激活阻塞标签
if (OutActivationBlocked)
{
OutActivationBlocked->AppendTags(Tags.ActivationBlockedTags);
}
}
}
}
// 检查能力是否被标签取消
bool ULyraAbilityTagRelationshipMapping::IsAbilityCancelledByTag(const FGameplayTagContainer& AbilityTags, const FGameplayTag& ActionTag) const
{
// 简单迭代处理
for (int32 i = 0; i < AbilityTagRelationships.Num(); i++)
{
// 获取标签关系
const FLyraAbilityTagRelationship& Tags = AbilityTagRelationships[i];
// 检查动作标签是否匹配且要取消的标签包含任何能力标签
if (Tags.AbilityTag == ActionTag && Tags.AbilityTagsToCancel.HasAny(AbilityTags))
{
return true;
}
}
return false;
}
📁 LyraAbilitySystemGlobals.h - 能力系统全局配置
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "AbilitySystemGlobals.h" // 包含能力系统全局
#include "LyraAbilitySystemGlobals.generated.h" // 生成的UCLASS头文件
class UObject;
struct FGameplayEffectContext;
// 配置游戏特定的能力系统全局设置
UCLASS(Config=Game) // 配置类,使用Game配置文件
class ULyraAbilitySystemGlobals : public UAbilitySystemGlobals // 继承自能力系统全局
{
GENERATED_UCLASS_BODY() // 生成UCLASS构造函数
//~UAbilitySystemGlobals接口
virtual FGameplayEffectContext* AllocGameplayEffectContext() const override; // 分配游戏效果上下文
//~End of UAbilitySystemGlobals接口
};
使用方法:
// 在DefaultGame.ini中配置
[/Script/LyraGame.LyraAbilitySystemGlobals]
; 游戏特定的全局配置
📁 LyraAbilitySystemGlobals.cpp - 能力系统全局实现
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraAbilitySystemGlobals.h"
#include "LyraGameplayEffectContext.h" // 包含Lyra游戏效果上下文
#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraAbilitySystemGlobals) // 包含生成的代码
// 前向声明
struct FGameplayEffectContext;
// 构造函数
ULyraAbilitySystemGlobals::ULyraAbilitySystemGlobals(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) // 调用父类构造函数
{
}
// 分配游戏效果上下文
FGameplayEffectContext* ULyraAbilitySystemGlobals::AllocGameplayEffectContext() const
{
// 返回Lyra特定的游戏效果上下文
return new FLyraGameplayEffectContext();
}
📁 LyraAbilitySourceInterface.h - 能力源接口
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "UObject/Interface.h" // 包含接口
#include "LyraAbilitySourceInterface.generated.h" // 生成的UINTERFACE头文件
class UObject;
class UPhysicalMaterial;
struct FGameplayTagContainer;
/** 作为能力计算源的任何东西的基础接口 */
UINTERFACE() // UINTERFACE用于蓝图
class ULyraAbilitySourceInterface : public UInterface
{
GENERATED_UINTERFACE_BODY() // 生成UINTERFACE构造函数
};
// 实际的接口类
class ILyraAbilitySourceInterface
{
GENERATED_IINTERFACE_BODY() // 生成IINTERFACE构造函数
public:
/**
* 计算效果随距离衰减的乘数
*
* @param Distance 能力计算中从源到目标的距离(枪的子弹行进距离等)
* @param SourceTags 来自源的聚合标签
* @param TargetTags 目标当前拥有的聚合标签
*
* @return 由于距离要应用到基础属性值的乘数
*/
virtual float GetDistanceAttenuation(float Distance, const FGameplayTagContainer* SourceTags = nullptr, const FGameplayTagContainer* TargetTags = nullptr) const = 0;
// 获取物理材质衰减
virtual float GetPhysicalMaterialAttenuation(const UPhysicalMaterial* PhysicalMaterial, const FGameplayTagContainer* SourceTags = nullptr, const FGameplayTagContainer* TargetTags = nullptr) const = 0;
};
使用方法:
// 实现接口
class UMyWeapon : public UObject, public ILyraAbilitySourceInterface
{
// 实现接口方法
virtual float GetDistanceAttenuation(float Distance, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags) const override
{
return FMath::Max(1.0f - Distance / MaxRange, 0.0f);
}
virtual float GetPhysicalMaterialAttenuation(const UPhysicalMaterial* PhysicalMaterial, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags) const override
{
return PhysicalMaterial ? PhysicalMaterial->SurfaceType : 1.0f;
}
};
📁 LyraAbilitySourceInterface.cpp - 能力源接口实现
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraAbilitySourceInterface.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraAbilitySourceInterface) // 包含生成的代码
// UINTERFACE构造函数
ULyraAbilitySourceInterface::ULyraAbilitySourceInterface(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) // 调用父类构造函数
{}
📁 LyraGameplayEffectContext.h - 游戏效果上下文扩展
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameplayEffectTypes.h" // 包含游戏效果类型
#include "LyraGameplayEffectContext.generated.h" // 生成的USTRUCT头文件
// 前向声明
class AActor;
class FArchive;
class ILyraAbilitySourceInterface;
class UObject;
class UPhysicalMaterial;
// Lyra特定的游戏效果上下文结构体
USTRUCT()
struct FLyraGameplayEffectContext : public FGameplayEffectContext // 继承自基础游戏效果上下文
{
GENERATED_BODY()
// 默认构造函数
FLyraGameplayEffectContext()
: FGameplayEffectContext() // 调用父类构造函数
{
}
// 带参数的构造函数
FLyraGameplayEffectContext(AActor* InInstigator, AActor* InEffectCauser)
: FGameplayEffectContext(InInstigator, InEffectCauser) // 调用父类构造函数
{
}
/** 从句柄中提取包装的FLyraGameplayEffectContext,如果不存在或类型错误则返回nullptr */
static LYRAGAME_API FLyraGameplayEffectContext* ExtractEffectContext(struct FGameplayEffectContextHandle Handle);
/** 设置用作能力源的对象 */
void SetAbilitySource(const ILyraAbilitySourceInterface* InObject, float InSourceLevel);
/** 返回与源对象关联的能力源接口。仅在权威端有效。 */
const ILyraAbilitySourceInterface* GetAbilitySource() const;
// 复制上下文
virtual FGameplayEffectContext* Duplicate() const override
{
FLyraGameplayEffectContext* NewContext = new FLyraGameplayEffectContext();
*NewContext = *this; // 复制所有成员
if (GetHitResult())
{
// 对命中结果进行深拷贝
NewContext->AddHitResult(*GetHitResult(), true);
}
return NewContext;
}
// 获取脚本结构
virtual UScriptStruct* GetScriptStruct() const override
{
return FLyraGameplayEffectContext::StaticStruct();
}
/** 重写以序列化新字段 */
virtual bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) override;
/** 如果存在,从命中结果返回物理材质 */
const UPhysicalMaterial* GetPhysicalMaterial() const;
public:
/** 用于识别属于同一弹药的多个子弹的ID */
UPROPERTY()
int32 CartridgeID = -1; // 弹药ID,默认为-1表示无效
protected:
/** 能力源对象(应实现ILyraAbilitySourceInterface)。当前未复制 */
UPROPERTY()
TWeakObjectPtr<const UObject> AbilitySourceObject; // 弱对象指针,避免循环引用
};
// 模板特化,启用网络序列化和复制
template<>
struct TStructOpsTypeTraits<FLyraGameplayEffectContext> : public TStructOpsTypeTraitsBase2<FLyraGameplayEffectContext>
{
enum
{
WithNetSerializer = true, // 启用网络序列化
WithCopy = true // 启用复制
};
};
使用方法:
// 创建效果上下文
FGameplayEffectContextHandle ContextHandle = MakeEffectContext();
if (FLyraGameplayEffectContext* LyraContext = FLyraGameplayEffectContext::ExtractEffectContext(ContextHandle))
{
// 设置弹药ID
LyraContext->CartridgeID = CartridgeID;
// 设置能力源
LyraContext->SetAbilitySource(this, 1.0f);
}
// 应用效果
ApplyGameplayEffectToTarget(EffectClass, TargetASC, 1.0f, ContextHandle);
📁 LyraGameplayEffectContext.cpp - 游戏效果上下文实现
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraGameplayEffectContext.h"
#include "AbilitySystem/LyraAbilitySourceInterface.h" // 包含能力源接口
#include "Engine/HitResult.h" // 包含命中结果
#include "PhysicalMaterials/PhysicalMaterial.h" // 包含物理材质
// Iris网络复制支持
#if UE_WITH_IRIS
#include "Iris/ReplicationState/PropertyNetSerializerInfoRegistry.h"
#include "Serialization/GameplayEffectContextNetSerializer.h"
#endif
#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraGameplayEffectContext) // 包含生成的代码
// 前向声明
class FArchive;
// 从句柄提取效果上下文
FLyraGameplayEffectContext* FLyraGameplayEffectContext::ExtractEffectContext(struct FGameplayEffectContextHandle Handle)
{
// 获取基础效果上下文
FGameplayEffectContext* BaseEffectContext = Handle.Get();
// 检查上下文是否有效且是Lyra类型的子类
if ((BaseEffectContext != nullptr) && BaseEffectContext->GetScriptStruct()->IsChildOf(FLyraGameplayEffectContext::StaticStruct()))
{
return (FLyraGameplayEffectContext*)BaseEffectContext; // 转换为Lyra上下文
}
return nullptr; // 返回nullptr如果提取失败
}
// 网络序列化
bool FLyraGameplayEffectContext::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
// 调用父类网络序列化
FGameplayEffectContext::NetSerialize(Ar, Map, bOutSuccess);
// 不为激活后使用序列化:
// CartridgeID
return true;
}
// Iris网络序列化支持
#if UE_WITH_IRIS
namespace UE::Net
{
// 转发到FGameplayEffectContextNetSerializer
// 注意:如果修改了FLyraGameplayEffectContext::NetSerialize(),必须实现自定义NetSerializer,因为当前的fallback将不再足够
UE_NET_IMPLEMENT_FORWARDING_NETSERIALIZER_AND_REGISTRY_DELEGATES(LyraGameplayEffectContext, FGameplayEffectContextNetSerializer);
}
#endif
// 设置能力源
void FLyraGameplayEffectContext::SetAbilitySource(const ILyraAbilitySourceInterface* InObject, float InSourceLevel)
{
// 将能力源接口转换为UObject并存储为弱指针
AbilitySourceObject = MakeWeakObjectPtr(Cast<const UObject>(InObject));
//SourceLevel = InSourceLevel; // 注释掉的源级别
}
// 获取能力源
const ILyraAbilitySourceInterface* FLyraGameplayEffectContext::GetAbilitySource() const
{
// 将弱指针转换为能力源接口
return Cast<ILyraAbilitySourceInterface>(AbilitySourceObject.Get());
}
// 获取物理材质
const UPhysicalMaterial* FLyraGameplayEffectContext::GetPhysicalMaterial() const
{
// 如果存在命中结果,返回其物理材质
if (const FHitResult* HitResultPtr = GetHitResult())
{
return HitResultPtr->PhysMaterial.Get(); // 返回物理材质
}
return nullptr; // 没有命中结果则返回nullptr
}
📁 LyraGameplayAbilityTargetData_SingleTargetHit.h - 目标数据扩展
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Abilities/GameplayAbilityTargetTypes.h" // 包含游戏能力目标类型
#include "LyraGameplayAbilityTargetData_SingleTargetHit.generated.h" // 生成的USTRUCT头文件
// 前向声明
class FArchive;
struct FGameplayEffectContextHandle;
/** 单目标命中跟踪的游戏特定添加 */
USTRUCT()
struct FLyraGameplayAbilityTargetData_SingleTargetHit : public FGameplayAbilityTargetData_SingleTargetHit // 继承自基础单目标命中
{
GENERATED_BODY()
// 默认构造函数
FLyraGameplayAbilityTargetData_SingleTargetHit()
: CartridgeID(-1) // 初始化弹药ID为-1
{ }
// 重写:添加目标数据到上下文
virtual void AddTargetDataToContext(FGameplayEffectContextHandle& Context, bool bIncludeActorArray) const override;
/** 用于识别属于同一弹药的多个子弹的ID */
UPROPERTY()
int32 CartridgeID;
// 网络序列化
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
// 获取脚本结构
virtual UScriptStruct* GetScriptStruct() const override
{
return FLyraGameplayAbilityTargetData_SingleTargetHit::StaticStruct(); // 返回自身静态结构
}
};
// 模板特化,启用网络序列化
template<>
struct TStructOpsTypeTraits<FLyraGameplayAbilityTargetData_SingleTargetHit> : public TStructOpsTypeTraitsBase2<FLyraGameplayAbilityTargetData_SingleTargetHit>
{
enum
{
WithNetSerializer = true // 启用网络序列化,对于FGameplayAbilityTargetDataHandle网络序列化是必需的
};
};
使用方法:
// 创建目标数据
FLyraGameplayAbilityTargetData_SingleTargetHit TargetData;
TargetData.CartridgeID = CartridgeID;
// 设置命中结果
TargetData.HitResult = HitResult;
// 添加到目标数据句柄
FGameplayAbilityTargetDataHandle TargetDataHandle;
TargetDataHandle.Add(TargetData);
📁 LyraGameplayAbilityTargetData_SingleTargetHit.cpp - 目标数据实现
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraGameplayAbilityTargetData_SingleTargetHit.h"
#include "LyraGameplayEffectContext.h" // 包含Lyra游戏效果上下文
#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraGameplayAbilityTargetData_SingleTargetHit) // 包含生成的代码
// 前向声明
struct FGameplayEffectContextHandle;
//////////////////////////////////////////////////////////////////////
// 添加目标数据到上下文
void FLyraGameplayAbilityTargetData_SingleTargetHit::AddTargetDataToContext(FGameplayEffectContextHandle& Context, bool bIncludeActorArray) const
{
// 调用父类实现
FGameplayAbilityTargetData_SingleTargetHit::AddTargetDataToContext(Context, bIncludeActorArray);
// 添加游戏特定数据
if (FLyraGameplayEffectContext* TypedContext = FLyraGameplayEffectContext::ExtractEffectContext(Context))
{
TypedContext->CartridgeID = CartridgeID; // 设置弹药ID
}
}
// 网络序列化
bool FLyraGameplayAbilityTargetData_SingleTargetHit::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
// 调用父类网络序列化
FGameplayAbilityTargetData_SingleTargetHit::NetSerialize(Ar, Map, bOutSuccess);
// 序列化弹药ID
Ar << CartridgeID;
return true;
}
📁 LyraGameplayCueManager.h - 游戏提示管理器
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameplayCueManager.h" // 包含游戏提示管理器
#include "LyraGameplayCueManager.generated.h" // 生成的UCLASS头文件
// 前向声明
class FString;
class UClass;
class UObject;
class UWorld;
struct FObjectKey;
/**
* ULyraGameplayCueManager
*
* 游戏提示的特定游戏管理器
*/
UCLASS()
class ULyraGameplayCueManager : public UGameplayCueManager // 继承自游戏提示管理器
{
GENERATED_BODY()
public:
// 构造函数
ULyraGameplayCueManager(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
// 获取单例实例
static ULyraGameplayCueManager* Get();
//~UGameplayCueManager接口
virtual void OnCreated() override; // 创建时调用
virtual bool ShouldAsyncLoadRuntimeObjectLibraries() const override; // 是否异步加载运行时对象库
virtual bool ShouldSyncLoadMissingGameplayCues() const override; // 是否同步加载缺失的游戏提示
virtual bool ShouldAsyncLoadMissingGameplayCues() const override; // 是否异步加载缺失的游戏提示
//~End of UGameplayCueManager接口
// 转储游戏提示信息
static void DumpGameplayCues(const TArray<FString>& Args);
// 当延迟加载提示时,这将加载必须始终加载的提示
void LoadAlwaysLoadedCues();
// 更新单一游戏提示主资源的bundles
void RefreshGameplayCuePrimaryAsset();
private:
// 游戏标签加载回调
void OnGameplayTagLoaded(const FGameplayTag& Tag);
// 垃圾回收后处理
void HandlePostGarbageCollect();
// 处理已加载的标签
void ProcessLoadedTags();
// 处理要预加载的标签
void ProcessTagToPreload(const FGameplayTag& Tag, UObject* OwningObject);
// 预加载提示完成回调
void OnPreloadCueComplete(FSoftObjectPath Path, TWeakObjectPtr<UObject> OwningObject, bool bAlwaysLoadedCue);
// 注册预加载的提示
void RegisterPreloadedCue(UClass* LoadedGameplayCueClass, UObject* OwningObject);
// 地图加载后处理
void HandlePostLoadMap(UWorld* NewWorld);
// 更新延迟加载委托监听器
void UpdateDelayLoadDelegateListeners();
// 是否延迟加载游戏提示
bool ShouldDelayLoadGameplayCues() const;
private:
// 处理已加载游戏标签的数据结构
struct FLoadedGameplayTagToProcessData
{
FGameplayTag Tag; // 游戏标签
TWeakObjectPtr<UObject> WeakOwner; // 弱引用的所有者
FLoadedGameplayTagToProcessData() {}
FLoadedGameplayTagToProcessData(const FGameplayTag& InTag, const TWeakObjectPtr<UObject>& InWeakOwner) : Tag(InTag), WeakOwner(InWeakOwner) {}
};
private:
// 由于被内容引用而在客户端预加载的提示
UPROPERTY(transient) // 临时属性,不保存
TSet<TObjectPtr<UClass>> PreloadedCues; // 预加载的提示集合
// 预加载提示的引用者映射
TMap<FObjectKey, TSet<FObjectKey>> PreloadedCueReferencers;
// 在客户端预加载且将始终加载的提示(代码引用或显式始终加载)
UPROPERTY(transient)
TSet<TObjectPtr<UClass>> AlwaysLoadedCues; // 始终加载的提示集合
// 要处理的已加载游戏标签队列
TArray<FLoadedGameplayTagToProcessData> LoadedGameplayTagsToProcess;
// 处理队列的临界区
FCriticalSection LoadedGameplayTagsToProcessCS;
// 是否在垃圾回收后处理加载的标签
bool bProcessLoadedTagsAfterGC = false;
};
使用方法:
// 获取游戏提示管理器
ULyraGameplayCueManager* CueManager = ULyraGameplayCueManager::Get();
// 加载始终加载的提示
CueManager->LoadAlwaysLoadedCues();
// 转储游戏提示信息(控制台命令)
Lyra.DumpGameplayCues
📁 LyraGameplayCueManager.cpp - 游戏提示管理器实现
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraGameplayCueManager.h"
#include "Engine/AssetManager.h" // 包含资源管理器
#include "LyraLogChannels.h" // 包含日志通道
#include "GameplayCueSet.h" // 包含游戏提示集
#include "AbilitySystemGlobals.h" // 包含能力系统全局
#include "GameplayTagsManager.h" // 包含游戏标签管理器
#include "UObject/UObjectThreadContext.h" // 包含UObject线程上下文
#include "Async/Async.h" // 包含异步支持
#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraGameplayCueManager) // 包含生成的代码
//////////////////////////////////////////////////////////////////////
// 编辑器加载模式枚举
enum class ELyraEditorLoadMode
{
// 预先加载所有提示;编辑器加载时间较长但PIE时间短,效果从不播放失败
LoadUpfront,
// 编辑器外:在注册提示标签时异步加载
// 编辑器中:在调用提示时异步加载
// 注意:这可能导致PIE中出现一些"为什么我没有看到X效果"的问题,对迭代速度有好处,但对设计师不利
PreloadAsCuesAreReferenced_GameOnly,
// 在注册提示标签时异步加载
PreloadAsCuesAreReferenced
};
// 控制台变量命名空间
namespace LyraGameplayCueManagerCvars
{
// 转储游戏提示的控制台命令
static FAutoConsoleCommand CVarDumpGameplayCues(
TEXT("Lyra.DumpGameplayCues"),
TEXT("显示通过LyraGameplayCueManager加载且当前在内存中的所有资源。"),
FConsoleCommandWithArgsDelegate::CreateStatic(ULyraGameplayCueManager::DumpGameplayCues));
// 加载模式静态变量
static ELyraEditorLoadMode LoadMode = ELyraEditorLoadMode::LoadUpfront;
}
// 即使在编辑器中也预加载
const bool bPreloadEvenInEditor = true;
//////////////////////////////////////////////////////////////////////
// 游戏提示标签线程同步图任务
struct FGameplayCueTagThreadSynchronizeGraphTask : public FAsyncGraphTaskBase
{
TFunction<void()> TheTask; // 要执行的任务
FGameplayCueTagThreadSynchronizeGraphTask(TFunction<void()>&& Task) : TheTask(MoveTemp(Task)) { }
void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) { TheTask(); }
ENamedThreads::Type GetDesiredThread() { return ENamedThreads::GameThread; } // 需要在游戏线程执行
};
//////////////////////////////////////////////////////////////////////
// 构造函数
ULyraGameplayCueManager::ULyraGameplayCueManager(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) // 调用父类构造函数
{
}
// 获取单例实例
ULyraGameplayCueManager* ULyraGameplayCueManager::Get()
{
// 从能力系统全局获取游戏提示管理器并转换为Lyra类型
return Cast<ULyraGameplayCueManager>(UAbilitySystemGlobals::Get().GetGameplayCueManager());
}
// 创建时调用
void ULyraGameplayCueManager::OnCreated()
{
// 调用父类实现
Super::OnCreated();
// 更新延迟加载委托监听器
UpdateDelayLoadDelegateListeners();
}
// 加载始终加载的提示
void ULyraGameplayCueManager::LoadAlwaysLoadedCues()
{
// 检查是否应该延迟加载游戏提示
if (ShouldDelayLoadGameplayCues())
{
// 获取游戏标签管理器
UGameplayTagsManager& TagManager = UGameplayTagsManager::Get();
//@TODO: 尝试通过过滤GameplayCue标签从原生游戏标签中收集这些?
TArray<FName> AdditionalAlwaysLoadedCueTags;
// 处理每个始终加载的提示标签
for (const FName& CueTagName : AdditionalAlwaysLoadedCueTags)
{
// 请求游戏标签
FGameplayTag CueTag = TagManager.RequestGameplayTag(CueTagName, /*ErrorIfNotFound=*/ false);
if (CueTag.IsValid())
{
// 处理标签预加载
ProcessTagToPreload(CueTag, nullptr);
}
else
{
// 记录警告日志
UE_LOG(LogLyra, Warning, TEXT("ULyraGameplayCueManager::AdditionalAlwaysLoadedCueTags contains invalid tag %s"), *CueTagName.ToString());
}
}
}
}
// 是否异步加载运行时对象库
bool ULyraGameplayCueManager::ShouldAsyncLoadRuntimeObjectLibraries() const
{
// 根据加载模式决定
switch (LyraGameplayCueManagerCvars::LoadMode)
{
case ELyraEditorLoadMode::LoadUpfront:
return true; // 预先加载模式:异步加载
case ELyraEditorLoadMode::PreloadAsCuesAreReferenced_GameOnly:
#if WITH_EDITOR
// 在编辑器中不异步加载
if (GIsEditor)
{
return false;
}
#endif
break;
case ELyraEditorLoadMode::PreloadAsCuesAreReferenced:
break;
}
// 如果不延迟加载游戏提示,则异步加载
return !ShouldDelayLoadGameplayCues();
}
// 是否同步加载缺失的游戏提示
bool ULyraGameplayCueManager::ShouldSyncLoadMissingGameplayCues() const
{
return false; // 不同步加载
}
// 是否异步加载缺失的游戏提示
bool ULyraGameplayCueManager::ShouldAsyncLoadMissingGameplayCues() const
{
return true; // 异步加载
}
// 转储游戏提示信息
void ULyraGameplayCueManager::DumpGameplayCues(const TArray<FString>& Args)
{
// 获取游戏提示管理器
ULyraGameplayCueManager* GCM = Cast<ULyraGameplayCueManager>(UAbilitySystemGlobals::Get().GetGameplayCueManager());
if (!GCM)
{
UE_LOG(LogLyra, Error, TEXT("DumpGameplayCues failed. No ULyraGameplayCueManager found."));
return;
}
// 检查是否包含引用信息参数
const bool bIncludeRefs = Args.Contains(TEXT("Refs"));
// 转储始终加载的游戏提示通知
UE_LOG(LogLyra, Log, TEXT("=========== Dumping Always Loaded Gameplay Cue Notifies ==========="));
for (UClass* CueClass : GCM->AlwaysLoadedCues)
{
UE_LOG(LogLyra, Log, TEXT(" %s"), *GetPathNameSafe(CueClass));
}
// 转储预加载的游戏提示通知
UE_LOG(LogLyra, Log, TEXT("=========== Dumping Preloaded Gameplay Cue Notifies ==========="));
for (UClass* CueClass : GCM->PreloadedCues)
{
// 查找引用者集合
TSet<FObjectKey>* ReferencerSet = GCM->PreloadedCueReferencers.Find(CueClass);
int32 NumRefs = ReferencerSet ? ReferencerSet->Num() : 0;
UE_LOG(LogLyra, Log, TEXT(" %s (%d refs)"), *GetPathNameSafe(CueClass), NumRefs);
// 如果需要包含引用信息
if (bIncludeRefs && ReferencerSet)
{
for (const FObjectKey& Ref : *ReferencerSet)
{
UObject* RefObject = Ref.ResolveObjectPtr();
UE_LOG(LogLyra, Log, TEXT(" ^- %s"), *GetPathNameSafe(RefObject));
}
}
}
// 转储按需加载的游戏提示通知
UE_LOG(LogLyra, Log, TEXT("=========== Dumping Gameplay Cue Notifies loaded on demand ==========="));
int32 NumMissingCuesLoaded = 0;
if (GCM->RuntimeGameplayCueObjectLibrary.CueSet)
{
// 遍历所有游戏提示数据
for (const FGameplayCueNotifyData& CueData : GCM->RuntimeGameplayCueObjectLibrary.CueSet->GameplayCueData)
{
// 检查提示类是否已加载但不在始终加载或预加载列表中
if (CueData.LoadedGameplayCueClass && !GCM->AlwaysLoadedCues.Contains(CueData.LoadedGameplayCueClass) && !GCM->PreloadedCues.Contains(CueData.LoadedGameplayCueClass))
{
NumMissingCuesLoaded++;
UE_LOG(LogLyra, Log, TEXT(" %s"), *CueData.LoadedGameplayCueClass->GetPathName());
}
}
}
// 转储游戏提示通知摘要
UE_LOG(LogLyra, Log, TEXT("=========== Gameplay Cue Notify summary ==========="));
UE_LOG(LogLyra, Log, TEXT(" ... %d cues in always loaded list"), GCM->AlwaysLoadedCues.Num());
UE_LOG(LogLyra, Log, TEXT(" ... %d cues in preloaded list"), GCM->PreloadedCues.Num());
UE_LOG(LogLyra, Log, TEXT(" ... %d cues loaded on demand"), NumMissingCuesLoaded);
UE_LOG(LogLyra, Log, TEXT(" ... %d cues in total"), GCM->AlwaysLoadedCues.Num() + GCM->PreloadedCues.Num() + NumMissingCuesLoaded);
}
// 游戏标签加载回调
void ULyraGameplayCueManager::OnGameplayTagLoaded(const FGameplayTag& Tag)
{
// 使用临界区保护
FScopeLock ScopeLock(&LoadedGameplayTagsToProcessCS);
// 检查是否启动任务
bool bStartTask = LoadedGameplayTagsToProcess.Num() == 0;
// 获取序列化上下文和所有者对象
FUObjectSerializeContext* LoadContext = FUObjectThreadContext::Get().GetSerializeContext();
UObject* OwningObject = LoadContext ? LoadContext->SerializedObject : nullptr;
// 添加到处理队列
LoadedGameplayTagsToProcess.Emplace(Tag, OwningObject);
// 如果需要启动任务
if (bStartTask)
{
// 创建并分发图任务
TGraphTask<FGameplayCueTagThreadSynchronizeGraphTask>::CreateTask().ConstructAndDispatchWhenReady([]()
{
// 检查游戏是否在运行
if (GIsRunning)
{
// 获取强引用
if (ULyraGameplayCueManager* StrongThis = Get())
{
// 如果正在垃圾回收,我们不能调用StaticFindObject(或其他一些静态uobject函数),所以我们将等待GC结束然后处理标签
if (IsGarbageCollecting())
{
StrongThis->bProcessLoadedTagsAfterGC = true;
}
else
{
StrongThis->ProcessLoadedTags();
}
}
}
});
}
}
// 垃圾回收后处理
void ULyraGameplayCueManager::HandlePostGarbageCollect()
{
// 如果需要在GC后处理标签
if (bProcessLoadedTagsAfterGC)
{
ProcessLoadedTags();
}
bProcessLoadedTagsAfterGC = false;
}
// 处理已加载的标签
void ULyraGameplayCueManager::ProcessLoadedTags()
{
TArray<FLoadedGameplayTagToProcessData> TaskLoadedGameplayTagsToProcess;
{
// 使用临界区保护,复制并清空队列
FScopeLock TaskScopeLock(&LoadedGameplayTagsToProcessCS);
TaskLoadedGameplayTagsToProcess = LoadedGameplayTagsToProcess;
LoadedGameplayTagsToProcess.Empty();
}
// 检查游戏是否在运行
if (GIsRunning)
{
// 检查提示集是否有效
if (RuntimeGameplayCueObjectLibrary.CueSet)
{
// 处理每个已加载的标签数据
for (const FLoadedGameplayTagToProcessData& LoadedTagData : TaskLoadedGameplayTagsToProcess)
{
// 检查提示集是否包含此标签
if (RuntimeGameplayCueObjectLibrary.CueSet->GameplayCueDataMap.Contains(LoadedTagData.Tag))
{
// 检查所有者是否有效
if (!LoadedTagData.WeakOwner.IsStale())
{
// 处理标签预加载
ProcessTagToPreload(LoadedTagData.Tag, LoadedTagData.WeakOwner.Get());
}
}
}
}
else
{
UE_LOG(LogLyra, Warning, TEXT("ULyraGameplayCueManager::OnGameplayTagLoaded processed loaded tag(s) but RuntimeGameplayCueObjectLibrary.CueSet was null. Skipping processing."));
}
}
}
// 处理要预加载的标签
void ULyraGameplayCueManager::ProcessTagToPreload(const FGameplayTag& Tag, UObject* OwningObject)
{
// 根据加载模式决定
switch (LyraGameplayCueManagerCvars::LoadMode)
{
case ELyraEditorLoadMode::LoadUpfront:
return; // 预先加载模式:不处理
case ELyraEditorLoadMode::PreloadAsCuesAreReferenced_GameOnly:
#if WITH_EDITOR
// 在编辑器中不处理
if (GIsEditor)
{
return;
}
#endif
break;
case ELyraEditorLoadMode::PreloadAsCuesAreReferenced:
break;
}
// 检查提示集有效性
check(RuntimeGameplayCueObjectLibrary.CueSet);
// 查找标签在提示数据映射中的索引
int32* DataIdx = RuntimeGameplayCueObjectLibrary.CueSet->GameplayCueDataMap.Find(Tag);
if (DataIdx && RuntimeGameplayCueObjectLibrary.CueSet->GameplayCueData.IsValidIndex(*DataIdx))
{
// 获取提示通知数据
const FGameplayCueNotifyData& CueData = RuntimeGameplayCueObjectLibrary.CueSet->GameplayCueData[*DataIdx];
// 查找已加载的游戏提示类
UClass* LoadedGameplayCueClass = FindObject<UClass>(nullptr, *CueData.GameplayCueNotifyObj.ToString());
if (LoadedGameplayCueClass)
{
// 注册预加载的提示
RegisterPreloadedCue(LoadedGameplayCueClass, OwningObject);
}
else
{
// 确定是否始终加载提示
bool bAlwaysLoadedCue = OwningObject == nullptr;
TWeakObjectPtr<UObject> WeakOwner = OwningObject;
// 请求异步加载
StreamableManager.RequestAsyncLoad(CueData.GameplayCueNotifyObj,
FStreamableDelegate::CreateUObject(this, &ThisClass::OnPreloadCueComplete, CueData.GameplayCueNotifyObj, WeakOwner, bAlwaysLoadedCue),
FStreamableManager::DefaultAsyncLoadPriority, false, false, TEXT("GameplayCueManager"));
}
}
}
// 预加载提示完成回调
void ULyraGameplayCueManager::OnPreloadCueComplete(FSoftObjectPath Path, TWeakObjectPtr<UObject> OwningObject, bool bAlwaysLoadedCue)
{
// 检查所有者是否有效或是始终加载的提示
if (bAlwaysLoadedCue || OwningObject.IsValid())
{
// 解析对象路径为类
if (UClass* LoadedGameplayCueClass = Cast<UClass>(Path.ResolveObject()))
{
// 注册预加载的提示
RegisterPreloadedCue(LoadedGameplayCueClass, OwningObject.Get());
}
}
}
// 注册预加载的提示
void ULyraGameplayCueManager::RegisterPreloadedCue(UClass* LoadedGameplayCueClass, UObject* OwningObject)
{
// 检查类有效性
check(LoadedGameplayCueClass);
// 确定是否始终加载的提示
const bool bAlwaysLoadedCue = OwningObject == nullptr;
if (bAlwaysLoadedCue)
{
// 添加到始终加载列表,从预加载列表移除
AlwaysLoadedCues.Add(LoadedGameplayCueClass);
PreloadedCues.Remove(LoadedGameplayCueClass);
PreloadedCueReferencers.Remove(LoadedGameplayCueClass);
}
// 检查所有者是否有效且不是提示类自身,且不在始终加载列表中
else if ((OwningObject != LoadedGameplayCueClass) && (OwningObject != LoadedGameplayCueClass->GetDefaultObject()) && !AlwaysLoadedCues.Contains(LoadedGameplayCueClass))
{
// 添加到预加载列表
PreloadedCues.Add(LoadedGameplayCueClass);
// 添加或查找引用者集合
TSet<FObjectKey>& ReferencerSet = PreloadedCueReferencers.FindOrAdd(LoadedGameplayCueClass);
ReferencerSet.Add(OwningObject); // 添加所有者引用
}
}
// 地图加载后处理
void ULyraGameplayCueManager::HandlePostLoadMap(UWorld* NewWorld)
{
// 检查提示集有效性
if (RuntimeGameplayCueObjectLibrary.CueSet)
{
// 从提示集中移除始终加载的提示类
for (UClass* CueClass : AlwaysLoadedCues)
{
RuntimeGameplayCueObjectLibrary.CueSet->RemoveLoadedClass(CueClass);
}
// 从提示集中移除预加载的提示类
for (UClass* CueClass : PreloadedCues)
{
RuntimeGameplayCueObjectLibrary.CueSet->RemoveLoadedClass(CueClass);
}
}
// 清理预加载的提示引用
for (auto CueIt = PreloadedCues.CreateIterator(); CueIt; ++CueIt)
{
// 获取引用者集合
TSet<FObjectKey>& ReferencerSet = PreloadedCueReferencers.FindChecked(*CueIt);
// 移除无效的引用者
for (auto RefIt = ReferencerSet.CreateIterator(); RefIt; ++RefIt)
{
if (!RefIt->ResolveObjectPtr())
{
RefIt.RemoveCurrent();
}
}
// 如果没有引用者,移除提示
if (ReferencerSet.Num() == 0)
{
PreloadedCueReferencers.Remove(*CueIt);
CueIt.RemoveCurrent();
}
}
}
// 更新延迟加载委托监听器
void ULyraGameplayCueManager::UpdateDelayLoadDelegateListeners()
{
// 移除所有现有的委托
UGameplayTagsManager::Get().OnGameplayTagLoadedDelegate.RemoveAll(this);
FCoreUObjectDelegates::GetPostGarbageCollect().RemoveAll(this);
FCoreUObjectDelegates::PostLoadMapWithWorld.RemoveAll(this);
// 根据加载模式决定
switch (LyraGameplayCueManagerCvars::LoadMode)
{
case ELyraEditorLoadMode::LoadUpfront:
return; // 预先加载模式:不添加监听器
case ELyraEditorLoadMode::PreloadAsCuesAreReferenced_GameOnly:
#if WITH_EDITOR
// 在编辑器中不添加监听器
if (GIsEditor)
{
return;
}
#endif
break;
case ELyraEditorLoadMode::PreloadAsCuesAreReferenced:
break;
}
// 添加游戏标签加载委托
UGameplayTagsManager::Get().OnGameplayTagLoadedDelegate.AddUObject(this, &ThisClass::OnGameplayTagLoaded);
// 添加垃圾回收后委托
FCoreUObjectDelegates::GetPostGarbageCollect().AddUObject(this, &ThisClass::HandlePostGarbageCollect);
// 添加地图加载后委托
FCoreUObjectDelegates::PostLoadMapWithWorld.AddUObject(this, &ThisClass::HandlePostLoadMap);
}
// 是否延迟加载游戏提示
bool ULyraGameplayCueManager::ShouldDelayLoadGameplayCues() const
{
// 客户端延迟加载游戏提示
const bool bClientDelayLoadGameplayCues = true;
// 如果不是专用服务器且启用客户端延迟加载,则延迟加载
return !IsRunningDedicatedServer() && bClientDelayLoadGameplayCues;
}
// 主资源类型和名称常量
const FPrimaryAssetType UFortAssetManager_GameplayCueRefsType = TEXT("GameplayCueRefs");
const FName UFortAssetManager_GameplayCueRefsName = TEXT("GameplayCueReferences");
const FName UFortAssetManager_LoadStateClient = FName(TEXT("Client"));
// 刷新游戏提示主资源
void ULyraGameplayCueManager::RefreshGameplayCuePrimaryAsset()
{
TArray<FSoftObjectPath> CuePaths;
// 获取运行时提示集
UGameplayCueSet* RuntimeGameplayCueSet = GetRuntimeCueSet();
if (RuntimeGameplayCueSet)
{
// 获取所有软对象路径
RuntimeGameplayCueSet->GetSoftObjectPaths(CuePaths);
}
// 创建资源包数据
FAssetBundleData BundleData;
BundleData.AddBundleAssetsTruncated(UFortAssetManager_LoadStateClient, CuePaths);
// 创建主资源ID
FPrimaryAssetId PrimaryAssetId = FPrimaryAssetId(UFortAssetManager_GameplayCueRefsType, UFortAssetManager_GameplayCueRefsName);
// 添加动态资源
UAssetManager::Get().AddDynamicAsset(PrimaryAssetId, FSoftObjectPath(), BundleData);
}
🎯 核心架构总结
1. 效果上下文扩展系统
-
FLyraGameplayEffectContext:扩展基础效果上下文,添加游戏特定数据 -
弹药ID跟踪:支持同一弹药的多发子弹识别
-
能力源接口集成:统一的能力计算源管理
2. 目标数据增强
-
FLyraGameplayAbilityTargetData_SingleTargetHit:增强单目标命中数据 -
网络序列化支持:确保数据在网络间正确传输
-
上下文数据集成:与效果上下文无缝配合
3. 游戏提示优化管理
-
ULyraGameplayCueManager:智能的游戏提示加载和管理 -
多模式加载策略:支持预先加载、引用时预加载等模式
-
内存优化:通过延迟加载和引用计数减少内存占用
4. 能力集批量管理
-
ULyraAbilitySet:统一的能力、效果、属性集授予系统 -
句柄跟踪:支持精确的授予和收回操作
-
数据资产驱动:通过配置数据定义能力组合
5. 标签关系映射
-
ULyraAbilityTagRelationshipMapping:定义能力间的交互规则 -
阻塞和取消关系:控制能力的互斥行为
-
激活需求管理:动态调整能力的激活条件
6. 全局系统配置
-
ULyraAbilitySystemGlobals:游戏特定的全局配置 -
自定义效果上下文:确保使用Lyra特定的上下文类型
7. 能力源接口
-
ILyraAbilitySourceInterface:统一的能力计算源标准 -
距离衰减计算:支持基于距离的效果强度变化
-
物理材质影响:考虑物理材质对效果的影响
这个完整的架构提供了高度模块化、可配置和优化的能力系统,支持复杂的游戏机制和性能要求。
6610

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



