Tizeng's blog Ordinary Gamer

GAS的基本类型

2022-08-29
Tizeng
GAS

之前有关GAS的笔记统一移到这里,主要记录GAS中重要的类型和它们的关系。

(2022.3.16)ui方面的事情总算告一段落,有时间继续去看技能有关的文档了。

(2025.2.28)没学会的知识不会自己跑回来找你。

什么是GameplayAbilitySystem(GAS)

GAS是虚幻引擎下的一套技能框架插件,它的优势是易于扩展,实现复杂的技能流程,而且支持网络复制,它在EPIC公司内部已经被Fornite登大型的商业联机游戏所使用,开发联机游戏的坑很多都帮你踩过了,避免重复造轮子的麻烦。其中分离的GA、GE模块实现了技能核心逻辑和技能效果的解耦,易于修改和复用,开发时能够专注在某个特定模块,而不是在一个技能中考虑所有事情。

主要参考的资料是GASDocument及其中文翻译,还有虚幻官方的视频介绍和GAS入门的直播录像

AbilitySystemComponent(ASC)

要使用GAS的功能就必须为某个Actor挂上ASC,主要包含以下信息:

  • ActiveGameplayEffects:目前激活的GE
  • ActivatableAbilities:可激活的GA
  • AbilityScopeLockCount:用来判断是否在当前作用域被锁定,以延迟一些行为的发生,GA中也有类似的处理
  • AbilityPendingAdds:GASpec数组,用来延迟处理增加GA
  • AbilityPendingRemoves:GASpecHandle数组,用来延迟处理移除GA

在对他们进行遍历时,要加上宏ABILITYLIST_SCOPE_LOCK以确保遍历的元素在当前作用域不会被删除,它利用的是本地声明一个FScopedAbilityListLock类型的变量,它在构造和析构中控制了ASC锁定的计数。GiveAbilityClearAbility会检查这个计数,只有当计数归零的时候才会去真正执行,下面会详细说明。

  • GiveAbility:传入GASpec,必须在有Authority的Owner上调用
    • 检查锁定计数是否大于0
      • 是:将传入的spec放进AbilityPendingAdds并返回
      • 否:加作用域锁,将传入的spec加入ActivatableAbilities数组中
    • 根据实例化policy,决定是否需要实例化一个GA,然后根据同步policy决定加入同步列表NonReplicatedInstancesReplicatedInstances,掉用OnGiveAbility,返回handle
  • OnGiveAbility:处理GA上的FAbilityTriggerData数据,增加spec信息,调用PrimaryInstance或CDO的同名方法
  • ClearAbility:传入GASpecHandle,必须在Authority的Owner上调用
    • 遍历AbilityPendingAdds数组看看其中有没有传入的handle,如果有将其删除,然后直接返回
    • 遍历ActivatableAbilities数组,通过handle找到spec
    • 检查AbilityScopeLockCount是否大于0
      • 是:将传入的handle放进AbilityPendingRemoves并返回
      • 否:加作用域锁,调用OnRemoveAbility,从ActivatableAbilities移除这个handle,更新关联的信息
  • OnRemoveAbility:清除技能上触发事件的有关信息,删除所有生成的GA实例(如果技能处于激活状态,则先调用EndAbility并注册删除事件),调用PrimaryInstance或CDO的同名方法

在作用域锁被析构时会调用ASC的DecrementAbilityListLock方法,除了会减少AbilityScopeLockCount外,还会检测其是否到了0,如果到0了则将两个pending列表中的数据拿出来处理。

  • TryActivateAbility:调用GA的CanActivateAbility,以及各种检查,最后CommitAbility

GameplayTag

这是UE自带的功能,GAS大量使用了tag来实现互斥、加状态、条件判断等。它的特点是用字符串表示的多层级的结构,这样的好处是查找起来可以按层级索引,而不用每个具体的标签都去遍历,而且字符串处理起来也非常简单方便。

  • 通常角色身上会有不只一个标签,因此会用FGameplayTagContainer来储存各种FGameplayTag
  • RegisterGameplayTagEvent是ASC中监听某个Tag的方法,它接收一个FGameplayTag和一个枚举,判断是否只在Tag新增或删除的时候才通知。在ASC中使用FGameplayTagCountContainer结构储存了现有的Tag信息,以及更新的接口。
  • UAsyncTaskGameplayTagAddedRemoved中又封装了一层蓝图接口,实现了Tag增加和删除可以注册的操作。
  • FGameplayTag中提供了比较各种情况tag的接口,如完全匹配、部分匹配、container中匹配等

  • RequestGameplayTag:根据名字得到Tag

GameplayAttribute

角色的各种属性,血量、体力、魔法之类,一般只通过GE去控制,其中维护两个值BaseValueCurrentValue,主要是为了方便在上buff之后恢复原先的值。Instant类型的GE改变BaseValue,Duration和Infinite类型改变的都是CurrentValue

多个属性可以组成属性集AttributeSet,它提供了一些宏可以直接为每个属性生成Setter、Getter以及Init函数,一般来说属性集会放在OwnerActor中。

AttributeSet

属性集从基类UAttributeSet上继承,基类中提供了获取ASC的接口

GameplayEffect(GE)

GE是纯配置蓝图,所有相关逻辑都通过配置来完成

技能产生的效果,如伤害、buff、增益、状态。通常用来修改属性。比如连击时,NPC被击杀后给玩家上一个加属性的GE,这个GE将玩家身上的“连击数”这一属性增加若干值,然后使用了ListenForAttributeChange的地方就能监听到该属性的改变。

常用的类:

  • FGameplayEffectContext:包含激活该GE的GA,释放该GE的Actor(Instigator)等
  • FGameplayEffectContextHandle:包含Context的结构,用来支持多态和网络复制,用MakeEffectContext生成
  • FGameplayEffectSpec:包含GE实例(Def),Context,等级,持续时间等,真正上GE的时候用的是这个
  • FGameplayEffectSpecHandle:包含Spec,可以用MakeOutgoingSpec生成,它会生成一个Context(或由外部传入),然后和GE类型的CDO生成一个Spec,最后用Spec生成handle返回

  • FActiveGameplayEffect:上GE成功后生成的类,包含handle、GESpec,以及相同类型的Next指针
  • FActiveGameplayEffectHandle:储存了一个int32成员Handle作为uid,用于在一个全局Map中索引对应的ASC
  • FActiveGameplayEffectsContainer:包含FActiveGameplayEffect指针作为链表头节点

常用接口:

  • SetByCaller:一种设置值的方式,代表该值由生成此GE的地方决定
  • ExecutionCalculation:可以在GE执行后做一些复杂的属性运算
  • MakeOutgoingGameplayEffectSpec:根据GE类型创建SpecHandle

要给角色上GE,可以用下面的代码:

// Can run on Server and Client
FGameplayEffectContextHandle EffectContext = AbilitySystemComponent->MakeEffectContext();
EffectContext.AddSourceObject(this);

FGameplayEffectSpecHandle NewHandle = AbilitySystemComponent->MakeOutgoingSpec(DefaultAttributes, GetCharacterLevel(), EffectContext);
if (NewHandle.IsValid())
{
	FActiveGameplayEffectHandle ActiveGEHandle = AbilitySystemComponent->ApplyGameplayEffectSpecToTarget(*NewHandle.Data.Get(), AbilitySystemComponent.Get());
}

可以看到在上GE前需要准备如下数据:

  • 创建一个EffectContext的handle
  • 用GE的类创建一个EffectSpec 最后使用ASC的方法ApplyGameplayEffectSpecToTarget,它最终会调用ApplyGameplayEffectSpecToSelf

上面是通过ASC中的接口去上,还可以通过技能去上:


GE激活流程

ASC上GE和技能上GE有什么区别 GE在哪里改的属性 属性属于谁 怎么取消GE

MMC

ExecutionCalculation

GameplayAbility(GA)

GA是技能的核心逻辑,任何行为都可以是GA,除了常见的人物主动释放的技能,还可以是交互、格挡甚至受击,但要注意基础的移动和UI上的交互不宜使用GA,GA通常用来触发某种特定条件下的行为,而不是平时一直会做的事情。

以下列举会使用到的类:

  • FGameplayAbilityActorInfo:包含该GA的OwnerActor、表现上的AvatarActor等和使用该GA的Actor有关的信息
  • FGameplayAbilityActivationInfo:激活状态,是否是Authority,以及预测情况
  • FGameplayAbilitySpec:除了等级和handle还包含以下信息
    • GA的实例,如果是从类初始化则获取其CDO
    • SourceObject:谁创建的这个GA
    • 赋予这个GA的GE的handle(FActiveGameplayEffectHandle
    • InputId:用来识别输入
    • ActivationInfo:包含激活模式和一个FPredictionKey,做预测的时候会用到
  • FGameplayAbilitySpecHandle:spec在构造时做的第一件事就是生成一个handle,区分ASC中特定的GA,全局唯一
  • FGameplayAbilitySpecDef:包含被GE赋予时所需要的信息,可以用来生成Spec,其中包含GA的类

常用的接口:

  • CanActivateAbility:检查技能释放条件,流程如下
    • 检查Avatar、网络情况(排除Role为Simulated的Avatar)和ASC
    • 检查CostGE中的Modifier是否在Apply之后属性值仍都大于0
    • 检查cd,看看ASC上有没有该技能的cd标签
    • 检查标签是否满足条件
    • 检查inputid是否被屏蔽
  • ActivateAbility
  • CommitAbility:技能真正释放,调用CommitExecute,上冷却GE和消耗GE
  • EndAbility
  • CancelAbility
  • GetPrimaryInstance:只有当实例化策略为InstancedPerActor时才会尝试从同步列表中获取GA的指针
  • SendGameplayEvent:调用ASC的HandleGameplayEvent处理响应事件,先用tag找到ASC上绑定的GASpec,然后根据GA实现的ShouldAbilityRespondToEvent接口返回值,决定是否激活GA

FGameplayAbilityTargetDataHandle FGameplayAbilityTargetData

技能释放流程

要释放一个技能,先得有这个技能,调用GiveAbility赋予玩家(或其他角色)

  1. 接收输入或事件(有时候释放技能是被动的)
  2. 检测释放条件
  3. 创建释放所需的类,拿到数据
  4. 激活技能逻辑
  5. 根据需要结束技能

如果要释放一个技能,首先要Give,这样GA(的Spec)才会被加入到ActivatableAbilities的数组中,释放的时候一般传入GA类型,然后根据CDO在数组中找到对应的GA,调用ActivateAbility

怎么上的GE,如果技能结束了,之前上的GE怎么处理? 根据RemovalPolicy的配置决定

GameplayCue

技能的视觉特效,控制技能特效的播放和停止

GameplayTask

执行异步任务的框架

UAbilityTask_WaitGameplayTag


Similar Posts

下一篇 GAS的使用

Comments