虚幻四Gameplay Ability System入门(5)-冲刺奔跑和深入Attribute

在这篇文章开始前,先分享一个惨痛的经历,就因为在虚幻四的源码中加了两句注释,项目的编译就走向了拥有3000+ Errors的不归路 T T,这是啥原理啊。

这次我们要实现的功能是角色的冲刺奔跑,操作就是点击shift后角色的移动速度会增加。这个能力的实现应该是挺简单的,但是我会扩展一部分的GAS源码,深入一下GAS的Attribute,希望能够帮助到一部分读者。有问题也希望大家可以在评论或者私信告诉我。

接下来进入正题,首先还是讲解一下加速跑的实现过程:

  1. shift点击后activiate加速跑技能。
  2. 加速跑技能会添加一个GE,这个GE会增加角色Move Speed Attribute
  3. 角色的Character.h/cpp中添加function,将它与move speed的change delegate绑定在一起。在该function中会提高角色Movement Component的移动速度。
  4. shift键松开后发送一个Gameplay Event告诉奔跑能力End Ability。

添加MoveSpeed Attribute

打开AttributeSetBase.h/cpp,然后添加MoveSpeed Attribute。这个操作流程已经重复无数次啦,相信能看到这里的读者应该都掌握了,我就不重复了。

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Attributes", ReplicatedUsing=OnRep_MoveSpeed)
    FGameplayAttributeData MoveSpeed;
    ATTRIBUTE_ACCESSORS(UAttributeSetBase, MoveSpeed);

UFUNCTION()
    virtual void OnRep_MoveSpeed(const FGameplayAttributeData& OldMoveSpeed);
复制代码

但是我们创建了这么多次的Attibute,还不了解它的数据结构是咋样的,打开AttributeSetBase的父类AttributeSet.h。我删除了其它代码,重要的是下面的部分。它表明一个Attribute中有两个float值Base Value,和Current Value。

USTRUCT(BlueprintType)
struct GAMEPLAYABILITIES_API FGameplayAttributeData
{
    protected:
    UPROPERTY(BlueprintReadOnly, Category = "Attribute")
    float BaseValue;

    UPROPERTY(BlueprintReadOnly, Category = "Attribute")
    float CurrentValue;
};
复制代码

为什么属性需要Base和Current两个值呢?根据大佬的文档github.com/tranek/GASD…, Base Value的值属性的基础,它是永久(permanent)的,Current Value相反,它是临时的。

打个比方,比如我们要实现的加速奔跑,它改变的是实际是Current Value,当加速效果结束后,Current Value就会降低回默认值。类似的效果还有某种buff会暂时提高角色的生命值或者护甲等等,这个buff改变的就是这种属性的Current Value。

相反,当角色的生命值被攻击后扣除,它改变的就是Health属性的Base Value,可以把攻击造成的伤害当作是永久改变的,毕竟没有其它影响角色的生命值就不会发生改变了。

那么哪些操作会改变Base值,哪些会改变Current值呢?打开一个Gameplay Effect,然后点击Duration Policy,可以看到有三种模式,其中Instant改变的是Base值,Infinite和Has Duration改变的是Current Value,因为某种意义上这两个效果是有持续时间的。但是Duration policy的特殊情况,当它存在period的时候,它改变的同样是Base值,因为Period Duration可以理解为每一个period触发一次的instant。
1.PNG

创建完毕后,记得需要给予MoveSpeed Attribute一个初值。
2.PNG

实现Sprint技能

创建Gameplay Ability命名为GA_Sprint,作为加速跑技能。创建Gameplay Effect命名为GE_Sprint_SpeedUp负责提高移动速度。
3.png

打开GE_Sprint_SpeedUp。技能效果为永久(Infinite)提高MoveSpeed属性2000,同时需要给该GE添加Tag为Ability.Sprint.SpeedUp
5.PNG
5.1.png

然后打开GA_Sprint。Sprint的流程为对拥有该技能的角色申请一个GE,就是刚才我们创建的那个。然后等待Gameplay Event,这个事件带有标签Ability.Sprint.EndAbility。接收到时间后,移除带有Ability.Sprint.SpeedUp标签的GE,然后结束能力。
4.PNG

打开角色蓝图。

添加能力(Give Ability),绑定输入。这里当shift松开后,会向自己发送一个Gameplay Event,带有标签Ability.Sprint.EndSprint。这就是在能力中等待的事件。
6.PNG

MoveSpeed Change Delegate

打开CharacterBase.h/cpp,然后创建function

void OnMoveSpeedAttributeChanged(const FOnAttributeChangeData& Data);
复制代码

实现,当接收到发生改变的Attribute后,改变MovementComponent的MaxWalkSpeed,注意,这里不能直接通过GetCharacterMovement()修改移动速度。

void ACharacterBase::OnMoveSpeedAttributeChanged(const FOnAttributeChangeData& Data)
{
    UCharacterMovementComponent *MovementPtr =  Cast(GetCharacterMovement());
    MovementPtr->MaxWalkSpeed = Data.NewValue;
}
复制代码

然后在BeginPlay中将该function与MoveSpeed的Change Delegate绑定。

void ACharacterBase::BeginPlay()
{
    Super::BeginPlay();

    if(AbilitySystem)
    {
        AbilitySystem->GetGameplayAttributeValueChangeDelegate(UAttributeSetBase::GetMoveSpeedAttribute())
        .AddUObject(this, &ACharacterBase::OnMoveSpeedAttributeChanged);
    }
}
复制代码

响应Attribute的改变

刚才实现的相应Attribute改变实际上发生在属性值已经改变后,但是我们处理最大生命值,生命值不能小于0等事件不适合在角色代码里实现,实际上GAS系统提供了接口可以在属性值发生改变前进行处理。

重写两个方法。PreAttributeChange负责在属性值的Current Value发生改变前进行处理。PostGameplayEffectExecute发生在Base Value发生改变前。

virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;

virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;
复制代码

我们可以在这里设置属性值的范围。比如下图的处理就可以让生命值保持在0到100之间。

void UAttributeSetBase::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
    Super::PostGameplayEffectExecute(Data);

    if(Data.EvaluatedData.Attribute == GetHealthAttribute())
    {
        SetHealth(FMath::Clamp(GetHealth(), 0.0f, 100.0f));
    }
}

你可能感兴趣的:(虚幻四Gameplay Ability System入门(5)-冲刺奔跑和深入Attribute)