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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2017-07-14 14:41  小江村儿的文杰  阅读(5720)  评论(0编辑  收藏  举报