跳到主要内容
信息
文章中可能会出现一些错误,希望大佬们可以在评论区指出错误,感谢支持!

02 - GameplayTag

本文主要说明了UE5中有关 GameplayTag 的概念和基本用法。GameplayTag可用于给Actor分类,并据此进行快速筛选。

基于FName的Tag

该Tag和GameplayTag不同,前者只是FName类,后者则是进行一定封装的结构体。可以在编辑器中设置值,然后使用如GetAllActorsOfClassWithTag()等方法筛选特定的Actor

GameplayTag

GameplayTag是用户定义的字符串,给游戏对象提供概念性的分层标签。可以使用GameplayTag表达许多不同的概念,例如:

  • 对象的属性Character.Ememy.Zombie
  • 对象在执行或能够执行的事情Movement.Mode.Swimming
  • 游戏事件和触发器GameplayEvent.RequestReset

定义

可以通过三种方式添加(或删除)标签:

  • 项目设置(Project Settings) 中添加或删除;
  • 数据表格(Data Table) 资产导入;
  • 使用 C++ 定义;

项目设置

这是最简单手动添加GameplayTag的方式,需要执行如下操作:

  1. 启用 从配置导入标签(Import Tags From Config) 。这会导入 .ini 文件中的所有Gameplay标签,包括 Config/DefaultGameplayTags.ini 以及 Config/Tags 中的所有标签。
  2. (可选)点击 添加新Gameplay标签源(Add new Gameplay Tag source) 按钮,在 Config/Tags 中创建新的源 .ini 文件来存储Gameplay标签。为项目的各个方面创建单独的源文件,可能对于大型项目的组织和协作很有用。
  3. 点击 Gameplay标签列表(Gameplay Tag List) 条目旁边的 管理Gameplay标签(Manage Gameplay Tags) 按钮。这会打开 Gameplay标签管理器(Gameplay Tag Manager) 窗口。
  4. Gameplay标签管理器(Gameplay Tag Manager) 窗口中,点击左上角的 添加(Add (+)) 按钮。
  5. 输入所需的 名称(Name)注释(Comment)源(Source) 。注释显示在标签的提示文本上,源是存储标签的 .ini 文件。
  6. 点击 添加新标签(Add New Tag) 按钮。
备注

该方法通过修改.ini文件实现GameplayTag的配置,也可以直接修改它并重启编辑器完成配置。

数据表格

可以使用行类型GameplayTagTableRow从数据表中导入Gameplay标签,这需要:

  1. 点击 Gameplay标签表列表(Gameplay Tag Table List) 旁边的 添加元素(Add Element (+)) 按钮。
  2. 点击新索引的下拉菜单并选择你的数据表。

使用该方法的好处为:

  • 数据表编辑器(Data Table Editor) 中管理标签。
  • 在编辑器运行期间更改数据表。
  • 通过将 .csv.json 文件作为数据表导入来添加标签。

C++

可使用NativeGameplayTags.h提供的宏定义Gameplay标签:

  • UE_DECLARE_GAMEPLAY_TAG_EXTERN :在 .h 文件中用于声明 .cpp 文件中定义的标签。
  • UE_DEFINE_GAMEPLAY_TAG :在 .cpp 文件中用于定义 .h 文件中声明的标签,不带提示文本注释。
  • UE_DEFINE_GAMEPLAY_TAG_COMMENT :在 .cpp 文件中用于定义 .h 文件中声明的标签,带有提示文本注释。
  • UE_DEFINE_GAMEPLAY_TAG_STATIC :在 .cpp 文件中用于定义仅对定义文件可用的标签。不同于其他 DEFINE 宏,这不应该与 DECLARE 宏调用配对。
提示

要想在C++中使用此功能,需要在Build.cs中添加GameplayTags模块。

示例程序如下:

MyTagType.h
#include "NativeGameplayTags.h"

// 声明GameplayTag
UE_DECLARE_GAMEPLAY_TAG_EXTERN(My_Mode_Coding);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(My_Mode_Working);
MyTagType.cpp
#include "MyTagType.h"

// 定义GameplayTag (给外部使用)
UE_DEFINE_GAMEPLAY_TAG_COMMENT(My_Mode_Coding, "My.Mode.Coding", "Tag for coding");
UE_DEFINE_GAMEPLAY_TAG_COMMENT(My_Mode_Working, "My.Mode.Working", "Tag for working");
MyActor.h
#include "MyTagType.h"

// 仅对本文件可用的GameplayTag
UE_DEFINE_GAMEPLAY_TAG_STATIC(My_Mode_Idle, "My.Mode.Idle");

然后就能在项目设置中的 Gameplay标签列表 查看新添加的GameplayTag了。

使用

定义好GameplayTag后,就能将其应用于游戏对象,并进行一些标签求值操作了。

应用于对象

要想让游戏对象使用刚刚定义的GameplayTag,需要执行如下操作:

  1. GameplayTag容器FGameplayTagContainer类型变量添加到对象,该变量用于存储多个GameplayTag
  2. 使用AddTag()RemoveTag()AppendTag()等函数管理GameplayTag

示例程序如下:

MyActor.h
// ...
public:
// 声明GameplayTag容器
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FGameplayTagContainer MyTagContainer;
MyActor.cpp
void AMyActor::BeginPlay()
{
Super::BeginPlay();

// 添加GameplayTag
// RequestGameplayTag()的使用前提是, 该tag必须是已知存在的
MyTagContainer.AddTag(FGameplayTag::RequestGameplayTag(TEXT("My.Mode.Coding")));
MyTagContainer.AddTag(My_Mode_Idle);
}

使用条件函数对标签求值

接下来就能通过标签容器进行简单的筛选操作,FGameplayTagContainer提供如下条件函数:

  • 有标签HasTag()
  • 有任何标签HasAny()
  • 有所有标签HasAll()
高级查询 FGameplayTagQuery

还能通过创建FGameplayTagQuery类型变量进行高级查询,它组合了条件函数,以更直白精简的方式建立复杂逻辑。

首先是子表达式:

  • 任何标签匹配(Any Tags Match) :测试是否能在容器中发现查询中的至少一个标签。
  • 所有标签匹配(All Tags Match) :测试查询中的所有标签是否都在容器中。如果查询为空,这会返回true。
  • 无标签匹配(No Tags Match) :测试查询中的所有标签是否都不在容器中。如果查询为空,这会返回true。

然后是在子表达式组合求值之上的根表达式:

  • 任何表达式匹配(Any Expressions Match) :测试是否有任何子表达式返回true。
  • 所有表达式匹配(All Expressions Match) :测试是否所有子表达式都返回true。如果没有子表达式,这会返回true。
  • 无表达式匹配(No Expressions Match) :测试是否没有子表达式返回true。如果没有子表达式,这会返回true。

高级话题

设置标签编辑限制

在团队协作时,可通过在项目设置的高级 Gameplay 标签中对一些标签的配置文件或列表进行编辑限制。

简化C++中的标签访问

可以使用IGameplayTagAssetInterface改进你的Gameplay标签实现。该接口提供了以下优势:

  • 你不用显式将对象转型就可以获取对象的标签。
  • 你可以为每种可能的类型编写自定义代码。

实现该接口并重载GetOwnedGameplayTags()函数,就能创建一种能够被蓝图访问的方法,来为Gameplay标签容器填充与该对象关联的标签。在大部分情况下,这意味着将基类中的标签复制到新容器中,但你的实现可以从多个容器收集标签,或调用蓝图函数以访问蓝图声明的标签或你的对象需要的任意内容。

示例代码如下:

MyActor.h
// ...
#include "GameplayTagAssetInterface.h"
// ...

UCLASS()
class MYDEMO_API AMyActor : public AActor, public IGameplayTagAssetInterface
{
// ...
protected:
void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override;
}
MyActor.cpp
void AMyActor::GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const
{
TagContainer.AppendTags(MyTagContainer);
// 还可添加其他TagContainer...
}

参考资料