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的方式,需要执行如下操作:
- 启用 从配置导入标签(Import Tags From Config) 。这会导入
.ini文件中的所有Gameplay标签,包括Config/DefaultGameplayTags.ini以及Config/Tags中的所有标签。 - (可选)点击 添加新Gameplay标签源(Add new Gameplay Tag source) 按钮,在
Config/Tags中创建新的源.ini文件来存储Gameplay标签。为项目的各个方面创建单独的源文件,可能对于大型项目的组织和协作很有用。 - 点击 Gameplay标签列表(Gameplay Tag List) 条目旁边的 管理Gameplay标签(Manage Gameplay Tags) 按钮。这会打开 Gameplay标签管理器(Gameplay Tag Manager) 窗口。
- 在 Gameplay标签管理器(Gameplay Tag Manager) 窗口中,点击左上角的 添加(Add (+)) 按钮。
- 输入所需的 名称(Name) 、 注释(Comment) 和 源(Source) 。注释显示在标签的提示文本上,源是存储标签的
.ini文件。 - 点击 添加新标签(Add New Tag) 按钮。
该方法通过修改.ini文件实现GameplayTag的配置,也可以直接修改它并重启编辑器完成配置。
数据表格
可以使用行类型GameplayTagTableRow从数据表中导入Gameplay标签,这需要:
- 点击 Gameplay标签表列表(Gameplay Tag Table List) 旁边的 添加元素(Add Element (+)) 按钮。
- 点击新索引的下拉菜单并选择你的数据表。
使用该方法的好处为:
- 在 数据表编辑器(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模块。
示例程序如下:
#include "NativeGameplayTags.h"
// 声明GameplayTag
UE_DECLARE_GAMEPLAY_TAG_EXTERN(My_Mode_Coding);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(My_Mode_Working);
#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");
#include "MyTagType.h"
// 仅对本文件可用的GameplayTag
UE_DEFINE_GAMEPLAY_TAG_STATIC(My_Mode_Idle, "My.Mode.Idle");
然后就能在项目设置中的 Gameplay标签列表 查看新添加的GameplayTag了。
使用
定义好GameplayTag后,就能将其应用于游戏对象,并进行一些标签求值操作了。
应用于对象
要想让游戏对象使用刚刚定义的GameplayTag,需要执行如下操作:
- 将
GameplayTag容器FGameplayTagContainer类型变量添加到对象,该变量用于存储多个GameplayTag; - 使用
AddTag(),RemoveTag(),AppendTag()等函数管理GameplayTag;
示例程序如下:
// ...
public:
// 声明GameplayTag容器
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FGameplayTagContainer MyTagContainer;
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标签容器填充与该对象关联的标签。在大部分情况下,这意味着将基类中的标签复制到新容器中,但你的实现可以从多个容器收集标签,或调用蓝图函数以访问蓝图声明的标签或你的对象需要的任意内容。
示例代码如下:
// ...
#include "GameplayTagAssetInterface.h"
// ...
UCLASS()
class MYDEMO_API AMyActor : public AActor, public IGameplayTagAssetInterface
{
// ...
protected:
void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override;
}
void AMyActor::GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const
{
TagContainer.AppendTags(MyTagContainer);
// 还可添加其他TagContainer...
}