Unreal Game Ability System (GAS)

Posted by Caroline飘小蝎 on September 6, 2024

参考资料:

学习大纲

插件应用

并在Build.cs中增加插件依赖:

1
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "GameplayAbilities", "GameplayTags", "GameplayTasks" });

基础

技能 AbilitySystemComponent (ASC)

以英雄联盟为例:

  • Lv
  • Gameplay Effect (GE)
    • 消耗
      • HP, MP, Strength
    • 伤害
      • 魔法,物理
  • Gameplay Cue (Cue)
    • 特效
    • 声音
  • Gameplay Task
    • 效果(击飞等等)
  • Gameplay Tag (Tag)
    • 技能之间的相互影响(优先级打断)

属性 Gameplay Attribute (GA)

以英雄联盟为例:

  • HP
  • MP
  • 冷却速度
  • 移动速度

项目配置

  • 在Project Settings中关闭自动曝光(Auto Exposure)
  • 创建BaseCharacter (C++), BP_BaseCharacter, BP_Player, BP_Enemy, BP_GameMode, BP_PlayerController
  • 在BP_Player中新增SpringArm和Camera

基本角色与相机控制

角色控制

角色移动、转向、跳跃

将第一人称控制模式转换为第三人称控制:

  • BP_Player:取消勾选Use Controller Rotation Yaw

  • SpringArm:勾选Use Pawn Control Rotation
  • CharacterMovement:勾选Orient Rotation to Movement

相机锁定模式和夹角限制

在BP_Player中限制夹角

相机视角锁定与非锁定设置

平A与移动动画制作

动画制作:创建BlendSpace和AnimationBlueprint

BS_Walk

ABP_Sinbi

Event Graph:

Anim Graph:

创建状态机StateMachine

Idle/Jog:

JumpStart:

JumpLoop:

JumpEnd:

Idle/Jog to JumpStart

JumpStart to JumpLoop

JumpLoop to JumpEnd

JumpEnd to Idle/Jog

创建蒙太奇 Montage

MA_MeleeAttack

在BP_BaseEnemy中创建CustomEvent

再在PlayerController中调用这个Event

目前存在的问题:边走边平A的时候腿部不动,非常违和

解决方法:动作融合

动画融合

ABP_Sinbi

并在Layered Blend per bone设置:

但存在问题:效果像是在腰部被切断一样

解决方法:在前几帧将动画控制权交给未融合的Montage

在脚离地及落地的时候分别Add Notify,对应StartMontage和EndMontage

在ABP_Sinbi的Event Graph中增加是否需要Move蒙太奇的设置

在AnimGraph中实现是否需要Move蒙太奇的切换实现

创建一个平A及CD

添加AbilitySystemComponent和GameplayAbility基类

在BP_BaseCharacter上新增Ability System这个Component

创建GAB_BaseAbility(父类是GameAbility)以及GAB_MeleeAttack(父类是GAB_BaseAbility),并给后者增加标签

GAS结合gameplay的做法

在BP_BaseCharacter中新增触发技能函数ActivateAbility,并在Gameplay Tag Container中配置所有的Tag

用触发技能函数实现MeleeAttack Event

在BP_BaseCharacter开始的时候执行GiveAbility

在GAB_MeleeAttack中实现蒙太奇的播放

注意:子类BP_Player的BeginPlay一定要继承父类的方法(即GiveAbility),否则动画无法正常生效

平A的CD设置

新增GE_MeleeCD(父类是GameEffect)并配置0.5s的CD时间

注意还需要在GAB_MeleeAttack中提交CD

平A碰撞检测

在BP_BaseCharacter的Mesh下新增胶囊体碰撞,并设置Parent Socket为weapon_r,调整位置及大小

碰撞逻辑实现:

解决不在攻击时仍然判定伤害的bug:

增加StartATK和EndATK的Notify

改变BP_BaseCharacter的碰撞设置

角色属性

创建角色属性

创建BaseAttributeSet(C++,父类为AttributeSet)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#pragma once

#include "CoreMinimal.h"
#include "AttributeSet.h"
#include "AbilitySystemComponent.h"
#include "BaseAttributeSet.generated.h"

#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
 	GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
 	GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
 	GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
 	GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)


UCLASS()
class UNREALGAS_API UBaseAttributeSet : public UAttributeSet
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere,BlueprintReadWrite,Category = "BaseAttributeSet")
	FGameplayAttributeData HP;
	ATTRIBUTE_ACCESSORS(UBaseAttributeSet, HP);

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "BaseAttributeSet")
	FGameplayAttributeData MaxHP;
	ATTRIBUTE_ACCESSORS(UBaseAttributeSet, MaxHP);

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "BaseAttributeSet")
	FGameplayAttributeData MP;
	ATTRIBUTE_ACCESSORS(UBaseAttributeSet, MP);

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "BaseAttributeSet")
	FGameplayAttributeData MaxMP;
	ATTRIBUTE_ACCESSORS(UBaseAttributeSet, MaxMP);

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "BaseAttributeSet")
	FGameplayAttributeData Strength;
	ATTRIBUTE_ACCESSORS(UBaseAttributeSet, Strength);

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "BaseAttributeSet")
	FGameplayAttributeData MaxStrength;
	ATTRIBUTE_ACCESSORS(UBaseAttributeSet, MaxStrength);

};

初始化属性值并给予普攻伤害

创建DT_CharacterAttribute(Row元素类型为AttributeMetaData)

并将这张数据表设置在BP_BaseCharacter的AbilitySystem的AttributeTest中

新建GE_Melee_Damage(基类为GameplayEffect)作为普攻伤害,配置好属性值

在检测碰撞处加上伤害的GameEffect

夹值处理

为了避免属性值低于最小值或高于最大值,需要进行夹值处理

镜头处理(碰撞、延迟跟随)

将BP_Player上的SpringArm中的DoCollisionTest取消勾选,EnableCameraLag勾选

不同等级伤害配表

创建MeleeDamageLevel.cvs文件,配置好数值后导入UE,选择为CurveTable和Constant

在GE_Melee_Damage中运用这个表

在ApplyGE的时候运用等级

敌人行为树

创建行为树BT_Behaviour及黑板BBP_Value,在行为树中新建BTT_FindPlayer和BTT_AttackMelee的Task,把Wait时间改成1s

在黑板中新建Player属性

配置BTT_FindPlayer

配置BTT_AttackMelee

取消MoveTo节点中AllowPatialPath的勾选,并将BlackBoardKey设置为Player,将BTT_FindPlayer节点中的Player也设置为Player

新建BP_AIController(基类为AIController),在EventBeginPlay时执行行为树

在游戏场景中增加导航网格NavMesh,按P键显示

在BP_Enemy的AIControllerClass中配置BP_AIController,将AutoPossessAI属性值设置为Placed In World or Spawned

生命值变化时进行广播

多播委托参数

使用多播动态委托的原因:多播委托所带的dynamic的才能被蓝图调用

1
2
3
4
5
6
7
8
9
10
11
12
//BasicCharacter.h
#include "GameplayEffectTypes.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHealthChangeEvent, float, NewValue);

class UNREALGAS_API ABaseCharacter : public ACharacter
{
    UPROPERTY(BlueprintAssignable, Category = "Ability")
	FOnHealthChangeEvent HPChangeEvent;
    
    void OnHealthAttributeChanged(const FOnAttributeChangeData& Data);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//BasicCharacter.cpp
#include "AbilitySystemComponent.h"
#include "BaseAttributeSet.h"

void ABaseCharacter::BeginPlay()
{
	Super::BeginPlay();
	UAbilitySystemComponent* MyAbilitySystemComponent = this->FindComponentByClass<UAbilitySystemComponent>();
	if (MyAbilitySystemComponent)//如果存在
	{
		MyAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UBaseAttributeSet::GetHPAttribute()).AddUObject(this, &ABaseCharacter::OnHealthAttributeChanged);
	}
}

void ABaseCharacter::OnHealthAttributeChanged(const FOnAttributeChangeData& Data)
{
	HPChangeEvent.Broadcast(Data.NewValue);//广播参数
}

在蓝图中调用

死亡动画

创建死亡的蒙太奇MA_Dead