【USparkle專欄】如果你深懷絕技,愛“搞點研究”,樂於分享也博採眾長,我們期待你的加入,讓智慧的火花碰撞交織,讓知識的傳遞生生不息!
前言
最近在用UE做單機ARPG的戰鬥系統,研究了一下GAS。本文主要介紹GAS各個模組的用途,以及特定功能的多種實現方法。為了讓大部分人能快速上手,不會涉及太多C++和網路同步的內容。
推薦快速過一遍Unreal官方的GAS外掛介紹再來食用本文。
[中文直播]第31期|GAS外掛介紹(入門篇)
[UnrealOpenDay2020]深入GAS架構設計
文末附參考文獻。
一、GameplayTags與GAS
1.1 GameplayTags
FGameplayTags是一種層級標籤,如Parent.Child.GrandChild。
透過GameplayTagManager進行註冊。替代了原來的Bool,或Enum的結構,可以在玩法設計中更高效地標記物件的行為或狀態。
GameplayTags是一個內建的外掛,不屬於GAS。
但是GAS會大量使用Tag,在編輯-專案設定裡可以找到。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/1.png)
GA、GE、GameplayEvent、GameplayCue都會大量使用Tag。如果你還不懂這些名詞的含義,可以先往後看。
同時角色本身的列舉、布林等狀態(非屬性)變數也可以用Tag儲存,非常好用,我自己的用法是在讓AI在行為樹的Decorator節點中,透過玩家擁有的GameplayTag來判斷玩家是否處於無敵幀、喝藥。
但因此Tag的層級關係也需要合理設計,到了後期修改成本比較大。
個人的建議是設計Ability、Effect、GameplayCue、Event、Character、Cooldown等基礎標籤,在對應的模組只檢測對應的Tag(比如監聽動畫通知傳送的Event,就可以是Event.AnimNotify.Fire),避免重複或者模糊定義。
舉一個反面例子,比如將Attack的GA標籤和GE標籤、角色處於攻擊狀態的標籤都設定為Character.Attack,不利於追述Tag的來源。
其他的Tag可以根據需求新增,如Item等。
同時要注意,Tag的父子關係也要考慮的,若合理設計,可以用於批次篩選同一類Tag。
在Editor內批次修改Tag可能不是那麼方便,這裡有一個比較好的管理方法:
《Ue4Config方法總結與另外一種GameplayTag管理方法》
1.2 Gameplay Ability System
GAS主要包含以下內容:
- ASC(Ability System Component)主要元件,由C++編寫,程式碼裡有很多方法是藍圖未實現的。
- GA(Gameplay Abilities)角色的技能,包括攻擊、疾跑、施法、翻滾、使用道具等,但不包括基礎移動和UI。
- AS(Attribute Set)角色身上可以用float表示的屬性,如生命值、體力值、魔力值等。
- GE(Gameplay Effects)用於修改屬性,如增加50移動速度10s;還能配合GA實現更多玩法。
- GC(Gameplay Cues)播放特效、音效等。
如果看過《深入GAS架構設計》,可以發現應該還有Task和Event兩個額外功能。
嚴格意義上這兩個功能和Tag一樣是UE原生內容,在GA部分比較常用,因此本文選擇將其放在GA部分講解。
二、Ability System Component
2.1 ASC元件介紹
Ability System Component(ASC)是整個GAS的基礎元件。
ASC本質上是一個UActorComponent,用於處理整個框架下的互動邏輯,包括使用技能 (GameplayAbility)、包含屬性(AttributeSet)、處理各種效果(GameplayEffect)。
所有需要應用GAS的物件(Actor),都必須擁有GAS元件。
擁有ASC的Actor被稱為ASC的OwnerActor,ASC實際作用的Actor叫做AvatarActor。ASC可以被賦予某個角色ASC,也可以被賦予PlayerState(可以儲存死亡角色的一些資料)
簡單來說,ASC是一種角色元件,負責和GA、GE、AS打交道。
一般只放在Character or PlayerState上,在武器上加ASC元件也不是不行,但是並沒有很好的實踐供參考,官方文件提到過這一點。
OwnerActor和AvartarActor是比較常見的概念,如果ASC在Character類身上,那麼二者是相同的。
如果Character需要銷燬再重新生成,如MOBA遊戲角色死亡後泉水復活,那麼ASC可以放在PlayerState上避免隨著角色一同銷燬。此時的OwnerActor是PlayerState,AvatarActor則是Character。
學習動畫系統後的一些補充想法:如果希望角色部分為純藍圖實現,以便直接指定父類為一些模板角色藍圖,如ALSv4,這種情況下也許放在PlayerState裡會更好?
2.2 新增ASC元件
一開始的設定需要一些C++,這裡IDE建議使用Rider for Unreal,會自動補全一些標頭檔案宣告,防止遺漏。
C++基礎知識補充:
UE的C++類分為兩部分,一個是.h標頭檔案,一個是.cpp檔案。
屬性和方法的宣告寫在.h檔案裡,方法的實現寫在.cpp裡,包括建構函式。
在進行接下來的操作之前,你需要自己建立一個繼承自ACharacter的C++類,如下圖的ARPGCharacterBase。(圖源右下角)
之後想用藍圖,就從這個自定義的C++Character類派生就可以了。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/2.png)
圖源@開發遊戲的老王
1、角色.h中宣告ASC。
#include "AbilitySystemComponent.h" public: UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Abilities") class UAbilitySystemComponent* AbilitySystemComponent;
不要忘了在專案Build.cs檔案的PrivateDependencyModuleNames里加上“GameplayAbilities”,“GameplayTags”,“GameplayTasks”三個模組。
2、在.cpp中建構函式部分例項化ASC。
//例項化ASC AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystem"));
3、角色類繼承IAbilitySystemInterface介面,並實現GetASC函式。
#include "AbilitySystemInterface.h" class ARPG_UNREAL_API ACharacterBase : public ACharacter, public IAbilitySystemInterface public: UAbilitySystemComponent* GetAbilitySystemComponent()const override;
.cpp
UAbilitySystemComponent* ACharacterBase::GetAbilitySystemComponent()const{return AbilitySystemComponent;}
上面的示例程式碼使用的是原生的ASC元件。如果想自己繼承一個ASC子元件封裝一些功能,可以參考官方的Action RPG例項專案裡的寫法。
2.3 ASC元件的功能
GAS的大部分功能都在ASC元件的原始碼中,並且只有一部分暴露給了藍圖,有些功能如新增GA(見3.2)還需要透過程式碼實現。由於本文只是一篇快速入門手冊,不再過多贅述。
想要詳細瞭解GAS系統,可以先從ASC元件的原始碼入手,有時可以避免重複造輪子。
具體的功能會在下文使用到。
三、Gameplay Ability
3.1 GA介紹
Gameplay Ability(GA)標識了遊戲中一個物件(Actor)可以做的行為或技能。能力(Ability)可以是普通攻擊或者吟唱技能,可以是角色被擊飛倒地,還可以是使用某種道具,互動某個物件,甚至跳躍、飛行等角色行為也可以是Ability。
Ability可以被賦予物件或從物件的ASC中移除,物件同時可以啟用多個GameplayAbility。*基本的移動輸入、UI互動行為則不能或不建議透過GA來實現
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/3.png)
一個GA藍圖大概就長這樣
角色需要擁有GA後,才能使用GA。
GA的使用分為例項化和釋放兩個過程,前者主要是生成一個FGameplayAbilitySpec物件,併為一部分非公有(非靜態)屬性賦值,如當前GA的等級。後者操作的實際物件則為Spec。
可以把Spec理解為GA的例項,GE等其他類也有相似的概念。
通常來說,使用GA時不用去考慮兩個過程的區別,除非你需要在例項化Spec後,手動修改一些在GA類上定義好的屬性再去手動釋放。在GE篇會詳細介紹,用於實現技能的冷卻、消耗。
3.2介紹GA的不同獲得方式,3.3介紹GA藍圖的製作,3.4介紹GA的使用。
3.2 新增GA
如果不使用C++修改,只能透過GE去新增GA,非常不方便。
這裡介紹兩種修改方法。
3.2.1 在角色類中建立一個陣列,遊戲啟動時自動新增陣列裡的GA
注意:使用這一種方法不易控制每個GA的初始等級。
1、在角色標頭檔案宣告陣列:
public: // 將在遊戲啟動時被賦予角色的Abilities陣列 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Abilities") TArray<TSubclassOf<class UGameplayAbility>> PreloadedAbilities;
2、在角色的BeginPlay()裡遍歷陣列,使用AbilitySystemComponent->GiveAbility()新增Ability:
Super::BeginPlay(); if (AbilitySystemComponent != nullptr) { //初始化技能 if (PreloadedAbilities.Num() > 0) { for (auto i = 0; i < PreloadedAbilities.Num(); i++) { if (PreloadedAbilities[i] != nullptr) { // FGameplayAbilitySpec是GA的例項,其建構函式的第二個引數代表GA的等級,這裡暫令其全部為1 AbilitySystemComponent->GiveAbility( FGameplayAbilitySpec(PreloadedAbilities[i].GetDefaultObject(), 1)); } } } //初始化ASC AbilitySystemComponent->InitAbilityActorInfo(this, this); }
3、在角色藍圖的Details皮膚找到陣列,填好GA。
如果後面做完一個GA發現沒反應,可能就是忘記Give剛做好的GA給角色了,或者場景中的角色物件擁有的技能沒有和類預設值同步。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/4.png)
3.2.2 在角色藍圖中使用Give Ability函式手動新增Ability
上面提到的AbilitySystemComponent->GiveAbility()方法在藍圖中無法使用。
為了在藍圖中動態新增Ability,我們需要在藍圖中實現自己的GiveAbility()。
按理說在自己的ASC子類中實現最好,這裡在角色藍圖中實現。
1、CharacterBase.h
public: //新增Ability UFUNCTION(BlueprintCallable, Category = "Ability System") void GiveAbility(TSubclassOf<UGameplayAbility> Ability, int32 Level = 1);
2、CharacterBase.cpp
void ACharacterBase::GiveAbility(TSubclassOf<UGameplayAbility> Ability, int32 Level) { if (AbilitySystemComponent) { if (HasAuthority() && Ability) { AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(Ability, Level)); } AbilitySystemComponent->InitAbilityActorInfo(this, this); } }
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/5.png)
使用例
這也是一個將ASC中未暴露給藍圖的函式進行封裝的例子,如果想在藍圖中使用其他的ASC函式可以進行參考。
3.2.3 使用GE新增GA
新建一個GE,在Granted Abilities條目裡新增的GA都會在GE被Apply到角色身上時賦予(Grant)。
引數的具體含義詳見5.2.10。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/6.png)
然後在藍圖裡呼叫Apply GameplayEffect to Self節點即可(這裡的Level是GE的等級,不是GA)。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/7.png)
3.3 製作GA
在內容瀏覽器右鍵,建立Gameplay→Gameplay技能藍圖:
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/8.png)
繼承自GameplayAbility:
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/9.png)
最下面兩個是內建的GA,上面的是自制的GA
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/10.png)
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/11.png)
GA藍圖的結構
一般GA要做的事有:
- 設定GA的Tag、CD、Cost等屬性。
- 獲取必要資訊,主要透過Get Actor Info。如果是透過Event呼叫的GA(使用Activate Ability From Event節點作為輸入),還可以透過Gameplay Event Data獲取。
- 編寫邏輯,如播放動畫、應用GE、應用衝量等。
- 一定不要忘了EndAbility。
3.4 呼叫GA
我把GA的呼叫分成了主動呼叫(釋放技能)和被動呼叫(捱打)兩類,下面依次介紹不同的呼叫方法。
3.4.1 主動呼叫
在藍圖中主要有by Class和by Tag兩種呼叫方法。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/12.png)
byClass一次只能Activate一個GA,byTag可以Activate任意多個GA,配合Tag容器使用。
如果使用EnhancedInputAction外掛來管理輸入,要注意在某些設定下Trigger會每幀都進行輸出(本人測試環境為4.27,UE5似乎有一些改動。古代山谷專案就使用了新版輸入外掛和GAS系統,可以看一看實現方法)。
只要能獲取ASC,就可以在任何地方呼叫GA,比如行為樹Task藍圖,甚至在GA藍圖中呼叫其他GA。
3.4.2 被動呼叫
Trigger可以理解為一個Tag,當ASC元件收到一個Trigger時,就會自動呼叫所有擁有該Trigger的GA。
Trigger的Tag在GA的Details皮膚中設定。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/13.png)
Trigger的觸發方式有三種,分別是:
- Gameplay Event:當Owner收到一個帶有Tag的Gameplay Event(不是Gameplay Effect的GE!)時呼叫一次GA,此時Owner不會擁有對應的Tag。
- Owner Tag Added:當Owner獲取對應Tag的時候呼叫一次GA。
- Owner Tag Present:當Owner擁有Tag時呼叫GA,失去Tag時移除。
一般使用第一種方法,並配合SendGameplayEventToActor節點使用,如下圖所示。(這張圖是很久以前截的,Tag建議以Event開頭。)
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/14.png)
受擊效果的例子,傳送一個Tag為Hit的Event給碰撞檢測到的Actor
使用Gameplay Event呼叫的好處是,可以傳入資料(Payload),是除了Get Actor Info外的另一種資訊傳遞方法。
此時應該刪除ActiveAbility節點,轉而使用ActivateAbilityFromEvent事件。(不要透過在左上角過載函式的方式,右鍵空白處搜尋才是對的。)
3.5 設定GA觸發條件
3.5.1 GA的標籤
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/15.png)
可以限制各種技能的相互關係,比如受擊時候不能翻滾。
這時候Tag的父子層級關係設計就尤為重要,可以把受擊時不能釋放的技能都放在同一個父層級下。
Tag建議以Ability開頭。
Ability Tags:該GA的標籤。
Cancel Abilities with Tag:啟用該GA時,打斷其他擁有所選標籤的GA。
Block Abilities with Tag:啟用該GA時,阻止啟用擁有所選標籤的GA(已經啟用的不會被中斷)。
Activation Owned Tags:啟用該GA時,賦予ASC所選GA。
Activation Required Tags:啟用GA時,ASC需要的標籤。
Activation Blocked Tags:啟用GA時,ASC不能有的標籤。
Source Required Tags:啟用GA時,Source需要的標籤。
Source Blocked Tags:啟用GA時,Source不能有的標籤。
Target Required Tags:啟用GA時,Target需要的標籤。
Target Blocked Tags:啟用GA時,Target不能有的標籤。
上圖就是一個防止重複觸發GA的簡單設定。
3.5.2 冷卻與消耗
想要新增冷卻與消耗,就需要寫好對應的GE,建議先看完GE篇。
在GA的Details皮膚的Cost和Cooldown條目中選擇對應的GE即可。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/16.png)
一個Cooldown GE僅需滿足以下要求:
- 為Has Duration型別,Duration Magnitude計算方式為Set By Caller或Custom Calculation Class。
- Granted Tags為技能的冷卻Tag,如Cooldown.skill1。
在Cooldown GE持續期間,玩家的ASC元件就會攜帶對應技能的Cooldown Tag,本質是透過Tag來限制的。
*冷卻Tag建議以Cooldown開頭統一管理。
一個Cost GE僅需滿足以下要求:
- 為Instant型別。
- 有一個或多個Modifier去修改對應的屬性,計算方式為Custom Calculation Class。
但這樣一來每個GA都要寫一遍Cost和CD的GE,非常麻煩。
官方文件4.5.14和4.5.15小節有介紹最佳化方法:
https://github.com/tranek/GASDocumentation#concepts-ge-cost
這裡參考官方文件簡單地實現一下,原理為在例項化生成GE Spec時,修改其Cost和Cooldown屬性後再將其應用。
首先建立一個GA基類,新增CD時長、Cost數值(包括生命值和法力值兩種型別的Cost)、以及Cooldown Tag等屬性,並過載GetCooldownTags、ApplyCooldown、GetCostGameplayEffect三個方法。
GameplayAbilityBase.h
#pragma once #include "CoreMinimal.h" #include "Abilities/GameplayAbility.h" #include "GameplayAbilityBase.generated.h" /** * */ UCLASS() class ARPG_UNREAL_API UGameplayAbilityBase : public UGameplayAbility { GENERATED_BODY() public: UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Cooldowns") FScalableFloat CooldownDuration; UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Cooldowns") FGameplayTagContainer CooldownTags; UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Costs") FScalableFloat HealthCost; // 根據需要可以設定多種型別Cost UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Costs") FScalableFloat ManaCost; UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Costs") FScalableFloat StaminaCost; // Temp container that we will return the pointer to in GetCooldownTags(). // This will be a union of our CooldownTags and the Cooldown GE's cooldown tags. UPROPERTY(Transient) FGameplayTagContainer TempCooldownTags; // Return the union of our Cooldown Tags and any existing Cooldown GE's tags. virtual const FGameplayTagContainer* GetCooldownTags() const override; // Inject our Cooldown Tags and to add the SetByCaller to the cooldown GameplayEffectSpec. virtual void ApplyCooldown(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) const override; virtual UGameplayEffect* GetCostGameplayEffect() const override; };
GameplayAbilityBase.cpp
#include "ARPG_Unreal/Public/GameplayAbilitySystem/GameplayAbilityBase.h" const FGameplayTagContainer* UGameplayAbilityBase::GetCooldownTags() const { FGameplayTagContainer* MutableTags = const_cast<FGameplayTagContainer*>(&TempCooldownTags); MutableTags->Reset(); // MutableTags writes to the TempCooldownTags on the CDO so clear it in case the ability cooldown tags change (moved to a different slot) const FGameplayTagContainer* ParentTags = Super::GetCooldownTags(); if (ParentTags) { MutableTags->AppendTags(*ParentTags); } MutableTags->AppendTags(CooldownTags); return MutableTags; } void UGameplayAbilityBase::ApplyCooldown(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) const { UGameplayEffect* CooldownGE = GetCooldownGameplayEffect(); if (CooldownGE) { FGameplayEffectSpecHandle SpecHandle = MakeOutgoingGameplayEffectSpec(CooldownGE->GetClass(), GetAbilityLevel()); SpecHandle.Data.Get()->DynamicGrantedTags.AppendTags(CooldownTags); SpecHandle.Data.Get()->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Cooldown")), CooldownDuration.GetValueAtLevel(GetAbilityLevel())); ApplyGameplayEffectSpecToOwner(Handle, ActorInfo, ActivationInfo, SpecHandle); } } UGameplayEffect* UGameplayAbilityBase::GetCostGameplayEffect() const { return Super::GetCostGameplayEffect(); }
然後建立繼承UGameplayModMagnitudeCalculation,建立對應屬性的Cost MMC,這裡僅展示法力值消耗,Stamina消耗同理。
ManaMMC.h
#pragma once #include "CoreMinimal.h" #include "GameplayModMagnitudeCalculation.h" #include "ManaMMC.generated.h" /** * */ UCLASS() class ARPG_UNREAL_API UManaMMC : public UGameplayModMagnitudeCalculation { GENERATED_BODY() virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override; };
ManaMMC.cpp
#include "GameplayAbilitySystem/ManaMMC.h" #include "GameplayAbilitySystem/GameplayAbilityBase.h" float UManaMMC::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const { const UGameplayAbilityBase* Ability = Cast<UGameplayAbilityBase>(Spec.GetContext().GetAbilityInstance_NotReplicated()); if (!Ability) { return 0.0f; } return Ability->ManaCost.GetValueAtLevel(Ability->GetAbilityLevel()); }
最後寫一個通用的Cost和CD GE,所有的GA都使用這兩個GE建立Spec。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/17.png)
Cost GE
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/18.png)
Cooldown GE
注意上面的Data Tag並不等同於Cooldown Tag,只是用於告訴GE的修改器(Modifier)需要修改(Modify)的Data是什麼。Cooldown Tag才是CD期間擁有的Tag,以Cooldown開頭。
之後建立一個GA藍圖基類,之後所有的GA都繼承自這個基類,配置好CD、Tag和Cost,然後呼叫Commit Ability節點就好了。如果不需要Cost或CD,最好取消選擇Cooldown GE Class和Cost GE Class,以避免當魔力值歸零時無法釋放0消耗技能的問題。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/19.png)
基類設定
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/20.png)
具體GA配置
3.6 Ability Task
GA是在一幀內完成的,如果想要實現類似Wait的非同步邏輯需要使用Task。
圖中所示就是Ability Task,是基於原生的Gameplay Task實現的。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/21.png)
可以看見,GAS內建了許多Task,圖中用的是一個播放蒙太奇的Task(注意與UE原生的播放蒙太奇節點不同,在GAS系統中最好使用PlayMontageAndWait)。
自帶的Task的功能講解可以參考這篇文章。
《GAS AbilityTask節點功能整理》
如果想要實現自己的Task,如監聽玩家輸入等,需要使用C++。具體實現可以參考這篇文章,不再贅述。
《虛幻四Gameplay Ability System入門(12)-Ability Task》
四、AttributeSet
4.1 Attribute與AS
AttributeSet負責定義和持有屬性,並且管理屬性的變化,包括網路同步。需要在Actor中被新增為成員變數,並註冊到ASC(C++)。
一個ASC可以擁有一個或多個(不同的)AttributeSet,因此可以角色共享一個很大的 AttributeSet,也可以每個角色按需新增AttributeSet。
可以在屬性變化前(PreAttributeChange)後(PostGameplayEffectExecute)處理相關邏輯,可以透過委託的方式繫結屬性變化。
正如字面意思,AS是Attribute的集合。
Attribute就是HP、MP、Speed、ATK等可以用float表示的屬性。
因為Attribute是包含了兩個float變數的結構體,分別是Base Value和Current Value。
Base Value表示基礎值,Current Value表示臨時值。
如臨時增加100生命值10s,改變的就是Current Value,10s後自動變回Base Value。
做GE時要注意修改的是哪種Value(詳見5.2.1)。
AS只能使用C++建立。
4.2 AS新增
建立AttributeSetBase類,這裡需要使用AbilitySystemComponent.h的宏ATTRIBUTE_ACCESSORS()。
對每一個FGameplayAttributeData都應用一遍宏。
這裡建立Health和MaxHealth作為示範。
AttributeSetBase.h
#pragma once #include "CoreMinimal.h" #include "AttributeSet.h" #include "AbilitySystemComponent.h" #include "AttributeSetBase.generated.h" // Uses macros from AttributeSet.h #define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \ GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName) /** * */ UCLASS() class ARPG_UNREAL_API UAttributeSetBase : public UAttributeSet { GENERATED_BODY() public: // Attributes UPROPERTY(VisibleAnywhere, BlueprintReadWrite); FGameplayAttributeData Health; ATTRIBUTE_ACCESSORS(UAttributeSetBase, Health); UPROPERTY(VisibleAnywhere, BlueprintReadWrite); FGameplayAttributeData MaxHealth; ATTRIBUTE_ACCESSORS(UAttributeSetBase, MaxHealth); };
最後在ASC元件裡指定好就可以使用了。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/22.png)
Default Starting Table是用於屬性初始化用的
(詳見下一節)
4.3 AS初始化
AS可以透過GE和DataTable兩種不同方式初始化,Epic推薦使用GE。
4.3.1 透過GE初始化
建議先看完GE部分。
建立一個GE,命名為GE_InitAttributes,找到Gameplay Effect條目。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/23.png)
一個Modifiers對應一個Attributes。
新增新的Modifier,選擇要修改的屬性,Modifier Op(修改方式)選擇Override。
Modifier Magnitude(修改值)選擇Scalable Float,填入想設定的預設值。
然後在藍圖中Apply該GE即可。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/24.png)
4.3.2 透過DataTable初始化
建立一個DataTable,行結構選擇AttributeMetaData,其格式如下。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/25.png)
可以使用Excel做好後儲存為csv檔案再快速匯入。
注意這裡的最大最小值沒有任何作用,為未完成功能,實現方法見4.5。
因此最好單獨建立一個MaxHealth屬性。
屬性的名稱需要帶上完整類名。
然後找到Character的ASC元件,在Attribute Test條目填上表格即可。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/26.png)
4.4 AS獲取
搜尋Get Attribute即可,Current Value和Base Value都可以獲得。
可以透過ASC元件呼叫,也可以使用GAS的藍圖函式庫裡的函式。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/27.png)
4.5 監聽Attribute修改事件
建議先看完GE部分,瞭解GE的機制後再回來看此部分。
4.5.1 PreAttributesChange和PostGameplayEffectExecute
AttributeSet提供了兩個方法用於監聽Value的改變:
- PreAttributeChange:用於Attribute的Current Value被改變前呼叫,對應Infinite和Has Duration的GE。
- PostGameplayEffectExecute:用於Base Value改變後呼叫,對應InstantGE。
virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override; virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;
這兩個事件適用於Clamp屬性,確保其不超出臨界值。
void UAttributeSetBase::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) { Super::PreAttributeChange(Attribute, NewValue); if (Attribute == GetHealthAttribute()) { NewValue = FMath::Clamp(NewValue, 0.0f, GetMaxHealth()); } } // 這個方法也行,但是需要"GameplayEffectExtension.h" void UAttributeSetBase::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) { Super::PostGameplayEffectExecute(Data); if(Data.EvaluatedData.Attribute == GetHealthAttribute()) { SetHealth(FMath::Clamp(GetHealth(), 0.0f, GetMaxHealth())); } }
4.5.2 GetGameplayAttributeValueChangeDelegate
如果想要監聽Attribute的變化以更新UI,則不適合用上面的方法,應該在角色類中建立一個回撥,以及藍圖事件:
CharacterBase.h
// Attribute Change Callbacks void OnHealthChanged(const FOnAttributeChangeData& Data); // Attribute Change Event in Blueprint DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHealthChangeEvent, float, NewHealth); UPROPERTY(BlueprintAssignable, Category="Ability") FOnHealthChangeEvent HealthChangeEvent;
CharacterBase.cpp
void ACharacterBase::OnHealthChanged(const FOnAttributeChangeData& Data) { HealthChangeEvent.Broadcast(Data.NewValue); }
然後在BeginPlay()裡將其註冊到ASC:
void ACharacterBase::BeginPlay() { Super::BeginPlay(); if (AbilitySystemComponent != nullptr) { //初始化技能... //初始化ASC... //註冊Attribute變化事件 AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UAttributeSetBase::GetHealthAttribute()).AddUObject(this, &ACharacterBase::OnHealthChanged); } }
之後就可以從藍圖呼叫生命值變化事件了。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/28.png)
五、Gameplay Effect
5.1 GE介紹
Gameplay Effect(GE)是Ability對自己或他人產生影響的途徑。GE通常可以被理解為我們遊戲中的Buff。比如增益/減益效果(修改屬性)。
但是GAS中的GE也更加廣義,釋放技能時候的傷害結算,施加特殊效果的控制、霸體效果 (修改GameplayTag)都是透過GE來實現的。
GE相當於一個可配置的資料表,不可以新增邏輯。開發者建立一個UGameplayEffect的派 生藍圖,就可以根據需求製作想要的效果。
GE就是一張資料表,不負責邏輯處理,定義Attribute修改的值。
GE是修改Attribute的唯一渠道!
其工作流程可以簡單分為以下幾步:
- 建立一個例項Spec。
- (可選)修改Spec的一些值。
- 如果允許,應用(Apply)GE,但是Attribute仍未被修改。
- 如果允許,使Modifier生效,修改Attribute。
- 滿足條件後,移除該Spec。
其功能非常多且強大,提供了非常多的可配置項。來一張圖感受一下:
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/29.png)
GE的配置項可以滿足絕大多數遊戲的需求,尤其是MOBA遊戲和RPG遊戲。
比較重要的功能有巢狀呼叫GE、賦予GA、呼叫GC。
此外也能根據等級計算資料、實現多層GE疊加、設定GE應用的條件、機率等。
5.2介紹上面的大部分配置項,5.3介紹GE的核心配置——Modifier,5.4介紹GE的使用。
5.2 GE配置項講解
5.2.1 Gameplay Effect
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/30.png)
最核心的配置項。
Duration Policy:GE的持續型別,有三種。
- Instand:立即改變Base Value(扣血)。
- Infinite:永久改變Current Value(按下疾跑修改速度)只能透過GA或ASC取消。
- Has Duration:臨時修改Current Value(臨時Buff)。
Modifiers:選擇你要修改的Attribute,支援數值等級曲線和Tag,會單獨講。
Executions:同樣也是修改屬性,支援更復雜的運算。
Conditional Gameplay Effects:當GE成功應用時,可以應用其他GE,巢狀呼叫GE的方法之一。
如果是Has Duration的GE,那麼我們需要設定Duration的時長,稱為Duration Magnitude。
而下文的Modifier裡面也有一個Magnitude的概念,二者的設定方法是一樣的,詳見5.3.2。
5.2.2 Period
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/31.png)
設定GE的觸發週期,僅有Infinite和Has Duration的GE才顯示前兩項設定。
Period:
如果是Infinite模式,加上Period後等價於週期執行的Instant;
如果是Has Duration模式,就是普通的週期重複。(也有說法是週期執行的Instance,待證實。)
Execute Periodic Effect on Application:t=0的時候是否觸發。(如LOL中點燃技能就是使用後立即造成傷害,之後每秒應用一次)。
Periodic Inhibition Policy:GE中斷並恢復後的處理方式。
- Never Reset:從被打斷時的位置開始計算週期,相當於暫停再播放。
- Reset Period:從0開始計算週期。
- Execute and Reset Period:打斷時立即執行一次,下次從0開始計算週期。
5.2.3 Application
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/32.png)
設定GE的應用機率和條件。
機率支援曲線圖表。
條件可以簡單地用Tag去限制,也可以用Application Requirement進行更復雜的邏輯判斷。
需要新增一個自定義的Custom Application Requirement(CAR)藍圖類,過載裡面的唯一方法,如下圖所示。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/33.png)
官方文件推薦在以下情況使用CAR藍圖類:
- Target需要有一定數量的屬性時;
- Target需要GE堆疊到一定數量時;
- 除此之外CARs還能夠做更多事情,比如檢查Target是否應用了一個GameplayEffect 的例項,在應用一個新例項時如果同型別的例項已存在則只改變其持續時間(CanApplyGameplayEffect()要返回false)。
5.2.4 Stacking
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/34.png)
用於疊加多個GE的效果,僅能用於Infinite和Has Duration的GE。
Stack Limit Count:最大層數。
Stacking Type:疊加棧在目標身上or施法者身上。
舉個例子,假設層數為3,如果是by Target模式,那麼3個敵人對我釋放的Debuff只能疊三層。
如果是by Source模式,那麼3個敵人可以對我疊加9層Debuff。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/35.png)
每層Effect如果是Modifiers來計算,則為直接疊加的效果,比如用Modifiers來增加3攻擊力,則第一層為增加3攻擊力,第二層為增加6攻擊力,第三層為增加9攻擊力,而如果需要根據層數不同而改變增加的值,則需要使用Executions。
Stack Duration Refresh Policy:Apply新GE時是否重新整理持續時間,注意溢位的Apply也會重新整理,想關閉可以在下面的Overflow條目關閉。
Stack Period Reset Policy:同上,是否重新整理週期。
Stack Expiration Policy:當一層GE的Duration到期後的處理方式。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/36.png)
- Clear Entire Stack:清空全部層數,如LOL征服者。
- Remove Single Stack and Refresh Duration:清空一層,如LOL致命節奏。
- Refresh Duration:不清空,相當於無限長的Duration,但可以透過呼叫FActiveGameplayEffectsContainer::OnStackCountChange(FActiveGameplayEffect& ActiveEffect, int32 OldStackCount, int32 NewStackCount)方法來自己處理細節,如一次掉兩層。
5.2.5 Overflow
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/37.png)
可以設定Stack溢位會Apply的GE。透過GE應用GE的方法之一,需要配合Stacking來使用。
Deny Overflow Application:如果為True,則溢位的Apply不會重新整理Duration。
Clear Stack On Overflow:字面意思,需要勾選上一個選項後才能選中。
5.2.6 Expiration
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/38.png)
當GE的Duration被打斷或結束時的行為。透過GE應用GE的方法之一,僅能用於Has Duration的GE。
Premature Expiration Effect Classes:打斷時Apply的GE。
Routine Expiration Effect Classes:正常結束時Apply的GE。
5.2.7 Immunity
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/39.png)
Immunity和Tag類似,也可以用來限制GE。
透過Tag匹配來實現,匹配的目標是Target的ASC元件以及擁有的GA。
如果擁有Require Tags的所有Tag,並且沒有Ignore Tags的所有Tag,則認為匹配成功,該GE不會被Apply。
和Tags相比,Immunity提供了一個回撥UAbilitySystemComponent::OnImmunityBlockGameplayEffectDelegate。
下面的Granted Application Immunity Query是更高階的匹配,但是更消耗效能。
比較特殊的是最後三個選項,分別是根據GE修改的Attribute匹配、根據GE來源匹配以及根據GE的類匹配。
5.2.8 Tags
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/40.png)
和GA的Tag條目類似,設定各種限制條件。
GameplayEffectAssetTag:GE的Tag。Combined Tag為計算結果,不可編輯,計算方式是繼承的Tag+Added-Removed。
GrantedTags:GE會賦予目標ASC的Tag,僅適用於Infinite和Has Duration的GE。
Application Tag Requirements:GE滿足Tag條件才能應用(Apply)。
Ongoing Tag Requirements:GE滿足Tag條件才能修改值(Modifier or Execution)。透過這項設定GE可以僅Apply而不修改值,僅適用於Infinite和Has Duration的GE。
Removal Tag Requirements:GE滿足Tag條件就會被移除。
Remove Gameplay Effects with Tags:Apply後移除指定Tag的GE。
Remove Gameplay Effect Query:上面一條的高階版,可以匹配GE的類(Effect Definition),匹配來源(Effect Source)以及匹配GE修改的屬性(Modifying Attribute),移除成功匹配的GE。
5.2.9 Display
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/41.png)
與特效相關的設定,呼叫Gameplay Cue的方式之一,詳見6.3.1。
5.2.10 Granted Abilities
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/42.png)
使用GE新增GA的方式。僅支援Infinite和Has Duration的GE。
Level:GA的等級。
Input ID:如果使用舊版輸入,每一個操作對映都對應著一個列舉值,輸入對應的列舉值就可以將這個新GA繫結到輸入上。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/43.png)
Removal Policy:設定當GE被移除時,GA是否要移除。
- Cancel Ability Immediately:移除,並觸發事件EndAbility。
- Remove Ability on End:移除,但是不觸發EndAbility。
- Do Nothing:GA不會被移除。
5.3 Modifier與Execution
5.3.1 Modifier是什麼
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/44.png)
Modifier在Gameplay Effect目錄下,作用是修改Attribute,一個Modifier對應一個Attribute。
Attribute:要修改的Attribute,AttributeSetBase是自己寫的C++ AS類。
Modifier Op:運算子(Operator),有加、乘、除和覆蓋四種。
Modifier Magnitude:運算值,與Attribute進行Modifier Op選擇的運算。
Tags:是否能修改該Attribute的限制條件,這裡指ASC元件上的Tag。
我們製作GE,主要任務就是確定Attribute、Op與Magnitude,而Magnitude是最靈活的一部分,可以透過四種方式得到,下面即將介紹。
5.3.2 Magnitude的計算
Magnitude的計算方式有四種,對應四種Magnitude Calculation Type:
- Scalable Float:不計算,直接給定一個浮點數作為Magnitude的值,也可以從等級曲線中獲得。要注意的是如果使用了曲線圖表,圖表裡獲得的值會和輸入的數相乘。
- Attribute Based:讀取玩家或目標屬性作為一個值,可以進行簡單線性計算。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/45.png)
主要分為三個部分,上面的部分是運算係數,公式為:
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/46.png)
要修改的Attribute和用來計算Magnitude的Attribute是不一樣的,為了區分這裡稱後者為Attr。Coe,Pre,Post都可以透過等級圖表獲得。中間的部分為Attr的來源,圖例為目標的生命值。可以實現如偷取敵方最大生命值20%的效果。AttributeCurve的存在意義不是很清楚。
根據《GameplayEffect(一)功能》的說明,正確的公式應該如下,有待測試:
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/47.png)
關於Snapshot,官方文件是這麼說明的:
快照(Snapshot)意味著取GameplayEffectSpec被建立時屬性的值,否則取GameplayEffectSpec被應用時屬性的值。
正常情況下,我們為角色應用GE需要呼叫ApplyGEToOwner節點(詳見5.4.1),此時系統會自動幫我們建立一個Spec例項並將其Apply。
但我們也可以手動地呼叫MakeOutgoingGESpec節點來例項化一個GE,修改其值後,再使用ApplyGESpecToOwner節點應用該Spec(該方法在3.5.2有使用)。
如果想要獲取修改前的值,就可以勾選Snapshot。
下面的Attribute Calculation Type有三種,代表Attr使用的值:
- Attribute Magnitude:使用Current Value。
- Attribute Base Value:字面意思。
- Attribute Bonus Magnitude:使用Current Value - Base Value。
Tag也是計算限制條件,不過是對Attr的限制,而不是上文的Attribute。
- Custom Calculation Class:自定義的更復雜的運算規則,與AttributeBase相比好處是可以獲取任意數量的Attr。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/48.png)
點選Calculation Class旁邊的加號,將建立一個GameplayModMagnitudeCalculation藍圖類。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/49.png)
裡面唯一的過載函式就是寫計算邏輯的地方,返回的float就是Magnitude值。
此外還有一個繼承的變數RelevantAttributeToCapture,可以在類預設值設定要Capture的Attribute及其來源。
但這個藍圖應該只是半成品,Spec和GE Attribute Capture Definition結構體都沒法拆分,想要在藍圖使用還需要去C++部分自己實現一些函式給藍圖。
如果使用C++的方式編寫MMC,可以參考這篇文章:
《虛幻四Gameplay Ability System入門(7)-Gameplay Effect詳解(2)自定義Calculation Class》
對照著上圖理解會更直觀一些。同時在3.5.2處設定Cost時也有一個使用MMC的例子。
- Set By Caller:透過藍圖獲得Magnitude。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/50.png)
一般情況下,我們Apply一個GE後,系統會自動幫我們生成一個GE的Spec並新增到目標的ASC上。5.4會說明GE的一般使用方法。
這裡的思路不太一樣,我們先是建立了一個GE的例項Spec,用Caller修改指定Modifier的Magnitude之後,再將Spec Apply到目標上。
而Data Tag則用於區分多個Modifier,告訴藍圖修改哪個Modifier的Magnitude,建議用Data開頭。
比較簡單,也非常好用。配置完之後可以從GA或是ASC按照下圖所示方法使用該GE。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/51.png)
圖例為建立一個Cooldown GE的例項後,再將Cooldown值賦給對應Data的Magnitude。Cooldown的實現並不是這樣,這裡只是一個演示,可以自行換成其他屬性。
5.3.3 Execution介紹
更高階的Modifier,一個Execution就能設定多個Attribute。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/52.png)
和上面計算Modifier的Magnitude用的CalculationClass類似,區別在於上文用到的MagnitudeCalculation是獲取多個Attr以計算Magnitude,再透過Magnitude修改Attribute。
而這個ExecutionCalculation是直接獲取多個Attribute進行修改。
具體實現可以參考5.3.2的第3小節。
此外,Conditional GE可以設定Execution成功執行時候Apply的GE,這也是非主動應用GE的方法之一。
圖的最下面也有一個Conditional GE,注意二者是不一樣的,在5.2.1有提到。滑鼠懸停也能檢視二者的差別。
5.4 GE的應用
GE的應用我們稱為Apply,可以從GA或者ASC去Apply一個GE。
GE可以透過藍圖手動應用,也可以透過GE的配置項,使GE在特定條件下巢狀應用其他GE。
5.4.1 GE的主動應用
應用GE的時候,我們可以設定GE的等級。
Stacks表示應用多少層的GE,僅在GA裡Apply GE時才能設定此項。
注意重複呼叫該節點也算多層GE疊加,Stack詳見5.2.4。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/53.png)
GA中Apply GE的例子
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/54.png)
ASC中Apply GE的例子
Apply的物件有Owner也有Target,Owner比較省事。
如果想讓敵人扣血,可以在GA裡先Send一個Gameplay Event,透過Event呼叫Target的播放受擊動畫的GA,再在GA裡Apply一個扣血GE To Self。
5.4.2 GE的巢狀應用
涉及3個配置項,對應不同的條件:
- Gameplay Effect:當前GE成功應用後,應用配置好的GE,見5.2.1。
- Overflow:GE層數溢位時,應用配置好的GE,可以做滿層爆炸的效果,見5.2.5。
- Expiration:GE中斷或結束時,應用配置好的GE,見5.2.6。
六、Gameplay Cue
6.1 GC介紹
GameplayCues(GC)執行非遊戲性相關的事情,比如音效,粒子特效,震屏等。GameplayCues通常會被複制和預測(除非設定Executed, Added或Removed是本地的)。
主要有Static和Actor兩類GC。
Static適用於單次播放的特效。由於其是靜態的,不會產生例項,因此在其藍圖裡建立的變數都是隻讀的。對應Instant和Periodic的GE。
Actor適用於持久的,不定時長的特效。其繼承自一個場景Actor,每次使用會產生一個對應例項。對應Infinity和Has Duration的GE。
6.2 GC的製作
開啟視窗-GameplayCue編輯器,可以看到如下頁面:
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/55.png)
每一個GC(處理器/Handler)需要一個對應的Tag,點選新增會顯示GC藍圖建立頁面。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/56.png)
根據需求選擇即可。
不用GameplayCue編輯器,直接建立GC藍圖也是可以的,但是記得在類預設值中設定Tag。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/57.png)
6.2.1 Static型別GC設定
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/58.png)
對於Static的GC,僅需過載OnExecute函式即可。
透過獲取傳入引數Target的根元件,就可以附加粒子系統發射器了。
Parameter用於傳入一些引數,如傷害飄字的數值等,具體的設定會在6.3.1說明。
6.2.2 Actor型別GC設定
Actor型別GC繼承自場景Actor,因此有Tick、BeginPlay、Overlap等其他函式。
因此其類預設值也有很多設定,最主要的是Gameplay Cue和CleanUp兩個目錄。
這裡我們過載OnActive和OnRemove兩個函式即可。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/59.png)
和Static型別的GC不同的是,如果我們勾選了Gameplay Cue的Auto Attach GC To Owner,我們可以用GC自身的根元件作為發射器要附加的元件(在不需要繫結到指定骨骼的情況下)。
此外,由於Actor型別的GC是非靜態的,可以產生例項,因此是可以建立變數並寫入的。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/60.png)
6.3 GC的呼叫
一般透過GE配置,也可以在GA裡呼叫Execute/Add觸發。
6.3.1 從GE配置GC
選擇GC對應的Tag即可,可以同時選擇多個Tag,觸發多個GC。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/61.png)
Require Modifier Success to Trigger Cues:需要GE成功修改Attribute後才呼叫GC,而不僅僅是Apply該GE。
Suppress Stacking Cues:多個GE存在Stack中時是否例項化多個GC(如果使用了Stack,對應的一定是可以例項化的Actor類GC)。
Min、Max Level和Magnitude Attribute則與傳入引數有關。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/62.png)
Raw Magnitude即為Magnitude Attribute的值,而Normalized Magnitude的計算方式如下:
$Normalized = (Raw - Min) / (Max - Min)$
如上圖所示,當Min=0且Max=100時,Normalized = Raw / 100,即百分比。
6.3.2 從GA呼叫GC
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/63.png)
共有五個相關函式,Add&Remove與Execute分別對應Actor型別和Static型別的GC,具體用法見圖。
七、Debug方法
可以參考《GameplayAbility中的Debug方法》。
八、總結
整個GAS系統的工作流程如圖所示。
ASC管理GA、GE、Attribute。
GE可以用來給予ASC一個GA,也可以修改Attribute。(甚至還能Apply其他的GE,圖中沒有提到。)
GA可以傳送Event給其他ASC,呼叫對應的GA;也可以對目標Apply一個GE,修改其屬性。
GE和GA都可以用來觸發GC。
![【Unreal】虛幻GAS系統快速入門](http://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_GAS/64.png)
如果看到這裡所有內容都明白了,那麼可以看[UnrealOpenDay2020]深入GAS架構設計 | EpicGames 大釗,系統講解了GAS的整體框架,讀原始碼會更容易。
參考文獻
-
https://github.com/tranek/GASDocumentation
-
虛幻引擎遊戲技能系統文件_玉田白菜的部落格-CSDN部落格
-
Gameplay技能系統 | 虛幻引擎文件(unrealengine.com)
-
[中文直播]第31期|GAS外掛介紹(入門篇) | 伍德 大釗_嗶哩嗶哩_bilibili
-
[UnrealOpenDay2020]深入GAS架構設計 | EpicGames 大釗_嗶哩嗶哩_bilibili
-
虛幻四教程 - 知乎(zhihu.com)
-
GAS AbilityTask節點功能整理
-
GameplayAbility中的Debug方法
-
虛幻外掛GAS分析3-0 GameplayEffect標籤面面觀-標籤欄
-
[玩轉UE4/UE5動畫系統>技能系統(GAS)篇] 二 技能 Gameplay Ability(GA)
-
虛幻引擎遊戲技能系統 - 知乎 (zhihu.com)
-
Unreal Engine GAS系統_虎牙維護世界和平-CSDN部落格
-
8.10學習記錄,UE4官方GASdemo,ARPG_StrangerMQ的部落格-CSDN部落格
-
UE4 GAS/ActionRPG學習導圖——AttributeSet(Gameplay Ability System)_jkchen's Haven-CSDN部落格
這是侑虎科技第1584篇文章,感謝作者LunarMaxim供稿。歡迎轉發分享,未經作者授權請勿轉載。如果您有任何獨到的見解或者發現也歡迎聯絡我們,一起探討。(QQ群:793972859)
作者主頁:https://www.zhihu.com/people/LunarMaxim
再次感謝LunarMaxim的分享,如果您有任何獨到的見解或者發現也歡迎聯絡我們,一起探討。(QQ群:793972859)