斯坦福 UE4 C++ ActionRoguelike游戏实例教程 14. 使用GameplayTag实现防守反击技能

发布时间 2023-04-24 20:15:18作者: 仇白

斯坦福课程 UE4 C++ ActionRoguelike游戏实例教程 0.绪论

概述

本篇文章对应Lecture 17 - GameplayTags, 70节。本文将会结合前几节课使用的能力系统和GameplayTag系统,实现一个防守反击的技能,按下R键,即可在1秒钟内反弹魔法子弹的攻击。

如果不清楚能力系统可以查看之前写的文章https://www.cnblogs.com/Qiu-Bai/p/17326408.html

防守反击技能

如果玩过守望先锋,想必会对源氏的E技能“闪”印象深刻,该技能在两秒钟内反弹源氏面前的所有投射类物体,无论是子弹还是各种技能。

img

源氏的E技能 图源百度

而这篇文章,将会实现一个简易版的防守反击技能,在你按下释放键后,将会给你一个持续一段时间的“招架”状态,此时会反弹所有打向你的魔法子弹。

我们将使用GamePlayTag来实现状态的赋予。原理很简单,当我们按下技能键后,会给角色上一个“招架(parring)”的Tag,当子弹打到角色身上时,会检查角色身上的Tag,如果有parry的Tag,那么就将其速度和方向取反。具体怎么实现,我们边做边说。


由于是子弹检测角色身上的Tag,所以检测Tag部分的逻辑我们写在子弹的类中。

如下所示,我们在头文件中定义了要检测的Tag类型 ParryTag,并修改了OnOverlapBegin里面的部分逻辑,添加了了检测目标角色身上的Tag,如果目标身上有指定的ParryTag,就将子弹的速度取反,并重新设置Instigator。

//SurMagicProjectile.h
protected:
	UPROPERTY(EditAnywhere, Category = "Tags")
	FGameplayTag ParryTag;


//SurMagicProjectile.cpp
void ASurMagicProjectile::OnOverlapBegin(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
   if(OtherActor && OtherActor != GetInstigator())
   {
      // 被魔法球攻击的Actor如果有ParryTag,那么它将反弹魔法球
      USurActionComponent* ActionComp = Cast<USurActionComponent>(OtherActor->GetComponentByClass(USurActionComponent::StaticClass()));
      if (ActionComp && ActionComp->ActiveGameplayTags.HasTag(ParryTag))
      {
         MoveComp->Velocity = -MoveComp->Velocity;

         SetInstigator(Cast<APawn>(OtherActor));// 反弹魔法球之后,魔法球的Instigator改为反弹者
         return;
      } 
       ...
   }
}

代码就修改到这边,后面的步骤并不复杂,主要用蓝图实现。

首先添加Parrying的Tag。

image-20230418172906616

添加Status.Parrying

值得一提的是,课程里提到为Tag起名并做好分类是非常重要的事情。这里为Parrying添加了Status的前缀,推测其目的是为之后的角色状态相关设计打下基础。熟练的游戏设计者考虑问题总是先人一步,但是作为新手,如果缺乏经验的话,在一开始确实很难想到这么多。

然后是创建招架的技能。继承自前几节课创建的SurAction,将其命名为Action_Parry。将其细节设置如下:

image-20230418163429392

将新能力起名为Parry

重载StartAction事件,在这个技能持续期间,会为角色的ActionComponent组件添加上Parrying标签(父类StartAction),持续一秒。

image-20230418163358514

Action_Parry的事件图表

注意要将SurAction的StopAction设置为BluprintCallable,否则在蓝图中是无法直接调用的。

延续上面的话题,在项目越来越大的时候,我们应该考虑使用GameplayTag来代替这里使用的FName,因为为能力命名实际上也是一门不小的学问,我们在实现越来越多的Action时,也许会遗忘某个Action的ActionName,或者会疑惑“当时我为什么要这么命名?”,又或者不小心打错了某个字符,在后期开发了几百上千个Action后,为每个Action命名也是相当繁琐且困难的。

但是,如果用标签的组合来标识每一个Action的话,也许在开发新Action的时候就会省力很多,毕竟我们可以自由选择标签组合来赋予Action特征和属性,也不用不厌其烦地为Action起名了。当然,这一切的前提是你有一套足够合理的标签系统,这又是一门大学问了。现在我们的项目只是为了练手和入门,就先不考虑这些了。


技能创建好后,为技能绑定按键。这里将其设置为R键。

image-20230418164255441

项目设置->引擎->输入->操作映射

在我们的角色类中,为该键绑定事件,和之前一样,直接调用ActionComponent的StartActionByName。注意Action Name要和技能里的Fname一样。

image-20230418163748315

绑定按键事件

为角色添加默认技能:

image-20230418164323634

添加默认技能

对了,前面还为子弹添加了一个ParryTag成员。别忘了为子弹设置好要检测的标签。

image-20230418164228809

设置要检测的标签

进入游戏看看效果,当AI小兵向我们发射子弹的时候,我眼疾手快,按下R键释放格挡反击技能。左上角提示,现在角色带有了Parry标签,子弹命中我的时候,检测到Parry标签,将自己的速度取反,并重新设置Instigator,相当于这个子弹是被我发射出来的。最终,小兵被反弹的子弹击杀。

最后玩点花的,我们可以给AI小兵也加上ActionComponent,为其添加一个parry标签,这样子弹打在小兵身上时就会反弹回来,如果此时我们角色身上也有parry标签,最后双方互相反弹,跟打乒乓球一样,十分有趣。

期间我一直在狂按R键

添加AI角色伤害数字UI

因为这部分东西很少,都是以前做过的内容,出于记录的目的,姑且放在本篇文章中。

image-20230421115415536

在Ai角色蓝图中绑定OnHealthChanged事件,创建DamagePopup_Widget控件

image-20230421115509479

这里我为DamagePopup_Widget的伤害数字增加了简单的上浮动画

image-20230421115650282

为DamagePopup_Widget添加播放动画的蓝图节点

总结

本篇文章结合了前面实现过的Action System和UE中的GameplayTag系统,实现了可以反弹子弹的格挡反击技能。

注意到利用GamePlayTag实现这些效果并没有与特定的Actor或者组件相关联,我们可以将游戏标签用在游戏项目里的各种地方上,大大降低了程序的耦合性,这是游戏标签非常棒的特点之一。

通过这几节课的学习,我们已经基本掌握了GameplayTag的基本用法,除此以外,它还有大量的用法尚待发掘,相信在学完了这几节课后,大家对游戏的开发会更加得心应手。