UE4 GAS外掛入門學習記錄1——初步認識及使用
引言
本系列文章內容僅為個人學習記錄,使用UE的版本為4.27
學習來源及各參考文件:
原文地址:
翻譯地址:
B站up主:
1.GAS認識(GameplayAbilitySystem)
摘自
Gameplay技能系統 是一個高度靈活的框架,可用於構建你可能會在RPG或MOBA遊戲中看到的技能和屬性型別。你可以構建可供遊戲中的角色使用的動作或被動技能,使這些動作導致各種屬性累積或損耗的狀態效果,實現約束這些動作使用的"冷卻"計時器或資源消耗,更改技能等級及每個技能等級的技能效果,啟用粒子或音效,等等。簡單來說,此係統可幫助你在任何現代RPG或MOBA遊戲中設計、實現及高效關聯各種遊戲中的技能,既包括跳躍等簡單技能,也包括你喜歡的角色的複雜技能集。
對於人物的基礎移動和對UI的互動(例如遊戲商店購買物品)則不建議使用GAS(可以但不建議)。
對於GAS外掛中各模組的介紹及一些名詞解釋:
- GSC:指GameplaySystemComponent元件,所有需要使用GAS或者響應GAS系統的都需要有這個元件
- GameplayAbility(GA):用於執行角色能力(Ability)和技能(Skill),比如讓人物跳躍,衝刺,攻擊等,可以設定等級、發動技能所需消耗量、技能冷卻等。
- Attribute:用於管理角色或者actor的數值,例如人物血量,藍量,體力等。
- GameplayEffect(GE):為Actor應用狀態效果 ,注意:這個不是用於顯示特效音效或者動作,而是用於新增狀態,例如人物中毒灼燒扣血。
- GameplayTag:Tag意思為標籤,用於為角色新增一個或多個標籤,GameplayTag是GAS的一個重要組成部分,可用於各狀態之間的限制(感覺像是替代了常規的布林判斷),比如在受到攻擊時,如果自己有進行格擋,常規寫法是用一個bool判斷人物是否有格擋,而在GAS中則是使用GameplayTag在你使用格擋時給你新增一個標誌為格擋的Tag,受擊時則只需要判斷人物是否有這個Tag即可。
- GameplayCue(GC):用於生成視覺或聲音效果 。
- OwnerActor:擁有ASC的actor,如果是多人遊戲,ASC元件一般會放在PlayerState中,這時,PlayerState就是OwnerActor,如果是單機遊戲,則ASC可以放在character中,那麼Owneractor就是character。
- AvatarActor:指被控制的actor物理形體,如果ASC元件在PlayerState中,PlayerState就是OwnerActor,而被控制的character就是AvatarActor,單機遊戲時,ASC在character中,則OwnerActor和AvatarActor都是character。
- 還有一個為以上提到的所有應用同步(Replication).
GAS必須由C++建立,但是GameplayAbility
和GameplayEffect
可由設計師在藍圖中建立.
GAS中的現存問題:
- GameplayEffect延遲調節(Latency Reconciliation).(不能預測能力冷卻時間, 導致高延遲玩家相比低延遲玩家, 對於短冷卻時間的能力有更低的啟用速率.
- 不能預測性地移除GameplayEffect. 然而我們可以反向預測性地新增GameplayEffect, 從而高效的移除它. 但是這不總是合適或者可行的, 因此這仍然是個問題.
2.在專案中新增GAS
GAS是UE中的一個外掛,需要使用的話要先在外掛中啟用它,在設定-外掛中搜索GameplayAbilities勾選後重啟專案。
然後開啟專案C++類中的 "GameplayAbilities", "GameplayTasks".
新增完成後重新編譯生成一下專案
3.使用GAS實現簡單的技能(跳躍、衝刺)
以UE自帶的C++第三人稱模板為例,製作多人遊戲相關的GAS
首先,要實現GAS,就必須要有一個ASC(
),ASC是GAS的核心,如何需要與GAS互動的Actor都必須要附加ASC,這些物件都存於ASC並由其管理和同步(除了由AttributeSet同步的Attribute).ASC 附加的 Actor 被引用作為該 ASC的OwnerActor,該 ASC的物理代表 Actor 被稱為 AvatarActor. OwnerActor 和 AvatarActor 可以是同一個 Actor,比如MOBA遊戲中的一個簡單AI小兵;它們也可以是不同的 Actor,比如MOBA遊戲中玩家控制的英雄,其中 ownerActor是Playerstate,AvatarActor 是英雄的character 類.絕大多數Actor的 ASC 都附加在其自身,如果你的Actor會重生並且重生時需要持久化Attribute或 GameplayEffect (比如MOBA中的英雄)那麼 ASC理想的位置就是Playerstate.
注意:如果ASC位於playerState,則需要將NetUpdateFrequency值調高,參考視訊NetUpdateFrequency設定
建立ASC:(這裡我是在繼承PlayerState的類中)
.h檔案:
包含標頭檔案:
1 #include "AbilitySystemInterface.h"
繼承介面IAbilitySystemInterface:
重寫介面函式(必須重寫):
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
宣告ASC元件:
UPROPERTY() class UAbilitySystemComponent* AbilitySystemComponent;
.cpp檔案:
包含標頭檔案:
#include "AbilitySystemComponent.h"
在建構函式中建立例項:
APlayerStateBase::APlayerStateBase() { //建立ASC例項 AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystemComponent")); //開啟網路複製 AbilitySystemComponent->SetIsReplicated(true); }
實現介面函式:
UAbilitySystemComponent* APlayerStateBase::GetAbilitySystemComponent() const { return AbilitySystemComponent; }
跳躍實現:
找到專案檔案,例如我的專案名是GAS_TestDemo,那對應專案檔案則為GAS_TestDemo.h
.h檔案:
建立列舉:用於對映角色輸入,UMETA(DisplayName = "Jump")裡的"Jump"是這個列舉值在藍圖的別名
UENUM(BlueprintType) enum class EGASAbilityInputID : uint8 { // None UMETA(DisplayName = "None"), //跳躍 Jump UMETA(DisplayName = "Jump"), //衝刺 Sprint UMETA(DisplayName = "Sprint"), //攻擊 Attack UMETA(DisplayName = "Atk"), };
在UE編輯器的專案設定-輸入中建立對應名字的輸入操作:(注意:這裡的名字需要和上面列舉的名字一致)
因為這裡的ASC在playerState,所以在character中需要獲得對ASC的引用
.h檔案中:
先宣告ASC
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "GameplayAbilitySystem") class UAbilitySystemComponent* AbilitySystemComponent;
重寫character的函式PossessedBy(),作用好像是在character被控制器控制時會回撥這個函式
//在被控制器控制時呼叫 virtual void PossessedBy(AController* NewController) override;
.cpp檔案中:
需要將輸入和Ability聯絡起來,在SetupPlayerInputComponent函式中新增:
void AGAS_TestDemoCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) { // Set up gameplay key bindings check(PlayerInputComponent); //舊的跳躍繫結 //PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump); //PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping); //在專案檔案中定義的全域性結構體EGASAbilityInputID,用於和專案設定的輸入操作名字對應 //將輸入和能力元件繫結 //FGameplayAbilityInputBinds的第三個引數EGASAbilityInputID為專案檔案建立的列舉型別 if (IsValid(AbilitySystemComponent)) { AbilitySystemComponent->BindAbilityActivationToInputComponent(PlayerInputComponent, FGameplayAbilityInputBinds( FString(), FString(), FString(TEXT("EGASAbilityInputID")), static_cast<int32>(EGASAbilityInputID::None), static_cast<int32>(EGASAbilityInputID::None) ) ); } }
實現PossessedBy():注意:因為這裡的GSC在playerState中,所以需要引入標頭檔案。同時,UGameplayAbility_CharacterJump也需要引入標頭檔案:
#include "PlayerStateBase.h" //這個標頭檔案是自己定義ASC的那個標頭檔案 #include "GameplayAbilities/Public/Abilities/GameplayAbility_CharacterJump.h"
void AGAS_TestDemoCharacter::PossessedBy(AController* NewController) { Super::PossessedBy(NewController); //獲取玩家的PlayerState APlayerStateBase* PS = GetPlayerState<APlayerStateBase>(); if (PS) { AbilitySystemComponent = Cast<APlayerStateBase>(PS)->GetAbilitySystemComponent(); /** *初始化了Abilities的ActorInfo(參與者資訊),該結構儲存了有關我們正在對誰採取行動以及誰控制我們的資訊。即OwnerActor和AvatarActor *OwnerActor是邏輯上擁有此元件的參與者。 *AvatarActor是pawn。 */ PS->GetAbilitySystemComponent()->InitAbilityActorInfo(PS, this); } //***判斷多人遊戲時是否是在伺服器端**********判斷ASC元件是否有效 if (GetLocalRole() != ROLE_Authority || !IsValid(AbilitySystemComponent)) { return; } /*給予跳躍能力, *FGameplayAbilitySpec第一個引數就是當操作發生後,需要執行的東西,第二個為技能等級(暫時不知道怎麼用,預設1),第三個就是繫結的輸入操作鍵,第四個應該是繫結該技能的類吧(應該) *其中UGameplayAbility_CharacterJump::StaticClass()是UEGAS封裝好的,類似於正常繫結跳躍時的引數&ACharacter::Jump * EGASAbilityInputID::Jump為專案檔案中定義的那個列舉,用於和輸入操作繫結 */ AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(UGameplayAbility_CharacterJump::StaticClass(), 1, static_cast<int32>(EGASAbilityInputID::Jump), this)); }
至此,角色的跳躍就已經被封裝成了一個技能,由於這個跳躍是自帶的,我們好像並沒有做什麼東西,因此繼續實現一個衝刺技能
衝刺技能實現:
在角色類中:
.h檔案:
宣告一個GA,由藍圖去指定:
/** * @brief 宣告一個能力GA */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameplayAbilitySystem") TSubclassOf<class UGameplayAbility> SprintAbility;
.cpp檔案:
在PossessedBy()函式中繼續新增:(為什麼在這裡新增?放在這裡,衝刺技能就會在控制器控制character時啟用此技能)
/* * 給予衝刺能力 * 因為SprintAbility是由藍圖指定,所以需要判斷有效性,避免出錯 */ if (SprintAbility) { AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(SprintAbility, 1, static_cast<int32>(EGASAbilityInputID::Sprint), this)); }
具體邏輯實現:
藍圖中:(也可以在C++中實現)
建立一個繼承C++第三人稱character的藍圖,在事件圖表中建立一個自定義事件或者函式名為Sprint:
在內容瀏覽器中右鍵選擇GameplayAbility,建立並命名為GA_Sprint,開啟:
編寫:
將此GA指定到角色藍圖中對應的值:
這樣就實現了由GAS封裝的角色衝刺技能,所以技能實現邏輯還是在人物身上,只是由GAS去封裝呼叫,可以由GAS來賦予或剝除。
如果沒有反應,看看專案輸入的名字是否和專案檔案的列舉對應上。
目前只有給予技能,還沒有學習到移除技能,先這樣吧。