Unreal Engine 4(虛幻UE4)GameplayAbilities 插件入門教程(二)


我們接着學習。如果沒有學習第一篇,請前往學習。

由於GameplayAbilities插件基本上沒有資料(除了前面提供的那篇Dave的博文以外,再無資料,有跡象表明Dave是這個插件的開發者)。

 

這個插件究竟能做什么,簡單來說是可以制作復雜的技能系統,具體怎么奇妙現在還沒有辦法說明,不妨親自動手做一做吧!

 

 

第零步【此步驟的目的是建立AbilitySystem】:補充昨天的一個步驟,在人物的cpp文件的構造函數中加入

 

//頭文件加入:#include "AbilitySystemComponent.h"

//請注意:如果在h文件中加入頭文件,不要添加在generate.h的后面,而應該添加在它的前面。

 

AGATutCharacter::AGATutCharacter()

{

//省略一大段代碼

……

// Create a follow camera

FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));

FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation

FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm

 

//在底部新增!

AbilitySystem = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystem"));

 

 

// Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)

// are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++)

}

 

 

編譯完成之后看到藍圖里出現了AbilitySystem組件:

 

 

在藍圖中其實還用不上這個東西,我們繼續回到cpp文件。

 

0.5步【本步驟的目的是為了綁定ability到輸入,但是具體如何綁定的不清楚,但是此步驟不可以缺少,有點下文中的AbilityInput枚舉類好像沒有使用到,此步驟存疑(建議大家稍微粗略看看即可,並將下面提及的代碼添上)】:

這里貼一段理論介紹及其機器翻譯(引用了這篇文章:)

https://wiki.unrealengine.com/GameplayAbilities_and_You

 

Binding to Character Input

綁定到人物的輸入

 

First of all, we will bind our ability system to our character's input, because it's the slightly more complicated issue and it's actually pretty interesting on how you do it. So first of all, go back to your character's cpp file, and go to the SetupPlayerInputComponent function. It's the one responsible for binding your character's inputs to the player controlling it, and takes a UInputComponent as parameter. This is important, we need it to bind our ability system to it. We want to call AbilitySystem->BindAbilityActivationToInputComponent within the SetupPlayerInputComponent. It takes two parameters: The UInputComponent pointer at hand and a struct called FGameplayAbiliyInputBinds. This is not a typo! It is not called FGameplayAbilityInputBinds, but FGameplayAbiliyInputBinds!

首先,我們會將我們的能力系統綁定到我們的角色的輸入,因為這是一個稍微復雜的問題,它實際上很有趣你是怎么做的。首先,回到你的角色的cpp文件,SetupPlayerInputComponent函數。它負責將角色的輸入綁定到控制它的玩家,並將UInputComponent作為參數。這很重要,我們需要它來約束我們的能力系統。我們想叫AbilitySystem - > BindAbilityActivationToInputComponent SetupPlayerInputComponent內。它接受兩個參數:UInputComponent指針和結構稱為FGameplayAbiliyInputBinds。這不是一個打字錯誤!這不是叫FGameplayAbilityInputBinds,FGameplayAbiliyInputBinds !

(筆者:將AbilitySystem綁定到人物上需要這個關鍵的一步,但是實踐上有出入

 

 

The constructor for FGameplayAbiliyInputBinds takes at least 3 parameters: The first two are strings, and represent the input names that will be used to define "Confirm" and "Cancel"-input commands. You do not necessarily need these depending on your game, but abilities can be set up to listen to these while they're active, and targeting actors (basically, actors that return an ability viable targets/locations to aim at for an ability, if an ability requests one) will use these too, so generally it can't hurt to have these even if you will never use them. The third parameter is the name of an arbitrary UEnum of all things. This is one of the witchcraft-ier aspects of the system: The ability system component will look into the enum whose name you've given and will map its ability slots to the names of the elements contained within the enum. This probably sounds way complicated from the way I'm describing this, but it's actually quite simple. This is an input enum lifted from my own project:

FGameplayAbiliyInputBinds至少需要三個參數的構造函數:前兩個是字符串,並代表輸入名稱將用於定義固化確認取消命令。你不一定需要這些取決於你的游戲,但是能力可以設置聽這些活動時,和目標參與者(基本上,演員還能切實可行的目標/位置的瞄准能力,如果能力請求一個)將使用這些,所以一般不會傷害這些即使你永遠不會使用它們。第三個參數是所有東西的任意枚舉的名稱。這是系統的一個主要方面:能力系統組件將查看枚舉的名稱,並將它的能力位置映射到enum中包含的元素的名稱。這聽起來很復雜,但實際上很簡單。這是一個從我自己的項目中提出來的輸入enum:

 

//Example for an enum the FGameplayAbiliyInputBinds may use to map input to ability slots.
 
//It's very important that this enum is UENUM, because the code will look for UENUM by the given name and crash if the UENUM can't be found. BlueprintType is there so we can use these in blueprints, too. Just in case. Can be neat to define ability packages.
UENUM(BlueprintType) 
enum class AbilityInput : uint8
{
    UseAbility1 UMETA(DisplayName = "Use Spell 1"), //This maps the first ability(input ID should be 0 in int) to the action mapping(which you define in the project settings) by the name of "UseAbility1". "Use Spell 1" is the blueprint name of the element.

    UseAbility2 UMETA(DisplayName = "Use Spell 2"), //Maps ability 2(input ID 1) to action mapping UseAbility2. "Use Spell 2" is mostly used for when the enum is a blueprint variable.
    UseAbility3 UMETA(DisplayName = "Use Spell 3"),
    UseAbility4 UMETA(DisplayName = "Use Spell 4"),
    WeaponAbility UMETA(DisplayName = "Use Weapon"), //This finally maps the fifth ability(here designated to be your weaponability, or auto-attack, or whatever) to action mapping "WeaponAbility".
 
        //You may also do something like define an enum element name that is not actually mapped to an input, for example if you have a passive ability that isn't supposed to have an input. This isn't usually necessary though as you usually grant abilities via input ID,
        //which can be negative while enums cannot. In fact, a constant called "INDEX_NONE" exists for the exact purpose of rendering an input as unavailable, and it's simply defined as -1.
        //Because abilities are granted by input ID, which is an int, you may use enum elements to describe the ID anyway however, because enums are fancily dressed up ints.
};

 

 

第一步【此步驟的目的是給人物添加Ability】:

在GATUTCharacter.h文件中加入Ability,下面我加入的是數組,暴露給藍圖。

同時您也看到了,添上一個Beginplay方便使用。

 

第二步【此步驟是為了登記這些Ability,雖然有點復雜,但是是必要的,從第三步開始就能看到一些有意思的內容了】:

 

void AGATutCharacter::BeginPlay()
{
    Super::BeginPlay();
    if (AbilitySystem == nullptr)return;

    UE_LOG(LogTemp, Warning, TEXT("%s"), *FString("Char Beginplay 97"));
    if (HasAuthority() && MyAbilities.Num()) {
        UE_LOG(LogTemp, Warning, TEXT("%s"), *FString("Char Bplay 99"));
        for (auto i = 0; i<MyAbilities.Num(); i++)
        {
            if (MyAbilities[i] == nullptr)continue;
            AbilitySystem->GiveAbility(FGameplayAbilitySpec(MyAbilities[i].GetDefaultObject(), 1, 0));
            UE_LOG(LogTemp, Warning, TEXT("%s"), *FString("103bp we register an ability!"));
        }
        UE_LOG(LogTemp, Warning, TEXT("%s"), *FString("All Ablities registered"));
    }
    AbilitySystem->InitAbilityActorInfo(this, this);
    //這兩個參數以為着Owner和Avatar
    UE_LOG(LogTemp, Warning, TEXT("%s"), *FString("Char Bplay 105"));
}

 

第三步:

編譯,如果沒有成功的話,請看原文

https://wiki.unrealengine.com/GameplayAbilities_and_You

 

第四步【關鍵!此步驟是為了建立一個Ability藍圖】:

建立一個名為A2Ability如下:

第五步【配置input,請注意不用完全和我的截圖一樣】:

第5.5步【在藍圖中為MyAbilities數組賦值】:如果沒有這一個步驟的話,第六步是沒有效果的,也就是說我們的人物要有A2的聲明,AbilitySystem組件才能夠認識到這個人物有這個Ability(還記得前文在beginplay中有一句:

AbilitySystem->GiveAbility(FGameplayAbilitySpec(MyAbilities[i].GetDefaultObject(), 1, 0));

嗎?這一句話將這些Ability賦予給了AbilitySystem

第六步【HelloWorld】:在人物的藍圖中寫這個輸入事件,然后執行,查看結果是否和我的截圖效果一致:

恭喜,你已經HelloWorld完畢了!到這一步我花了四五個小時了。接下來我們研究一些稍微有意思一點的理論知識(大家要勤動腦筋啊)。

 

第七步【這一步是探究步驟】:

什么是Ability的實例化?

 

An ability is also able to control its own instancing state, and each ability may independently choose whether they do not want to be instanced (no ability tasks, no personal state and variables and some other limited functionality, but ridiculously cheap so preferable if you can get away with it).

 

一種能力也能夠控制自己的實例狀態,並且每種能力都可以獨立地選擇是否不想被實例化(沒有能力任務,沒有個人狀態和變量,以及其他一些有限的功能,但是如果你能僥幸逃脫的話,它將會變得非常便宜)

 

instanced on activation (personal state limited to a per-activation basis, variables and such can be replicated but it is not recommended).

 

實例化激活(個人狀態限制為每個激活的基礎,變量,可以復制,但是不推薦)

 

or instanced per ability owner (most expensive, but variables can easily be replicated, state can be carried across activations [for example, a fireball that gets stronger with each use would be possible without permanently considering the ability active] and most functions are intact).

 

或者是每個能力所有者(最昂貴的,但是變量可以很容易被復制,狀態可以跨激活)(例如,一個在每次使用時變得更強的火球,如果沒有永久地考慮激活的能力)和大多數功能都是完整的。

 

 

 

用筆者自己的話來說是分為三種:

 

1.每一個Actor(比如每一個英雄)實例化一個此Ability

 

2.每一次執行實例化一個此Ability

 

3.永不實例化Ability

 

 

 

實驗:

 

Step1:向上面一樣,建立一個Ability稱為A3.

Step2:這個A3里面有一個整數,然后打印一下執行次數:

Step3:在A3中看Class Default,設置為每個Actor實例化一次。

Step4:在人物中綁定一下動作和AbilityActivate

Step5:運行查看結果:

對照實驗2

將上面步驟中的實例化模式設置為Instanced per execution,將會得到:

為什么?心造不宣。

 

對照實驗3

設置為no instanced,運行,結果宕機。具體是怎么使用的,存疑。

 

本節的內容還算簡單,下一節講技能標簽(AbilityTags)。

——小江村兒的文傑 zouwj5@qq.com 2017年7月14日14:40:39

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM