[原创]UE基础—一定要用 FGameplayTag
GameplayTag 简介FGameplayTag,可以简单理解为游戏标签。虚幻引擎自带的技能系统(Gameplay Ability System,下文简称GAS)重度使用了它;虚幻是欧美那边的游戏引擎,因此很多设计理念也带有他们的文化色彩。欧美喜欢给人贴标签 , 比如:LGBT、ABC、黑命贵、环保主义等。如果一个人能叠这么多层标签,那他/她在欧美就是人上人,甚至有可能会被 Calvin Klein 签约
FGameplayTag 基于FName实现因此性能很好。针对多个标签的管理,系统提供了一个标签容器类 FGameplayTagContainer 增加/移除标签、匹配单个/多个标签等接口都有。我们先看一些标签增加印象
[*]Buff.HP.Add
[*]DamageType.Energy
[*]Ability.Jump
标签的好处就是根据标签名能知道它的用处或者意义。我们甚至可以根据标签来定义伤害类型,比如:DamageType.Magic.Fire,DamageType.Physics.Pistol、DamageType.Physics.Dagger 说了这么多,那标签到底有什么用呢?
GameplayTag 的妙用1
游戏内玩家使用某个道具,这个道具的作用是让玩家免受物理攻击。常规做法可能是设置一个布尔变量 bImmunePhysicalDamage,手枪打中玩家时伪代码如下:
void PistolFire(ATestActor* Target, float Damage)
{
if(Target->bImmunePhysicalDamage)
{
return;
}
Target->OnDamageHandle(Damage);//处理伤害流程
}
如果该道具是免疫魔法伤害呢?添加新的布尔变量 bImmuneMagicDamage,魔法球打中玩家时伪代码如下:
void MagicFire(ATestActor* Target, float Damage)
{
if(Target->bImmuneMagicDamage)
{
return;
}
Target->OnDamageHandle(Damage);//处理伤害流程
}
如果该道具是免疫所有伤害呢?添加新的布尔变量 bImmuneDamage,伪代码如下:
void PistolFire(ATestActor* Target, float Damage)
{
if(Target->bImmuneDamage || Target->bImmunePhysicalDamage)
{
return;
}
Target->OnDamageHandle(Damage);//处理伤害流程
}
void MagicFire(ATestActor* Target, float Damage)
{
if(Target->bImmuneDamage || Target->bImmuneMagicDamage)
{
return;
}
Target->OnDamageHandle(Damage);//处理伤害流程
}
有没有发现问题?代码改动很频繁,不易扩展。比如:目标处于隐身状态,无敌状态等,是不是又要添加新的代码。使用标签后,代码就会焕然一新
玩家身上有个标签容器变量 CharacterTags,如果玩家免疫物理伤害,免疫魔法伤害,处于隐身状态;那么就给玩家添加以下标签:
CharacterTags.AddTag("Buff.ImmunityDamage.Physical"); //免疫物理伤害
CharacterTags.AddTag("Buff.ImmunityDamage.Magic"); //免疫魔法伤害
CharacterTags.AddTag("Status.invisibility"); //处于隐身状态
而手枪开火造成伤害,魔法球造成伤害也可以定义互斥标签,伪代码如下:
void PistolInit()
{
//玩家身上有这些Tag任意一个,开枪对玩家不起作用
BlockedTags.AddTag("Buff.ImmunityDamage.Physical","Status.invisibility");
}
void PistolFire(ATestActor* Target, float Damage)
{
if(Target->CharacterTags.HasAny(BlockedTags))
{
return;
}
Target->OnDamageHandle(Damage);//处理伤害流程
}
void MagicInit()
{
//玩家身上有这些Tag任意一个,魔法球对玩家不起作用
BlockedTags.AddTag("Buff.ImmunityDamage.Magic");
}
void MagicFire(ATestActor* Target, float Damage)
{
if(Target->CharacterTags.HasAny(BlockedTags))
{
return;
}
Target->OnDamageHandle(Damage);//处理伤害流程
}
BlockedTags 内的数据可从DataTable配置读取,方便策划随时更改规则;不需要在 ATestActor 内定义无数个布尔变量。还可以定义依赖,比如:要求玩家必须是站立状态才受伤害,伪代码如下:
void PistolInit()
{
//玩家身上有这些Tag任意一个,开枪对玩家不起作用
BlockedTags.AddTag("Buff.ImmunityDamage.Physical","Status.invisibility");
//玩家身上必须有这些Tag,开枪才会对它起作用
RequiredTags.AddTag("Status.Stand");
}
void PistolFire(ATestActor* Target, float Damage)
{
if(Target->CharacterTags.HasAny(BlockedTags) ||
!Target->CharacterTags.HasAll(RequiredTags))
{
return;
}
Target->OnDamageHandle(Damage);//处理伤害流程
}
是不是简单清晰,框架容易扩展而且很灵活
GameplayTag 的妙用2
标签除了上述妙用,还能做啥呢?当物品ID,比如:以前定义各种表格的时候 10001,20001,30001 当物品ID,潜规则1打头的是货币,2打头的是武器,3打头的是普通道具等。那如果用标签当ID呢?
[*]Item.Money.Gold,Item.Money.Coin
[*]Item.Weapon.Pistol
[*]Item.Normal.EnergyBottle
是不是一目了然。它不仅可以用来表示物品,还可以用来表示属性 Attribute.Health,角色类型 Character.Citizen,Character.Police 等
GameplayTag 的妙用3
发送事件的时候(GameplayTag也支持网络同步),可以用标签当作事件Key,比如:
[*]Event.Ability.Cast,Event.Ability.End
[*]Event.UI.ShowMenu,Event.UI.HideMenu
除了见字如意之外,还有个好处是可以监听父层级;比如:我想监听所有UI事件
ListenGameplayTagEvent("Event.UI", this, &OnHandleGameplayTagChanged);
//所有Event.UI 事件都会被监听
void UMWCrimeComponent::OnHandleGameplayTagChanged(const FGameplayTag Tag)
{
//do something
}
GameplayTag 和 Actor/Componet Tags 的区别
默认的标签系统(Actor/Component Tags)是个FName数组,并且没有 GameplayTags 的层级功能,非常不灵活,强烈不推荐使用
GAS And GameplayTags
如果你对 GAS 不是很了解,我个人建议下载 ActionRoguelike 这个项目,看看他的技能实现。等你研究明白了这个项目,你大概就明白了GAS的框架(当然,GAS更加强大)
[*]USActionComponent -> UAbilitySystemComponent
[*]USActor-> UGameplayAbility
[*]USActionEffect -> UGameplayEffect
[*]USAttributeComponent->UAttributeSet
以上严格来说,并不是一一对应,但是整体设计思路与GAS一致,是个很棒的GAS入门项目
本文参考
[*]Gameplay标签
[*]Why you should be using GameplayTags in Unreal Engine
页:
[1]