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);