系列文章目录
文章目录
Gameplay Abilities (GA)
- 游戏玩法能力是角色在游戏中可以执行的动作或技能,它是GAS的核心之一,它是从游戏玩法能力派生出来的一个类,它定义了一个能力的功能以及它可以被使用的条件
- 要使用能力,必须将其授予能力系统组件,我们在服务器上执行这个操作,这时就会授予一个能力Spec(Ability Spec),并将其复制到拥有该能力的客户端
- 要使用一个能力,必须激活它,它可以一直处于活跃状态,直到被结束或取消
- 能力内置了成本和冷却(Cost and Cooldown)的概念
- 能力可以异步运行,并且多个能力可以同时处于活跃状态
- 一个能力可以运行能力任务(Ability Tasks),这些是异步操作,将行为封装到单独的类中,每个类都可以执行自己特定的任务(Tasks暂时没有学习,后面会讲到)
①创建 Gameplay Abilities 并简单授予能力
创建 GA 类
![[image-20240801145355788.png]]
授予能力
- 在 游戏能力系统组件类创建一个函数(这里我是AddCharacterAbilities)用来添加能力
- 在游戏能力系统组件类创建一个包含多个或者一个的能力数组,供AddCharacterAbilities函数遍历使用
- 人物角色类也创建AddCharacterAbilities函数,在角色类里调用游戏能力系统组件的AddCharacterAbilities函数
- 授予能力通常在服务端完成,因此我们要去到角色的OnPosses函数(因为角色一旦被possessed服务端会调用这个函数,所以在这里初始化最好)里进行授予
![[image-20240801152358252.png]]
- 获得能力并且立马激活能力,用于调试 ,调试成功后改回GiveAbility 函数不要Activate的,因为能力应该是被什么东西所触发的,这只是用于测试所以一获得能力就激活能力
![[image-20240801152425171.png]]
AAuracharacter继承于AcharacterBase,因此直接调用函数即可添加能力
![[image-20240801152452310.png]]
创建GA的蓝图类,然后放到数组里 开始游戏即可
![[image-20240801152643671.png]]
![[image-20240801152652939.png]]
![[image-20240801152701538.png]]
② GA蓝图类中的设置
Tags栏
![[image-20240801162739840.png]]
![[image-20240801160959896.png]]
- Ability Tags:这些是当前能力蓝图类所能拥有的标签,能力系统组件有一个函数是根据标签激活GA的函数,要想让这个函数成功激活能力,那么我们在蓝图类中的AbilityTags容器里就得添加标签
- Cancel Abilities with Tag: 当执行当前蓝图类的能力时,有这些标签的物体身上的能力会被取消
- Block Abilities with Tag:含有这些标签的能力在此能力激活时会无法激活
- Activation Owned Tags:这个容器中的任何标签都会被赋予激活者,并且在能系统全局中有一个设置,可以设置为Replicated这些
- Activation Required Tags:只有当激活这个能力的角色或组件有容器里的的所有标签时这个能力才能被激活
- Activation Blocked Tags:如果激活当前能力的角色或组件拥有这些标签中的任何一个,这个能力将被阻止无法激活能力
- Source Required Tags:它表示只有当源角色/组件拥有全部这些标签时,这个能力才能被激活
- Source Blocked Tags:源阻止标签类似于第六点的Activation Blocked Tags,只不过它是激活者,而这个是源角色
- Target Required Tags:它表示只有当 目标 角色/组件拥有全部这些标签时,这个能力才能被激活
- Target BBlockedTags:如果目标角色或组件拥有这些标签中的任何一个,这个能力将被阻止,无法激活目标的能力
Input栏
![[image-20240801162857858.png]]
Replicate Input Directly: 如果每帧都在接收输入,那么会将这些输入按下和释放事件始终复制(replicate)到服务器
Advanced栏
![[image-20240801164231187.png]]
- Replication Policy 复制策略:它说明了一个能力如何复制状态(State)和事件(events)到网络上的每个人,默认情况下能力是会被复制的,不太需要调整这个
-Server Respects Remote Ability Cancellation :如果设置了这个,服务器端的能力版本可以被客户端版本取消,也就是说如果你在客户端取消这个能力,那么服务器将会被通知同时能力也会被取消,这在某种程度上赋予客户端取消能力的权限- Retrigger Instanced Ability:如果为true,当尝试激活一个已经激活的实例能力时会结束能力同时重新触发能力
- Net Security Policy网络安全策略:客户端是否被允许更改能力的执行
Instancing Policy 实例的策略
![[image-20240801170522577.png]]
Instancing Policy:实例化策略是能力如何被实例化或可能不被实例化
实例策略共有三种选项:每个角色实例化、每次执行实例化和非实例化,所以GA是可以实例化的类
![[image-20240801165131764.png]]
- Instanced Per Actor:如果我们选择的策略为每个角色实例化,那么我们只会为当前的这个Actor实例化成一个单例的实例,能力类在每次激活时,这个单例都会被重用,也就是说这个能力类是持久的,它可以存储持久数据,可以有变量,如果需要在每次激活能力时重置变量,你必须手动操作,因为这个能力从未被销毁,它被重用,始终存在,这个性能稍微比Instanced Per Execution 好一点
- Instanced Per Execution:顾名思义即每次都执行实例化,我们在每次激活能力时都创建一个新的GA实例,所以我们不断创建和销毁能力,它在激活期间不存储持久数据,性能是三者中最差的
- Non-Instanced:不实例化GA类,从不创建游戏能力类的实例,只是使用类的默认对象,但是这将大大限制能力的功能,因为你无法存储状态、无法绑定到能力任务的委托, 可以类比静态函数 无法保存值也不创建对象,可以接受输入,性能最佳
Net Execution Policy 网络执行策略
即能力如何在网络上执行,客户端是询问并预测、询问并等待,还是不询问,直接执行
![[image-20240801170614524.png]]
有4个选项:仅本地、本地预测、仅服务器和服务器初始化时候
![[image-20240801170638453.png]]
- Local Only: 当我们激活一个能力时,该激活只会在本地客户端运行,服务器甚至不会运行该能力,它不会执行任何功能,这适合一些仅本地客户端需要看到的外观效果,并不涉及游戏中其他任何人,这些东西不会对游戏玩法产生太大影响
- Local Predicted:本地预测是在客户端和服务器上激活的,因此需要一点时间传输到服务器,利用了预测,服务器能够回滚无效的更改,就像游戏玩法中的预测处理一样,即 我们可以激活一个能力,并在客户端本地执行操作并且无需等待服务器的确认
- Server Only:仅在服务器上运行,这可能是一些被动游戏能力,我们希望它们一直运行,一直在做某事或监听某事
- Server Initiated:在服务器上运行,但随后会复制到拥有的客户端,必须先在服务器上运行
Costs栏
![[image-20240801164250798.png]]
- GA 有资源需求或成本的概念,它与属性直接相关,cost以GE(Gameplay effect)的形式实现,一般我们创建一个成本游戏效果并设置它
- 这个Cost GE class决定了激活这个能力需要使用哪些属性,以及消耗多少资源,后面我们将在大多数能力(Ability)中实现Cost
Triggers栏
![[image-20240801164730391.png]]
能力触发器允许我们在什么情况下触发当前这个蓝图类的能力,即 这个能力可以在事件发生时或标签被添加时触发
Cooldowns栏
![[image-20240801164902633.png]]
泠却时间,就像成本cost一样,冷却时间也是以GE的形式实现,GE将决定等待多久才能再次激活这个能力
设置时的注意事项
以下设置是不建议使用的
![[image-20240801172031099.png]]
分别是:
- Replication Policy,实际上游戏能力已经从服务器复制到拥有的客户端,参考![[Ability System Questions.docx]]
- ServerRespectsRemoteAbilityCancellation:客户端可以取消服务器的能力,一般来说服务器是权威,应该听从服务器的,所以也不使用这个设置
- ReplicateInputDirectly,每帧都会发送事件给服务器,在什么情况下都是坏主意
③ 通过输入(Input)来激活能力(GA)
1.选择输入激活GA的输入系统
- 将输入直接绑定到能力系统组件(Ability System Component) 旧版输入系统:
- 在没有增强输入的时候,我们可以直接绑定到组件上,然后按下一个按钮,能力会自动接收该输入并激活或执行它想做的事情
- 但是实现这个方法是通过创建一个包含能力输入常量的枚举来实现的,这是一个严格(rigid)的系统,比较硬编码?
- Enhanced Input 增强输入:
- 事情的处理方式有所不同,灵活度更大,输入动作(IA)通过输入映射上下文(Input Mapping Context)绑定到输入
- 我们可以决定如何根据这些输入来激活能力,我们可以以任何方式来实现,官方模版Lyra项目就提供了一个例子,将增强输入与该项目的游戏能力系统能力连接的例子
- 采用数据驱动,本例子采用数据驱动方法,能够在运行时更改输入到能力的映射,也就是说当按下左键时激活特定能力是可以在运行时更改成其它能力的,我们可以创建一个数据资产(Data asset)将每一个输入动作IA(Input Action)与GT游戏标签连接起来,而每个能力又与其输入标签相关联,这样一来就可以对某个按键触发相应的能力进行检查和修改
2.创建数据资产
![[image-20240801174736546.png]]
![[image-20240801175013778.png]]
在类中声明结构体(这里是InputToGameplaTag)和一个数组(装载着InputToGameplaTag的数组) ,以及这个数组相应的查找方法
![[image-20240801185705016.png]]
添加新的游戏标签 (一一对应按键)
![[image-20240801185753663.png]]
分别为1、2、3、4、鼠标左右键创建输入动作IA,类型都为Axis1D,并添加到IMC映射中
![[image-20240801185926308.png]]
IMC配置好
![[image-20240801185941681.png]]
最后以刚刚的类为基础创建资产蓝图类并进行填充
![[image-20240801190026883.png]]
3.创建自定义增强输入组件类
- 创建我们自己的增强输入(UEnhancedInputComponent)类,在这个类中遍历刚刚创建的数据资产中的所有IA绑定回调函数,这里我们叫做BindAbilityActions函数
- 回到编辑器设置默认的输入类为我们创建的继承于UEnhancedInputComponent的类,我这里是UBaseEnhancedInputComponent
- 增强输入类有bindAction函数,分别吧按下、释放、持续按下的函数都给绑定上去,这些函数都带有一个函数参数,即游戏标签,放到模板函数的最后面即可
- 该函数需要一个数据资产,就是我们刚刚创建的那个资产,这里叫URPGAuraInputConfig
![[image-20240805201636002.png]]
别忘了设置默认的增强输入类组件
![[image-20240805202315100.png]]
4.在能力系统类创建激活能力的函数
- 进入我们自己的能力系统组件类
- 分别为按下、释放、持续按压创建三个不同的函数,供我们后面在玩家的控制器里绑定输入的回调函数里调用这三个函数
![[image-20240805204424646.png]]
函数实现
![[image-20240805204953086.png]]
Held函数与Pressed的暂时一样
![[image-20240805205026213.png]]
释放函数这里暂时不做任何终止能力的事情
![[image-20240805205046083.png]]
5.输入绑定到能力
- 去到你的控制器Controller类,找到(覆盖)SetupInputComponent()函数,在该函数里将Input组件转换成我们的增强输入类,然后调用BindAbilityActions方法
- 创建按下、释放、持续按压的回调函数 (数据资产里的InputAction全都会绑定同一个函数,可以根据传进来的游戏标签进行对应的操作)
![[image-20240805212700309.png]]
- 这里为什么能直接获取标对应的标签进行比较,请参考[[#④ 在Cpp中获取Tag]]
- 这里的获取能力系统的方法需要自行创建,之前我们在能力系统中创建了三个对应的函数,因此这里直接调用即可
- 剩下的Held和释放函数也是一样,直接调用能力系统组件里的Held和released函数即可
![[image-20240805212858642.png]]
- 最后就是测试,之前我们已经在[[#①创建 Gameplay Abilities 并简单授予能力]] 里创建了一个能力数组,因此我们在编辑器创建一个能力蓝图添加能力蓝图类到数组就行了,游戏开始的时候角色就有这个能力了只是没有激活
- 现在我们进行按键就能激活所有允许激活的能力了
6.InvokeReplicatedEvent 执行复制事件
建议学习完[[#(5) Gameplay Abilities (GA)]]之后再来
在[[#4.在能力系统类创建激活能力的函数]] 知道我们可以通过AbilitySpecInputPressed()或者Release告知能力系统我们对应按键来启动的能力输入已经按下或者释放,但是并没有看到有什么实际作用,那么到底有什么用呢,我们可以去到任意一个GA蓝图类中找到如下图方法:
![[image-20241029104206468.png]]
其实很简单,当我们触发一个持续性的技能时候(比如电击),我们需要知道按键什么时候释放以便终止我们的能力或者做其它操作
- TODO
④ 创建执行自定义能力的Ability Class
- 继承自我们基础的能力类(UBaseGameplayAbility),这个基础能力类是继承自系统的UGameplayAbility类的,所以我们覆写ActivateAbility函数即可
- 这样AbilitySystemComponent激活能力时就会调用这个函数
- 最后以该类创建蓝图类即可
![[image-20240805214201027.png]]
这里我创建的是飞弹能力,仅仅是生成飞弹而已,飞弹不会调整方向朝着敌人方向射出,当然这里也可以获取数据来设置方向,但是类似这种操作我们后面会用AbilityTask辅助我们以完成这种操作
![[image-20240805214501549.png]]
⑤ 通过GameplayTag启动能力
在能力系统中调用该函数 (别忘了先进行授予能力[[#授予能力]] 不然无法激活能力):
- 标签容器GameplayTagContainer: 填入 FGameplayTagContainer(你的游戏标签)
- bAllowRemoteActivation:该参数如果你是在服务端调用的话就不用设置了
UAbilitySystemComponent::TryActivateAbilitiesByTag(const FGameplayTagContainer& GameplayTagContainer, bool bAllowRemoteActivation)
注意用该函数的时候,你的能力蓝图里的Ability tags标签容器 得包含刚刚填入的标签,例如
![[image-20240814213649242.png]]
蓝图里要包含该标签
![[image-20240814213723035.png]]