UE4 技能系统(GAS插件的使用) 02--MeleeAttackAbility 近战攻击技

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

MeleeAttackAbility 近战攻击技

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

01、首先在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中看到目前为止的改动的文件数,

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

 

2、创建Melee Ability和Cool Down

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

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

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

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

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

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

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值

4、添加Melee Overlaping事件

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

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

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

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

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

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

 5、Animation Notify State and Send GameplayEvent

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

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

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

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

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

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

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

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

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

6、处理伤害

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

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

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

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,调整颜色,大小等

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

 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;
};

 04、在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

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

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

 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

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

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值