在這篇文章開始前,先分享一個慘痛的經(jīng)歷,就因為在虛幻四的源碼中加了兩句注釋涝动,項目的編譯就走向了擁有3000+ Errors的不歸路 T T,這是啥原理啊炬灭。
這次我們要實現(xiàn)的功能是角色的沖刺奔跑醋粟,操作就是點擊shift后角色的移動速度會增加。這個能力的實現(xiàn)應(yīng)該是挺簡單的重归,但是我會擴展一部分的GAS源碼米愿,深入一下GAS的Attribute,希望能夠幫助到一部分讀者鼻吮。有問題也希望大家可以在評論或者私信告訴我育苟。
接下來進入正題,首先還是講解一下加速跑的實現(xiàn)過程:
- shift點擊后activiate加速跑技能狈网。
- 加速跑技能會添加一個GE宙搬,這個GE會增加角色Move Speed Attribute
- 角色的Character.h/cpp中添加function笨腥,將它與move speed的change delegate綁定在一起拓哺。在該function中會提高角色Movement Component的移動速度。
- shift鍵松開后發(fā)送一個Gameplay Event告訴奔跑能力End Ability脖母。
添加MoveSpeed Attribute
打開AttributeSetBase.h/cpp士鸥,然后添加MoveSpeed Attribute。這個操作流程已經(jīng)重復(fù)無數(shù)次啦谆级,相信能看到這里的讀者應(yīng)該都掌握了烤礁,我就不重復(fù)了讼积。
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Attributes", ReplicatedUsing=OnRep_MoveSpeed)
FGameplayAttributeData MoveSpeed;
ATTRIBUTE_ACCESSORS(UAttributeSetBase, MoveSpeed);
UFUNCTION()
virtual void OnRep_MoveSpeed(const FGameplayAttributeData& OldMoveSpeed);
復(fù)制代碼
但是我們創(chuàng)建了這么多次的Attibute,還不了解它的數(shù)據(jù)結(jié)構(gòu)是咋樣的脚仔,打開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;
};
復(fù)制代碼
為什么屬性需要Base和Current兩個值呢猎醇?根據(jù)大佬的文檔github.com/tranek/GASD…, Base Value的值屬性的基礎(chǔ)窥突,它是永久(permanent)的,Current Value相反硫嘶,它是臨時的阻问。
打個比方,比如我們要實現(xiàn)的加速奔跑沦疾,它改變的是實際是Current Value称近,當加速效果結(jié)束后,Current Value就會降低回默認值哮塞。類似的效果還有某種buff會暫時提高角色的生命值或者護甲等等煌茬,這個buff改變的就是這種屬性的Current Value。
相反彻桃,當角色的生命值被攻擊后扣除坛善,它改變的就是Health屬性的Base Value,可以把攻擊造成的傷害當作是永久改變的邻眷,畢竟沒有其它影響角色的生命值就不會發(fā)生改變了眠屎。
那么哪些操作會改變Base值,哪些會改變Current值呢肆饶?打開一個Gameplay Effect改衩,然后點擊Duration Policy,可以看到有三種模式驯镊,其中Instant改變的是Base值葫督,Infinite和Has Duration改變的是Current Value,因為某種意義上這兩個效果是有持續(xù)時間的板惑。但是Duration policy的特殊情況橄镜,當它存在period的時候,它改變的同樣是Base值冯乘,因為Period Duration可以理解為每一個period觸發(fā)一次的instant洽胶。實現(xiàn)Sprint技能
創(chuàng)建Gameplay Ability命名為GA_Sprint姊氓,作為加速跑技能丐怯。創(chuàng)建Gameplay Effect命名為GE_Sprint_SpeedUp負責提高移動速度。打開角色藍圖矛洞。
添加能力(Give Ability),綁定輸入烫映。這里當shift松開后沼本,會向自己發(fā)送一個Gameplay Event,帶有標簽Ability.Sprint.EndSprint锭沟。這就是在能力中等待的事件抽兆。MoveSpeed Change Delegate
打開CharacterBase.h/cpp,然后創(chuàng)建function
void OnMoveSpeedAttributeChanged(const FOnAttributeChangeData& Data);
復(fù)制代碼
實現(xiàn)族淮,當接收到發(fā)生改變的Attribute后辫红,改變MovementComponent的MaxWalkSpeed,注意祝辣,這里不能直接通過GetCharacterMovement()修改移動速度贴妻。
void ACharacterBase::OnMoveSpeedAttributeChanged(const FOnAttributeChangeData& Data)
{
UCharacterMovementComponent *MovementPtr = Cast<UCharacterMovementComponent>(GetCharacterMovement());
MovementPtr->MaxWalkSpeed = Data.NewValue;
}
復(fù)制代碼
然后在BeginPlay中將該function與MoveSpeed的Change Delegate綁定。
void ACharacterBase::BeginPlay()
{
Super::BeginPlay();
if(AbilitySystem)
{
AbilitySystem->GetGameplayAttributeValueChangeDelegate(UAttributeSetBase::GetMoveSpeedAttribute())
.AddUObject(this, &ACharacterBase::OnMoveSpeedAttributeChanged);
}
}
復(fù)制代碼
響應(yīng)Attribute的改變
剛才實現(xiàn)的相應(yīng)Attribute改變實際上發(fā)生在屬性值已經(jīng)改變后蝙斜,但是我們處理最大生命值名惩,生命值不能小于0等事件不適合在角色代碼里實現(xiàn),實際上GAS系統(tǒng)提供了接口可以在屬性值發(fā)生改變前進行處理孕荠。
重寫兩個方法娩鹉。PreAttributeChange負責在屬性值的Current Value發(fā)生改變前進行處理。PostGameplayEffectExecute發(fā)生在Base Value發(fā)生改變前稚伍。
virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;
復(fù)制代碼
我們可以在這里設(shè)置屬性值的范圍弯予。比如下圖的處理就可以讓生命值保持在0到100之間。
void UAttributeSetBase::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
Super::PostGameplayEffectExecute(Data);
if(Data.EvaluatedData.Attribute == GetHealthAttribute())
{
SetHealth(FMath::Clamp(GetHealth(), 0.0f, 100.0f));
}
}