// Copyright Epic Games, Inc. All Rights Reserved.
// 版权所有 (c) Epic Games, Inc. 保留所有权利。
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "UObject/SoftObjectPath.h"
#include "GameplayTagContainer.h"
#include "Engine/DataAsset.h"
#include "GameplayEffectTypes.h"
#include "GameplayPrediction.h"
#include "Engine/World.h"
#include "AssetRegistry/AssetData.h"
#include "Engine/StreamableManager.h"
#include "GameplayCue_Types.h"
#include "GameplayCueTranslator.h"
#include "GameplayCueManager.generated.h"
// 定义调试宏,如果为 1 则开启调试功能
#define GAMEPLAYCUE_DEBUG 0
// 前置声明
class AGameplayCueNotify_Actor;
class UAbilitySystemComponent;
class UObjectLibrary;
// 声明委托,当游戏玩法提示通知集加载完成时调用
DECLARE_DELEGATE_OneParam(FOnGameplayCueNotifySetLoaded, TArray<FSoftObjectPath>);
// 声明委托,用于游戏玩法提示代理的更新
DECLARE_DELEGATE_OneParam(FGameplayCueProxyTick, float);
// 声明委托,用于判断是否应该加载游戏玩法提示通知
DECLARE_DELEGATE_RetVal_TwoParams(bool, FShouldLoadGCNotifyDelegate, const FAssetData&, FName);
/** Options to specify what parts of gameplay cue execution should be skipped */
/** 指定游戏玩法提示执行过程中应跳过哪些部分的选项 */
enum class EGameplayCueExecutionOptions : int32
{
// Default options, check everything
// 默认选项,检查所有内容
Default = 0,
// Skip gameplay cue interface check
// 跳过游戏玩法提示接口检查
IgnoreInterfaces = 0x00000001,
// Skip spawning notifies
// 跳过生成通知
IgnoreNotifies = 0x00000002,
// Skip tag translation step
// 跳过标签翻译步骤
IgnoreTranslation = 0x00000004,
// Ignores suppression check, always spawns
// 忽略抑制检查,总是生成
IgnoreSuppression = 0x00000008,
// Don't show debug visualizations
// 不显示调试可视化信息
IgnoreDebug = 0x00000010
};
// 为枚举类添加位操作标志
ENUM_CLASS_FLAGS(EGameplayCueExecutionOptions);
/** An ObjectLibrary for the GameplayCue Notifies. Wraps 2 underlying UObjectLibraries plus options/delegates for how they are loaded */
/** 用于游戏玩法提示通知的对象库。包装了两个底层的 UObjectLibrary,并包含加载它们的选项和委托 */
USTRUCT()
struct FGameplayCueObjectLibrary
{
GENERATED_BODY()
FGameplayCueObjectLibrary()
: ActorObjectLibrary(nullptr)
, StaticObjectLibrary(nullptr)
, CueSet(nullptr)
, AsyncPriority(0)
, bShouldSyncScan(false)
, bShouldAsyncLoad(false)
, bShouldSyncLoad(false)
, bHasBeenInitialized(false)
{
}
/** Paths to search for */
/** 要搜索的路径 */
UPROPERTY()
TArray<FString> Paths;
/** Callback for when load finishes */
/** 加载完成时的回调 */
FOnGameplayCueNotifySetLoaded OnLoaded;
/** Callback for "should I add this FAssetData to the set" */
/** 判断是否应将此 FAssetData 添加到集合中的回调 */
FShouldLoadGCNotifyDelegate ShouldLoad;
/** Object library for actor based notifies */
/** 基于 Actor 的通知的对象库 */
UPROPERTY()
TObjectPtr<UObjectLibrary> ActorObjectLibrary;
/** Object library for object based notifies */
/** 基于对象的通知的对象库 */
UPROPERTY()
TObjectPtr<UObjectLibrary> StaticObjectLibrary;
/** Set to put the loaded asset data into. If null we will use the global set (RuntimeGameplayCueObjectLibrary.CueSet) */
/** 用于存放加载的资产数据的集合。如果为空,则使用全局集合(RuntimeGameplayCueObjectLibrary.CueSet) */
UPROPERTY()
TObjectPtr<UGameplayCueSet> CueSet;
/** Priority to use if async loading */
/** 异步加载时使用的优先级 */
TAsyncLoadPriority AsyncPriority;
/** Should we force a sync scan on the asset registry in order to discover asset data, or just use what is there */
/** 是否应该对资产注册表进行同步扫描以发现资产数据,还是仅使用现有的数据 */
UPROPERTY()
bool bShouldSyncScan;
/** Should we start async loading everything that we find (that passes ShouldLoad delegate check) */
/** 是否应该开始异步加载我们找到的所有内容(通过 ShouldLoad 委托检查的内容) */
UPROPERTY()
bool bShouldAsyncLoad;
/** Should we sync load everything that we find (that passes ShouldLoad delegate check) */
/** 是否应该同步加载我们找到的所有内容(通过 ShouldLoad 委托检查的内容) */
UPROPERTY()
bool bShouldSyncLoad;
/** True if this has been initialized with correct data */
/** 如果已使用正确的数据进行初始化,则为 true */
UPROPERTY()
bool bHasBeenInitialized;
};
/** Singleton manager object that handles dispatching gameplay cues and spawning GameplayCueNotify actors as needed */
/** 单例管理器对象,用于处理游戏玩法提示的分发,并在需要时生成 GameplayCueNotify 演员 */
UCLASS()
class GAMEPLAYABILITIES_API UGameplayCueManager : public UDataAsset
{
GENERATED_UCLASS_BODY()
// -------------------------------------------------------------
// Wrappers to handle replicating executed cues
// -------------------------------------------------------------
// 处理复制已执行提示的包装器
virtual void InvokeGameplayCueExecuted_FromSpec(UAbilitySystemComponent* OwningComponent, const FGameplayEffectSpec& Spec, FPredictionKey PredictionKey);
virtual void InvokeGameplayCueExecuted(UAbilitySystemComponent* OwningComponent, const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayEffectContextHandle EffectContext);
virtual void InvokeGameplayCueExecuted_WithParams(UAbilitySystemComponent* OwningComponent, const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters);
virtual void InvokeGameplayCueAddedAndWhileActive_FromSpec(UAbilitySystemComponent* OwningComponent, const FGameplayEffectSpec& Spec, FPredictionKey PredictionKey);
/** Start or stop a gameplay cue send context. Used by FScopedGameplayCueSendContext above, when all contexts are removed the cues are flushed */
/** 开始或停止游戏玩法提示发送上下文。由上面的 FScopedGameplayCueSendContext 使用,当所有上下文都被移除时,提示将被刷新 */
void StartGameplayCueSendContext();
void EndGameplayCueSendContext();
/** Send out any pending cues */
/** 发送任何待处理的提示 */
virtual void FlushPendingCues();
/** Broadcasted when ::FlushPendingCues runs: useful for custom batching/gameplay cue handling */
/** 当 ::FlushPendingCues 运行时广播:对于自定义批处理/游戏玩法提示处理很有用 */
FSimpleMulticastDelegate OnFlushPendingCues;
/** Called when manager is first created */
/** 管理器首次创建时调用 */
virtual void OnCreated();
/** Called when engine has completely loaded, this is a good time to finalize things */
/** 引擎完全加载完成后调用,这是完成最终设置的好时机 */
virtual void OnEngineInitComplete();
/** Process a pending cue, return false if the cue should be rejected. */
/** 处理一个待处理的提示,如果该提示应被拒绝,则返回 false。 */
virtual bool ProcessPendingCueExecute(FGameplayCuePendingExecute& PendingCue);
/** Returns true if two pending cues match, can be overridden in game */
/** 如果两个待处理的提示匹配,则返回 true,可以在游戏中重写此方法 */
virtual bool DoesPendingCueExecuteMatch(FGameplayCuePendingExecute& PendingCue, FGameplayCuePendingExecute& ExistingCue);
// -------------------------------------------------------------
// Handling GameplayCues at runtime:
// -------------------------------------------------------------
// 运行时处理游戏玩法提示:
/** Main entry point for handling a gameplaycue event. These functions will call the 3 functions below to handle gameplay cues */
/** 处理游戏玩法提示事件的主要入口点。这些函数将调用下面的 3 个函数来处理游戏玩法提示 */
virtual void HandleGameplayCues(AActor* TargetActor, const FGameplayTagContainer& GameplayCueTags, EGameplayCueEvent::Type EventType, const FGameplayCueParameters& Parameters, EGameplayCueExecutionOptions Options = EGameplayCueExecutionOptions::Default);
virtual void HandleGameplayCue(AActor* TargetActor, FGameplayTag GameplayCueTag, EGameplayCueEvent::Type EventType, const FGameplayCueParameters& Parameters, EGameplayCueExecutionOptions Options = EGameplayCueExecutionOptions::Default);
/** 1. returns true to ignore gameplay cues */
/** 1. 如果返回 true,则忽略游戏玩法提示 */
virtual bool ShouldSuppressGameplayCues(AActor* TargetActor);
/** 2. Allows Tag to be translated in place to a different Tag. See FGameplayCueTranslorManager */
/** 2. 允许将标签就地转换为不同的标签。请参阅 FGameplayCueTranslorManager */
void TranslateGameplayCue(FGameplayTag& Tag, AActor* TargetActor, const FGameplayCueParameters& Parameters);
/** 3. Actually routes the gameplaycue event to the right place. */
/** 3. 实际将游戏玩法提示事件路由到正确的位置。 */
virtual void RouteGameplayCue(AActor* TargetActor, FGameplayTag GameplayCueTag, EGameplayCueEvent::Type EventType, const FGameplayCueParameters& Parameters, EGameplayCueExecutionOptions Options = EGameplayCueExecutionOptions::Default);
/**
* Convenience methods for invoking non-replicated gameplay cue events.
*
* We want to avoid exposing designers the choice of "is this gameplay cue replicated or non-replicated?".
* We want to make the decision for them in most cases:
* - Abilities will always use replicated GameplayCue events because they are not executed on simulated proxies.
* - Animations always use non-replicated GameplayCue events because they are always executed on simulated proxies.
*
* Sometimes it will be useful to give designers both options: in actor classes where there are many possible use cases.
* Still, we should keep the choice confined to the actor class, and not globally. E.g., Don't add both choices to the function library
* since they would appear everywhere. Add the choices to the actor class so they only appear there.
*/
/**
* 用于调用非复制游戏玩法提示事件的便捷方法。
*
* 我们希望避免让设计师选择“这个游戏玩法提示是复制的还是非复制的?”。
* 在大多数情况下,我们希望为他们做出决定:
* - 技能将始终使用复制的 GameplayCue 事件,因为它们不会在模拟代理上执行。
* - 动画始终使用非复制的 GameplayCue 事件,因为它们总是在模拟代理上执行。
*
* 有时,为设计师提供两种选择会很有用:在有许多可能用例的 Actor 类中。
* 不过,我们应该将选择限制在 Actor 类中,而不是全局范围。例如,不要将这两种选择添加到函数库中,
* 因为它们会出现在所有地方。将选择添加到 Actor 类中,这样它们只会出现在那里。
*/
static void AddGameplayCue_NonReplicated(AActor* Target, const FGameplayTag GameplayCueTag, const FGameplayCueParameters& Parameters);
static void RemoveGameplayCue_NonReplicated(AActor* Target, const FGameplayTag GameplayCueTag, const FGameplayCueParameters& Parameters);
static void ExecuteGameplayCue_NonReplicated(AActor* Target, const FGameplayTag GameplayCueTag, const FGameplayCueParameters& Parameters);
// -------------------------------------------------------------
/** Force any instanced GameplayCueNotifies to stop */
/** 强制任何实例化的 GameplayCueNotifies 停止 */
virtual void EndGameplayCuesFor(AActor* TargetActor);
/** Returns the cached instance cue. Creates it if it doesn't exist. */
/** 返回缓存的实例提示。如果不存在,则创建它。 */
virtual AGameplayCueNotify_Actor* GetInstancedCueActor(AActor* TargetActor, UClass* GameplayCueNotifyActorClass, const FGameplayCueParameters& Parameters);
/** Notify that this actor is finished and should be destroyed or recycled */
/** 通知该 Actor 已完成,应被销毁或回收 */
virtual void NotifyGameplayCueActorFinished(AGameplayCueNotify_Actor* Actor);
/** Notify to say the actor is about to be destroyed and the GC manager needs to remove references to it. This should not happen in normal play with recycling enabled, but could happen in replays. */
/** 通知该 Actor 即将被销毁,GC 管理器需要移除对它的引用。在启用回收功能的正常游戏中不应发生这种情况,但在回放中可能会发生。 */
virtual void NotifyGameplayCueActorEndPlay(AGameplayCueNotify_Actor* Actor);
/** Resets preallocation for a given world */
/** 重置给定世界的预分配 */
void ResetPreallocation(UWorld* World);
/** Prespawns a single actor for gameplaycue notify actor classes that need prespawning (should be called by outside gamecode, such as gamestate) */
/** 为需要预生成的游戏玩法提示通知 Actor 类预生成一个 Actor(应由外部游戏代码调用,如游戏状态) */
void UpdatePreallocation(UWorld* World);
void OnPostWorldCleanup(UWorld* World, bool bSessionEnded, bool bCleanupResources);
void OnPreReplayScrub(UWorld* World);
/** Prints what classes exceeded their preallocation sizes during runtime */
/** 打印在运行时哪些类超出了它们的预分配大小 */
void DumpPreallocationStats(const FPreallocationInfo& PreallocationInfo, bool bWarnOnActiveActors = false);
// -------------------------------------------------------------
// Loading GameplayCueNotifies from ObjectLibraries
//
// There are two libraries in the GameplayCueManager:
// 1. The RunTime object library, which is initialized with the "always loaded" paths, via UAbilitySystemGlobals::GetGameplayCueNotifyPaths()
// -GC Notifies in this path are loaded at startup
// -Everything loaded will go into the global gameplaycue set, which is where GC events will be routed to by default
//
// 2. The Editor object library, which is initialized with the "all valid" paths, via UGameplayCueManager::GetValidGameplayCuePaths()
// -Only used in the editor.
// -Never loads clases or scans directly itself.
// -Just reflect asset registry to show about "all gameplay cue notifies the game could know about"
//
// -------------------------------------------------------------
// 从对象库加载游戏玩法提示通知
//
// GameplayCueManager 中有两个库:
// 1. 运行时对象库,通过 UAbilitySystemGlobals::GetGameplayCueNotifyPaths() 使用“始终加载”的路径进行初始化
// - 此路径中的 GC 通知在启动时加载
// - 加载的所有内容将进入全局游戏玩法提示集,默认情况下,GC 事件将路由到这里
//
// 2. 编辑器对象库,通过 UGameplayCueManager::GetValidGameplayCuePaths() 使用“所有有效”的路径进行初始化
// - 仅在编辑器中使用。
// - 从不直接加载类或进行扫描。
// - 仅反映资产注册表,以显示“游戏可能知道的所有游戏玩法提示通知”
//
/** Returns the Ryntime cueset, which is the global cueset used in runtime, as opposed to the editor cue set which is used only when running the editor */
/** 返回运行时提示集,这是运行时使用的全局提示集,与仅在运行编辑器时使用的编辑器提示集相反 */
UGameplayCueSet* GetRuntimeCueSet();
/** Called to setup and initialize the runtime library. The passed in paths will be scanned and added to the global gameplay cue set where appropriate */
/** 调用此方法