前言

上一节我们分析了 FireGun 技能的整个流程,想必大家已经对 GAS 的使用有了一个初步的了解了,也大致知道了 GA、GE、GC 这些概念以及如何相互调用。本节就来继续分析另一个技能,Meteor。

Meteor

这个技能也是由纯蓝图创建的,技能整体流程包括目标确认、技能消耗、技能冷却、技能本体(陨石)、击中伤害与眩晕、击中效果(倒地)等组成,相比于 FireGun 要更为复杂。让我们逐一分析。

GA_Meteor_BP

这个蓝图就是 Meteor 技能的 GA 了,同样继承自 GDGameplayAbility。来分析蓝图中实现了哪些功能。

作为一个 GA,最重要的肯定还是重写 ActivateAbility

image.png

首先还是从 ActorInfo 中获取了释放者的信息并且存在了 OwningHero 变量中。

image.png

其次是关于一些辅助信息显示的设置以及移动相机,在释放这种需要瞄准的技能时,移动相机是一个不错的做法,可以使视野开阔一些。

image.png

随后就是较为重要的瞄准部分了,这里先获取了 OwnerActor 即释放者的位置,再向前移动 200 个单位作为瞄准的初始位置。随后也启动了一个 Task。在上一节的 FireGun 中,是利用 PlayMontageAndWaitForEvent 这个 Task 来播放动画并等待事件,这里则是利用了 WaitTargetData 这一 Task,来等待瞄准的信息。

同样的,这个类也是 GAS 自带的,暂且不进行更深入的分析。但有一些性质还是需要了解的。

第一节 中,我们分析了输入绑定,在 AGDHeroCharacter::BindASCInput 中,可以看到这里不仅绑定了 EGDAbilityInputID 即技能对应的按键,还额外绑定了 ConfirmTargetCancelTarget 两个按键;没错,这两个按键就会在 WaitTargetData 这样的 Task 中起作用。

如果在该 Task 执行过程中按下 ConfirmTarget,则会进入 ValidData 分支,继续技能流程,若按下 CancelTarget 则会进入 Cancelled 分支,即取消技能释放。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void AGDHeroCharacter::BindASCInput()
{
if (!ASCInputBound && AbilitySystemComponent.IsValid() && IsValid(InputComponent))
{
AbilitySystemComponent->BindAbilityActivationToInputComponent(InputComponent,
FGameplayAbilityInputBinds(
FString("ConfirmTarget"),
FString("CancelTarget"),
FString("EGDAbilityInputID"),
static_cast<int32>(EGDAbilityInputID::Confirm),
static_cast<int32>(EGDAbilityInputID::Cancel)
)
);

ASCInputBound = true;
}
}

image.png

可以看到在进入 Cancelled 分支后,会直接调用 CancelAbility 取消技能释放,否则将会执行 CommitAbility

需要注意的是,处理技能的 cost 与 cooldown 都会在 Commit 中进行,也就是说一个 GA 在 Commit 之前是不会消耗蓝量与进入冷却的。同时,这里还设置了只在 Server 才继续执行,否则会直接结束技能。

image.png

在 Server 端会先通过两个 GE 来生成 GESpec(Gameplay Effect Spec),这两个 GE 其中一个负责造成伤害,另一个负责给目标施加眩晕效果,前者的流程与 FireGun 中相同,都会在 GESpecHandle 中设置一个 Tag 为 Data.Damage 的变量,代表将会造成的伤害,最终会由 GDDamageExecCalculation 将该值取出并计算伤害。

image.png

最后,会由 WaitTargetData(Task) 得到的 TargetData 确定 Meteor 的生成位置,并且会把前面得到的两个 GESpec 传递给 RockClass 的两个成员变量,最后结束技能。

蓝图中对于 OnEndAbility 也进行了重写,主要负责重置相机位置以及取消辅助信息的显示,这里就不再分析。

OK,这样我们就分析完了 Meteor 的技能流程,下一节再见!

现在我们分析完了 Meteor 技能的总体流程,接下来开始分析其中的各部分内容。

WaitTargetData & TargetActor

以下内容来自 GASDocumentation_Chinese:TargetActor

GameplayAbility使用 WaitTargetData AbilityTask 生成 TargetActor 以在世界中可视化和获取定位信息. TargetActor 可以选择使用 GameplayAbilityWorldReticles 显示当前目标. 经确认后, 定位信息将作为 TargetData 返回, 之后其可以传递给GameplayEffect

TargetActor 是基于 AActor 的, 因此它可以使用任意种类的可视组件来表示其在何处和如何定位的, 例如静态网格物(Static Mesh)和贴花(Decal). 静态网格物(Static Mesh)可以用来可视化角色将要建造的物体, 贴花(Decal)可以用来表现地面上的效果区域. 样例项目使用带有地面贴花的 AGameplayAbilityTargetActor_GroundTrace 表示陨石技能的伤害区域效果。

其使用基本的射线或者 Overlap 捕获定位信息, 并根据 TargetActor 的实现将FHitResultAActor 数组转换为TargetDataWaitTargetData AbilityTask 通过 TEnumAsByte<EGameplayTargetingConfirmation::Type> ConfirmationType 参数决定目标何时被确认, 当不使用TEnumAsByte<EGameplayTargetingConfirmation::Type::Instant 时, TargetActor 一般就在 Tick() 中执行射线/Overlap, 并根据它的实现来更新位置信息到FHitResult。尽管是在 Tick() 中执行的射线/Overlap, 但是一般不用担心, 因为它是不同步的并且一般没有多个(尽管存在多个) TargetActor 同时运行, 只是要留意它使用的是 Tick()

对于TEnumAsByte<EGameplayTargetingConfirmation::Type::Instant, TargetActor 会立即生成, 产生 TargetData, 然后销毁, 并且从不会调用Tick()

WaitTargetData AbilityTask 将一个 AGameplayAbilityTargetActor 类作为参数, 其会在每次 AbilityTask 激活时生成一个实例并且在 AbilityTask 结束时销毁该 TargetActorWaitTargetDataUsingActor AbilityTask 接受一个已经生成的TargetActor, 但是在该 AbilityTask 结束时仍会销毁它. 这两种 AbilityTask 都是低效的, 因为它们在每次使用时都要生成或需要一个新生成的 TargetActor, 它们用于原型开发是很好的, 但是在实际发布版本中, 如果有持续产生 TargetData 的场景, 像全自动开火, 你可能就要探索优化它的办法。

--------------------- over --------------------

在 Meteor 的确认目标阶段中,会执行 WaitTargetData 任务,该 Task 会生成一个 TargetActor 用于确认目标,在技能中,该 class 被指定为 BP_MeteorTargetActor,这个类继承自 GameplayAbilityTargetActor_GroundTrace 并且有一个 Decal 组件用于在物体表面显示技能范围。

GameplayAbilityTargetActor_GroundTrace 也是 GAS 自带的类,用于地面目标的确认。类似地,GAS 还定义了许多其他的 TargetActor 类。

image.png

如果想要实现诸如《死亡搁浅》、《深海迷航》等的建造确认效果,或者简单地实现一个显示手榴弹轨迹确认的功能,就可以考虑使用这些类作为 TargetActor 或自定义一些子类。

Cost & Cooldown

以下内容翻译自 GASDocumentation-cost&cooldown:

GameplayAbilities 自带了可选的消耗和冷却功能。消耗是 ASC 必须拥有的预定义的属性量,以便激活用即时(instant) GameplayEffect(Cost GE)实现的 GameplayAbility。冷却是阻止 GameplayAbility 重新激活(直到冷却结束)的计时器,由持续 GameplayEffect(Cooldown GE)实现。

GameplayAbility 调用 UGameplayAbility::Activate() 之前,它会调用 UGameplayAbility::CanActivateAbility()。这个函数会检查所属的 ASC 是否能够承受消耗(UGameplayAbility::CheckCost())并确保 GameplayAbility 不在冷却期(UGameplayAbility::CheckCooldown())。

GameplayAbility 调用 Activate() 之后,它可以选择性地使用 UGameplayAbility::CommitAbility() 随时提交消耗和冷却,UGameplayAbility::CommitAbility() 会调用 UGameplayAbility::CommitCost()UGameplayAbility::CommitCooldown()。如果它们不需要同时提交,设计师可以选择分别调用 CommitCost()CommitCooldown()。提交消耗和冷却会再次调用 CheckCost()CheckCooldown(),这是 GameplayAbility 失败的最后机会。所属的 ASC 的属性可能在 GameplayAbility 激活后发生变化,导致在提交时无法满足消耗。如果在提交时预测键有效,那么提交消耗和冷却可以在客户端进行预测。

----------------- over ----------------

可以看出,GA 可以配置 CostGE 与 CooldownGE 两个 GE 用于限制 GA 的使用,这两个 GE 会在 CommitAbility() 阶段进行调用并实际消耗属性与计算 cd,并且在 UGameplayAbility::CanActivateAbility() 阶段还会检查技能能否释放。但 CostGE 中消耗的属性一定要是在 AttributeSet 中定义好的。

image.png

这里的两个 GE 都比较简单,就不再分析了。值得注意的是 CooldownGE 一定要是一个 Duration GE,并且 DurationTime 就代表了冷却时间。

StunGE & GC

顺着蓝图的流程,现在就来到了 GESpec 的阶段,上文中分析过,Meteor 这个 GA 会施加两个 GE,分别是 Damage 与 Stun,生成 GESpec 后会传给生成的 MeteorActor。其中 DamageGE 的作用过程与 FireGun 中完全相同,这里就不再分析,仅分析一下 StunGE。

查看 GE_MeteorStun 的蓝图,有两个需要注意的地方:

image.png

首先,在 Display 这里同样也配置了一个 GC,用于播放技能效果;其次,在 GrantedTags 中配置了 State.Debuff.Stun,这就意味着该 GE 在作用时会将此 Tag 赋予角色。

image.png

此外,该 GE 也是一个 DurationGE,不难想到,在 GE 结束时会把 GrantedTag 去除,从而达成一定时间内晕眩这样的效果。问题在于该如何将 Tag 与角色的动画联系起来,达到被击中就会倒地的效果,并且一段时间后就会恢复。

查看角色的动画蓝图 ABP_Hero,会发现这样的一些节点:

image.png

这里会检查角色是否具有 State.Debuff.Stun 的 Tag 并将结果赋给 IsStunned 变量。

image.png

同时,在动画机的 Statuses 中可以看到这样的状态转换,就是利用 IsStunned 作为条件。现在就真相大白了,通过在动画蓝图中检测角色是否具有某个 Tag 的方式来完成动画与 Tag 的联动,这种方法也值得大家借鉴。

GC_Stun 负责播放陨石击中后的特效,该 GC 继承自 GameplayCueNotify_Actor,由于 GameplayCueNotify_Actor 本身就继承自 Actor,因此可以在该 GC 中额外处理许多逻辑。如 Tick() 等,案例中这个 GC 比较简单,就是一个循环播放的特效,并且重写了 OnRemove(),使得 GC 在结束后会播放另一个特效。

其次,在玩家处于 Stun 状态时,也是不能释放其他技能与移动的,前者可以通过在其他技能中配置 BlockTags 的方式实现,但移动毕竟不作为 GA,该如何实现 Stun 状态无法移动呢?

项目中通过重写 CharacterMovementComponentGetMaxSpeed() 的方式来实现这个功能,

1
2
3
4
if (Owner->GetAbilitySystemComponent()->HasMatchingGameplayTag(FGameplayTag::RequestGameplayTag(FName("State.Debuff.Stun"))))
{
return 0.0f;
}

当玩家带有 State.Debuff.Stun 标签时,设置最大速度为 0,这样就无法移动了。

BP_Meteor

最后就是分析技能的本体 BP_Meteor 了,就像 FireGun 的 GE 是通过子弹击中角色赋予的一样,Meteor 技能的生效也是通过该 Actor 的 BeginOverlap() 事件触发。BP_Meteor 继承自 GDProjectile,因此具有 ProjectileMovement 组件,也可以设置范围。

image.png

在碰撞到地面时触发之后的流程。

image.png

image.png

只会在 Server 端触发后续的逻辑,会检测所有的 OverlapActor 并找出所有 GDCharacterBase 施加 Damage 与 Stun 两个 GE。但不会包括释放者自己。

总结

本节我们分析了 Meteor 这个技能的作用流程,该技能比 FireGun 要复杂许多,其中最主要的两个是 WaitTargetData 这个 Task 的使用以及 Tag 和 AnimationBlueprint 联动的方式,掌握了这些相信大家就能制作出具有瞄准阶段的技能了。