游戏性技能基础

GameplayAbility(游戏性技能)

到这里https://github.com/tranek/GASDocumentation?tab=readme-ov-file#46-gameplay-abilities 学习游戏技能的基础。

游戏技能定义

在游戏世界中,一个Actor能执行的许多功能都可以通过游戏技能(GA)实现。

你可以通过蓝图/C++继承自GameplayAbility或者GGA_GameplayAbility来创建新的技能。

并覆写对应的函数以实现技能的相关功能。

简单技能案例(跳跃技能)

gga guide 23

它在技能激活时让角色跳跃,在技能结束时取消跳跃,同时由于这个技能不存储任何状态/数据,只执行逻辑,所以它的实例化策略可以设置为NonInstanced(不创建技能实例)

gga guide 24

它同时还重写了CanActivateAbility,用于检查当前角色是否可以跳跃。

游戏技能标签

看这里:https://github.com/tranek/GASDocumentation?tab=readme-ov-file#469-ability-tags

理解技能定义和技能实例

首先,有几个概念我需要澄清,它非常令人混淆,即便是C++用户很多时候也难以搞清楚它们之间的关系。

技能定义:任何在编辑器下创建的继承自GameplayAbility的蓝图资产,或者在C++继承自GameplayAbility的子类,你都可以理解为是技能的定义。

技能实例:将技能定义赋予给AbilitySystem组件后创建的结构体GameplayAbilitySpec可以理解为技能实例。

技能定义实例:毕竟技能定义是一个Class,而该类的实例化后的对象称之为技能定义实例,而技能定义实例的创建规则则由技能定义中的实例化策略决定。

gga guide 25

技能实例

当技能被赋予到AbilitySystem组件后,会创建一个结构体叫做GameplayAbilitySpec,你可以将它理解为技能的实例,它包含了大量关于该技能的信息,如它创建时用到的定义Class,它的等级等等。

同时GAS还提供了另外一个轻量的结构体叫做GameplayAbilitySpecHandle,你可以理解为它是指向技能实例的"指针",在实际开发中,一般在各种代码执行过程中将SpecHandle进行传递,从而避免直接传递这个具备很多信息的AbilitySpec本身。

GameplayAbilitySpec里的API基本上被隐藏在C++,但是GGA提供了函数可以拿到Spec里的信息。

而大多数情况下,蓝图用户只需要通过AbilitySpecHandle进行操作即可。

技能实例化策略

在技能定义中,你可以选择三种不同的实例化策略。

gga guide 26

非实例化

不要再使用NonInstanced了,从UE5.5开始,该策略已经被弃用

每Actor一个实例

这个策略指的是在赋予技能定义时,为技能定义创建一个实例。这是最常用的,也是最符合GAS设计的一种执行策略,即:每一个Actor拥有自己独一无二的那一份能力。

每次执行时实例化

这个策略指的是在每次尝试激活该技能时创建一个新的实例来执行技能。在实际项目开发中,这非常昂贵并很少用到。如果你是新手,避开它。

如何选择

大多数情况下请选择Instanced Per Actor以保持简单和避免潜在的异常问题。

获取技能实例

在技能赋予到AbilitySystem组件后,会被添加到组件内部维持的一份技能实例列表。

在你需要对技能进行操作时,你得先获取你需要操作的技能实例。

GAS默认提供如下函数可以获取技能的实例。

gga guide 27

但GGA提供更多方便的函数来获取到技能的实例。

gga guide 28

激活技能实例

下面是激活技能的主要方式。

gga guide 29

避免通过Class来激活技能,因为它不够灵活,它让你的代码与具体的技能定义产生了耦合,并失去了GameplayTags带来的多态性。

(比如两个不同的近战AI,你通过Tag:LightAttack激活攻击,但由于它们是不同的AI,LightAttack关联的可能是它们自己的专属版LightAttack。)

你还可以通过如下API检查某个技能是否可以被激活。

gga guide 30

取消技能实例

除了在技能的内部自身调用EndAbility以外,你还可以在外部取消技能。

gga guide 31

获取技能定义实例

取决于技能定义的实例化策略,当你拿到一个技能实例后,你可以根据实例化策略选择合适的API拿到技能定义实例。

gga guide 32