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

embedded/2024/12/28 4:41:37/

目录

概念

主要作用

1 组织与分类游戏元素

1.1 驱动游戏逻辑

1.2 便于查询与筛选

2 助力网络同步与优化

定义Gameplay标签

1 在项目设置中添加标签 

2 从数据表资产导入标签

3 使用C++定义标签

3.1 UE_DECLARE_GAMEPLAY_TAG_EXTERN

3.2 UE_DEFINE_GAMEPLAY_TAG

3.3 UE_DEFINE_GAMEPLAY_TAG_COMMENT

3.4 UE_DEFINE_GAMEPLAY_TAG_STATIC

*3.5 完整步骤

使用Gameplay标签

1 将标签应用于对象

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

3 Gameplay标记查询

4 在C++中使用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 等宏来完成,用法如下:

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

3.2 UE_DEFINE_GAMEPLAY_TAG

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

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

3.3 UE_DEFINE_GAMEPLAY_TAG_COMMENT

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

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 标签管理过程中(例如避免被某些全局的标签查找、修改机制影响等),用法如下:

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标签的名称。


http://www.ppmy.cn/embedded/149360.html

相关文章

选择屏幕的用法

**************************定义控件*********************************** SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE text-002. SELECT-OPTIONS bukrs FOR iloa-bukrs . "公司代码 SELECT-OPTIONS swerk FOR iloa-swerk OBLIGATORY . "工厂 SELECT-O…

基于Spring Boot的旅游推荐系统

一、系统背景与意义 随着旅游业的快速发展,旅游信息在种类和数量上不断增多,管理难度也在增大。基于Spring Boot的旅游推荐系统旨在解决这一问题,通过收集、处理和分析旅游数据,为用户推荐符合其偏好和需求的旅游线路&#xff0c…

每天40分玩转Django:实操在线商城

实操在线商城 一、今日学习内容概述 模块重要程度主要内容商品模型⭐⭐⭐⭐⭐商品信息、分类管理购物车系统⭐⭐⭐⭐⭐购物车功能实现订单系统⭐⭐⭐⭐⭐订单处理、支付集成用户中心⭐⭐⭐⭐订单管理、个人信息 二、模型设计 # models.py from django.db import models fro…

短视频运营行业该如何选择服务器?

在互联网快速发展的时代,短视频行业也应运而生,企业为了保证用户能够浏览流畅且稳定的短视频,则需要选择一台合适的服务器来运行相关业务,本文就来探讨一下短视频运营行业该如何选择服务器吧! 短视频行业一般需要处理大…

android 登录界面编写

1、登录页面实现内容 1.实现使用两个EditText输入框输入用户名和密码。 2.使用CheckBox控件记住密码功能。 3.登录时候,验证用户名和密码是否为空。 4.当前CheckBox控件记住密码勾上时,使用SharedPreferences存储用户名和密码。 5.登录时候使用Prog…

PDF书籍《手写调用链监控APM系统-Java版》第3章 配置文件系统的建立

本人阅读了 Skywalking 的大部分核心代码,也了解了相关的文献,对此深有感悟,特此借助巨人的思想自己手动用JAVA语言实现了一个 “调用链监控APM” 系统。本书采用边讲解实现原理边编写代码的方式,看本书时一定要跟着敲代码。 作者…

框架专题:设计模式

1. 什么是设计模式? 设计模式是一套经过验证的解决方案,用于解决软件开发中的常见问题。它不是具体的代码实现,而是一种可复用的思想和原则。设计模式的核心目标是提高代码的复用性、可扩展性和可维护性。 1.1 设计模式的三大特性 可重用性…

【C++boost::asio网络编程】有关服务端退出方法的笔记

有关服务端退出方法的笔记 C风格的信号关闭boost::asio中的关闭方式 原来服务端的main函数如下 int main() {try{boost::asio::io_context ioc;Server s(ioc, 8888);ioc.run();}catch (const std::exception&){}return 0; }上面弊端在于缺乏好的退出机制,目前&a…