FGameplayTags
是通过 GameplayTagManager
进行注册的具备一系列层级的标签名字,如 Parent.Child.Grandchild...
。这些标签在分类和描述一个对象的状态时格外有用。例如,如果一个角色被眩晕了,我们可以在眩晕的持续时间之内给它一个 State.Debuff.Stun
的 GameplayTag
。
你会发现其实你是用 GameplayTags
替代了之前用布尔或者枚举来处理的内容,并且通过判断某个对象是否具备特定的 GameplayTags
来进行一些布尔逻辑运算。
当为某个对象赋予标签时,我们通常会将标签添加到对象上的 ASC
,这样 GAS 就能和他们产生相应的交互。UAbilitySystemComponent
实现了接口 IGameplayTagAssetInterface
中的给定方法,来访问它所有拥有的 GameplayTags
。
多个 GameplayTags
可以存储在一个 FGameplayTagContainer
里。这里通常我们更偏向于使用 GameplayTagContainer
而不是 TArray<FGameplayTag>
,因为 GameplayTagContainers
中有一些高效的工具。因为标签本质上就是标准的 FName
,如果项目设置中的 Fast Replication
处于激活状态的话,他们可以被高效得打包在一起到 FGameplayTagContainers
中,以方便网络复制的使用。Fast Replication
要求服务器和客户端们有着相同的 GameplayTags
列表。这通常并不会有什么问题,所以你尽管激活这个选项就好了。GameplayTagContainers
在遍历时也可以一返回到一个 TArray<FGameplayTag>
数组。
存储在 FGameplayTagCountContainer
的 GameplayTags
有着一个 TagMap
,其内存储着相应 GameplayTag
的实例的数量。FGameplayTagCountContainer
可能还存在某些 GameplayTag
,其对应的 TagMapCount
实际上已经是 0 了。当调试的时候可能就会遇到这种情况。HasTag()
或者 HasMatchingTag()
或者相类似的函数都会去检查 TagMapCount
,并且在 GameplayTag
不存在或者相应的TagMapCount
为0的情况下直接返回false。
GameplayTags
必须在 DefaultGameplayTags.ini
中提早进行定义。虚幻编辑器在项目设置中提供了一个编辑界面来让开发者管理 GameplayTags
,而无需手动编辑 DefaultGameplayTags.ini
文件。GameplayTag
的编辑器内可以进行创建、改名、查找引用以及删除操作。
查找 GameplayTag
的引用将会在编辑器内打开一个类似 Reference Viewer
的图形界面,其中展示了所有引用 GameplayTag
的资产。当然,不包括任何 C++ 类。
重命名 GameplayTag
实际上是创建了一个重定向,与原 GameplayTag
有引用关系的资产可以通过这个重定向找到新的 GameplayTag
。我偏向于创建一个新的 GameplayTag
,然后手动更新所有的引用,然后删除掉旧的 GameplayTag
,从而避免创建一个重定向。
除了 Fast Replication
,GameplayTag
编辑器中有一个选项可以选择常用的网络复制的 GameplayTag
,从而进一步实现优化。
如果 GameplayTag
是由 GameplayEffect
添加的话,那他们就会被复制。ASC
可以让你添加不会被复制并且必须手动管理的 LooseGameplayTags
。示例项目使用 LooseGameplayTag
来作为 State.Dead
,这样所属客户端在他们的hp降为0后就能够立即响应。重生的时候需要手动得将 TagMapCount
设置回 0。仅在使用 LooseGameplayTags
时才会用到手动调整 TagMapCount
。我偏向于使用 UAbilitySystemComponent::AddLooseGameplayTag()
和 UAbilitySystemComponent::RemoveLooseGameplayTag()
这两个函数,而不是直接手动调整TagMapCount
。
在C++中去获取GameplayTag
的引用:
FGameplayTag::RequestGameplayTag(FName("Your.GameplayTag.Name"))
至于高级的 GameplayTag
的相关操作如获取父级或者子级的 GameplayTag
,参考 GameplayTagManager
中提供的一些方法。要访问 GameplayTagManager
,首先要包含 GameplayTagManager.h
头文件,然后调用 UGameplayTagManager::Get().FunctionName
函数即可。实际上 GameplayTagManager
将 GameplayTag
存储为关系节点(父,子,等等),所以处理速度要快于字符串的操作和比较。
GameplayTags
和 GameplayTagContainers
可以通过 UPROPERTY
中的说明符 Meta = (Categories = "GameplayCue")
来过滤蓝图中的标签,从而仅显示父级标签为 GameplayCue
的那些 GameplayTags
。当你知道那些仅用于 GameplayCues
的 GameplayTag
或者 GameplayTagContainer
变量时这样的操作就非常有用处了。
此外,还有一个单独的结构体 FGameplayCueTag
封装了 FGameplayTag
,这样也可以实现上面的那种过滤的效果。
如果你想在一个函数中过滤 GameplayTag
参数时,可以使用 UFUNCTION
中的说明符Meta = (GameplayTagFilter = "GameplayCue")
。而函数中的 GameplayTagContainer
参数是不可以被过滤的(译者注:现在已经支持过滤的特性,但是后续翻译时仍然保留了相关描述,目的是让大家了解相关特性是如何通过元数据进行支持的)。如果你想要让你的引擎支持这个特性,参考 Engine\Plugins\Editor\GameplayTagsEditor\Source\GameplayTagsEditor\Private\SGameplayTagGraphPin.cpp
中的函数 SGameplayTagGraphPin::ParseDefaultValueData()
是怎样调用 FilterString = UGameplayTagsManager::Get().GetCategoriesMetaFromField(PinStructType);
并且在函数 SGameplayTagGraphPin::GetListContent()
中传递 FilterString
到 SGameplayTagWidget
。Engine\Plugins\Editor\GameplayTagsEditor\Source\GameplayTagsEditor\Private\SGameplayTagContainerGraphPin.cpp
中的 GameplayTagContainer
相关函数并没有检查元字段属性并代入过滤器。
示例项目广泛使用了 GameplayTags
。
4.2.1 响应游戏标签的变化 - Responding to Changes in Gameplay Tags
ASC
为 GameplayTags
的添加和删除提供了相应的委托。需要给定一个 EGameplayTagEventType
来表明事件类型,即是 GameplayTags
的增加亦或是删除,还是 GameplayTag
的 TagMapCount
发生了变化之类的。
AbilitySystemComponent->RegisterGameplayTagEvent(FGameplayTag::RequestGameplayTag(FName("State.Debuff.Stun")), EGameplayTagEventType::NewOrRemoved).AddUObject(this, &AGDPlayerState::StunTagChanged);
回调函数中有两个参数,分别是 GameplayTag
和新的 TagCount
。
virtual void StunTagChanged(const FGameplayTag CallbackTag, int32 NewCount);