翻译自Udemy的视频课程introduction-to-unreal-engine-4-ability-system

MeleeAttackAbility 近战攻击技

1、用C++继承AbilitySystem中的方法和组件

01、首先在UE4中启用插件,

如何启动mongodb zip_UE4

02、由于GAS还不完全支持蓝图,需要用到C++ ,首先在工程的cs配置文件中添加 "GameplayAbilities", "GameplayTags", "GameplayTasks",添加后编译一遍工程,有必要的话重新再打开工程,以便VS2019能够识别一些新加载的符号

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" , "GameplayAbilities", "GameplayTags", "GameplayTasks" });
03、BaseCharacter.cpp文件中添加头文件,并且继承IAbilitySystemInterface接口,在其中添加一个UAbilitySysrtemComponent,另外继承接口就需要重写方法
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
 #include "GameFramework/Character.h"
#include "AbilitySystemInterface.h"
 #include "AbilitySystemComponent.h"
 #include "BaseCharacter.generated.h"UCLASS()
 class ABILITYSYSTEM_API ABaseCharacter : public ACharacter,public IAbilitySystemInterface
 {
//IAbilitySystemInterface是用于获取AbilitySystemComponent的接口,
 //其内部就只有一个GetAbilitySystemComponent()纯需方法方法
     GENERATED_BODY()public:
     // Sets default values for this character's properties
     ABaseCharacter();protected:
     // Called when the game starts or when spawned
     virtual void BeginPlay() override;public:    
     // Called every frame
     virtual void Tick(float DeltaTime) override;    // Called to bind functionality to input
     virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
    //声明一个UAbilitySystemComponent组件,一样的使用CreateDefaultSubobject的方式来定义
     UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = BaseCharacter)
     UAbilitySystemComponent* AbilitySystemComp;
    //重写接口中的虚方法
     virtual UAbilitySystemComponent* GetAbilitySystemComponent()const;    //该函数用于获取具体的Ability
     UFUNCTION(BlueprintCallable, Category = BaseCharacter)
         void AquireAbility(TSubclassOf<UGameplayAbility>AbilityToAquire);
 };
  // Fill out your copyright notice in the Description page of Project Settings.
 #include "public/BaseCharacter.h"
// Sets default values
 ABaseCharacter::ABaseCharacter()
 {
      // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
     PrimaryActorTick.bCanEverTick = true;
    AbilitySystemComp = CreateDefaultSubobject<UAbilitySystemComponent>("AbilitySystemComp");
 }// Called when the game starts or when spawned
 void ABaseCharacter::BeginPlay()
 {
     Super::BeginPlay();
     
 }// Called every frame
 void ABaseCharacter::Tick(float DeltaTime)
 {
     Super::Tick(DeltaTime);}
// Called to bind functionality to input
 void ABaseCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
 {
     Super::SetupPlayerInputComponent(PlayerInputComponent);}
UAbilitySystemComponent* ABaseCharacter::GetAbilitySystemComponent()const {
     return AbilitySystemComp ; 
 }
 void ABaseCharacter::AquireAbility(TSubclassOf<UGameplayAbility> AbilityToAquire)
 {
    //先判断AbilitySytemcomp是否为空
     if (AbilitySystemComp) {
        //HasAuthrity用于判断客户端是否对Actor有控制权
         //再判断传入的技能是否为空
         if (HasAuthority() && AbilityToAquire) {
            //GiveAbility用于具体的为AbilitySystemComp赋予能力,会调用TryActiveAbility
             //FGameplayAbilitySpec是一个用于存储被赋予Ability相关的数据信息的结构体
             AbilitySystemComp->GiveAbility(FGameplayAbilitySpec(AbilityToAquire, 1, 0));
            //InitAbilityActorInfo用于说明是谁在逻辑上控制Actor(Pawn、Characrter等等)、谁在物理上控制(可以是建筑,也可能和OwnedActor相同)
             AbilitySystemComp->InitAbilityActorInfo(this, this);
         }
     }
 }

04、视频中推荐使用番茄插件,其实我并不推荐。一是版权问题(正版并不是太贵),二是性能问题(如果开多线程,每次打开工程CPU会满载),三是安全性(破解的基本都带有病毒),四是目前VS2019对UE4的智能提示已经可以了,里面的符号基本上都能加载出来,没必要再搞个插件。

05、添加.gitignore文件,里面加入一行Content/ParagonShinbi/ 用于忽略掉商城的资源,可以在VS2019中看到目前为止的改动的文件数,

如何启动mongodb zip_如何启动mongodb zip_02

另外上传到仓库的资源大小为787K,

 

如何启动mongodb zip_GAS_03

2、创建Melee Ability和Cool Down

01、创建一个GameplayAbility蓝图,命名为GA_Melee,这个可以在BP_BaseCharacter中用AquireAbility获取,在之前一节中PrimaryAttack会play montage,现在把它放到GA_Melee中,使用playMontageAndWait节点,同样的把Rate调低一点,使得动画播放的不要太快

如何启动mongodb zip_UE4_04

02、在人物蓝图中,使用AquireAbility(这是在C++中写的函数)获取与使用TryActiveAbilityByClass触发技能,可以看到,把人物蓝图中的play montage放到具体的技能中

如何启动mongodb zip_如何启动mongodb zip_05

03、创建GamePlayEffect蓝图GE_Melee_CoolDown,GamePlayEffect这个是纯信息类,主要是用于说明法术消耗,能量消耗,冷却时间等信息,需要在GA_Melee中的ClassDefaults中的CoolDowns中指定Cost Gameplay EffectClass为GameEffect_Melee

如何启动mongodb zip_GAS_06

04、另外具体设置GE_Melee_CoolDown里的信息,

如何启动mongodb zip_GAS_07

05、 添加tag,当一个AbilitySystemComp获得一个技能的时候,他也会相应的增加一个与技能相关的Tag,当该技能冷却完成后,会移除相应的Tag,从而能够再次释放,这里需要填在GrantedTags中,填错Cool Down没效果

如何启动mongodb zip_技能系统_08

06、为了使得CoolDown生效,需要在GA_Melee中 ActiveAbility事件中CommitAbility

如何启动mongodb zip_UE4_09

3、创建属性集和health属性

当前我们有了Melee attack这个技能,我们想让这个技能发生作用,需要用到attribute set

01、创建C++ AttributeSet 类,命名为BaseAttributeSet,

#pragma once
#include "CoreMinimal.h"
 #include "AttributeSet.h"
 #include "BaseAttributeSet.generated.h"/**
  * 
  */
 UCLASS()
 class ABILITYSYSTEM_API UBaseAttributeSet : public UAttributeSet
 {
     GENERATED_BODY()public:
    //创建一个构造函数
     UBaseAttributeSet();
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AttributeSet)
         FGameplayAttributeData Health;
     //FGameplayAttributeData拥有BaseValue和AttributeValue两个值
 };#include "BaseAttributeSet.h"
UBaseAttributeSet::UBaseAttributeSet() :Health(200.f)
 {    //构造函数用于初始化Health
}
 
 02、然后要让我们的BaseCharacter拥有这个Attribute Set,使用前置声明的方式添加如下代码
//声明一个和UBaseAttributeSet组件,一样的使用CreateDefaultSubobject的方式来定义
     UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = BaseCharacter)
     class UBaseAttributeSet* AttributeSetComp;#include "public/BaseCharacter.h"
#include "BaseAttributeSet.h"
 // Sets default values
 ABaseCharacter::ABaseCharacter()
 {
      // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
     PrimaryActorTick.bCanEverTick = true;
     AbilitySystemComp = CreateDefaultSubobject<UAbilitySystemComponent>("AbilitySystemComp");
    AttributeSetComp = CreateDefaultSubobject<UBaseAttributeSet>("AttributeSetComp");
 }

03、编译,然后就可以在BP_BaseCharacter中Get和Set这个AttributeSetComp了。如下代码打印测试CurrentHealth值

如何启动mongodb zip_技能系统_10

4、添加Melee Overlaping事件

01、为Mesh中的sword添加capsule碰撞体,调整Socket为Weapon_r,调节大小和位置,效果如下

如何启动mongodb zip_#include_11

02、ctrl+W复制BP_BaseCharacter,命名为BP_Enemy,修改Enemy的Mesh,换一个皮肤,以便区分(原视频是先02后01,不妥)

03、为SwordCollision添加Overlap事件,需要判断一下碰撞到的物体是不是敌人,不能是剑,也不能是自身,

首先设置SwordCollision,只和Pawn发生碰撞,

如何启动mongodb zip_#include_12

 在Ovelap事件中首先判断不能是自身,然后判断是否是敌人,是的话,打印碰撞体的名称

如何启动mongodb zip_#include_13

为了屏幕更清晰,断开其他Print String的连接,也可以观察到一次攻击会产生多个碰撞检测。需要在下一节中设置notify来解决

如何启动mongodb zip_UE4_14

 5、Animation Notify State and Send GameplayEvent

实现的功能就是普通状态,即没有进行攻击动作的时候,Sword是不进行碰撞检测的,只有当攻击动作进行,即动画蒙太奇播放的时候,在Notify处才进行碰撞检测。

01、首先,将SwordCollision设置CollisionEnabled为NoCollision,这样子在普通状态下无碰撞检测

如何启动mongodb zip_GAS_15

 02、创建一个蓝图AnimationNotifyState,命名为ANS_MeleeAttack,首先要在Montage中使用这个NotifyState,放到8-10帧的位置上

如何启动mongodb zip_如何启动mongodb zip_16

03、ANS_MeleeAttack其中的EventGraph重写NotifyBegin和NotifyEnd方法

首先getowner,如果是BP_BaseCharacter的话,获取SwordCollision,然后EnableCollision,设置为query only,不检测physicbody

如何启动mongodb zip_技能系统_17

首先getowner,如果是BP_BaseCharacter的话,获取SwordCollision,然后DisableCollision,

如何启动mongodb zip_GAS_18

04、现在想在Sword发生碰撞的时候发送GameplayEvent事件(这是GAS自带的事件),其中payload传递技能所作用于的对象

,使用GameplayEventData打包Sword碰撞到的物体作为Payload

如何启动mongodb zip_如何启动mongodb zip_19

05、,然后就可以在GA_Melee中监听gameplayEvent,在playmontage后修改如下
:当Montage播放完成的时候,应该EndAbility,

如何启动mongodb zip_UE4_20

6、处理伤害

01、处理伤害需要另外的GameplayEffect,命名为GE_Melee_Damage,指定作用的属性值Health,伤害的大小-20,为Instant立即伤害

如何启动mongodb zip_技能系统_21

02、增添一个新的tag,这次是使用GameplayEffectAssetTag

如何启动mongodb zip_如何启动mongodb zip_22

03、在GA_Melee中实际使用这个GameplayEffect,将之前的printstring 替换如下,作用就是将Damage实际应用到Health上

如何启动mongodb zip_UE4_23

04、为了实际的查看应用的效果,C++中在BaseAttributeSet重写PostGameplayEffectExecute方法,该方法会在属性值发生修改后执行(UE4.22中这个不能正常工作,没能够正常的打印,Debug时不会执行这一块的代码,也就是说,没能够正常的修改属性值,)问题解决了,之前的BP_Enemy在复制的过程中AttributeSet comp为none,所以对Enmemy使用ApplyDamageToTarget的时候并不会对Health产生影响。应该从BP_BaseCharacter重新派生一个BP_BaseEnemy出来(用派生的方式产生类,不要用复制的方式)

在BaseAttributeSet.h中添加:

    virtual void PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData& Data)override;

对应的Cpp文件中添加 

#include "BaseAttributeSet.h"
#include"GameplayEffect.h"
#include "GameplayEffectExtension.h"

//PostGameplayEffectExecute是在属性值发生修改后自动调用
void UBaseAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{

    if (Data.EvaluatedData.Attribute.GetUProperty() == FindFieldChecked<UProperty>(UBaseAttributeSet::StaticClass(), GET_MEMBER_NAME_CHECKED(UBaseAttributeSet, Health))) {
        //暂时先打印修改后的属性值
        UE_LOG(LogTemp, Warning, TEXT("Oh,i got som damagem,now my health is:%f"),Health.GetCurrentValue());
    }
}
 

 

 7、添加生命值UI

01、制作HealthBar,锚点居中,添加一个ProgressBar,调整颜色,大小等

如何启动mongodb zip_UE4_24

 02、在Graph中添加一个custom event,命名为SetPercentage,添加一个float型的输入percentage,

如何启动mongodb zip_UE4_25

 03、在BaseAttributeSet中设置一个Delegate,用来传递属性值的修改

#include "CoreMinimal.h"
 #include "AttributeSet.h"
 #include "BaseAttributeSet.generated.h"/**
  * 
  */
 //创建一个Multicast多播类型的委托,里面传入两个参数
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnHealthChangeDelegate, float, Health, float, MaxHealth);
 UCLASS()
 class ABILITYSYSTEM_API UBaseAttributeSet : public UAttributeSet
 {
     GENERATED_BODY()public:
     //创建一个构造函数
     UBaseAttributeSet();
     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AttributeSet)
         FGameplayAttributeData Health;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AttributeSet)
         FGameplayAttributeData MaxHealth;
     //FGameplayAttributeData拥有BaseValue和AttributeValue两个值
     virtual void PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData& Data)override;
    FOnHealthChangeDelegate OnHealthChange;
 };PostGameplayEffectExecute中广播出去
UBaseAttributeSet::UBaseAttributeSet() :Health(200.f),MaxHealth(200.f)
 {
     //构造函数用于初始化Health
 }void UBaseAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
 {    if (Data.EvaluatedData.Attribute.GetUProperty() == FindFieldChecked<UProperty>(UBaseAttributeSet::StaticClass(), GET_MEMBER_NAME_CHECKED(UBaseAttributeSet, Health))) {
         //暂时先打印修改后的属性值
         UE_LOG(LogTemp, Warning, TEXT("Oh,i got som damagem,now my health is:%f"),Health.GetCurrentValue());
         //当属性值发生修改的时候广播出去
         OnHealthChange.Broadcast(Health.GetCurrentValue(), MaxHealth.GetCurrentValue());
     }
 }05、在BaseCharacter中接收 
    UFUNCTION()
         void OnHealthChange(float Health, float MaxHealth);
     UFUNCTION(BlueprintImplementableEvent, Category = BaseCharacter)
         void BP_OnHealthChange(float Health, float MaxHealth);//蓝图中实现// Called when the game starts or when spawned
 void ABaseCharacter::BeginPlay()
 {
     Super::BeginPlay();
     //在BeginPlay中动态的绑定委托
     AttributeSetComp->OnHealthChange.AddDynamic(this, &ABaseCharacter::OnHealthChange);
 }void ABaseCharacter::OnHealthChange(float Health, float MaxHealth)
 {
     BP_OnHealthChange(Health, MaxHealth);//通知蓝图事件调用
 }

06、在BP_BaseEnemy中添加一个widget,命名为HealthBar,并且指定其中的UI为HealthBar

如何启动mongodb zip_#include_26

 07、在BP_BaseEnemy的EventGraph中,设置HealthBar的数值显示

如何启动mongodb zip_技能系统_27

08、有必要重新关闭Editor,再打开编译一下,运行后效果如下就OK了

如何启动mongodb zip_如何启动mongodb zip_28

 8、添加死亡动画

01、首先为了人物的生命值不会降低到0以下,需要添加

void UBaseAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
 {    if (Data.EvaluatedData.Attribute.GetUProperty() == FindFieldChecked<UProperty>(UBaseAttributeSet::StaticClass(), GET_MEMBER_NAME_CHECKED(UBaseAttributeSet, Health))) {
        //这一段的逻辑放在PreAttributeChange(这个是在属性值发生修改前调用)更好一些
        Health.SetCurrentValue(FMath::Clamp(Health.GetCurrentValue(), 0.f, MaxHealth.GetCurrentValue()));
         Health.SetBaseValue(FMath::Clamp(Health.GetBaseValue(), 0.f, MaxHealth.GetCurrentValue()));        //暂时先打印修改后的属性值
         UE_LOG(LogTemp, Warning, TEXT("Oh,i got som damagem,now my health is:%f"),Health.GetCurrentValue());
         //当属性值发生修改的时候广播出去
         OnHealthChange.Broadcast(Health.GetCurrentValue(), MaxHealth.GetCurrentValue());
     }
 }

02、当生命值为0时,播放死亡动画,再在BaseCharacter中添加一个蓝图事件函数

UFUNCTION(BlueprintImplementableEvent, Category = BaseCharacter)
         void BP_Die();
 protected:
     bool bIsDeath;void ABaseCharacter::OnHealthChange(float Health, float MaxHealth)
 {
     if (Health <= 0.f&& !bIsDeath) {
         //生命值小于0,调用蓝图事件来播放死亡动画
         bIsDeath = true;
         BP_Die();
         
     }
     BP_OnHealthChange(Health, MaxHealth);//通知蓝图事件调用
 }

03、在BP_BaseEnemy中 ,另外需要添加AM_ShinbiDeath动画蒙太奇,注意指定该蒙太奇的Slot

如何启动mongodb zip_UE4_29

接下来一节介绍设置简单的敌人AI,让敌人能够发现玩家,并且移动至玩家处,然后使用MeleeAttack技能