Lyra源码分析003:AbilitySystem文件夹

LyraStarterGame/Source/LyraGame/AbilitySystem

AbilitySystem文件夹目录:

这些文件展示了Lyra游戏能力系统的核心架构:

🎯 核心架构总结

  1. 标签化系统LyraTaggedActor 提供统一的标签管理接口

  2. 全局能力管理LyraGlobalAbilitySystem 实现跨ASC的能力和效果管理

  3. 效果上下文扩展LyraGameplayEffectContext 添加游戏特定的上下文数据

  4. 能力提示管理LyraGameplayCueManager 优化游戏提示的加载和管理

  5. 目标数据扩展LyraGameplayAbilityTargetData_SingleTargetHit 增强目标数据

  6. 标签关系映射LyraAbilityTagRelationshipMapping 定义能力间的阻塞和取消关系

  7. 全局系统配置LyraAbilitySystemGlobals 配置全局能力系统行为

  8. 能力组件扩展LyraAbilitySystemComponent 增强标准ASC功能

  9. 能力源接口LyraAbilitySourceInterface 定义能力计算源接口

  10. 能力集管理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:统一的能力计算源标准

  • 距离衰减计算:支持基于距离的效果强度变化

  • 物理材质影响:考虑物理材质对效果的影响

这个完整的架构提供了高度模块化、可配置和优化的能力系统,支持复杂的游戏机制和性能要求。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值