【UE5 C++课程系列笔记】12——Gameplay标签的基本使用

目录

概念

主要作用

[1 组织与分类游戏元素](#1 组织与分类游戏元素)

[1.1 驱动游戏逻辑](#1.1 驱动游戏逻辑)

[1.2 便于查询与筛选](#1.2 便于查询与筛选)

[2 助力网络同步与优化](#2 助力网络同步与优化)

定义Gameplay标签

[1 在项目设置中添加标签](#1 在项目设置中添加标签)

[2 从数据表资产导入标签](#2 从数据表资产导入标签)

[3 使用C++定义标签](#3 使用C++定义标签)

[3.1 UE_DECLARE_GAMEPLAY_TAG_EXTERN](#3.1 UE_DECLARE_GAMEPLAY_TAG_EXTERN)

[3.2 UE_DEFINE_GAMEPLAY_TAG](#3.2 UE_DEFINE_GAMEPLAY_TAG)

[3.3 UE_DEFINE_GAMEPLAY_TAG_COMMENT](#3.3 UE_DEFINE_GAMEPLAY_TAG_COMMENT)

[3.4 UE_DEFINE_GAMEPLAY_TAG_STATIC](#3.4 UE_DEFINE_GAMEPLAY_TAG_STATIC)

[*3.5 完整步骤](#*3.5 完整步骤)

使用Gameplay标签

[1 将标签应用于对象](#1 将标签应用于对象)

[2 使用条件函数对标签求值](#2 使用条件函数对标签求值)

[3 Gameplay标记查询](#3 Gameplay标记查询)

[4 在C++中使用Gameplay标签](#4 在C++中使用Gameplay标签)

[5 通过IGameplayTagAssetInterface访问Gameplay标签](#5 通过IGameplayTagAssetInterface访问Gameplay标签)


概念

Gameplay标签(分层标签系统) 是用户定义的字符串,充当概念性的分层标签。你可以将Gameplay标签应用于项目中的对象,并对其求值以驱动你的Gameplay实现,类似于检查布尔值或标记。Gameplay标签本质上是一种有层级结构的文本标识,通常以 "." 作为层级分隔符,形如 "Category.Subcategory.Item",这样的结构方便进行分类管理以及灵活的查询和匹配操作。

主要作用

1 组织与分类游戏元素

游戏中存在大量不同种类的对象、状态、行为等,比如角色的各种技能(攻击技能、防御技能、辅助技能等)、不同类型的道具(武器、防具、消耗品等)、场景中的各种交互元素(可破坏物、可触发机关等)。通过为它们赋予相应的 Gameplay 标签,可以清晰地将这些元素按照不同的逻辑范畴进行归类。例如,可以给所有治疗技能贴上 "Skill.Healing" 标签,给近战武器贴上 "Item.Weapon.Melee" 标签,方便开发人员从整体上把握游戏内容的构成。

1.1 驱动游戏逻辑

条件判断依据:在编写游戏逻辑时,常常需要根据不同的条件来决定是否执行某些操作。Gameplay 标签就可以作为很好的判断条件。例如,在一个角色扮演游戏中,技能释放逻辑可能会检查角色是否具有 "Status.Buff.Strength"(力量增益状态)标签,若有,则某个特定的强力攻击技能可以被释放;或者检查目标是否带有 "Status.Debuff.Vulnerable"(易伤状态)标签,来决定伤害计算的系数等。

触发事件响应:可以基于 Gameplay 标签的添加、移除等变化来触发相应的事件。比如当角色进入 "State.Invisible"(隐身状态)这个标签所代表的状态时,游戏中的敌人 AI 可以监听该标签变化事件,进而调整搜索策略或者进入警戒状态等,实现不同游戏系统之间的协同和交互。

1.2 便于查询与筛选

精确查找:开发人员可以根据具体的标签精确地找到特定的游戏元素。比如在一个复杂的道具系统中,要找到所有带有 "Item.Consumable.HealthPotion"(消耗品 - 生命药水)标签的道具,以便进行批量处理,如更新其属性、调整其在商店中的售价等。

模糊筛选:凭借标签的层级结构,还能进行模糊筛选。假设想获取所有属于 "Item.Weapon"(武器类物品)的元素,无论它是近战、远程还是其他细分类型的武器,都可以通过查找以该父标签开头的所有标签对应的元素来实现,这种方式极大地增强了对游戏内容管理和操作的灵活性。

2 助力网络同步与优化

在网络游戏中,为了减少不必要的数据传输量、节省网络带宽以及降低服务器和客户端的运算负载,Gameplay 标签发挥着重要作用。可以规定只有带有特定关键 Gameplay 标签的游戏数据才需要在网络间进行同步。例如,对于场景中那些带有 "Object.NetworkSync"(网络同步对象)标签的物体,才会将其位置、状态等关键信息发送给客户端,而其他无关紧要的物体信息则无需同步,以此提高网络传输的效率。

定义Gameplay标签

1 在项目设置中添加标签

在"项目设置-》GameplayTags"中点击"新增Gameplay标签源"

这里命名为"Test.ini"

点击"管理Gameplay标签"

在打开的"Gameplay标签管理器"界面中点击加号来新增Gameplay标签

编辑标签的命名、注释如下,设置源为刚创建的"Test.ini"

编辑好后点击"添加新标签"

添加好后可以在项目目录中找到"Test.ini"

打开后内容如下

在"Gameplay标签管理器"中可以对Gameplay标签进行添加子标签、重命名、删除等操作。

2 从数据表资产导入标签

添加一个数据表格

行结构选择"GameplayTagTableRow"

这里命名为"DT_TableTag"

打开"DT_TableTag",这里添加3行数据

对这3行数据的标签和注释进行编辑

在"项目设置-》GameplayTags"中,点击添加一个Gameplay标签列表元素

这里设置为刚创建的"DT_TableTag"

此时再次打开"Gameplay标签管理器"

可以看到"Gameplay标签管理器"界面中出现了在"DT_TableTag"中编辑的标签

3 使用C++定义标签

可以使用宏"UE_DECLARE_GAMEPLAY_TAG_EXTERN"、"UE_DEFINE_GAMEPLAY_TAG"、"UE_DEFINE_GAMEPLAY_TAG_COMMENT"、"UE_DEFINE_GAMEPLAY_TAG_STATIC"来定义Gameplay标签。

3.1 UE_DECLARE_GAMEPLAY_TAG_EXTERN

宏"UE_DECLARE_GAMEPLAY_TAG_EXTERN"用于在头文件中声明一个外部的 Gameplay 标签变量,通常在你想要在多个源文件中使用同一个 Gameplay 标签定义时使用。它只是声明了这个标签的存在,具体的定义会在其他源文件中通过对应的 UE_DEFINE_GAMEPLAY_TAG 等宏来完成,用法如下:

cpp 复制代码
// 例如,在某个头文件(比如 MyTags.h)中声明一个外部的 Gameplay 标签
UE_DECLARE_GAMEPLAY_TAG_EXTERN(TagName)  //TagName 是要声明的 Gameplay 标签的名称

3.2 UE_DEFINE_GAMEPLAY_TAG

用于定义一个 Gameplay 标签,将其注册到虚幻引擎的 Gameplay 标签系统中,使其能够在游戏中被识别和使用。它通常与 UE_DECLARE_GAMEPLAY_TAG_EXTERN 配合使用,先在头文件中声明,再在源文件中定义,不过也可以单独使用来直接定义一个标签,用法如下:

cpp 复制代码
// 在对应的源文件(比如 MyTags.cpp)中定义之前声明的 Gameplay 标签
UE_DEFINE_GAMEPLAY_TAG(TagName, "My.Gameplay.Tag")

3.3 UE_DEFINE_GAMEPLAY_TAG_COMMENT

该宏除了可定义 Gameplay 标签外,还允许为该标签添加注释说明。这个注释可以帮助其他开发人员更好地理解这个标签所代表的含义、用途等信息,对于代码的可读性和维护性很有帮助,尤其是在项目中有大量 Gameplay 标签存在的情况下,用法如下:

cpp 复制代码
UE_DEFINE_GAMEPLAY_TAG_COMMENT(TagName, "My.Gameplay.Tag", "This is a comment explaining the tag's purpose.")

3.4 UE_DEFINE_GAMEPLAY_TAG_STATIC

该宏用于静态地定义一个 Gameplay 标签,它与 UE_DEFINE_GAMEPLAY_TAG 的主要区别在于其定义的标签具有静态存储特性,适用于一些特定的场景,比如当你希望这个标签只在某个特定的编译单元内有效,或者不想让它参与到一些动态的 Gameplay 标签管理过程中(例如避免被某些全局的标签查找、修改机制影响等),用法如下:

cpp 复制代码
UE_DEFINE_GAMEPLAY_TAG_STATIC(TagName, "My.Gameplay.Tag")

*3.5 完整步骤

首先必须将 GameplayTags 模块添加到项目的 Build.cs 文件,才能在C++中访问Gameplay标签功能,如下所示:

新建一个C++类,这里命名为"TagType",用于定义所有的Gamelay标签

再新建一个Actor类,这里命名为"TagActor",用于使用Gamelay标签

在"TagType.h"中使用 NativeGameplayTags.h

继续声明Gameplay标签变量

在"TagType.cpp"中定义Gameplay 标签

在"TagActor.h"中导入"TagType.h",注意要写在"#include "TagActor.generated.h""之前

然后我们在"TagActor.cpp"中使用UE_DEFINE_GAMEPLAY_TAG_STATIC宏 定义一个 Gameplay 标签,如下:

编译后,在"Gameplay标签管理器"中可以看到添加的Gameplay标签如下:

使用Gameplay标签

1 将标签应用于对象

2 使用条件函数对标签求值

在"Gameplay标签管理器"中先添加新标签

接下来去使用Gameplay标签,这里通过关卡蓝图来测试使用。打开关卡蓝图,添加一个变量,这里命名为"TestTagContainer"

变量类型为"Gameplay标签容器"

首先通过"Has Tag"节点来判断"TestTagContainer"是否含有"Test.Two"标签,勾选"Exact Match"表示精确匹配。

判断 "TestTagContainer"是否同时含有"Test.One"和"Test.Three"标签

两个条件必需同时为false结果才是true

"Test.One"和"Test.Three"标签是否存在任意一个。只有当不存在"Test.Two"标签,不同时存在"Test.One"和"Test.Three"标签,并且"Test.One"和"Test.Three"标签至少存在一个的情况下,最终结果才为true。

设置 "TestTagContainer"默认值

运行测试,打印结果为true

3 Gameplay标记查询

Gameplay 标记查询主要通过FGameplayTagQuery来实现,FGameplayTagQuery是一种逻辑查询,用于针对FGameplayTagContainer进行查询操作,查询成功则称 "匹配"。查询是逻辑表达式,可测试标签容器的交集属性或子表达式的匹配状态,具有递归性和表达性。

接下来使用"Gameplay标记查询"的方式实现:只有当不存在"Test.Two"标签,不同时存在"Test.One"和"Test.Three"标签,并且"Test.One"和"Test.Three"标签至少存在一个的情况下,最终结果才为true的逻辑。

首先添加"Does Container Match Tag Query" 节点来判断标签容器是否与标签查询匹配,若匹配则返回true。

编辑标签查询条件

根表达式设置为所有表达式匹配

添加第1个表达式为"任意标签匹配",设置只要标签"Test.One"和"Test.Three"有一个匹配,则第1个表达式返回true

添加第2个表达式为"无表达式匹配",设置不能同时存在"Test.One"和"Test.Three"标签,也不能存在"Test.Two"

测试一下,当"TestTagContainer"只包含标签"Test.One"或"Test.Three"时,运行结果为true,除此外,运行结果为false。

4 在C++中使用Gameplay标签

在3.5小节中我们已经使用C++定义了3个Gameplay标签,现在我们继续使用它。

首先在"TagActor.h"中定义一个Gameplay标签容器,这里命名为"MyTagContainer"

在"TagActor.cpp"中,让"MyTagContainer"在BeginPlay后添加一个标签

编译后,创建一个派生自"TagActor"的蓝图类"BP_TagActor"

将"BP_TagActor"拖入场景中,在运行前可以看到"MyTagContainer"不包含任何标签

当运行后可以看到"MyTagContainer"成功添加了标签"Mode.Idle"

接下来继续使用"MyTagContainer"做匹配查询,在如下代码中,我们判断"MyTagContainer"是否同时包含标签"Mode_Idle"和"Mode_Coding",若包含则输出日志。

5 通过IGameplayTagAssetInterface访问Gameplay标签

IGameplayTagAssetInterface 接口主要用于使实现该接口的类能够方便地与 Gameplay 标签进行交互,提供了统一的方式来管理和获取与这些资产相关联的 Gameplay 标签信息。通过实现这个接口,不同类型的游戏资产可以融入到基于 Gameplay 标签构建的游戏逻辑体系中,例如方便地进行标签查询、根据标签来驱动相关行为等。

接下来开始使用IGameplayTagAssetInterface 接口。先在"TagActor.h"中引入#include "GameplayTagAssetInterface.h",然后让TagActor继承IGameplayTagAssetInterface

重写IGameplayTagAssetInterface的GetOwnedGameplayTags方法

实现 GetOwnedGameplayTags方法如下,该方法将"MyTagContainer"中的所有 Gameplay 标签添加到"TagContainer"中

打开蓝图"BP_TagActor",在事件开始运行后,通过"Get Owned Gameplay Tags"节点来获取资产上拥有的Gameplay标签,获取Gameplay标签后判断TagContainer是否同时包含"Mode.Coding"和"Mode.Idle"标签,延迟0.2s是因为蓝图的BeginPlay通常比其C++父类的BeginPlay要早执行。

可以看到打印结果为true

我们还可以通过"Get Debug String from Gameplay Tag Container"节点来打印资产上所有拥有的Gameplay标签的名称。

相关推荐
北冥没有鱼啊15 小时前
UE 滚动提示条材质制作
游戏·ue5·游戏引擎·ue4·虚幻·材质
星火撩猿3 天前
常见游戏引擎介绍与对比
unity·ue5·游戏引擎·godot
清流君4 天前
【MySQL】数据库 Navicat 可视化工具与 MySQL 命令行基本操作
数据库·人工智能·笔记·mysql·ue5·数字孪生
Involuter4 天前
UE5 Assimp 自用
ue5
电子云与长程纠缠4 天前
Unreal Niagara制作SubUV贴图翻页动画
学习·ue5·编辑器·贴图·niagara
子燕若水4 天前
“Daz to Unreal”将 G8 角色(包括表情)从 daz3d 导入到 UE5。在 UE5 中,我发现使用某个表情并与闭眼混合后,上眼睑出现了问题
3d·ue5
半天法师5 天前
UE5.2+VarjoXR3,Lumen、GI、Nanite无效的两种解决方案
ue5·xr·vr
ue星空5 天前
UE5摄像机画面没有填充满屏幕有黑边
ue5
李詹6 天前
游戏开发核心技术解析——从引擎架构到攻防体系的完整技能树
架构·ue5·游戏引擎·游戏程序·3dsmax·虚幻
子燕若水6 天前
UE5的 Modify Curve 蓝图节点
ue5