4.2 游戏标签(Gameplay Tags)
FGameplayTags
是一种层次化的名称,形式为 Parent.Child.Grandchild...
,并注册在 GameplayTagManager
中。这些标签非常有用,可以用于分类和描述对象的状态。例如,如果一个角色被眩晕,我们可以在眩晕期间给它一个 State.Debuff.Stun
的 GameplayTag
。
你会发现自己会用 GameplayTags
来替代以前用布尔值或枚举处理的事情,并进行布尔逻辑以判断对象是否具有某些 GameplayTags
。
当给对象赋予标签时,我们通常将它们添加到对象的 ASC
(如果有的话)中,以便GAS可以与它们交互。UAbilitySystemComponent
实现了 IGameplayTagAssetInterface
,提供了访问其拥有的 GameplayTags
的功能。
多个 GameplayTags
可以存储在一个 FGameplayTagContainer
中。使用 GameplayTagContainer
优于使用 TArray<FGameplayTag>
,因为 GameplayTagContainers
增加了一些效率上的魔法。虽然标签是标准的 FNames
,但如果在项目设置中启用了 Fast Replication
,它们可以被有效地打包在 FGameplayTagContainers
中进行复制。Fast Replication
要求服务器和客户端具有相同的 GameplayTags
列表。这通常不应该是个问题,所以你应该启用这个选项。GameplayTagContainers
还可以返回一个 TArray<FGameplayTag>
以供迭代。
存储在 FGameplayTagCountContainer
中的 GameplayTags
有一个 TagMap
,用于存储该 GameplayTag
的实例数量。一个 FGameplayTagCountContainer
可能仍然有 GameplayTag
,但其 TagMapCount
为零。如果一个 ASC
仍然有一个 GameplayTag
,你可能会在调试时遇到这种情况。任何 HasTag()
或 HasMatchingTag()
或类似的函数将检查 TagMapCount
,如果 GameplayTag
不存在或其 TagMapCount
为零,则返回 false。
GameplayTags
必须提前在 DefaultGameplayTags.ini
中定义。虚幻引擎编辑器在项目设置中提供了一个界面,让开发者可以管理 GameplayTags
,而无需手动编辑 DefaultGameplayTags.ini
。GameplayTag
编辑器可以创建、重命名、搜索引用和删除 GameplayTags
。
搜索 GameplayTag
引用将会在编辑器中显示熟悉的 Reference Viewer
图,显示所有引用该 GameplayTag
的资产。然而,这不会显示任何引用该 GameplayTag
的 C++ 类。
重命名 GameplayTags
会创建一个重定向,以便仍然引用原始 GameplayTag
的资产可以重定向到新的 GameplayTag
。如果可能的话,我更喜欢创建一个新的 GameplayTag
,手动更新所有引用到新的 GameplayTag
,然后删除旧的 GameplayTag
,以避免创建重定向。
除了 Fast Replication
,GameplayTag
编辑器还有一个选项,可以填充常用的复制 GameplayTags
以进一步优化它们。
如果 GameplayTags
是从 GameplayEffect
添加的,它们会被复制。ASC
允许你添加不被复制的 LooseGameplayTags
,并且必须手动管理。示例项目使用 LooseGameplayTag
来表示 State.Dead
,以便拥有的客户端可以立即响应其健康值降至零的情况。重生时手动将 TagMapCount
设置回零。只有在处理 LooseGameplayTags
时才手动调整 TagMapCount
。最好使用 UAbilitySystemComponent::AddLooseGameplayTag()
和 UAbilitySystemComponent::RemoveLooseGameplayTag()
函数,而不是手动调整 TagMapCount
。
在 C++ 中获取 GameplayTag
的引用:
FGameplayTag::RequestGameplayTag(FName("Your.GameplayTag.Name"))
对于高级的 GameplayTag
操作,如获取父或子 GameplayTags
,请查看 GameplayTagManager
提供的功能。要访问 GameplayTagManager
,请包含 GameplayTagManager.h
并使用 UGameplayTagManager::Get().FunctionName
调用它。GameplayTagManager
实际上将 GameplayTags
存储为关系节点(父、子等),以便比常量字符串操作和比较更快地处理。
GameplayTags
和 GameplayTagContainers
可以有可选的 UPROPERTY
指定符 Meta = (Categories = "GameplayCue")
,在蓝图中过滤标签以仅显示具有父标签 GameplayCue
的 GameplayTags
。当你知道 GameplayTag
或 GameplayTagContainer
变量应该仅用于 GameplayCues
时,这很有用。
另外,还有一个名为 FGameplayCueTag
的独立结构,封装了一个 FGameplayTag
,并且在蓝图中自动过滤 GameplayTags
,仅显示那些具有父标签 GameplayCue
的标签。
如果你想在函数中过滤 GameplayTag
参数,请使用 UFUNCTION
指定符 Meta = (GameplayTagFilter = "GameplayCue")
。函数中的 GameplayTagContainer
参数不能被过滤。如果你想编辑引擎以允许这样做,请查看 SGameplayTagGraphPin::ParseDefaultValueData()
如何从 Engine\Plugins\Editor\GameplayTagsEditor\Source\GameplayTagsEditor\Private\SGameplayTagGraphPin.cpp
调用 FilterString = UGameplayTagsManager::Get().GetCategoriesMetaFromField(PinStructType);
并将 FilterString
传递给 SGameplayTagWidget
在 SGameplayTagGraphPin::GetListContent()
中。这些函数的 GameplayTagContainer
版本在 Engine\Plugins\Editor\GameplayTagsEditor\Source\GameplayTagsEditor\Private\SGameplayTagContainerGraphPin.cpp
中不检查元字段属性并传递过滤器。
示例项目广泛使用 GameplayTags
。
4.2.1 响应Gameplay Tags的变化
ASC
提供了一个委托,用于在 GameplayTags
被添加或移除时触发。它接受一个 EGameplayTagEventType
,可以指定仅在 GameplayTag
被添加/移除时触发,或在 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);
4.2.2 从插件下的 .ini 文件中加载Gameplay Tags
如果你创建了一个带有自己 .ini 文件的插件,其中包含 GameplayTags
,你可以在插件的 StartupModule()
函数中加载该插件的 GameplayTag
.ini 目录。
例如,这是虚幻引擎附带的 CommonConversation 插件的做法:
void FCommonConversationRuntimeModule::StartupModule()
{
TSharedPtr<IPlugin> ThisPlugin = IPluginManager::Get().FindPlugin(TEXT("CommonConversation"));
check(ThisPlugin.IsValid());
UGameplayTagsManager::Get().AddTagIniSearchPath(ThisPlugin->GetBaseDir() / TEXT("Config") / TEXT("Tags"));
//...
}
这将查找目录 Plugins\CommonConversation\Config\Tags
,并在引擎启动时(如果插件已启用)将其中的任何 .ini 文件中的 GameplayTags
加载到你的项目中。