虚幻四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两个值呢?根据大佬的文档https://github.com/tranek/GASDocumentation#concepts-asc, 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负责提高移动速度。

虚幻四Gameplay Ability System入门(5)-冲刺奔跑和深入Attribute_第1张图片

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

虚幻四Gameplay Ability System入门(5)-冲刺奔跑和深入Attribute_第2张图片
虚幻四Gameplay Ability System入门(5)-冲刺奔跑和深入Attribute_第3张图片

然后打开GA_Sprint。Sprint的流程为对拥有该技能的角色申请一个GE,就是刚才我们创建的那个。然后等待Gameplay Event,这个事件带有标签Ability.Sprint.EndAbility。接收到事件后,移除带有Ability.Sprint.SpeedUp标签的GE,然后结束能力。
虚幻四Gameplay Ability System入门(5)-冲刺奔跑和深入Attribute_第4张图片

打开角色蓝图。

添加能力(Give Ability),绑定输入。这里当shift松开后,会向自己发送一个Gameplay Event,带有标签Ability.Sprint.EndSprint。这就是在能力中等待的事件。
虚幻四Gameplay Ability System入门(5)-冲刺奔跑和深入Attribute_第5张图片

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<UCharacterMovementComponent>(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,游戏开发,虚幻四,游戏,游戏开发,游戏引擎)