斯坦福UE4C++课程P66-P69游戏标签GameplayTags
FGameplayTag:(F开头表示是结构体)
FName wrapped in Struct 用结构体封装的FName
Alternative for:bool,enum,FName 能够被bool、enum、FName替代
created via Project Settings 通过项目设置创建
Editor support to easily select GameplayTags 从编辑器可以轻易处理GameplayTags
FGameplayTagContainer(Wrapped TArray of GameplayTags)FGameplayTagContainer使用TArray作为GameplayTags的容器
Extensively used by GAS 在GAS中被广泛使用
作者给了一些GameplayTags的学习资源,主要是官方文档。
首先我们到ActionRoguelike.Build.cs构建文件把GameplayTags模块包含到项目中,这样我们就能在C++中使用GameplayTags了。
在SActionComponent类中添加
权限给满,因为我们稍后要在蓝图里使用它,纯粹为了方便。
这里我们不使用指针,所以必须包含该结构体的头文件(编译器需要知道该变量的大小),如果是指针,其大小是固定的,就无需包含头文件。
FGameplayTagContainer中可以加是否正在冲刺、是否正在攻击、是否被击倒的这些信息。然后其他系统可以从这个容器中获取到这些信息,玩家目前在做什么,然后以此为依据去做决定。
下面我们在SAction类添加
在Action启用时,我们把GrantTags中的所有Tag都放入ActiveGameplayTags中;停用时全部移出:
BlockedTags先不加,我们在SActionComponent类TickComponent函数添加debug信息,看看效果再说:
我们让ActionComponent附加在的Actor名字、因Action发出而附加的Tags打印出来。
编译,进入project settings->GameplayTags->Add New Gameplay Tag,新建两个tag:
"Action.Sprinting"、"Action.Attacking"

到Action_Sprint蓝图,发现右侧Tags多了Grants Tags、Blocked Tags
调整如下:
、

表示我们希望冲刺时会生成Action.Sprinting的标签,且会受到Action.Attacking的阻止(冲刺时不能攻击)。
在其他SAction继承蓝图类也调整tags:

表示发射projectile的三种action都算Attacking,且冲刺时无法施放。
现在我们攻击时(魔法球、Dash、黑洞),debug信息出现在左上角。


接下来我们添加冲刺时阻止攻击、攻击时阻止冲刺逻辑。
在SAction的public下添加
函数体:
在SActionComponent添加判定,如果CanStart函数返回false,就不能开始Action。
我们还添加debug信息,表示如果当前不能开始此Action,就跳过当前循环,判定下一个Action,并打印失败的Action名字
如果存在不能开始的Action,因为我们在SAction_ProjectileAttack类中写了,一旦startAction函数执行,最后一个语句会执行stopAction。但存在不能开始的Action的话,我们就没有添加过tag,执行stopAction移除tag会出现错误。
所以我们在SAction添加一个bool变量,和获取它的函数:
StartAction_Implementation函数末尾加上
StopAction_Implementation函数末尾加上
并在开头添加
表示如果从未执行过StartAction,就不要执行StopAction。(PV操作的感觉??)
现在编译运行游戏,攻击时冲刺,打印"Failed to run:Sprint";冲刺时攻击,打印"Failed to run:PrimaryAttack"。
但现在攻击时仍能攻击。
在CanStart函数开头添加:
表示,如果一个action正在执行(还未执行stopAction),那么就不应该重复执行该action。
现在仍有一个问题,就是扔魔法球时虽然不能扔魔法球了,但可以扔Dash和黑洞出去。
我们只需添加blocktag即可:

现在攻击时,会阻挡攻击action。

意思就是,一个action执行,要执行另一个,将会把其信息打印在屏幕上并失败。

下面我们用游戏标签添加门和钥匙系统。
首先到project settings新建三个tag

更改leverBP如下:(Tag提升为变量,命名KeyCard)

在宝箱TreasureChest蓝图的Interact事件后添加(之前写的开宝箱的逻辑不变)

Has Tag节点判断Active Gameplay Tags中是否有Required Key Card作为Branch条件,表示如果tag匹配,才能开宝箱。Exact Match表示,只有tag完全一致才返回true。
我们可以给场景中实例化的宝箱分配三种颜色之一的tag,同理给杠杆lever分配tag。现在只有lever的tag和宝箱的tag相同,才能在和lever交互后获取到相应tag,才能开相应宝箱。
如果我们取消勾选Exact Match,表示Required Key Card属于Active Gameplay Tags中的一类Tag就行。举个例子,我们取消勾选Exact Match,然后一个箱子设置Required Key Card为KeyCard,一个设置KeyCard.Blue,和lever获取到KeyCard.Blue,那么只要和lever交互,就能够开这两个箱子(一个完全匹配(Exact Match,KeyCard.Blue==KeyCard.Blue),一个只是一类(KeyCard.Blue属于KeyCard,也能开))。


这一P我们学习用GameplayTags抵挡攻击(parrying mechanic)。
首先添加tag:Status.Parrying。
在魔法球C++类OnActorOverlap函数添加
现在测试,我们先到角色类蓝图Action组件,设置Active Gameplay Tags添加Status.Parrying,这样角色一出生就有这个Tag,便于测试。然后我们给魔法球勾选Status.Parrying,表示给C++的ParryTag赋值为Status.Parrying。
现在AI攻击玩家,玩家会反弹魔法球:

下面我们给AI角色添加Action组件。头文件添加ActionComp,构造函数初始化组件。
AI蓝图勾选Status.Parrying,让AI一开始也有此Tag。现在AI发射魔法球,打到玩家反弹,反弹到AI又会反弹,直到反弹到打不中二者,而击中墙壁为止。玩家攻击AI也是 一样。
我们稍微修改,让玩家按R能够有反弹能力1秒。
新建Action_Parry,继承自SAction(首先要在C++给Stop Action函数添加BlueprintCallable,让我们可以在蓝图调用;且需要调用父类节点,实现基类功能):

在Input->Action Mapping,新建名为Parry的Action映射,键为R(黑洞改为E,交互改为F)。我们取消勾选玩家和AI的开局就有ParryTag,给玩家添加Action_Parry的能力(在Default Actions数组新添加Action_Parry)。

在角色蓝图把R键和Parry动作绑定(C++中就是BindAction):

现在玩家按R,就开启了1秒内的反弹能力。
最后作者加了AI受伤害时,伤害数字的显示,之前我做过了。
还有AI血条的位置可以用之前做的World Offset来调,我们给z方向上加90,让血条被创建在AI头上。