找回密码
 立即注册
查看: 209|回复: 0

[原创]UE基础—一定要用 FGameplayTag

[复制链接]
发表于 2023-2-22 07:59 | 显示全部楼层 |阅读模式
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
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-4-28 11:44 , Processed in 0.095782 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表