GASDocumentation 案例项目分析(四)
前言
上一节我们分析了 FireGun 技能的整个流程,想必大家已经对 GAS 的使用有了一个初步的了解了,也大致知道了 GA、GE、GC 这些概念以及如何相互调用。本节就来继续分析另一个技能,Meteor。
Meteor
这个技能也是由纯蓝图创建的,技能整体流程包括目标确认、技能消耗、技能冷却、技能本体(陨石)、击中伤害与眩晕、击中效果(倒地)等组成,相比于 FireGun 要更为复杂。让我们逐一分析。
GA_Meteor_BP
这个蓝图就是 Meteor 技能的 GA 了,同样继承自 GDGameplayAbility
。来分析蓝图中实现了哪些功能。
作为一个 GA,最重要的肯定还是重写 ActivateAbility
。
首先还是从 ActorInfo
中获取了释放者的信息并且存在了 OwningHero
变量中。
其次是关于一些辅助信息显示的设置以及移动相机,在释放这种需要瞄准的技能时,移动相机是一个不错的做法,可以使视野开阔一些。
随后就是较为重要的瞄准部分了,这里先获取了 OwnerActor
即释放者的位置,再向前移动 200 个单位作为瞄准的初始位置。随后也启动了一个 Task。在上一节的 FireGun 中,是利用 PlayMontageAndWaitForEvent
这个 Task 来播放动画并等待事件,这里则是利用了 WaitTargetData
这一 Task,来等待瞄准的信息。
同样的,这个类也是 GAS 自带的,暂且不进行更深入的分析。但有一些性质还是需要了解的。
在 第一节 中,我们分析了输入绑定,在 AGDHeroCharacter::BindASCInput
中,可以看到这里不仅绑定了 EGDAbilityInputID
即技能对应的按键,还额外绑定了 ConfirmTarget
与 CancelTarget
两个按键;没错,这两个按键就会在 WaitTargetData
这样的 Task 中起作用。
如果在该 Task 执行过程中按下 ConfirmTarget
,则会进入 ValidData
分支,继续技能流程,若按下 CancelTarget
则会进入 Cancelled
分支,即取消技能释放。
1 | void AGDHeroCharacter::BindASCInput() |
可以看到在进入 Cancelled
分支后,会直接调用 CancelAbility
取消技能释放,否则将会执行 CommitAbility
。
需要注意的是,处理技能的 cost 与 cooldown 都会在 Commit 中进行,也就是说一个 GA 在 Commit 之前是不会消耗蓝量与进入冷却的。同时,这里还设置了只在 Server 才继续执行,否则会直接结束技能。
在 Server 端会先通过两个 GE 来生成 GESpec(Gameplay Effect Spec),这两个 GE 其中一个负责造成伤害,另一个负责给目标施加眩晕效果,前者的流程与 FireGun 中相同,都会在 GESpecHandle 中设置一个 Tag 为 Data.Damage
的变量,代表将会造成的伤害,最终会由 GDDamageExecCalculation
将该值取出并计算伤害。
最后,会由 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
的实现将FHitResult
或 AActor
数组转换为TargetData
。 WaitTargetData 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
结束时销毁该 TargetActor
。WaitTargetDataUsingActor AbilityTask
接受一个已经生成的TargetActor
, 但是在该 AbilityTask
结束时仍会销毁它. 这两种 AbilityTask
都是低效的, 因为它们在每次使用时都要生成或需要一个新生成的 TargetActor
, 它们用于原型开发是很好的, 但是在实际发布版本中, 如果有持续产生 TargetData
的场景, 像全自动开火, 你可能就要探索优化它的办法。
--------------------- over --------------------
在 Meteor 的确认目标阶段中,会执行 WaitTargetData
任务,该 Task 会生成一个 TargetActor 用于确认目标,在技能中,该 class 被指定为 BP_MeteorTargetActor
,这个类继承自 GameplayAbilityTargetActor_GroundTrace
并且有一个 Decal
组件用于在物体表面显示技能范围。
GameplayAbilityTargetActor_GroundTrace
也是 GAS 自带的类,用于地面目标的确认。类似地,GAS 还定义了许多其他的 TargetActor 类。
如果想要实现诸如《死亡搁浅》、《深海迷航》等的建造确认效果,或者简单地实现一个显示手榴弹轨迹确认的功能,就可以考虑使用这些类作为 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 中定义好的。
这里的两个 GE 都比较简单,就不再分析了。值得注意的是 CooldownGE 一定要是一个 Duration GE,并且 DurationTime 就代表了冷却时间。
StunGE & GC
顺着蓝图的流程,现在就来到了 GESpec 的阶段,上文中分析过,Meteor 这个 GA 会施加两个 GE,分别是 Damage 与 Stun,生成 GESpec 后会传给生成的 MeteorActor。其中 DamageGE 的作用过程与 FireGun 中完全相同,这里就不再分析,仅分析一下 StunGE。
查看 GE_MeteorStun
的蓝图,有两个需要注意的地方:
首先,在 Display 这里同样也配置了一个 GC,用于播放技能效果;其次,在 GrantedTags
中配置了 State.Debuff.Stun
,这就意味着该 GE 在作用时会将此 Tag 赋予角色。
此外,该 GE 也是一个 DurationGE,不难想到,在 GE 结束时会把 GrantedTag 去除,从而达成一定时间内晕眩这样的效果。问题在于该如何将 Tag 与角色的动画联系起来,达到被击中就会倒地的效果,并且一段时间后就会恢复。
查看角色的动画蓝图 ABP_Hero
,会发现这样的一些节点:
这里会检查角色是否具有 State.Debuff.Stun
的 Tag 并将结果赋给 IsStunned
变量。
同时,在动画机的 Statuses
中可以看到这样的状态转换,就是利用 IsStunned
作为条件。现在就真相大白了,通过在动画蓝图中检测角色是否具有某个 Tag 的方式来完成动画与 Tag 的联动,这种方法也值得大家借鉴。
GC_Stun
负责播放陨石击中后的特效,该 GC 继承自 GameplayCueNotify_Actor
,由于 GameplayCueNotify_Actor
本身就继承自 Actor
,因此可以在该 GC 中额外处理许多逻辑。如 Tick()
等,案例中这个 GC 比较简单,就是一个循环播放的特效,并且重写了 OnRemove()
,使得 GC 在结束后会播放另一个特效。
其次,在玩家处于 Stun 状态时,也是不能释放其他技能与移动的,前者可以通过在其他技能中配置 BlockTags 的方式实现,但移动毕竟不作为 GA,该如何实现 Stun 状态无法移动呢?
项目中通过重写 CharacterMovementComponent
的 GetMaxSpeed()
的方式来实现这个功能,
1 | if (Owner->GetAbilitySystemComponent()->HasMatchingGameplayTag(FGameplayTag::RequestGameplayTag(FName("State.Debuff.Stun")))) |
当玩家带有 State.Debuff.Stun
标签时,设置最大速度为 0,这样就无法移动了。
BP_Meteor
最后就是分析技能的本体 BP_Meteor
了,就像 FireGun 的 GE 是通过子弹击中角色赋予的一样,Meteor 技能的生效也是通过该 Actor 的 BeginOverlap()
事件触发。BP_Meteor
继承自 GDProjectile
,因此具有 ProjectileMovement
组件,也可以设置范围。
在碰撞到地面时触发之后的流程。
只会在 Server 端触发后续的逻辑,会检测所有的 OverlapActor 并找出所有 GDCharacterBase
施加 Damage 与 Stun 两个 GE。但不会包括释放者自己。
总结
本节我们分析了 Meteor 这个技能的作用流程,该技能比 FireGun 要复杂许多,其中最主要的两个是 WaitTargetData
这个 Task 的使用以及 Tag 和 AnimationBlueprint 联动的方式,掌握了这些相信大家就能制作出具有瞄准阶段的技能了。