引言
本系列文章内容仅为个人学习记录,使用UE的版本为4.27,如有侵权请联系我
学习来源及各参考文档:
原文地址: tranek/GASDocumentation
翻译地址: BillEliot/GASDocumentation_Chinese
B站up主:技术宅阿棍儿
1.GAS认识(GameplayAbilitySystem)
摘自官方文档:
Gameplay技能系统 是一个高度灵活的框架,可用于构建你可能会在RPG或MOBA游戏中看到的技能和属性类型。你可以构建可供游戏中的角色使用的动作或被动技能,使这些动作导致各种属性累积或损耗的状态效果,实现约束这些动作使用的"冷却"计时器或资源消耗,更改技能等级及每个技能等级的技能效果,激活粒子或音效,等等。简单来说,此系统可帮助你在任何现代RPG或MOBA游戏中设计、实现及高效关联各种游戏中的技能,既包括跳跃等简单技能,也包括你喜欢的角色的复杂技能集。
对于人物的基础移动和对UI的交互(例如游戏商店购买物品)则不建议使用GAS(可以但不建议)。
对于GAS插件中各模块的介绍及一些名词解释:
- GSC:指GameplaySystemComponent组件,所有需要使用GAS或者响应GAS系统的都需要有这个组件
- GameplayAbility(GA):用于执行角色能力(Ability)和技能(Skill),比如让人物跳跃,冲刺,攻击等,可以设定等级、发动技能所需消耗量、技能冷却等。
- Attribute:用于管理角色或者actor的数值,例如人物血量,蓝量,体力等。
- GameplayEffect(GE):为Actor应用状态效果 ,注意:这个不是用于显示特效音效或者动作,而是用于添加状态,例如人物中毒灼烧扣血。
- GameplayTag:Tag意思为标签,用于为角色添加一个或多个标签,GameplayTag是GAS的一个重要组成部分,可用于各状态之间的限制(感觉像是替代了常规的布尔判断),比如在受到攻击时,如果自己有进行格挡,常规写法是用一个bool判断人物是否有格挡,而在GAS中则是使用GameplayTag在你使用格挡时给你添加一个标志为格挡的Tag,受击时则只需要判断人物是否有这个Tag即可。
- GameplayCue(GC):用于生成视觉或声音效果 。
- OwnerActor:拥有ASC的actor,如果是多人游戏,ASC组件一般会放在PlayerState中,这时,PlayerState就是OwnerActor,如果是单机游戏,则ASC可以放在character中,那么Owneractor就是character。
- AvatarActor:指被控制的actor物理形体,如果ASC组件在PlayerState中,PlayerState就是OwnerActor,而被控制的character就是AvatarActor,单机游戏时,ASC在character中,则OwnerActor和AvatarActor都是character。
- 还有一个为以上提到的所有应用同步(Replication).
GAS必须由C++创建,但是GameplayAbility
和GameplayEffect
可由设计师在蓝图中创建.
GAS中的现存问题:
- GameplayEffect延迟调节(Latency Reconciliation).(不能预测能力冷却时间, 导致高延迟玩家相比低延迟玩家, 对于短冷却时间的能力有更低的激活速率.
- 不能预测性地移除GameplayEffect. 然而我们可以反向预测性地添加GameplayEffect, 从而高效的移除它. 但是这不总是合适或者可行的, 因此这仍然是个问题.
2.在项目中添加GAS
GAS是UE中的一个插件,需要使用的话要先在插件中启用它,在设置-插件中搜索GameplayAbilities勾选后重启项目。
编辑
编辑
然后打开项目C++类中的 "GameplayAbilities", "GameplayTasks".
编辑
添加完成后重新编译生成一下项目
3.使用GAS实现简单的技能(跳跃、冲刺)
以UE自带的C++第三人称模板为例,制作多人游戏相关的GAS
首先,要实现GAS,就必须要有一个ASC(Ability System Component),ASC是GAS的核心,如何需要与GAS交互的Actor都必须要附加ASC,这些对象都存于ASC并由其管理和同步(除了由AttributeSet同步的Attribute).
ASC 附加的 Actor 被引用作为该 ASC的OwnerActor,该 ASC的物理代表 Actor 被称为 AvatarActor. OwnerActor 和 AvatarActor 可以是同一个 Actor,比如MOBA游戏中的一个简单AI小兵;它们也可以是不同的 Actor,比如MOBA游戏中玩家控制的英雄,其中 ownerActor是Playerstate,AvatarActor 是英雄的character 类.绝大多数Actor的 ASC 都附加在其自身,如果你的Actor会重生并且重生时需要持久化Attribute或 GameplayEffect (比如MOBA中的英雄)那么 ASC理想的位置就是Playerstate.
注意:如果ASC位于playerState,则需要将NetUpdateFrequency值调高,参考视频NetUpdateFrequency设置
创建ASC:(这里我是在继承PlayerState的类中)
.h文件:
包含头文件:
1 #include "AbilitySystemInterface.h"
继承接口IAbilitySystemInterface:
重写接口函数(必须重写):
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
声明ASC组件:
UPROPERTY() class UAbilitySystemComponent* AbilitySystemComponent;
.cpp文件:
包含头文件:
#include "AbilitySystemComponent.h"
在构造函数中创建实例:
APlayerStateBase::APlayerStateBase() { //创建ASC实例 AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystemComponent")); //开启网络复制 AbilitySystemComponent->SetIsReplicated(true); }
实现接口函数:
UAbilitySystemComponent* APlayerStateBase::GetAbilitySystemComponent() const { return AbilitySystemComponent; }
跳跃实现:
找到项目文件,例如我的项目名是GAS_TestDemo,那对应项目文件则为GAS_TestDemo.h
.h文件:
创建枚举:用于映射角色输入,UMETA(DisplayName = "Jump")里的"Jump"是这个枚举值在蓝图的别名
UENUM(BlueprintType) enum class EGASAbilityInputID : uint8 { // None UMETA(DisplayName = "None"), //跳跃 Jump UMETA(DisplayName = "Jump"), //冲刺 Sprint UMETA(DisplayName = "Sprint"), //攻击 Attack UMETA(DisplayName = "Atk"), };
在UE编辑器的项目设置-输入中创建对应名字的输入操作:(注意:这里的名字需要和上面枚举的名字一致)
因为这里的ASC在playerState,所以在character中需要获得对ASC的引用
.h文件中:
先声明ASC
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "GameplayAbilitySystem") class UAbilitySystemComponent* AbilitySystemComponent;
重写character的函数PossessedBy(),作用好像是在character被控制器控制时会回调这个函数
//在被控制器控制时调用 virtual void PossessedBy(AController* NewController) override;
.cpp文件中:
需要将输入和Ability联系起来,在SetupPlayerInputComponent函数中添加:
void AGAS_TestDemoCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) { // Set up gameplay key bindings check(PlayerInputComponent); //旧的跳跃绑定 //PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump); //PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping); //在项目文件中定义的全局结构体EGASAbilityInputID,用于和项目设置的输入操作名字对应 //将输入和能力组件绑定 //FGameplayAbilityInputBinds的第三个参数EGASAbilityInputID为项目文件创建的枚举类型 if (IsValid(AbilitySystemComponent)) { AbilitySystemComponent->BindAbilityActivationToInputComponent(PlayerInputComponent, FGameplayAbilityInputBinds( FString(), FString(), FString(TEXT("EGASAbilityInputID")), static_cast<int32>(EGASAbilityInputID::None), static_cast<int32>(EGASAbilityInputID::None) ) ); } }
实现PossessedBy():注意:因为这里的GSC在playerState中,所以需要引入头文件。同时,UGameplayAbility_CharacterJump也需要引入头文件:
#include "PlayerStateBase.h" //这个头文件是自己定义ASC的那个头文件 #include "GameplayAbilities/Public/Abilities/GameplayAbility_CharacterJump.h"
void AGAS_TestDemoCharacter::PossessedBy(AController* NewController) { Super::PossessedBy(NewController); //获取玩家的PlayerState APlayerStateBase* PS = GetPlayerState<APlayerStateBase>(); if (PS) { AbilitySystemComponent = Cast<APlayerStateBase>(PS)->GetAbilitySystemComponent(); /** *初始化了Abilities的ActorInfo(参与者信息),该结构保存了有关我们正在对谁采取行动以及谁控制我们的信息。即OwnerActor和AvatarActor *OwnerActor是逻辑上拥有此组件的参与者。 *AvatarActor是pawn。 */ PS->GetAbilitySystemComponent()->InitAbilityActorInfo(PS, this); } //***判断多人游戏时是否是在服务器端**********判断ASC组件是否有效 if (GetLocalRole() != ROLE_Authority || !IsValid(AbilitySystemComponent)) { return; } /*给予跳跃能力, *FGameplayAbilitySpec第一个参数就是当操作发生后,需要执行的东西,第二个为技能等级(暂时不知道怎么用,默认1),第三个就是绑定的输入操作键,第四个应该是绑定该技能的类吧(应该) *其中UGameplayAbility_CharacterJump::StaticClass()是UEGAS封装好的,类似于正常绑定跳跃时的参数&ACharacter::Jump * EGASAbilityInputID::Jump为项目文件中定义的那个枚举,用于和输入操作绑定 */ AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(UGameplayAbility_CharacterJump::StaticClass(), 1, static_cast<int32>(EGASAbilityInputID::Jump), this)); }
至此,角色的跳跃就已经被封装成了一个技能,由于这个跳跃是自带的,我们好像并没有做什么东西,因此继续实现一个冲刺技能
冲刺技能实现:
在角色类中:
.h文件:
声明一个GA,由蓝图去指定:
/** * @brief 声明一个能力GA */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameplayAbilitySystem") TSubclassOf<class UGameplayAbility> SprintAbility;
.cpp文件:
在PossessedBy()函数中继续添加:(为什么在这里添加?放在这里,冲刺技能就会在控制器控制character时激活此技能)
/* * 给予冲刺能力 * 因为SprintAbility是由蓝图指定,所以需要判断有效性,避免出错 */ if (SprintAbility) { AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(SprintAbility, 1, static_cast<int32>(EGASAbilityInputID::Sprint), this)); }
具体逻辑实现:
蓝图中:(也可以在C++中实现)
创建一个继承C++第三人称character的蓝图,在事件图表中创建一个自定义事件或者函数名为Sprint:
在内容浏览器中右键选择GameplayAbility,创建并命名为GA_Sprint,打开:
编写:
将此GA指定到角色蓝图中对应的值:
这样就实现了由GAS封装的角色冲刺技能,所以技能实现逻辑还是在人物身上,只是由GAS去封装调用,可以由GAS来赋予或剥除。
如果没有反应,看看项目输入的名字是否和项目文件的枚举对应上。
目前只有给予技能,还没有学习到移除技能,先这样吧。
标签:AbilitySystemComponent,插件,character,GAS,ASC,UE4,Jump,技能 From: https://www.cnblogs.com/Zhh-ki/p/16976532.html