原文:
annas-archive.org/md5/697adf25bb6fdefd7e5915903f33de14
译者:飞龙
第八章:9.音频-视觉元素
概述
在本章中,我们将完成我们在过去四章中一直在努力的基于躲避球的游戏。我们将通过添加音效、粒子效果,并创建另一个关卡来结束本章,这次关卡中玩家必须遵循实际路径才能完成。到本章结束时,您将能够向您的 UE4 项目添加 2D 和 3D 音效,以及粒子效果。
介绍
在上一章中,我们学习了游戏 UI 以及如何创建和添加用户界面(也称为小部件)到屏幕上。
在本章中,我们将学习如何向我们的游戏添加音频和粒子效果。这两个方面都将提高我们游戏的质量,并为玩家提供更加沉浸式的体验。
在视频游戏中,声音可以以声音效果(也称为 SFX)或音乐的形式出现。声音效果使您周围的世界更加真实和生动,而音乐则有助于为您的游戏设定基调。这两个方面对于您的游戏都非常重要。
在竞技游戏中,如《反恐精英:全球攻势》(CS:GO)中,声音也非常重要,因为玩家需要听到他们周围的声音,如枪声和脚步声,以及它们来自哪个方向,以尽可能多地了解他们周围的情况。
粒子效果和音效之所以重要,是因为它们使您的游戏世界更加真实和沉浸。
让我们通过学习 UE4 中的音频工作来开始本章。
UE4 中的音频
任何游戏的基本组成部分之一是声音。声音使您的游戏更加真实和沉浸,这将为您的玩家提供更好的体验。视频游戏通常有两种类型的声音:
-
2D 声音
-
3D 声音
2D 声音不考虑听者的距离和方向,而 3D 声音可以根据玩家的位置在音量上升或下降,并在右侧或左侧移动。2D 声音通常用于音乐,而 3D 声音通常用于音效。主要的声音文件类型是.wav 和.mp3。
以下是 UE4 中与音频相关的一些资产和类:
-
“声音基础”:代表包含音频的资产。这个类主要用于 C++和蓝图,用于引用可以播放的音频文件。
-
声波:代表已导入到 UE4 中的音频文件。继承自“声音基础”。
-
“声音提示”:一个音频资产,可以包含与衰减(随着听者距离变化而音量如何变化)、循环、声音混合和其他与音频相关的功能相关的逻辑。它继承自“声音基础”。
-
“声音类”:允许您将音频文件分组并管理其中一些设置,如音量和音调。一个例子是将所有与音效相关的声音分组到
SFX
声音类中,将所有角色对话分组到“对话”声音类中,等等。 -
“声音衰减”:允许您指定 3D 声音的行为的资产;例如,它将从哪个距离开始降低音量,它将在哪个距离变得听不见(无法听到),如果音量会随着距离的增加而线性或指数变化等等。
-
音频组件:允许您管理音频文件及其属性的演员组件。用于设置连续播放声音,如背景音乐。
在 UE4 中,我们可以像导入其他资产一样导入现有的声音:通过将文件从 Windows 文件资源管理器拖放到“内容浏览器”中,或者通过在“内容浏览器”中点击“导入”按钮。我们将在下一个练习中进行这个操作。
练习 9.01:导入音频文件
在这个练习中,您将从计算机中导入一个现有的声音文件到 UE4 中。当躲避球从表面弹起时,将播放此音频文件。
注意
如果您没有音频文件(.mp3 或.wav 文件)可用来完成此练习,您可以在此链接下载.mp3 或.wav 文件:www.freesoundeffects.com/free-track/bounce-1-468901/
。
将此文件保存为BOUNCE.wav
。
一旦您有音频文件,请按照以下步骤操作:
-
打开编辑器。
-
转到“内容浏览器”界面内的“内容”文件夹,并创建一个名为“音频”的新文件夹:
图 9.1:内容浏览器中的音频文件夹
-
转到您刚刚创建的“音频”文件夹。
-
将您的音频文件导入此文件夹。您可以通过将音频文件从“Windows 文件资源管理器”拖放到“内容浏览器”中来执行此操作。
-
完成此操作后,应该会出现一个名为您音频文件的新资产,您可以在单击它时听到它:
图 9.2:导入的音频文件
- 打开此资产。您应该看到许多可供编辑的属性。但是,我们将仅专注于“声音”类别内的一些属性:
图 9.3:声音资产的设置
以下属性可在“声音”类别中使用:
-
“循环”:此声音在播放时是否循环。
-
“音量”:此声音的音量。
-
“音调”:此声音的音调。音调越高,频率越高,音调越高。
-
“类”:此声音的“声音类”。
我们将更改的唯一属性是“类”属性。我们可以使用 UE4 提供的现有“声音”类之一,但让我们为躲避球创建自己的“声音类”,以便为我们的游戏创建一组新的声音。
-
转到“内容浏览器”界面内的“音频”文件夹。
-
右键单击,转到“声音”类别(倒数第二个类别),然后转到“类别”类别,选择“声音类”。这将创建一个新的“声音类”资产。将此资产重命名为“躲避球”。
-
打开您导入的声音资产,并将其“类”属性设置为“躲避球”:
图 9.4:将类属性更改为躲避球声音类
现在,这个导入的声音资产属于特定的类,您可以将与躲避球相关的其他声音效果分组到同一个“声音类”中,并通过该“声音类”编辑它们的属性,包括“音量”、“音调”和许多其他属性。
有了这个,我们就可以结束我们的练习了。您已经学会了如何将声音导入到您的项目中,以及如何更改它们的基本属性。现在,让我们继续进行下一个练习,在这个练习中,我们将在我们的游戏中每当躲避球从表面弹开时播放声音。
练习 9.02:当躲避球从表面弹开时播放声音
在这个练习中,我们将为我们的DodgeballProjectile
类添加必要的功能,以便当躲避球从表面弹开时播放声音。
要做到这一点,请按照以下步骤操作:
-
关闭编辑器并打开 Visual Studio。
-
在
DodgeballProjectile
类的头文件中,添加一个受保护的class USoundBase*
属性,名为BounceSound
。此属性应该是一个UPROPERTY
,并具有EditDefaultsOnly
标记,以便可以在蓝图中进行编辑:
// The sound the dodgeball will make when it bounces off of a surface
UPROPERTY(EditAnywhere, Category = Sound)
class USoundBase* BounceSound;
- 完成此操作后,转到
DodgeballProjectile
类的源文件,并添加一个包含GameplayStatics
对象的包含:
#include "Kismet/GameplayStatics.h"
- 然后,在类的
OnHit
函数的实现开始之前,在对DodgeballCharacter
类的转换之前,检查我们的BounceSound
是否是有效属性(与nullptr
不同),以及NormalImpulse
属性的大小是否大于600
单位(我们可以通过调用其Size
函数来访问大小)。
正如我们在第八章,用户界面中看到的,NormalImpulse
属性表示在被击中后改变躲避球轨迹的方向和大小的力量。我们要检查它的大小是否大于一定数量的原因是,当躲避球开始失去动量并且每秒在地板上反弹多次时,我们不希望每秒播放BounceSound
多次;否则,会产生很多噪音。因此,我们将检查躲避球所受的冲量是否大于该数量,以确保这种情况不会发生。如果这两个条件都成立,我们将调用GameplayStatics
对象的PlaySoundAtLocation
。这个函数负责播放 3D 声音。它接收五个参数:
-
一个世界上下文对象,我们将作为
this
指针传递。 -
一个
SoundBase
属性,将是我们的HitSound
属性。 -
声音的来源,我们将使用
GetActorLocation
函数传递。 -
VolumeMultiplier
,我们将传递一个值为1
。这个值表示播放此声音时音量会高低多少。例如,值为2
表示音量会是原来的两倍。 -
PitchMultiplier
,表示播放此声音时音调会高低多少。我们将使用FMath
对象的RandRange
函数传递这个值,该函数接收两个数字作为参数,并返回这两个数字之间的随机数。为了在0.7
和1.3
之间随机生成一个数字,我们将使用这些值作为参数调用这个函数。
看一下以下代码片段:
if (BounceSound != nullptr && NormalImpulse.Size() > 600.0f)
{
UGameplayStatics::PlaySoundAtLocation(this, BounceSound, GetActorLocation(), 1.0f, FMath::RandRange(0.7f, 1.3f));
}
注意
负责播放 2D 声音的函数也可以从GameplayStatics
对象中获得,它被称为PlaySound2D
。这个函数将接收与PlaySoundAtLocation
函数相同的参数,除了第三个参数,即声音的来源。
-
编译这些更改,然后打开虚幻编辑器。
-
打开
BP_DodgeballProjectile
蓝图,转到其Class Defaults
选项卡,并将BounceSound
属性设置为你导入的声音资产:
图 9.5:将 BounceSound 属性设置为我们导入的声音
- 再次玩这个关卡,进入敌人角色的视线。你应该注意到每当敌人角色投掷的躲避球击中墙壁或地板(而不是玩家角色)时,会播放不同音调的声音:
图 9.6:玩家角色导致敌人角色投掷躲避球
如果这样做成功了,恭喜你——你已经成功使用 UE4 播放了声音!如果你听不到声音,确保它是可听到的(它有一个你可以听到的音量级别)。
然而,你可能会注意到的另一件事是,无论角色与反弹的躲避球的距离如何,声音总是以相同的音量播放:声音不是以 3D 方式播放,而是以 2D 方式播放。要在 UE4 中以 3D 方式播放声音,我们必须学习关于声音衰减资产的知识。
声音衰减
要在 UE4 中以 3D 方式播放声音,你必须创建一个声音衰减资产,就像我们在本章的第一节中提到的那样。声音衰减资产将让你指定当声音与听者的距离增加时,你希望特定声音如何改变音量。看一下以下示例。
打开虚幻编辑器,转到内容浏览器
界面内的Audio
文件夹,右键单击,转到声音
类别,并选择声音衰减
。将这个新资产命名为BounceAttenuation
:
图 9.7:创建声音衰减资产
打开这个BounceAttenuation
资产。
声音衰减资产有许多设置;然而,我们主要关注衰减距离
部分的一些设置:
-
内半径
:这个float
属性允许我们指定声音开始降低音量的距离。如果声音在小于这个值的距离播放,音量不会受到影响。将此属性设置为200
单位。 -
衰减距离
:这个浮点属性允许我们指定声音变得听不见的距离。如果声音在大于这个值的距离播放,我们将听不到它。声音的音量将根据其与听者的距离以及它是更接近内半径
还是衰减距离
而变化。将此属性设置为1500
单位:
图 9.8:声音衰减资产设置
将其视为玩家周围的两个圆,较小的圆是内圆(半径值为内半径
),较大的圆是衰减圆(半径值为衰减距离
)。如果声音起源于内圆内部,则以全音量播放,而起源于衰减圆外部的声音则不会播放。
注意
您可以在这里找到有关声音衰减资产的更多信息:
docs.unrealengine.com/en-US/Engine/Audio/DistanceModelAttenuation
。
现在您已经了解了声音衰减资产,让我们继续下一个练习,我们将把躲避球弹起时播放的声音变成 3D 声音。
练习 9.03:将弹跳声音变成 3D 声音
在这个练习中,我们将把上一个练习中添加的躲避球弹起时播放的声音变成 3D 声音。这意味着当躲避球从地面弹起时播放的声音将根据其与玩家的距离而音量有所变化。我们这样做是为了当躲避球远离时,声音音量会很低,而当它靠近时,音量会很高。
要使用我们在上一节中创建的BounceAttenuation
资产,请按照以下步骤进行:
- 转到
DodgeballProjectile
的头文件,并添加一个名为BounceSoundAttenuation
的protected
class USoundAttenuation*
属性。这个属性应该是一个UPROPERTY
,并且有EditDefaultsOnly
标记,以便可以在蓝图中进行编辑:
// The sound attenuation of the previous sound
UPROPERTY(EditAnywhere, Category = Sound)
class USoundAttenuation* BounceSoundAttenuation;
- 转到
DodgeballProjectile
类的源文件中的OnHit
函数的实现,并向PlaySoundAtLocation
函数的调用添加以下参数:
-
StartTime
,我们将传递一个值为0
。这个值表示声音开始播放的时间。如果声音持续 2 秒,我们可以通过传递值1
使这个声音从其 1 秒标记开始。我们传递一个值0
,以便从头开始播放声音。 -
SoundAttenuation
,我们将传递我们的BounceSoundAttenuation
属性:
UGameplayStatics::PlaySoundAtLocation(this, BounceSound, GetActorLocation(), 1.0f, 1.0f, 0.0f, BounceSoundAttenuation);
注意
尽管我们只想传递额外的SoundAttenuation
参数,但我们也必须传递所有其他在它之前的参数。
-
编译这些更改,然后打开编辑器。
-
打开
BP_DodgeballProjectile
蓝图,转到其类默认
选项卡,并将BounceSoundAttenuation
属性设置为我们的BounceAttenuation
资产:
图 9.9:将 BoundSoundAttenuation 属性设置为 BounceAttenuation 资产
- 再次播放关卡并进入敌人角色的视线范围。您现在应该注意到,每当敌人角色投掷的躲避球击中墙壁或地板时播放的声音会根据距离的不同以不同的音量播放,并且如果躲避球远了,您将听不到它:
图 9.10:玩家角色使敌人角色投掷躲避球
有了这个,我们可以结束这个练习。您现在知道如何使用 UE4 播放 3D 声音。我们将在下一个练习中为我们的游戏添加背景音乐。
练习 9.04:为我们的游戏添加背景音乐
在这个练习中,我们将为我们的游戏添加背景音乐。我们将通过创建一个带有音频组件的新 Actor 来实现这一点,正如我们之前提到的,这是适合播放背景音乐的。要实现这一点,请按照以下步骤进行:
-
下载位于
packt.live/3pg21sQ
的音频文件,并将其导入到Content Browser
界面的“音频”文件夹中,就像我们在“练习 9.01”、“导入音频文件”中所做的那样。 -
右键单击
Content Browser
界面内部,并使用Actor
类作为其父类创建一个新的 C++类。将这个新类命名为MusicManager
。 -
当为这个类生成文件并且 Visual Studio 自动打开时,关闭编辑器。
-
在
MusicManager
类的头文件中,添加一个名为AudioComponent
的新的受保护属性,类型为class UAudioComponent*
。将其设置为UPROPERTY
,并添加VisibleAnywhere
和BlueprintReadOnly
标签:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
class UAudioComponent* AudioComponent;
- 在
MusicManager
类的源文件中,添加AudioComponent
类的包含:
#include "Components/AudioComponent.h"
- 在这个类的构造函数中,将
bCanEverTick
属性更改为false
:
PrimaryActorTick.bCanEverTick = false;
- 在这一行之后,添加一个新的行,通过调用
CreateDefaultSubobject
函数并将UAudioComponent
类作为模板参数和"Music Component"
作为普通参数传递来创建AudioComponent
类:
AudioComponent = CreateDefaultSubobject<UAudioComponent>(TEXT("Music Component"));
-
进行这些更改后,编译您的代码并打开编辑器。
-
转到
Content Browser
界面中的ThirdPersonCPP
->Blueprints
文件夹,并创建一个从MusicManager
类继承的新蓝图类。将其命名为BP_MusicManager
。 -
打开这个资产,选择它的“音频”组件,并将该组件的“声音”属性设置为您导入的声音:
图 9.11:更新声音属性
-
将
BP_MusicManager
类的实例拖入关卡中。 -
播放关卡。您应该注意到游戏开始时音乐开始播放,并且当它到达结尾时也应该自动循环播放(这是通过音频组件实现的)。
注意
音频组件将自动循环播放它们正在播放的任何声音,因此不需要更改该声音资产的“循环”属性。
完成所有这些步骤后,我们已经完成了这个练习。您现在知道如何为您的游戏添加简单的背景音乐了。
现在,让我们进入下一个话题,即粒子系统。
粒子系统
让我们谈谈许多视频游戏中非常重要的另一个元素:粒子系统。
在视频游戏术语中,粒子实质上是 3D 空间中可以用图像表示的位置。粒子系统是许多粒子的集合,可能具有不同的图像、形状、颜色和大小。在下图中,您将找到在 UE4 中制作的两个粒子系统的示例:
图 9.12:UE4 中的两个不同的粒子系统
左侧的粒子系统应该是电火花,可能来自被切割并且现在处于短路状态的电缆,而右侧的粒子系统应该是火。虽然左侧的粒子系统相对简单,但您可以看出右侧的粒子系统内有多种类型的粒子,这些粒子可以组合在同一个系统中。
注意
UE4 有两种不同的工具用于创建粒子系统:Cascade
和Niagara
。Cascade 是自 UE4 开始就存在的工具,而 Niagara 是一个更近期和复杂的系统,自 2020 年 5 月以来才成熟可用,截至虚幻引擎版本 4.25。
在 UE4 中创建粒子系统超出了本书的范围,但建议您使用 Niagara 而不是 Cascade,因为它是引擎的最新添加。
在本章中,我们将只使用已经包含在 UE4 中的粒子系统,但如果您想创建自己的粒子系统,这些链接将为您提供有关 Cascade 和 Niagara 的更多信息:
Cascade:docs.unrealengine.com/en-US/Engine/Rendering/ParticleSystems/Cascade
www.youtube.com/playlist?list=PLZlv_N0_O1gYDLyB3LVfjYIcbBe8NqR8t
Niagara:docs.unrealengine.com/en-US/Engine/Niagara/EmitterEditorReference/index.html
docs.unrealengine.com/en-US/Engine/Niagara/QuickStart
我们将在下一个练习中学习如何将粒子系统添加到我们的游戏中。在本章中,我们将简单地使用已经由 UE4 团队制作的现有粒子系统。
练习 9.05:当躲避球击中玩家时生成一个粒子系统
在这个练习中,我们将了解如何在 UE4 中生成一个粒子系统。在这种情况下,当敌人投掷的躲避球击中玩家时,我们将生成一个explosion
粒子系统。
为了实现这一点,请按照以下步骤:
-
关闭编辑器,打开 Visual Studio。
-
在
DodgeballProjectile
类的头文件中,添加一个受保护的class UParticleSystem*
属性,名为HitParticles
。
UParticleSystem
类型是 UE4 中的粒子系统的指定。确保将其设置为UPROPERTY
并给予EditDefaultsOnly
标签,以便可以在蓝图类中进行编辑:
// The particle system the dodgeball will spawn when it hits the player
UPROPERTY(EditAnywhere, Category = Particles)
class UParticleSystem* HitParticles;
- 在
DodgeballProjectile
类的源文件中,在其OnHit
函数的实现中。在调用Destroy
函数之前,检查我们的HitParticles
属性是否有效。如果有效,调用GameplayStatics
对象的SpawnEmitterAtLocation
函数。
此函数将生成一个将播放我们传递的粒子系统的角色。它接收以下参数:
-
一个
World
对象,我们将使用GetWorld
函数传递。 -
一个
UParticleSystem*
属性,它将是我们的HitParticles
属性。 -
将播放粒子系统的角色的
FTransform
,我们将使用GetActorTransform
函数传递:
if (HitParticles != nullptr)
{
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), HitParticles, GetActorTransform());
}
注意
虽然我们在这个项目中不会使用它,但与生成粒子系统相关的另一个函数来自GameplayStatics
对象,即SpawnEmitterAttached
函数。此函数将生成一个粒子系统并将其附加到一个角色,如果您想要,例如,使一个移动的物体着火,以便粒子系统始终保持附加到该物体,这可能会有用。
-
编译这些更改,然后打开编辑器。
-
打开
BP_DodgeballProjectile
蓝图,转到其Class Defaults
选项卡,并将HitParticles
属性设置为P_Explosion
粒子系统资产:
图 9.13:将 HitParticles 属性设置为 P_Explosion
- 现在,播放关卡,让您的玩家角色被躲避球击中。现在您应该看到爆炸粒子系统正在播放:
图 9.14:当躲避球击中玩家时播放的爆炸粒子系统
这就结束了这个练习。现在你知道如何在 UE4 中播放粒子系统。粒子系统将为您的游戏增添视觉效果,使其在视觉上更具吸引力。
在下一个活动中,我们将通过在躲避球击中玩家时播放声音来巩固我们在 UE4 中播放音频的知识。
活动 9.01:当躲避球击中玩家时播放声音
在这个活动中,我们将创建逻辑,负责在玩家角色被躲避球击中时每次播放声音。在视频游戏中,以多种方式向玩家传递关键信息非常重要,因此除了改变玩家角色的生命值条外,当玩家被击中时我们还将播放声音,以便玩家知道角色正在受到伤害。
要做到这一点,请按照以下步骤进行:
- 将一个声音文件导入到
内容浏览器
界面内的Audio
文件夹中,该声音文件将在玩家角色被击中时播放。
注意
如果您没有声音文件,您可以使用www.freesoundeffects.com/free-track/punch-426855/
上提供的声音文件。
-
打开
DodgeballProjectile
类的头文件。添加一个SoundBase*
属性,就像我们在练习 9.02中所做的那样,当躲避球从表面弹开时播放声音,但这次称其为DamageSound
。 -
打开
DodgeballProjectile
类的源文件。在OnHit
函数的实现中,在你伤害了玩家角色并在调用Destroy
函数之前,检查DamageSound
属性是否有效。如果有效,调用GameplayStatics
对象的PlaySound2D
函数(在练习 9.02中提到,当躲避球从表面弹开时播放声音),将this
和DamageSound
作为该函数调用的参数。 -
编译您的更改并打开编辑器。
-
打开
BP_DodgeballProjectile
蓝图,并将其DamageSound
属性设置为您在本活动开始时导入的声音文件。
当您播放关卡时,您应该注意到每当玩家被躲避球击中时,您将听到您导入的声音被播放:
图 9.15:当玩家角色被击中时应该播放声音
完成了这些步骤后,您已经完成了这个活动,并巩固了在 UE4 中播放 2D 和 3D 声音的使用。
注意
此活动的解决方案可以在以下网址找到:packt.live/338jEBx
。
现在,让我们通过学习一些关于关卡设计概念来结束本章。
关卡设计
自第五章,线性跟踪,与我们的躲避球游戏相关,我们已经添加了相当多的游戏机制和游戏机会,以及一些视听元素,所有这些都在本章中处理。现在我们有了所有这些游戏元素,我们必须将它们汇集到一个可以由玩家从头到尾玩的关卡中。为此,让我们学习一些关于关卡设计和关卡布局的知识。
关卡设计是一种特定的游戏设计学科,专注于在游戏中构建关卡。关卡设计师的目标是制作一个有趣的关卡,通过使用为该游戏构建的游戏机制向玩家介绍新的游戏玩法概念,包含良好的节奏(充满动作和轻松的游戏序列的良好平衡),以及更多内容。
为了测试关卡的结构,关卡设计师将首先构建所谓的关卡布局。这是关卡的一个非常简单和简化版本,使用了最终关卡将包含的大部分元素,但只使用简单的形状和几何图形制作。这样做的原因是为了在需要修改关卡的部分时更容易和节省时间:
图 9.16:使用 BSP Brushes 在 UE4 中制作的关卡布局示例
注意
应该注意的是,关卡设计是一种特定的游戏开发技能,值得有一本专门的书来介绍,而实际上也有很多这样的书,但是深入讨论这个话题超出了本书的范围。
在下一个练习中,我们将使用我们在最近几章中构建的机制来构建一个简单的关卡布局。
练习 9.06:构建关卡布局
在这个练习中,我们将创建一个新的关卡布局,其中包含一些结构,玩家将从关卡的某个地方开始,并通过一系列障碍物到达关卡的结尾。我们将使用我们在最近几章中构建的所有机制和对象来制作一个玩家能够完成的关卡。
尽管在这个练习中我们将为您提供一个解决方案,但鼓励您发挥创造力,提出自己的解决方案,因为在这种情况下没有对错之分。
要开始这个练习,请按照以下步骤操作:
-
打开编辑器。
-
转到
ThirdPersonCPP
->“内容浏览器”中的“地图”文件夹,复制ThirdPersonExampleMap
资产,并将其命名为Level1
。您可以通过选择资产并按下Ctrl + W或右键单击资产并选择“复制”(第三个选项)来执行此操作。 -
打开新创建的
Level1
地图。 -
删除地图中具有网格的所有对象,除了以下对象:
-
玩家角色
-
敌人角色(注意两个角色看起来是一样的)
-
地板对象
-
我们创建的墙对象
-
胜利箱对象
请记住,与照明和声音相关的资产应保持不变。
-
通过按下“构建”按钮为
Level1
建立照明。该按钮位于编辑器窗口顶部的“工具栏”中,“播放”按钮的左侧。 -
在您按照这些步骤操作后,您应该有一个空的地板,只有您在这个关卡中需要的对象(在步骤 4中提到的对象)。以下是在您分别按照步骤 4 和 5之后的
Level1
地图之前和之后的情况:
图 9.17:删除所需对象之前
一旦你删除了对象,你的地板应该如下所示:
图 9.18:删除所需对象后
因为建立一个关卡,即使是一个简单的关卡,也需要很多步骤和指示,所以我们将简单地展示一些可能的关卡截图,并鼓励您自己想出解决方案。
- 在这种情况下,我们只是简单地使用了现有的
EnemyCharacter
、Wall
和GhostWall
对象,并将它们多次复制,以创建玩家可以从头到尾穿越的简单布局。我们还移动了VictoryBox
对象,使其与新关卡的结束位置匹配:
图 9.19:创建的关卡-等距视图
关卡可以从俯视图中看到如下:
图 9.20:创建的关卡-俯视图,玩家角色用箭头标记
一旦你对结果满意,这意味着你已经完成了你的躲避球游戏,现在可以邀请你的朋友和家人来玩,并看看他们的想法。干得好 - 你离掌握游戏开发的艺术又近了一步!
额外功能
在我们结束本章之前,这里有一些建议,关于接下来在这个躲避球项目中你可以做些什么:
-
使之前创建的普通“墙”类不会阻挡敌人的视线。这样,敌人将始终向玩家投掷躲避球,但仍然会被这堵墙挡住。
-
添加一个新功能,允许玩家通过“扫描轨迹”概念来可视化敌人角色投掷的躲避球首先会影响到哪里。
-
添加一种新类型的墙,可以阻挡玩家角色、敌人角色和躲避球,但也会受到躲避球的伤害,并在耗尽生命值时被摧毁。
这个项目的扩展空间是无限的。我们鼓励你运用所学的技能,并进行进一步的研究,为你的游戏添加新功能并增加更多的复杂性。
总结
你现在已经完成了躲避球游戏项目。在本章中,你学会了如何通过播放音频和使用粒子系统来为你的游戏增添亮点。你现在知道如何为你的游戏添加 2D 和 3D 声音,以及一些相关的工具。现在,你可以尝试为你的游戏添加更多的声音效果,比如当敌人角色第一次看到你时的特殊声音效果(比如《合金装备》中的情况)、脚步声音效果或者胜利声音效果。
你还使用了在前几章中制作的所有工具来构建一个关卡,从而汇总了我们在这个项目中构建的所有逻辑。
在下一章中,我们将开始一个新项目:《超级横向卷轴》游戏。在那个项目中,你将接触到诸如增益、可收集物品、敌人人工智能(AI)、角色动画等主题。你将创建一个横向卷轴平台游戏,控制一个角色完成关卡,收集宝石,并使用增益来避开敌人。你将学习的两个最重要的主题是 UE4 的行为树和黑板,它们支持 AI 系统,以及动画蓝图,它允许你管理角色的动画。
第九章:创建一个 SuperSideScroller 游戏
概述
在本章中,我们将为新的SuperSideScroller
游戏设置项目。您将了解横向滚动游戏的不同方面,包括强化道具、可收集物品和敌人人工智能,我们将在项目中使用所有这些。您还将了解游戏开发中的角色动画流程,并了解如何操纵我们游戏角色的移动。
在本章结束时,您将能够创建一个横向滚动项目,操纵我们角色的默认模特骨骼,导入角色和动画,并创建角色和动画蓝图。
介绍
到目前为止,我们已经学到了很多关于虚幻引擎、C++编程和一般游戏开发技术和策略的知识。在之前的章节中,我们涵盖了诸如碰撞、追踪、如何在虚幻引擎 4 中使用 C++,甚至蓝图可视化脚本系统等主题。除此之外,我们还获得了关于骨骼、动画和动画蓝图的关键知识,我们将在即将到来的项目中利用这些知识。
对于我们的最新项目SuperSideScroller
,我们将使用许多在之前章节中使用过的概念和工具来开发我们的游戏特性和系统。碰撞、输入和 HUD 等概念将是我们项目的重点;然而,我们还将深入研究涉及动画的新概念,以重新创建流行横向滚动游戏的机制。最终项目将是我们迄今为止在本书中学到的一切的结晶。
有无数的横向滚动游戏示例可供参考。最近一些流行的横向滚动游戏包括Celeste、Hollow Knight和Shovel Knight,但是横向滚动/平台游戏背后也有悠久而丰富的历史,我们将在本章中讨论。
项目分解
让我们考虑一下著名的超级马里奥兄弟的例子,该游戏于 1985 年在任天堂娱乐系统(NES)主机上发布。这款游戏是由任天堂制作,由宫本茂设计。对于不熟悉这个系列的人来说,一般的想法是:玩家控制马里奥,他必须穿越蘑菇王国的许多危险障碍和生物,希望从邪恶的酷霸王鲍斯那里救出桃花公主。
注意
为了更好地理解游戏的运作方式,请随时在supermariobros.io/
免费在线玩游戏。整个超级马里奥兄弟系列的更深入的维基可以在这里找到:www.mariowiki.com/Super_Mario_Bros
。
以下是这种类型游戏的核心特点和机制:
SuperSideScroller
游戏将是 3D 而不是纯 2D,我们角色的移动将与马里奥的移动方式完全相同,只支持垂直和水平移动:
图 10.1:2D 和 3D 坐标向量的比较
-
SuperSideScroller
游戏也不例外。有许多不同的游戏,如Celeste、Hollow Knight和Super Meat Boy,如前所述,都使用了跳跃功能-所有这些都是 2D 的。 -
角色强化道具:没有角色强化道具,许多横向滚动游戏会失去混乱感和可重复性。例如,在游戏奥里和失落的森林中,开发者引入了不同的角色能力,改变了游戏的玩法。像三段跳或空中冲刺这样的能力打开了各种可能性,使玩家能够根据其移动能力创建有趣的布局。
-
敌方 AI:引入具有各种能力和行为的敌人,以增加玩家的挑战层次,除了通过可用的移动机制单独导航关卡的挑战之外。
注意
游戏中的 AI 可以以哪些方式与玩家互动?例如,在《上古卷轴 V:天际》中,各个城镇和村庄中的 AI 角色可以与玩家进行对话,以阐述世界构建元素,如历史,向玩家出售物品,甚至向玩家提供任务。
SuperSideScroller
游戏将允许玩家收集硬币。
现在我们已经评估了我们想要支持的游戏机制,我们可以分解每个机制的功能,以及它如何与我们的SuperSideScroller
相关,以及我们需要做些什么来实现这些功能。
玩家角色
当使用虚幻引擎 4 的侧向滚动
游戏项目模板时,几乎所有我们想要的角色功能都已经默认给我们了。
注意
在撰写本文时,我们使用的是虚幻引擎版本 4.24.2;使用引擎的其他版本可能会导致编辑器、工具以及后续逻辑的一些差异,因此请记住这一点。
现在,让我们在下一个练习中开始创建我们的项目。
练习 10.01:创建侧向滚动项目并使用角色移动组件
在本练习中,您将使用侧向滚动
模板设置虚幻引擎 4。这个练习将帮助您开始我们的游戏。
以下步骤将帮助您完成练习:
-
首先,打开 Epic Games Launcher,导航到左侧选项底部的
Unreal Engine
选项卡,并在顶部选择Library
选项。 -
接下来,您将收到一个窗口提示您要么打开现有项目,要么创建特定类别的新项目。其中包括
游戏
类别;选择此选项以进行我们的项目。选择了项目类别后,您现在需要选择项目的模板。 -
接下来,点击
侧向滚动
选项,因为我们希望我们的游戏使用 3D 骨骼网格和动画,而不仅仅是 2D 纹理、翻页书和 Paper2D 工具集的其他功能。
注意
请务必选择正确的侧向滚动
选项,因为虚幻引擎 4 有两种类型的侧向滚动项目:侧向滚动
和2D 侧向滚动
。
我们将在本练习之后讨论这两种项目模板之间的主要区别。
最后,我们需要设置我们的项目设置。
-
选择基于
C++
的项目,而不是蓝图
,以包括入门内容
,并将我们的平台选择为桌面/控制台
。其余的项目设置可以保留为默认设置。选择位置并命名项目为SuperSideScroller
,并将项目保存在您选择的适当目录中。 -
应用这些设置后,选择
创建项目
。当编译引擎完成后,虚幻编辑器和 Visual Studio 都将打开,我们就可以开始了。
图 10.2:虚幻引擎编辑器现在应该已经打开
接下来,我们继续操作默认的SideScroller
角色内存在的角色移动组件,并查看这如何影响角色。Character Movement
组件只能在Character
类中实现,并允许双足化身通过行走、跳跃、飞行和游泳移动。这个组件还具有内置的网络复制功能,这对于多人游戏是必要的。
- 在
Content Browser
中,导航到/SideScrollerCPP/Blueprints/
目录,并找到SideScrollerCharacter
蓝图:
图 10.3:在内容浏览器中选择默认的 SideScrollerCharacter 蓝图
- 双击蓝图资产以打开蓝图。有时,如果蓝图没有任何图形逻辑,您将看到图 10.4中显示的内容。如果您看到这个,请只需左键单击“打开完整蓝图编辑器”:
图 10.4:当蓝图没有图形逻辑时
-
打开角色“蓝图”,我们可以左键单击“组件”选项卡中的“CharacterMovement(继承)”组件,以查看此组件的参数。
-
现在,在“详细信息”面板下,我们可以访问数十个影响角色移动的参数。在
Character Movement: Walking
类别中,我们有Max Walk Speed
参数。将此值从600.0f
更改为2000.0f
。 -
最后,编译并保存我们的角色蓝图。现在,如果我们在编辑器中播放,我们可以观察到我们的玩家角色移动得有多快:
图 10.5:如果我们在编辑器中播放,我们可以看到我们的角色移动得更快
现在您已经完成了这项练习,亲身体验了对玩家角色移动方式的控制!尝试更改“最大行走速度”的值,并观察这些更改如何影响角色。
侧向滚动与 2D 侧向滚动
让我们在这里花点时间了解“2D 侧向滚动”项目模板和“侧向滚动”模板之间的主要区别。 “2D 侧向滚动”模板使用了基于纸张 2D 系统构建的虚幻引擎 4,利用了基于纹理的动画,通过纹理、精灵和纸张翻书。
注意
有关 Paper2D 的更多详细信息,请参阅以下文档:docs.unrealengine.com/en-US/Engine/Paper2D/index.html
。
有关 Paper2D 的材料足够多,值得有一本专门的教材,因此我们不会再涉及太多这个主题。然而,“侧向滚动”模板几乎与 2D 版本相同,只是我们使用 3D 动画骨骼而不是 2D 动画。
现在,让我们继续并看看执行我们的第一个活动来操纵玩家角色的跳跃动作。
活动 10.01:使我们的角色跳得更高
在这项活动中,我们将操纵默认的“侧向滚动”角色蓝图中CharacterMovement
组件中存在的一个新参数(跳跃),以观察这些属性如何影响我们的角色移动。
我们将实施从练习 10.01中学到的内容,创建侧向滚动项目并使用角色移动组件,并将其应用于如何创建我们的角色强化道具以及角色的一般移动感觉。
以下步骤将帮助您完成这项活动:
-
转到
SideScrollerCharacter
蓝图,并在CharacterMovement
组件中找到Jump Z Velocity
参数。 -
将此参数从默认的
1000.0
f 更改为2000.0
f。 -
编译并保存
SideScrollerCharacter
蓝图,并在编辑器中播放。观察我们的角色使用键盘上的空格键可以跳多高。 -
停止在编辑器中播放,返回到
SideScrollerCharacter
蓝图,并将Jump Z Velocity
从2000.0
f 的值更新为200.0
f。 -
再次编译并保存蓝图,然后在编辑器中播放,观察角色的跳跃。
预期输出:
图 10.6:跳跃角色的预期输出
注意
此活动的解决方案可在此处找到:packt.live/338jEBx
。
现在我们已经完成了这个活动,对于CharacterMovement
组件参数的一些更改如何影响我们的玩家角色有了更好的理解。当我们需要给我们的角色基本的移动行为,比如行走速度
和跳跃 Z 速度
时,我们可以在以后使用这些知识来实现我们想要的角色感觉。在继续之前,将跳跃 Z 速度参数恢复到默认值 1000.0f。
在我们项目的后期,当我们开发我们的玩家角色增强道具时,我们也会记住这些参数。
我们横向卷轴游戏的特点
现在让我们花点时间来详细说明我们将要设计的游戏。这些特性中的许多将在后面的章节中实现,但现在是一个好时机来规划项目的愿景。
敌人角色
在玩SuperSideScroller
项目时,你应该已经注意到默认情况下没有提供敌人 AI。因此,让我们讨论我们希望支持的敌人类型以及它们的工作方式。我们的SuperSideScroller
项目将支持一种敌人类型。
敌人将有一个基本的来回移动模式,并不支持任何攻击;只有与玩家角色碰撞,他们才能造成伤害。然而,我们需要设置敌人 AI 要移动的两个位置,接下来,我们需要决定 AI 是否应该改变位置。他们应该不断在位置之间移动,还是在选择新位置移动之前应该暂停一下?
最后,我们决定我们的 AI 是否应该始终知道玩家的位置。如果玩家进入敌人的一定范围,敌人是否应该知道这一点,并积极地朝着玩家最后所在的位置移动?
在第十三章 敌人人工智能中,我们将使用虚幻引擎 4 中可用的工具来开发这种 AI 逻辑。
增强道具
SuperSideScroller
游戏项目将支持一种类型的增强道具,即玩家可以从环境中拾取的药水。这种药水增强道具将增加玩家的移动速度和最大跳跃高度。这些效果只会持续很短的时间,然后就会消失。
记住你在练习 10.01 创建横向卷轴项目并使用角色移动组件和活动 10.01 使我们的角色跳得更高中实现的内容,关于CharacterMovement
组件,你可以开发一个改变角色重力影响的增强道具,这将允许以新的有趣方式穿越关卡和与敌人战斗。
可收集物品
视频游戏中的可收集物品有不同的用途。在某些情况下,可收集物品被用作一种货币,用于购买升级、物品和其他商品。在其他情况下,可收集物品用来提高你的得分或在收集足够的可收集物品时奖励你。对于SuperSideScroller
游戏项目,硬币将只有一个目的:给玩家一个目标,尽可能多地收集硬币,而不被敌人摧毁。
让我们分解一下我们可收集物品的主要方面:
-
可收集物品需要与我们的玩家进行交互;这意味着我们需要使用碰撞检测让玩家收集它,并且为我们的 UI 添加信息。
-
可收集物品需要一个视觉静态网格表示,以便玩家可以在关卡中识别它。
我们SuperSideScroller
项目的最后一个元素是砖块。砖块将为SuperSideScroller
游戏提供以下用途:
-
砖块被用作关卡设计的一个元素。砖块可以用来进入其他无法到达的区域;敌人可以放置在不同高度的砖块上,以提供游戏玩法的变化。
-
砖块中可以包含可收集的硬币。这给玩家一个动力去尝试并查看哪些方块包含可收集物品,哪些不包含。
HUD(头顶显示)
HUD UI 可以用于根据游戏类型和您支持的机制向玩家显示重要和相关的信息。对于SuperSideScroller
项目,将有一个 HUD 元素,它将向玩家显示他们收集了多少个硬币。每当玩家收集一个硬币时,此 UI 将更新,并且当玩家被销毁时将重置为0
。
现在我们已经列出了这个项目的一些具体内容,我们将继续进行动画流程。
动画步骤
需要明确的是,本书不会涵盖动画制作。我们不会讨论和学习如何使用 3D 软件工具(如 3D Studio Max、Maya 或 Blender)制作动画。然而,我们将学习如何将这些资产导入虚幻引擎,使用引擎内的动画资产,并使用可用的动画工具集来赋予角色生命。
角色动画流程
对于本书的目的,我们只关注 3D 动画以及动画在虚幻引擎 4 中的工作方式;然而,简要讨论许多行业中用于创建角色及其动画的流程是很重要的。
概念阶段
第一阶段是开发我们想要创建并稍后进行动画的角色的概念。这几乎总是以 2D 形式完成,可以手工完成,也可以通过使用诸如 Photoshop 之类的计算机程序完成。对于 3D 建模师来说,有几个关于角色外观和相对大小的参考图,可以使建模过程更加容易。下面,我们看到一个棍人角色在不同姿势下的基本示例。注意角色以不同的方式摆姿势:
图 10.7:一个 2D 角色概念的非常简单的例子
3D 建模阶段
一旦角色概念完成,流程就可以转移到下一个阶段:制作角色的 3D 模型。模型通常是在 3D Studio Max 或 Maya 等程序中制作的,但这些软件相对昂贵,除非您有学生许可证,并且更常用于专业环境中。
不需要详细讨论 3D 建模的复杂性,我们只需要知道 3D 艺术家使用计算机软件来操纵 3D 空间中的点(称为顶点)来创建物体。然后将这些物体雕刻成我们的角色或环境部件的形状。
绑定阶段
一旦最终的角色模型完成,就可以开始绑定过程。通常用于建模角色的软件通常也用于绑定角色。绑定意味着构建一系列形成角色骨架的骨骼。
在人形角色的情况下,我们通常会看到头部、脊柱、臀部、腿部等骨骼;但是骨架的形状可能会因您制作的角色类型而有所不同。大象的骨骼结构与人类完全不同。同一个骨骼结构也可以应用于不同的角色。
动画
一旦我们的角色绑定完成并且有了骨骼层次结构,就该是动画师拿起这个网格并用动画赋予它生命的时候了。
3D 动画,基本上是对骨骼在时间上的操纵。记录骨骼位置、旋转和缩放随时间的变化过程就是动画的结果。动画完成后,我们可以从 3D 软件中导出资产,并将其导入引擎。
资产导出和导入
当我们有了我们的 3D 角色网格,它的骨骼系统和动画,就是时候将这些资产从 3D 软件导出并导入到虚幻引擎 4 中了。重要的是要注意,负责角色、骨骼和动画的艺术家们将不断地将正在进行中的工作资产导入引擎,以更好地了解最终在游戏中的效果。我们将在本章的Activity 10.03、导入更多自定义动画以预览角色奔跑及其相关练习中实施这一点。
练习 10.02:探索 Persona 编辑器并操纵默认人体骨骼权重
现在我们对动画流程有了更好的理解,让我们深入了解一下在Side Scroller
模板项目中给我们的默认人体骨骼网格。
我们的目标是更多地了解默认骨骼网格和 Persona 编辑器中给我们的工具,以便更好地了解骨骼、骨骼权重和骨骼在虚幻引擎 4 中的工作方式。
以下步骤将帮助您完成练习:
-
打开虚幻引擎编辑器,导航到
内容浏览器
。 -
导航到
/Mannequin/Character/Mesh/
文件夹并打开UE4_Mannequin_Skeleton
资产:
图 10.8:UE4_Mannequin_Skeleton 资产在此处被突出显示并可见
打开骨骼资产后,我们看到了Persona 编辑器
:
图 10.9:Persona 编辑器
](https://github.com/OpenDocCN/freelearn-c-cpp-zh/raw/master/docs/gm-dev-pj-ue/img/B16183_10_09.jpg)
图 10.9:Persona 编辑器
让我们简要地解释一下 Persona 的骨骼编辑器:
-
在左侧(标有 1)我们看到了骨骼层次结构。这是在角色的绑定过程中制作的骨骼。
root
骨骼,顾名思义,是骨骼层次结构的根。这意味着对这个骨骼的变换将影响层次结构中的所有骨骼。从这里,我们可以选择一个骨骼或一组骨骼,并查看它们在角色网格上的位置。 -
接下来,我们看到了骨骼网格预览窗口(标有 2)。它显示了我们的角色网格,并且有一些额外的选项,我们可以切换这些选项,以便预览我们的骨骼和权重绘制。
-
在右侧(标有 3)我们有基本的变换选项,可以修改单个骨骼或骨骼组。还有其他可用的设置,我们将在下一个练习中加以利用。现在我们更了解它是什么以及我们在看什么,让我们看看我们的人体骨骼网格上的实际骨架是什么样子。
- 导航到
Character
,如图 10.10所示:
图 10.10:角色选项菜单让您能够在网格上显示人体骨骼的能力
- 从下拉菜单中选择
Bones
选项。然后确保选择了All Hierarchy
选项。选择此选项后,您将看到人体骨骼网格上方的轮廓骨架渲染在人体模型上方:
图 10.11:骨架叠加在人体骨骼网格上
- 现在,隐藏网格,只是预览骨骼层次结构,我们可以禁用
Mesh
属性:
-
导航到
Character
,从下拉菜单中选择Mesh
选项。 -
取消
Mesh
选项,结果应该如下所示:
图 10.12:默认角色的骨骼层次结构
为了本练习的目的,让我们切换Mesh
可见性,这样我们就可以看到网格和骨骼层次结构。
最后,一起看一下我们默认角色的权重缩放。
- 要预览此内容,请转到
Character
,然后从下拉菜单中选择Mesh
选项。然后,在标有Mesh Overlay Drawing
的部分底部选择Selected Bone Weight
选项:
图 10.13:下拉选项显示人体模型骨骼的选定骨骼权重
- 现在,如果我们从层次结构中选择一个骨骼或一组骨骼,我们可以看到每个骨骼如何影响网格的某个区域:
图 10.14:这是 spine_03 骨的权重缩放
您会注意到,当我们预览特定骨骼的权重缩放时,骨骼网格的不同部分会显示一系列颜色。这是权重缩放的视觉显示,而不是数值上的。诸如红色
、橙色
和黄色
的颜色表示骨骼的权重较大,这意味着这些颜色的高亮区域将受到更大影响。在蓝色
、绿色
和青色
的区域,它们仍会受到影响,但影响不那么显著。最后,没有高亮叠加的区域将不受选定骨骼的操作影响。请记住骨骼的层次结构,因为即使左臂没有叠加颜色,当您旋转、缩放和移动spine_03
骨时,它仍会受到影响,因为手臂是spine_03
骨的子级。请参考下面的图像,看看手臂是如何连接到脊柱的:
图 10.15:clavicle_l 和 clavicle_r 骨是 spine_03 骨的子级
让我们继续操作人体模型骨骼网格中的一个骨骼,并看看这些变化如何影响其动画。
- 在 Persona 编辑器中,左键单击骨骼层次结构中的
thigh_l
骨:
图 10.16:这里选择了 thigh_l 骨
选择thigh_l
骨后,我们清楚地知道权重缩放将如何影响网格的其他部分。此外,由于骨骼的结构,对该骨骼的任何修改都不会影响网格的上半身:
图 10.17:您可以看到,在骨骼层次结构中,大腿骨是骨盆骨的子级
- 使用前几章的知识,更改
thigh_l
骨的本地位置、本地旋转和比例值,以偏移骨骼的变换。下面的图像显示了要使用的值示例。
图 10.18:大腿 _l 值已更新
对骨骼变换进行更改后,您会看到人体模型的左腿完全改变,看起来很荒谬:
图 10.19:人体模型角色的左腿完全改变
-
接下来,在
Details
面板中,转到标有Preview Scene Settings
的选项卡。左键单击此选项卡,您将看到新选项,显示一些默认参数和一个Animation
部分。 -
使用
动画
部分预览动画以及它们如何受到对骨骼所做更改的影响。对于预览控制器
参数,将其更改为使用特定动画
选项。通过这样做,将出现一个名为动画
的新选项。动画
参数允许我们选择与角色骨骼关联的动画来预览。 -
接下来,左键单击下拉菜单,选择
ThirdPersonWalk
动画。 -
最后,现在你可以看到模特角色正在播放行走动画,但他们的左腿完全错位和错缩:
图 10.20:模特角色更新动画的预览
在继续之前,请确保将thigh_l
骨骼恢复到其原始本地位置、本地旋转和比例;否则,向前进行的动画将不会看起来正确。
现在,您已经完成了我们第二项练习的最后部分,亲身体验了骨骼对角色和动画的影响。
现在,让我们继续进行第二项活动,操作模特角色的不同骨骼并观察应用不同动画的结果。
活动 10.02:骨骼操作和动画
对于这项活动,我们将实践我们对默认模特角色上的骨骼操作如何影响骨骼上的动画的知识。
以下步骤将帮助您完成此活动:
-
选择将影响整个骨骼的骨骼。
-
更改此骨骼的比例,使角色的尺寸减半。使用这些值将
Scale
更改为(X=0.500000,Y=0.500000,Z=0.500000
)。 -
将奔跑动画应用于
预览场景设置
选项卡中的这个骨骼网格,并观察半尺寸角色的动画:
以下是预期输出:
图 10.21:尺寸减半的角色执行奔跑动画
注意
此活动的解决方案可在以下网址找到:packt.live/338jEBx
。
完成此活动后,您现在已经实际了解了骨骼和骨骼网格的骨骼操作如何影响动画的应用。您还亲身见证了对骨骼的权重缩放对骨骼的影响。
虚幻引擎 4 中的动画
让我们分解动画在虚幻引擎内部的主要方面。关于本节中的主题的更深入信息可以在 Epic Games 的文档中直接找到:docs.unrealengine.com/en-US/Engine/Animation
。
骨骼
骨骼是虚幻引擎对外部 3D 软件中制作的角色骨骼的表示;我们在活动 10.02,骨骼操作和动画中看到了这一点。关于骨骼,我们已经讨论过的内容并不多,但主要的要点是一旦骨骼在引擎中,我们可以查看骨骼层次结构,操作每个单独的骨骼,并添加称为插座的对象。插座允许我们将对象附加到角色的骨骼上,并且我们可以使用这些插座来附加对象,如网格,并且在不破坏骨骼变换的情况下操纵插座的变换。在第一人称射击游戏中,通常会制作武器插座并将其附加到适当的手部。
骨骼网格
骨骼网格是一种特定类型的网格,它结合了 3D 角色模型和构成其骨架的骨骼层次结构。静态网格和骨骼网格的主要区别在于,骨骼网格用于使用动画的对象,而静态网格由于缺乏骨架而无法使用动画。我们将在下一章更深入地研究我们的主角骨骼网格,但我们将在本章后面的Activity 10.03中导入我们的主角骨骼网格,导入更多自定义动画以预览角色奔跑。
动画序列
最后,动画序列是一种可以在特定骨骼网格上播放的单独动画;它适用于的网格是在将动画导入到引擎时选择的骨架确定的。我们将在Activity 10.03中导入我们自己的角色骨骼网格和一个单独的动画资产,导入更多自定义动画以预览角色奔跑。
我们的动画序列中包含一个时间轴,允许我们逐帧预览动画,并附加了其他控件以暂停、循环、倒带等:
图 10.22:动画序列时间轴和预览窗口
在接下来的练习中,您将导入一个自定义角色和一个动画。自定义角色将包括一个骨骼网格和一个骨架,动画将被导入为动画序列。
练习 10.03:导入和设置角色和动画
对于我们的最后一个练习,我们将导入我们自定义的角色和一个我们将用于SuperSideScroller
游戏主角的动画,以及创建必要的角色蓝图和动画蓝图。
注意
本章附带了一个名为Assets
的文件夹中的一组文件,我们将导入这些文件到引擎中。这些资产来自 Mixamo:www.mixamo.com/
;请随意创建一个账户并查看那里提供的免费 3D 角色和动画内容。
Assets
内容可以在我们的 GitHub 上找到:packt.live/2IcXIOo
。
以下步骤将帮助您完成练习:
-
前往虚幻编辑器。
-
在“内容浏览器”中,创建一个名为
MainCharacter
的新文件夹。在这个文件夹中,创建两个名为Animation
和Mesh
的新文件夹。我们的“内容浏览器”选项卡现在应该看起来像下面的图片:
图 10.23:在内容浏览器中的 MainCharacter 目录中添加的文件夹
-
接下来,导入我们的角色模型。在我们创建的
Mesh
文件夹内,右键单击并选择“导入”选项,这将打开文件资源管理器菜单。导航到您保存了本章附带的Assets
文件夹的目录,并找到Character Mesh
文件夹内的MainCharacter.fbx
资产,例如\Assets\Character Mesh\MainCharacter.fbx
,然后打开该文件。 -
在选择此资产时,将出现 FBX 导入选项窗口。确保在各自的复选框中将“骨骼网格”和“导入网格”的选项设置为“检查”,并将其他选项保持为默认设置。
-
最后,我们可以选择“导入”选项,这样我们的 FBX 资产将被导入到引擎中。这将包括在 FBX 中创建的必要材质;一个物理资产,它将自动为我们创建并分配给“骨骼网格”;和“骨架资产”。
注意
忽略导入FBX
文件时可能出现的任何警告;它们不重要,不会影响我们未来的项目。
现在我们有了角色,让我们导入一个动画。
-
在
MainCharacter
文件夹目录中的Animation
文件夹内,再次右键单击并选择“导入”选项。 -
导航到保存了本章配套
Assets
文件夹的目录,并在Animations/Idle
文件夹中找到Idle.fbx
资产,例如\Assets\Animations\Idle\Idle.fbx
,然后打开该文件。
选择此资产时,将会出现一个几乎相同的窗口,就像我们导入角色骨骼网格时一样。由于这个资产只是一个动画,而不是骨骼网格/骨架,我们没有之前的选项,但有一个关键的参数需要正确设置:骨架
。
在我们的FBX
导入选项的网格
类别下的骨架
参数告诉动画应用于哪个骨架。如果不设置这个参数,我们无法导入我们的动画,将动画应用于错误的骨架可能会产生灾难性的结果,或者导致动画根本无法导入。幸运的是,我们的项目很简单,我们已经导入了角色骨骼网格和骨架。
- 选择
MainCharacter_Skeleton
并选择底部的导入
选项;将所有其他参数保持为默认设置。
图 10.24:导入 Idle.fbx 动画时的设置
现在我们知道要导入自定义角色网格和动画。了解这两种类型资产的导入过程至关重要,在下一个活动中,您将被挑战导入剩余的动画。让我们继续通过为SuperSideScroller
游戏的主角色创建角色蓝图和动画蓝图来进行这个练习。
现在,虽然侧向滚动模板项目确实包括了我们角色的蓝图和其他资产,比如动画蓝图,但为了组织和良好的开发实践,我们将要创建我们自己版本的这些资产。
- 在
内容浏览器
的MainCharacter
目录下创建一个名为蓝图
的新文件夹。在该目录中,基于所有类
下的SideScrollerCharacter
类创建一个新的蓝图。将这个新蓝图命名为BP_SuperSideScroller_MainCharacter
:
图 10.25:要用作角色蓝图父类的 SideScrollerCharacter 类
- 在我们的
蓝图
目录中,在内容浏览器
的空白区域右键单击,悬停在动画
选项上,然后选择动画蓝图
:
图 10.26:动画类别下的动画蓝图选项
- 选择此选项后,将会出现一个新窗口。这个新窗口要求我们为我们的动画蓝图应用一个父类和一个骨架。在我们的情况下,使用
MainCharacter_Skeleton
,选择确定,并将动画蓝图资产命名为AnimBP_SuperSideScroller_MainCharacter
:
图 10.27:创建动画蓝图时需要的设置
- 当我们打开我们的角色蓝图
BP_SuperSideScroller_MainCharacter
并选择网格
组件时,我们会发现一些可以更改的参数:
图 10.28:使用人体模型骨骼网格的 SuperSideScroller 角色蓝图
- 在
网格
类别下,我们有更新骨骼网格
的选项。找到我们的MainCharacter
骨骼网格并将其分配给这个参数:
图 10.29:我们的网格组件需要的设置,以正确使用我们的新骨骼网格和动画蓝图
在我们的角色蓝图中,选择Mesh
组件后,我们可以在Mesh
类别的正上方找到Animation
类别。幸运的是,默认情况下,Animation Mode
参数已经设置为Use Animation Blueprint
,这是我们需要的设置。
-
现在将
Anim
类参数分配给我们的新动画蓝图,AnimBP_SuperSideScroller_MainCharacter
。最后,返回到默认的SideScrollerExampleMap
关卡,并用我们的新角色蓝图替换默认角色。 -
接下来,请确保我们在
Content Browser
中选择了BP_SuperSideScroller_MainCharacter
,然后右键单击关卡中的默认角色,并选择用我们的新角色替换它:
图 10.30:在内容浏览器中选择角色蓝图后,我们可以简单地右键单击关卡中的默认角色,并用新角色替换它
- 在关卡中放置了我们的新角色后,我们现在可以在编辑器中进行游戏并在关卡中移动。结果应该看起来像下面的图片;我们的角色处于默认 T 形姿势并在关卡环境中移动:
图 10.31:您现在有自定义角色在关卡中奔跑
完成我们的最后一个练习后,您现在完全了解了如何导入自定义骨骼网格和动画。此外,您还学会了如何从头开始创建角色蓝图和动画蓝图,以及如何使用这些资产来创建SuperSideScroller
角色的基础。
让我们继续进行本章的最后一个活动,在这个活动中,您将被挑战导入角色的剩余动画,并在 Persona 编辑器中预览奔跑动画。
活动 10.03:导入更多自定义动画以预览角色奔跑
这个活动旨在导入剩余的动画,比如玩家角色的奔跑动画,并在角色骨架上预览奔跑动画,以确保它看起来正确。
在活动结束时,所有玩家角色动画将被导入项目中,您将准备好在下一章中使用这些动画来赋予玩家角色生命。
以下步骤将帮助您完成该活动:
-
作为提醒,我们需要导入的所有动画资产都存在于
\Assets\Animations
目录中,无论您将原始zip
文件夹保存在何处。导入MainCharacter/Animation
文件夹中的所有剩余动画。导入剩余的动画资产将与练习 10.03中的导入和设置角色和动画相同,当您导入Idle
动画时。 -
导航到
MainCharacter
骨架,并应用您在上一步中导入的Running
动画。 -
最后,应用
Running
动画后,在 Persona 编辑器中预览角色动画。
以下是预期输出:
图 10.32:带有额外自定义导入资产的角色的预期输出
注意
此活动的解决方案可在以下网址找到:packt.live/338jEBx
。
完成这个最后的活动后,您现在已经第一手体验了将自定义骨骼和动画资产导入虚幻引擎 4 的过程。无论您导入的资产类型如何,导入过程在游戏行业中很常见,您对此应该感到舒适。
总结
有了玩家角色的骨骼、骨骼网格和动画导入到引擎中,我们可以继续进行下一章,在那里您将准备角色移动和 UpdateAnimation 蓝图,以便角色在关卡中移动时能够进行动画。
通过本章的练习和活动,您了解了骨骼和骨骼如何用于给角色添加动画和操纵。通过第一手经验将动画导入并应用到虚幻引擎 4 中,您现在对动画流程有了深刻的理解,从角色概念到最终导入项目的资产。
此外,您还学习了我们将在下一章中使用的主题,比如用于角色移动动画混合的混合空间。有了创建的SuperSideScroller
项目模板和玩家角色准备就绪,在下一章中,让我们开始使用动画蓝图为角色添加动画。
第十章:11.混合空间 1D、键绑定和状态机
概述
本章首先创建所需的混合空间资产,以允许从空闲到行走,最终到奔跑的移动动画混合,根据玩家角色的速度。然后,我们将实现新的键映射,并在 C++中使用这些映射来为玩家角色编写游戏功能,如冲刺。最后,我们将在角色动画蓝图中创建一个新的动画状态机,以便玩家动画可以在移动和跳跃之间平滑过渡。
到本章结束时,当SuperSideScroller
玩家角色在环境中移动时,将正确地进行动画处理,并以最适合游戏的方式移动。这意味着玩家将支持空闲、行走和冲刺动画,同时还支持跳跃所需的动画。
介绍
在上一章中,我们对动画和SuperSideScroller
项目的游戏设计开发进行了高层次的审视。您只是在项目开发的最初阶段。您还准备了玩家角色的动画蓝图、角色蓝图,并导入了所有必需的骨骼和动画资产。
此时,角色可以在关卡中移动,但却被困在 T 形姿势中,根本没有动画。通过为玩家角色创建一个新的混合空间,可以解决这个问题,这将在本章的第一个练习中完成。完成混合空间后,您将在角色动画蓝图中实现这一点,以便角色在移动时进行动画处理。
在本章中,您将使用许多新的函数、资产类型和变量,以实现玩家角色的期望移动。其中一些包括“动画蓝图”中的“尝试获取所有者”函数、“1D 混合空间资产”类型和项目配置文件中的“输入绑定”。
让我们首先通过学习混合空间,然后创建您需要的混合空间资产,以便在移动时使玩家角色进行动画处理。
混合空间
如其名称所示,混合空间允许您根据一个或多个条件在多个动画之间进行混合。混合空间在不同类型的视频游戏中使用,但通常在玩家可以看到整个角色的游戏中使用。在虚幻引擎 4 提供的第一人称模板项目中,通常不使用混合空间,因为玩家只能看到角色的手臂,如下所示:
图 11.1:虚幻引擎 4 中第一人称项目模板中默认角色的第一人称视角。
在需要平滑混合角色基于移动的动画的第三人称游戏中,混合空间更常见。一个很好的例子是虚幻引擎 4 提供的第三人称模板项目,如下所示:
图 11.2:虚幻引擎 4 中第一人称项目模板中默认角色的第三人称视角
混合空间允许玩家角色根据变量或一组变量在动画之间进行混合。例如,在《最后生还者》中的乔尔,他的移动动画是基于他的移动速度的,这个速度是由玩家通过控制器摇杆(或摇杆)提供的。随着速度的增加,他的动画从行走更新到奔跑,然后到冲刺。这就是我们在本章中要实现的目标。
让我们看看 Unreal Engine 提供的混合空间资产,在创建侧向滚动
项目模板时,通过打开/Mannequin/Animations/ThirdPerson_IdleRun_2D
。这是为侧向滚动
人体模型骨骼网格创建的 1D 混合空间资产,以便玩家角色可以根据角色的速度在空闲、行走和奔跑动画之间平滑过渡。
如果你在Persona
中检查,在左侧的资产详情
面板中,你会看到轴设置
类别,其中有水平轴
参数,我们可以在我们的动画蓝图中引用的变量。请参考下面的图像查看Persona
中的轴设置
。
图 11.3:这里显示了 1D 混合空间的轴设置
在预览窗口下方,我们还会看到一个小图表,沿着从左到右的线有点;其中一个点将被突出显示为绿色
,而其他点为白色
。我们可以左键单击并沿着水平轴拖动这个绿色
点,以预览基于其值的混合动画。在速度为0
时,我们的角色处于空闲
状态,当我们沿着轴移动我们的预览时,动画将开始混合行走,然后是奔跑
。请参考下面的图像查看单轴图表。
图 11.4:这里突出显示了 1D 混合空间的关键帧时间轴
在下一节中,我们将研究 1D 混合空间与普通混合空间的区别。
1D 混合空间与普通混合空间
在继续使用 Unreal Engine 4 中的 1D 混合空间之前,让我们花点时间区分混合空间和 1D 混合空间之间的主要区别。
-
Unreal 中的混合空间资产由两个变量控制,由混合空间图的X和Y轴表示。
-
另一方面,1D 混合空间只支持一个轴。
试着把这个想象成一个 2D 图表。你知道每个轴都有自己的方向,你可以更好地想象出为什么以及何时需要使用这个混合空间,而不是只支持单一轴的 1D 混合空间。
比如,假设你想让玩家角色在左右移动的同时也支持前后移动。如果你要在图表上映射这种移动,它会看起来像下面的图:
图 11.5:这是一个简单图表上混合空间运动的样子
现在,想象一下玩家角色的移动,记住游戏是一个侧向滚动
。角色不会支持左右平移或前后移动。玩家角色只需要在一个方向上进行动画,因为侧向滚动
角色默认会朝着移动方向旋转。只需要支持一个方向是你使用 1D 混合空间而不是普通混合空间的原因。
我们需要为我们的主角设置这种类型的混合空间资产,并将其用于相同的目的,即基于移动的动画混合。在下一个练习中,让我们一起使用我们的自定义动画资产创建混合空间资产。
练习 11.01:创建角色移动 1D 混合空间
为了让玩家角色在移动时进行动画,你需要首先创建一个如前所述的混合空间。
在这个练习中,你将创建混合空间资产,添加空闲动画,并更新CharacterMovement
组件,以便分配与混合空间相对应的适当行走速度值。
以下步骤将帮助你完成练习:
-
在
Content Browser
中导航到/MainCharacter/Animation
文件夹,其中包含您在上一章中导入的所有新动画。 -
现在,在
Content Browser
的主区域中右键单击,从下拉菜单中悬停在Animation
选项上,然后从其附加的下拉菜单中左键单击选择Blend Space 1D
。 -
确保选择
MainCharacter_Skeleton
,而不是UE4_Mannequin_Skeleton
,作为混合空间的骨骼。
注意
如果应用了不正确的骨骼,那么在为需要骨骼的资产(如混合空间或动画蓝图)选择骨骼时,混合空间对于玩家角色和其自定义骨骼网格将无法正常工作。在这里,您正在告诉这个资产它与哪个骨骼兼容。通过这样做,在混合空间的情况下,您可以使用为该骨骼制作的动画,并确保一切与其他一切兼容。
-
将此混合空间资产命名为
SideScroller_IdleRun_1D
。 -
接下来,打开
SideScroller_IdleRun_1D
混合空间资产。您可以在预览窗口下方看到单轴图表:
图 11.6:Unreal Engine 4 中用于创建混合空间的编辑工具
在编辑器的左侧,您有包含Axis Settings
类别的Asset Details
面板。在这里,您将标记轴并提供最小和最大浮点值,这些值稍后将在玩家角色的Animation Blueprint
中对您有用。请参考下面的图表,查看为Horizontal Axis
设置的默认值。
图 11.7:影响混合空间轴的轴设置
- 现在,将
Horizontal Axis
重命名为Speed
:
图 11.8:水平轴现在命名为 Speed
- 下一步是建立
Minimum Axis Value
和Maximum Axis Value
。您希望最小值默认为0.0f
,因为玩家角色在完全不移动时将处于Idle
状态。
但Maximum Axis Value
呢?这个有点棘手,因为您需要记住以下几点:
-
您将支持角色的冲刺行为,允许玩家在按住左 Shift键盘按钮时移动得更快。释放时,玩家将返回默认行走速度。
-
行走速度要匹配
CharacterMovementComponent
的角色Max Walk Speed
参数。
在设置Maximum Axis Value
之前,您需要将角色的Max Walk Speed
设置为适合SuperSideScroller
游戏的值。
- 为此,导航到
/Game/MainCharacter/Blueprints/
并打开BP_SuperSideScroller_MainCharacter
蓝图:
图 11.9:SuperSideScroller 主角蓝图的目录
- 选择
Character Movement
组件,在Details
面板中,在Character Movement: Walking
类别下,找到Max Walk Speed
参数,并将该值设置为300.0f
。
将Max Walk Speed
参数设置后,返回到SideScroller_IdleRun_1D
混合空间,并设置Maximum Axis Value
参数。如果行走速度为300.0f
,最大值应该是多少?请记住,您将支持玩家角色的冲刺,因此这个最大值需要大于行走速度。
-
将
Maximum Axis Value
参数更新为500.0f
。 -
最后,将
Number of Grid Divisions
参数设置为5
。这样做的原因是,在处理分区时,每个网格点之间的100
单位间距使得更容易处理,因为Maximum Axis Value
是500.0f
。在应用动画沿网格时,这对于网格点捕捉非常有用。 -
将剩余的属性设置为默认值:
图 11.10:混合空间的最终轴设置
通过这些设置,您告诉混合空间使用0.0f
到500.0f
之间的传入浮点值来在下一步和活动中混合动画。通过将网格分成5
个部分,您可以轻松地在轴图表上的正确浮点值处添加所需的动画。
让我们继续创建混合空间,通过将第一个动画添加到轴图表中,即Idle
动画。
-
在网格的右侧,有
Asset Browser
选项卡。请注意,资产列表包括您在第十二章 动画混合和蒙太奇中导入的玩家角色的所有动画。这是因为您在创建混合空间时选择了MainCharacter_Skeleton
资产。 -
接下来,左键单击并将
Idle
动画拖动到我们的网格位置0.0
:
图 11.11:将 Idle 动画拖动到网格位置 0.0
注意,将此动画拖动到网格时,它将捕捉到网格点。一旦动画添加到混合空间中,玩家角色就会从其默认 T 形状改变,并开始播放Idle
动画:
图 11.12:将 Idle 动画添加到 1D 混合空间,玩家角色开始动画
完成这个练习后,您现在了解了如何创建 1D 混合空间,更重要的是,您知道了 1D 混合空间和普通混合空间之间的区别。此外,您知道了在玩家角色移动组件和混合空间之间对齐值的重要性,以及为什么需要确保行走速度与混合空间中的值适当地相关。
现在让我们继续进行本章的第一个活动,在这个活动中,您将像添加Idle
动画一样,将剩余的Walking
和Running
动画应用到混合空间中。
活动 11.01:将 Walking 和 Running 动画添加到混合空间
到目前为止,1D 运动混合空间进展顺利,但您缺少行走和奔跑动画。在本活动中,您将通过将这些动画添加到适合主角的水平轴值的混合空间来完成混合空间。
使用从练习 11.01 创建 CharacterMovement 1D 混合空间中获得的知识,执行以下步骤来完成角色移动混合空间:
-
继续进行练习 11.01 创建 CharacterMovement 1D 混合空间,返回
Asset Browser
。 -
现在,将
Walking
动画添加到水平网格位置300.0f
。 -
最后,将
Running
动画添加到水平网格位置500.0f
。
注意
请记住,您可以左键单击并沿着网格轴拖动绿色预览网格点,以查看动画根据轴值如何混合在一起,因此请注意角色动画预览窗口,以确保它看起来正确。
预期输出如下:
图 11.13:混合空间中的 Running 动画
当这个活动完成时,你将拥有一个功能性的混合空间,根据代表玩家角色速度的水平轴的值,将角色的移动动画从Idle
混合到Walking
再到Running
。
注意
这个活动的解决方案可以在以下网址找到:packt.live/338jEBx
。
主角动画蓝图
将动画添加到混合空间后,你应该能够四处走动并看到这些动画在起作用,对吗?嗯,不是的。如果选择在编辑器中播放,你会注意到主角仍然以 T 形姿势移动。原因是因为你还没有告诉动画蓝图使用我们的混合空间资产,这将在本章后面进行。
动画蓝图
在跳入上一章创建的动画蓝图之前,让我们简要讨论一下这种类型的蓝图是什么,以及它的主要功能是什么。动画蓝图是一种蓝图,允许你控制骨骼和骨骼网格的动画,此处指的是上一章导入的玩家角色骨骼和网格。
动画蓝图分为两个主要图表:
-
事件图
-
动画图
事件图的工作方式与普通蓝图相同,你可以使用事件、函数和变量来编写游戏逻辑。另一方面,动画图是动画蓝图独有的,这是你在其中使用逻辑来确定骨骼和骨骼网格在任何给定帧的最终姿势。在这里,你可以使用状态机、动画插槽、混合空间和其他与动画相关的节点,然后输出给角色的最终动画。
看一下以下示例(你可以跟着做)。
在MainCharacter/Blueprints
目录中打开AnimBP_SuperSideScroller_MainCharacter
动画蓝图。
默认情况下,AnimGraph
应该打开,你可以在其中看到角色预览、我们的Asset Browser
选项卡和主图表。就是在这个AnimGraph
中,你将实现刚刚创建的混合空间,以便在关卡中移动时玩家角色能够正确地进行动画。
让我们开始下一个练习,我们将在这个练习中做这个,并学习更多关于动画蓝图的知识。
练习 11.02:将混合空间添加到角色动画蓝图
在这个练习中,你将把混合空间添加到动画蓝图,并准备必要的变量来控制这个混合空间,根据玩家角色的移动速度。让我们从将混合空间添加到AnimGraph
开始。
以下步骤将帮助你完成这个练习:
- 通过在右侧找到
Asset Browser
,左键单击并将SideScroller_IdleRun_1D
混合空间资产拖入AnimGraph
中,将混合空间添加到AnimGraph
。
请注意,这个混合空间节点的变量输入标签为Speed
,就像混合空间内部的水平轴一样。请参考图 11.14,看看Asset Browser
中的混合空间。
注意
如果你给Horizontal Axis
取了不同的名字,新名字会显示为混合空间的输入参数。
图 11.14:Asset Browser 让你访问与 MainCharacter_Skeleton 相关的所有动画资产
- 接下来,将混合空间节点的
Output Pose
资产连接到Output Pose
节点的Result
引脚。现在,在预览中的动画姿势显示为角色的Idle
动画姿势:
图 11.15:你现在对混合空间有了有限的控制,并可以手动输入值到 Speed 参数中来更新角色的移动动画
- 如果你使用
PIE
,(Idle
动画而不是保持 T-Pose:
图 11.16:玩家角色现在在游戏中播放 Idle 动画
现在,你可以使用Speed
输入变量来控制我们的混合空间。有了使用混合空间的能力,你需要一种方法来存储角色的移动速度,并将该值传递给混合空间的Speed
输入参数。这就是你需要做的:
- 导航到我们的动画蓝图的
事件图
。默认情况下,会有事件蓝图更新动画
事件和一个纯Try Get Pawn Owner
函数。请参考图 11.17,查看事件图
的默认设置。该事件在每帧动画更新时更新,并在尝试获取更多信息之前返回SuperSideScroller
玩家角色蓝图类。
图 11.17:动画蓝图包括此事件和函数对,默认情况下在你的事件图中使用
注意
在虚幻引擎 4 中,Pure
函数和Impure
函数的主要区别在于,Pure
函数意味着它包含的逻辑不会修改它所使用的类的变量或成员。在Try Get Pawn Owner
的情况下,它只是返回动画蓝图的Pawn
所有者的引用。Impure
函数没有这个含义,并且可以自由修改任何它想要修改的变量或成员。
- 从
Try Get Pawn Owner
函数获取Return Value
,然后从出现的上下文敏感
菜单中搜索转换为SuperSideScrollerCharacter
:
图 11.18:上下文敏感菜单可以找到相关的函数或变量,基于这些可以对所检查的对象采取行动
- 将
事件蓝图更新动画
的执行输出引脚连接到转换的执行输入引脚:
图 11.19:在事件图中,使用 Try Get Pawn Owner 函数将返回的 Pawn 对象转换为 SuperSideScrollerCharacter 类
你创建的角色蓝图继承自SuperSideScrollerCharacter
类。由于这个动画蓝图的拥有者是你的BP_SuperSideScroller_MainCharacter
角色蓝图,并且这个蓝图继承自SuperSideScrollerCharacter
类,所以转换函数将成功执行。
- 接下来,将转换后的返回值存储到自己的变量中;这样,我们在动画蓝图中需要再次使用它时就有一个引用。参考图 11.20,确保将这个新变量命名为
MainCharacter
。
注意
在上下文敏感的下拉菜单中有提升为变量
的选项,它允许你将任何有效值类型存储到自己的变量中。
图 11.20:只要转换成功,你就会想要跟踪所拥有的角色
- 现在,要跟踪角色的速度,使用
MainCharacter
变量中的Get Velocity
函数。Actor
类的每个对象都可以访问这个函数,它返回对象移动的大小和方向向量:
图 11.21:GetVelocity 函数可以在 Utilities/Transformation 下找到
- 从“获取速度”中,您可以使用
VectorLength
函数来获取实际速度:
图 11.22:VectorLength 函数返回矢量的大小,但不返回方向
- 从
VectorLength
函数的Return Value
然后可以提升为自己的变量命名为Speed
:
图 11.23:每个角色都有 Get Velocity 函数,返回角色移动的大小和方向
在这个练习中,您可以使用GetVelocity
函数获得玩家角色的速度。从GetVelocity
函数返回的矢量给出了矢量的长度以确定实际速度。通过将这个值存储在Speed
变量中,您现在可以在动画蓝图的AnimGraph
中引用这个值来更新您的混合空间,在下一个练习中将会这样做。
速度矢量
在进行下一步之前,让我们解释一下当您获取角色的速度并将该矢量的矢量长度提升为Speed
变量时,您正在做什么。
什么是速度?速度是一个具有给定GetVelocity
函数和返回速度矢量上的VectorLength
函数的矢量;您正在获取我们角色的Speed
变量的值。这就是为什么您将该值存储在变量中并将其用于控制混合空间的原因,如下图所示,这是矢量的一个示例。其中一个具有正(右)方向,大小为100
,另一个具有负(左)方向,大小为35
。
图 11.24:显示两个不同的矢量的图
练习 11.03:将混合空间添加到角色动画蓝图
现在您对“矢量”以及如何存储玩家角色的Speed
变量有了更好的理解,您可以按照以下步骤将速度应用于本章前面创建的 1D 混合空间。
以下步骤将帮助您完成练习:
-
导航到您的
AnimBP_SuperSideScroller_MainCharacter
动画蓝图中的AnimGraph
。 -
使用
Speed
变量通过左键单击并将其拖动到AnimGraph
中实时更新混合空间,并将变量连接到Blendspace Player
函数的输入:
图 11.25:现在您可以在每帧更新动画时使用 Speed 变量来更新混合空间
- 接下来,编译动画蓝图。
现在您可以根据玩家角色的速度更新混合空间。当您使用PIE
时,您可以看到角色在移动时处于Idle
和Walking
动画中:
图 11.26:玩家角色最终能够在关卡中四处走动
最后,主角正在使用基于移动速度的移动动画。在下一个活动中,您将更新角色移动组件,以便可以从混合空间预览角色奔跑动画。
活动 11.02:在游戏中预览奔跑动画
通过更新动画蓝图并获取玩家角色的速度,您现在可以在游戏中预览Idle
和Walking
动画。
在这个活动中,您将更新玩家角色蓝图的CharacterMovement
组件,以便您还可以在游戏中预览Running
动画。
执行以下步骤来实现这一点:
-
导航到并打开
BP_SuperSideScroller_MainCharacter
玩家角色蓝图。 -
访问
CharacterMovement
组件。 -
将“最大行走速度”参数修改为
500.0
,以便您的角色可以快速移动,从“空闲”到“行走”,最终到“奔跑”时混合其动画。
在本活动结束时,您将允许玩家角色达到一定速度,以便在游戏中预览“奔跑”动画。
预期输出如下:
图 11.27:玩家角色奔跑
注意
可以在以下网址找到此活动的解决方案:packt.live/338jEBx
。
现在您已经处理了玩家角色从“空闲”到“行走”,最终到“奔跑”的混合移动,让我们继续下一步,添加功能以允许玩家角色通过奔跑移动得更快。
输入绑定
每个游戏都需要玩家的输入,无论是键盘上的按键,如W、A、S和D,用于移动玩家角色,还是控制器上的摇杆;这就是使视频游戏成为互动体验的原因。虚幻引擎 4 允许我们将键盘、鼠标、游戏手柄和其他类型的控件映射到标记的动作或轴上,然后您可以在蓝图或 C++中引用这些动作或轴,以允许角色或游戏功能发生。重要的是要指出,每个独特的动作或轴映射可以有一个或多个按键绑定,并且同一个按键绑定可以用于多个映射。输入绑定保存在名为DefaultInput.ini
的初始化文件中,并且可以在项目目录的Config
文件夹中找到。
注意
输入绑定可以直接从DefaultInput.ini
文件或通过编辑器中的“项目设置”进行编辑;后者在编辑时更容易访问,且更少出错。
让我们为玩家角色的“奔跑”功能添加一个新的输入绑定。
练习 11.04:添加奔跑和投掷输入
随着玩家角色在关卡中移动,您现在将为玩家角色实现一个独特的角色类,该类源自基本的SuperSideScrollerCharacter
C++类。这样做的原因是,您可以轻松区分玩家角色和敌人的类,而不仅仅依赖于独特的蓝图类。
在创建独特的 C++角色类时,您将实现“奔跑”行为,以允许玩家角色根据需要“行走”和“奔跑”。
让我们首先通过添加“奔跑”的输入绑定来实现“奔跑”机制:
-
在编辑器顶部的工具栏上导航到“编辑”选项,然后从下拉列表中选择“项目设置”。
-
在“项目设置”中,导航到左侧“引擎”类别下的“输入”选项。默认情况下,虚幻引擎提供的
Side Scroller
模板项目为“跳跃”提供了动作映射,键为W、上箭头键、空格键和游戏手柄底部按钮。 -
通过左键单击“动作映射”旁边的
+
按钮添加新的“动作映射”。将此映射标记为“奔跑”,并为其控件添加两个键;“左 Shift”和“游戏手柄右肩”。请参考下面的图示以获取更新后的绑定。
图 11.28:应用于按键绑定的跳跃和奔跑动作映射
有了“奔跑”输入绑定后,您需要为基于SuperSideScroller
角色类的玩家角色创建一个新的 C++类。
-
返回编辑器,导航到“文件”,然后从下拉列表中选择“新建 C++类”选项。
-
新的玩家角色类将继承自 SuperSideScrollerCharacter 父类,因为这个基类具有玩家角色所需的大部分功能。选择父类后,左键单击“下一步”。请参考以下图片,看看如何找到 SuperSideScrollerCharacter 类。
图 11.29:选择 SuperSideScrollerCharacter 父类
- 将这个新类命名为
SuperSideScroller_Player
。除非您有必要调整这个新类的文件目录,否则将路径保留为 Unreal Engine 为您提供的默认路径。在命名新类并选择要保存类的目录之后,左键单击“创建类”。
选择“创建类”后,Unreal Engine 将为您生成源文件和头文件,并且 Visual Studio 将自动打开这些文件。您会注意到头文件和源文件几乎是空的。这没关系,因为您是从 SuperSideScrollerCharacter 类继承的,您想要的大部分逻辑都在那个类中完成了。
- 在 SuperSideScroller_Player 中,您只会添加您需要的功能。您可以在 SuperSideScroller_Player.h 文件中查看继承正在发生的地方:
class SUPERSIDESCROLLER_API ASuperSideScroller_Player : public ASuperSideScrollerCharacter
这个类声明表示新的 ASuperSideScroller_Player 类继承自 ASuperSideScrollerCharacter 类。
通过完成这个练习,您可以为“冲刺”机制添加必要的“输入绑定”,然后可以在 C++中引用并用于允许玩家进行冲刺。现在您还创建了玩家角色的 C++类,您可以更新代码以添加“冲刺”功能,但首先您需要更新“蓝图”角色和动画蓝图以引用这个新类。让我们在下一个练习中完成这个任务。
当您将蓝图重新设置为新类时会发生什么?每个蓝图都继承自一个父类。在大多数情况下,这是Actor
,但在您的角色蓝图的情况下,它的父类是SuperSideScrollerCharacter
。从父类继承允许蓝图继承该类的功能和变量,以便逻辑可以在蓝图级别上重用。
例如,当从 SuperSideScrollerCharacter 类继承时,蓝图会继承诸如 CharacterMovement 组件和 Mesh 骨骼网格组件之类的组件,然后可以在蓝图中进行修改。
练习 11.05:重新设置角色蓝图的父类
现在您已经为玩家角色创建了一个新的角色类,您需要更新BP_SuperSideScroller_MainCharacter
蓝图,以使用SuperSideScroller_Player
类作为其父类。如果不这样做,那么您添加到新类的任何逻辑都不会影响蓝图中创建的角色。
按照以下步骤将蓝图重新设置为新的角色类:
-
导航到
/Game/MainCharacter/Blueprints/
,并打开BP_SuperSideScroller_MainCharacter
蓝图。 -
在工具栏上选择“文件”选项,然后从下拉菜单中选择“重新设置父蓝图”选项。
-
选择“重新设置父蓝图”选项时,Unreal 会要求您为蓝图重新设置父类。搜索
SuperSideScroller_Player
,然后通过左键单击从下拉菜单中选择该选项。
一旦您为蓝图选择了新的父类,Unreal 将重新加载蓝图并重新编译它,这两个过程都将自动进行。
注意
在将蓝图重新父类化为新的父类时要小心,因为这可能导致编译错误或设置被擦除或恢复为类默认值。虚幻引擎将在将蓝图重新父类化为新类后显示任何可能发生的警告或错误。这些警告和错误通常发生在蓝图逻辑引用不再存在于新父类中的变量或其他类成员的情况下。即使没有编译错误,最好确认在重新父类化之后您对蓝图所做的任何逻辑或设置仍然存在,然后再继续工作。
现在您的角色蓝图已正确重新父类化为新的SuperSideScroller_Player
类,您还需要更新AnimBP_SuperSideScroller_MainCharacter
动画蓝图,以确保在使用尝试获取所有者
函数时转换为正确的类。
-
接下来,导航到
/MainCharacter/Blueprints/
目录,并打开AnimBP_SuperSideScroller_MainCharacter
动画蓝图。 -
打开
事件图
。从尝试获取所有者
函数的返回值
中,搜索转换
为SuperSideScroller_Player
:
图 11.30:与转换为基本 SuperSideScrollerCharacter 类不同,您可以转换为新的 SuperSideScroller_Player 类
- 然后,将输出连接为
SuperSideScroller_Player
转换为MainCharacter
变量。这是因为MainCharacter
变量是SuperSideScrollerCharacter
类型,而新的SuperSideScroller_Player
类继承自该类:
图 11.31:您仍然可以使用 MainCharacter 变量,因为 SuperSideScroller_Player 基于 SuperSideScrollerCharacter 进行继承
现在,BP_SuperSideScroller_MainCharacter
角色蓝图和AnimBP_SuperSideScroller_MainCharacter
动画蓝图都引用了您的新SuperSideScroller_Player
类,现在可以安全地进入 C++并编写角色冲刺功能。
练习 11.06:编写角色冲刺功能
在上一次练习中正确实现了新的SuperSideScroller_Player
类引用后,现在是时候开始编写功能,允许玩家角色进行冲刺了。
执行以下步骤将冲刺
机制添加到角色中:
-
首先要处理的是
SuperSideScroller_Player
类的构造函数。返回 Visual Studio 并打开SuperSideScroller_Player.h
头文件。 -
您将在本练习的后面使用
构造函数
来为变量设置初始化值。现在,它将是一个空的构造函数。确保声明是在public
访问修饰符标题下进行的,就像下面的代码中所示:
//Constructor
ASuperSideScroller_Player();
- 构造函数声明后,在
SuperSideScroller_Player.cpp
源文件中创建构造函数定义:
ASuperSideScroller_Player::ASuperSideScroller_Player()
{
}
构造函数就位后,现在可以创建SetupPlayerInputComponent
函数,以便您可以使用之前创建的按键绑定来调用SuperSideScroller_Player
类中的函数。
SetupPlayerInputComponent
函数是角色类默认内置的函数,因此您需要将其声明为带有override
修饰符的虚拟
函数。这告诉虚幻引擎您正在使用此函数,并打算在这个新类中重新定义其功能。确保声明是在Protected
访问修饰符标题下进行的。
SetupPlayerInputComponent
函数需要将UInputComponent
类的对象传递到函数中,如下所示:
protected:
//Override base character class function to setup our player input component
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
UInputComponent* PlayerInputComponent
变量是从我们的ASuperSideScroller_Player()
类继承的UCharacter
基类中继承的,因此必须用作SetupPlayerInputComponent()
函数的输入参数。使用其他任何名称都将导致编译错误。
- 现在,在源文件中创建
SetupPlayerInputComponent
函数的定义。在函数的主体中,我们将使用Super
关键字来调用它:
//Not always necessary, but good practice to call the function in the base class with Super.
Super::SetupPlayerInputComponent(PlayerInputComponent);
Super
关键字使我们能够调用SetupPlayerInputComponent
父方法。有了SetupPlayerInputComponent
函数准备好了,您需要包含以下头文件,以便在继续进行此练习时不会出现任何编译错误:
-
#include "Components/InputComponent.h"
-
#include "GameFramework/CharacterMovementComponent.h"
您需要包含输入组件的头文件,以便将键映射绑定到接下来将创建的冲刺功能上。Character Movement
组件的头文件将对冲刺功能是必需的,因为您将根据玩家是否正在冲刺来更新Max Walk Speed
参数。以下是所有需要包含的玩家角色的头文件:
#include "SuperSideScroller_Player.h"
#include "Components/InputComponent"
#include "GameFramework/CharacterMovementComponent.h"
在SuperSideScroller_Player
类的源文件中包含了必要的头文件后,您现在可以创建用于使玩家角色移动更快的冲刺功能。让我们首先声明所需的变量和函数。
- 在
SuperSideScroller_Player
类的头文件中的Private
访问修饰符下,声明一个名为bIsSprinting
的新布尔变量。这个变量将被用作一个保险措施,以确切地知道玩家角色在进行任何移动速度更改之前是否正在冲刺:
private:
//Bool to control if we are sprinting. Failsafe.
bool bIsSprinting;
- 接下来,声明两个新函数,
Sprint();
和StopSprinting();
。这两个函数不需要任何参数,也不返回任何内容。在Protected
访问修饰符下声明这些函数:
//Sprinting
void Sprint();
//StopSprinting
void StopSprinting();
当玩家按住/释放与绑定的Sprint
键映射相对应的键时,将调用Sprint();
函数,并且当玩家释放与绑定的键相对应的键时,将调用StopSprinting()
函数。
- 从
Sprint();
函数的定义开始。在SuperSideScroller_Player
类的源文件中,创建此函数的定义,如下所示:
void ASuperSideScroller_Player::Sprint()
{
}
-
在函数内部,您首先要检查
bIsSprinting
变量的值。如果玩家bIsSprinting
为False
,则继续执行函数的其余部分。 -
在
If
语句内,将bIsSprinting
变量设置为True
。然后,您可以访问GetCharacterMovement()
函数并修改MaxWalkSpeed
参数。将MaxWalkSpeed
设置为500.0f
。请记住,移动混合空间的Maximum Axis Value
参数为500.0f
。这意味着玩家角色将达到使用Running
动画所需的速度:
void ASuperSideScroller_Player::Sprint()
{
if (!bIsSprinting)
{
bIsSprinting = true;
GetCharacterMovement()->MaxWalkSpeed = 500.0f;
}
}
StopSprinting()
函数几乎与您刚刚编写的Sprint()
函数相同,但它的工作方式相反。您首先要检查玩家是否正在冲刺,也就是bIsSprinting
为True
。如果是,就继续执行函数的其余部分。
- 在
If
语句内,将bIsSprinting
设置为False
。然后,访问GetCharacterMovement()
函数来修改MaxWalkSpeed
。将MaxWalkSpeed
设置回300.0f
,这是玩家角色行走的默认速度。这意味着玩家角色只会达到Walking
动画所需的速度:
void ASuperSideScroller_Player::StopSprinting()
{
if (bIsSprinting)
{
bIsSprinting = false;
GetCharacterMovement()->MaxWalkSpeed = 300.0f;
}
}
现在您已经拥有了需要进行冲刺的功能,是时候将这些功能绑定到您之前创建的动作映射上了。为了做到这一点,在SetupPlayerInputComponent
函数中执行以下步骤。
- 让我们开始绑定
Sprint()
函数。在SetupPlayerInputComponent
函数内部,使用传递给函数的PlayerInputComponent
变量来调用BindAction
函数。
我们需要BindAction
的参数如下:
-
在
Project Settings
中写入的动作映射的名称,这是您在此练习中之前设置的,这种情况下是Sprint
。 -
EInputEvent
类型的枚举值,您想要用于此绑定;在这种情况下,您将使用IE_Pressed
,因为这个绑定将是当按下Sprint
键时。
//Bind pressed action Sprint to your Sprint function
PlayerInputComponent->BindAction"Sprint", IE_Pressed, this, &ASuperSideScroller_Player::Sprint);
- 您将对
StopSprinting()
函数做同样的事情,但这次您需要使用IE_Released
枚举值,并引用StopSprinting
函数:
//Bind released action Sprint to your StopSprinting function
PlayerInputComponent->BindAction("Sprint", IE_Released, this, &ASuperSideScroller_Player::StopSprinting);
通过将Action Mappings
绑定到奔跑功能,您需要做的最后一件事是设置bIsSprinting
变量和Character Movement
组件的MaxWalkSpeed
参数的默认初始化值。
-
在您的
SuperSideScroller_Player
类的源文件中的constructor
函数中,添加bIsSprinting = false
行。这个变量被构造为 false,因为玩家角色默认情况下不应该在奔跑。 -
最后,通过添加一行
GetCharacterMovement()->MaxWalkSpeed = 300.0f
,将角色移动组件的MaxWalkSpeed
参数设置为300.0f
。请查看以下代码:
ASuperSideScroller_Player::ASuperSideScroller_Player()
{
//Set sprinting to false by default.
bIsSprinting = false;
//Set our max Walk Speed to 300.0f
GetCharacterMovement()->MaxWalkSpeed = 300.0f;
}
通过在构造函数中初始化变量,SuperSideScroller_Player
类现在已经完成。返回到虚幻引擎,左键单击工具栏上的Compile
按钮。这将重新编译代码并执行编辑器的热重载。
重新编译和热重载编辑器后,您可以在编辑器中进行播放,并看到您努力的成果。基本移动行为与以前相同,但现在如果您按住左 Shift或游戏手柄右肩,玩家角色将奔跑并开始播放Running
动画。
图 11.32:玩家角色现在可以奔跑
玩家角色能够奔跑后,让我们继续下一个活动,在这个活动中,您将以非常相似的方式实现基本的Throw
功能。
活动 11.03:实现投掷输入
这个游戏包含的一个功能是玩家能够向敌人投掷抛射物。在本章中,您不会创建抛射物或实现动画,但您将设置按键绑定和 C++实现,以便在下一章中使用。
在这个活动中,您需要为Throw
投射功能设置按键绑定,并在 C++中实现调试日志,当玩家按下与Throw
映射的按键时,执行以下操作。
-
在输入绑定中的
Project Settings
中添加一个新的Throw
输入。将此绑定命名为ThrowProjectile
,并将其绑定到左鼠标按钮和游戏手柄右扳机。 -
在 Visual Studio 中,向
SuperSideScroller_Player
的头文件中添加一个新的函数。将这个函数命名为ThrowProjectile()
。这将是一个没有参数的 void 函数。 -
在
SuperSideScroller_Player
类的源文件中创建定义。在这个函数的定义中,使用UE_LOG
打印一条消息,让您知道函数被成功调用。
注意
您可以在这里了解更多关于UE_LOG
的信息:www.ue4community.wiki/Legacy/Logs,_Printing_Messages_To_Yourself_During_Runtime
。
这个活动结束时的预期结果是,当您使用左鼠标按钮或游戏手柄右扳机时,输出日志
中将出现一条日志,让您知道ThrowProjectile
函数被成功调用。稍后您将使用这个函数来生成您的抛射物。
预期的输出如下:
图 11.33:预期的输出日志
注意
这个活动的解决方案可以在这里找到:packt.live/338jEBx
。
完成这个活动后,您现在已经在第十三章 敌人人工智能中创建了玩家投射物的功能。您现在也已经掌握了向游戏添加新的键映射的知识和经验,并且实现了利用这些映射来启用游戏功能的 C++功能。现在,您将继续更新玩家角色移动,以允许玩家跳跃时正确播放跳跃动画。
动画状态机
现在,让我们了解一下在虚幻引擎 4 和动画中状态机的概念。状态机是将动画或一组动画分类到它们自己的状态中的一种方式。状态可以被认为是玩家角色在特定时间内的条件。玩家当前是在走路吗?玩家在跳跃吗?在许多第三人称游戏中,比如最后的生还者,这是将移动、跳跃、蹲下和攀爬动画分离到它们自己的状态中。每个状态在游戏进行时都是可访问的。条件可以包括玩家是否在跳跃、玩家角色的速度以及玩家是否处于蹲下状态。状态机的工作是使用称为转换规则的逻辑决策在各个状态之间进行转换。当您创建多个状态和相互交织的多个转换规则时,状态机开始看起来像一个网络。
请参考以下图片,查看ThirdPerson_AnimBP
动画蓝图的状态机外观。
注意
可以在这里找到状态机的一般概述:docs.unrealengine.com/en-US/Engine/Animation/StateMachines/Overview/index.html
图 11.34:ThirdPerson_AnimBP 的状态机,包含在 SideScroller 项目模板中
对于玩家角色的状态机,这个状态机将处理默认玩家移动和跳跃的状态。目前,玩家角色通过使用由角色速度控制的混合空间来简单地进行动画。在下一个练习中,您将创建一个新的状态机,并将移动混合空间逻辑移动到该状态机内的自己状态。让我们开始创建新的状态机。
练习 11.07:玩家角色移动和跳跃状态机
在这个练习中,您将实现一个新的动画状态机,并将现有的移动混合空间集成到状态机中。此外,您将设置玩家跳跃开始时的状态,以及玩家在跳跃期间的状态。
让我们从添加这个新状态机开始:
-
导航到
/MainCharacter/Blueprints/
目录,并打开AnimBP_SuperSideScroller_MainCharacter
动画蓝图。 -
在
AnimGraph
中,在图表的空白处右键单击,并在上下文敏感搜索中搜索state machine
,以找到Add New State Machine
选项。将这个新状态机命名为Movement
。 -
现在,我们可以连接新状态机
Movement
的输出姿势到动画的输出姿势,而不是连接SideScroller_IdleRun
混合空间的输出姿势:
图 11.35:新的 Movement 状态机替换了旧的混合空间
将空状态机连接到动画蓝图的Output Pose
将导致显示下面的警告。这意味着在该状态机中没有任何操作,结果将无效到Output Pose
。不要担心;您将在下一步中解决这个问题。
图 11.36:空状态机导致编译警告
- 双击
Movement
状态机以打开状态机本身。下面的图像显示了这是什么样子。
图 11.37:这是创建的空状态机
您将首先添加一个新状态,该状态将处理角色之前的操作;空闲
,行走
和奔跑
。
- 从
Entry
点,左键单击并拖动以打开上下文敏感搜索。您会注意到只有两个选项-添加导管
和添加状态
。现在,您将添加一个新状态并将此状态命名为Movement
。请参考以下图像,查看已创建的Movement
状态。
图 11.38:在状态机内部,您需要添加一个新状态,该状态将处理您之前创建的移动混合空间
图 11.39:新的移动状态
- 复制并粘贴您在上一步中连接
Speed
变量到SideScroller_IdleRun
混合空间的逻辑到新创建的Movement
状态。将其连接到此状态的Output Animation Pose
节点的Result
引脚:
图 11.40:将混合空间的输出姿势连接到此状态的输出姿势
现在,如果重新编译动画蓝图,您会注意到首先看到的警告现在已经消失。这是因为您添加了一个新状态,该状态将动画输出到Output Animation Pose
,而不是拥有一个空状态机。
通过完成这个练习,您已经构建了您的第一个状态机。虽然这是一个非常简单的状态机,但现在您正在告诉角色默认进入并使用Movement
状态。如果您现在 PIE,您会看到玩家角色现在像之前制作状态机之前一样移动。这意味着您的状态机正在运行,并且您可以继续下一步,即添加跳跃所需的初始状态。让我们从创建JumpStart
状态开始。
转换规则
导管是告诉每个状态可以在何种条件下从一个状态转换到另一个状态的一种方式。在这种情况下,转换规则被创建为Movement
和JumpStart
状态之间的连接。这由连接状态之间的方向箭头指示。工具提示提到术语转换规则,这意味着您需要定义这些状态之间的转换如何发生,使用布尔值来实现。
图 11.41:需要有一个转换规则从移动到角色跳跃的开始
练习 11.08:向状态机添加状态和转换规则
在从玩家角色的默认移动混合空间过渡到跳跃动画的情况下,您需要知道玩家何时决定跳跃。这可以使用玩家角色的Character Movement
组件中的一个有用函数IsFalling
来完成。您将希望跟踪玩家当前是否正在下落,以便在跳跃中进行过渡。这样做的最佳方式是将IsFalling
函数的结果存储在自己的变量中,就像您在跟踪玩家速度时所做的那样。
以下步骤将帮助您完成此练习:
-
回到状态机的概述,左键单击并从
Movement
状态的边缘拖动以再次打开上下文敏感菜单。 -
选择
Add State
选项并将此状态命名为JumpStart
。当您这样做时,虚幻将自动连接这些状态并为您实现一个空的Transition Rule
:
图 11.42:虚幻自动为您连接两个状态时创建的 Transition Rule
- 返回到动画蓝图中的
Event Graph
,在那里您使用了事件蓝图更新动画事件来存储玩家角色的Speed
。
图 11.43:SuperSideScroller 玩家动画蓝图的 EventGraph
- 为
MainCharacter
创建一个 getter 变量并访问Character Movement
组件。从Character Movement
组件,左键单击并拖动以访问上下文敏感菜单。搜索IsFalling
:
图 11.44:如何找到 IsFalling 函数
- 角色移动组件可以通过
IsFalling
函数告诉您玩家角色当前是否在空中:
图 11.45:角色移动组件显示玩家角色的状态
- 从
IsFalling
函数的Return Value
布尔值,左键单击并拖动以搜索上下文敏感菜单中的Promote to Variable
选项。将此变量命名为bIsInAir
。在提升为变量时,返回值输出针应自动连接到新提升的变量的输入针。如果没有,请记得连接它们。
图 11.46:包含 IsFalling 函数值的新变量 bIsInAir
现在你正在存储玩家的状态以及他们是否正在下落,这是Movement
和JumpStart
状态之间的过渡规则的完美候选者。
- 在
Movement State
机器中,双击Transition Rule
进入其图表。您将只找到一个输出节点Result
,带有参数Can Enter Transition
。在这里,您只需要使用bIsInAir
变量并将其连接到该输出。现在,Transition Rule
表示如果玩家在空中,则可以发生Movement
状态和JumpStart
状态之间的过渡。
图 11.47:当在空中时,玩家将过渡到跳跃动画的开始
在Movement
和JumpStart
状态之间放置了Transition Rule
后,剩下的就是告诉JumpStart
状态使用哪个动画。
- 从状态机图中,双击
JumpStart
状态以进入其图表。从“资产浏览器”中,左键单击并将JumpingStart
动画拖到图表中:
图 11.48:在左键单击并将其拖入状态之前,确保在资产浏览器中选择了 JumpingStart 动画
- 将
Play JumpingStart
节点的输出连接到Output Animation Pose
节点的Result
引脚:
图 11.49:将 JumpingStart 动画连接到 JumpStart 状态的输出动画姿势
在进行下一个状态之前,需要更改JumpingStart
动画节点上的设置。
- 左键单击
Play JumpingStart
动画节点,并更新“详细信息”面板以具有以下设置:
-
“循环动画=假”
-
“播放速率=2.0”
请参考以下图表,查看Play JumpingStart
动画节点的最终设置。
图 11.50:由于 JumpStart 动画的缓慢,增加播放速率将导致整体跳跃动画更加流畅
您将将“循环动画”参数设置为False
,因为没有理由让这个动画循环;无论如何它都应该只播放一次。这个动画循环的唯一方式是玩家角色在这个状态下被卡住,但由于您将创建的下一个状态,这永远不会发生。将“播放速率”设置为3.0
的原因是因为动画本身,JumpingStart
,对于您正在制作的游戏来说太长了。动画让角色急剧弯曲膝盖,并在一秒多的时间内向上跳跃。对于JumpStart
状态,您希望角色更快地播放这个动画,以使其更流畅,并提供更平滑的过渡到下一个状态;JumpLoop
。
一旦玩家角色开始JumpStart
动画,动画中会有一个时间点,此时玩家在空中,并应该过渡到一个新状态。这个新状态将循环,直到玩家不再在空中,并可以过渡到结束跳跃的最终状态。接下来,让我们创建这个新状态,它将从JumpStart
状态过渡。
- 从状态机图中,左键单击并从
JumpStart
状态拖动并选择“添加状态”选项。将此新状态命名为JumpLoop
。与以前一样,虚幻将自动为您提供这些状态之间的“转换规则”,您将在下一个练习中添加。最后,重新编译动画蓝图,并忽略编译器结果下可能出现的任何警告。
图 11.51:创建另一个状态,将处理角色在初始跳跃后空中的动画
通过完成这个练习,您已经为JumpStart
和JumpLoop
添加并连接了自己的状态。这些状态通过“转换规则”连接,现在您对状态机中的状态如何通过每个转换规则中建立的规则从一个状态过渡到另一个状态有了更好的理解。
在下一个练习中,您将更深入地了解如何通过函数“剩余时间比例”从JumpStart
状态过渡到JumpLoop
状态。
练习 11.09:剩余时间比例函数
为了使“跳跃开始”状态顺利过渡到“跳跃循环”状态,您需要花一点时间思考确切地想要这个过渡如何工作。基于“跳跃开始”和“跳跃循环”动画的工作方式,最好在“跳跃开始”动画播放一定时间后过渡到“跳跃循环”动画。这样,“跳跃循环”状态就会在“跳跃开始”动画播放X
秒后平稳播放。
执行以下步骤来实现这一点:
-
双击“跳跃开始”和“跳跃循环”之间的“过渡规则”以打开其图表。您将应用的“过渡规则”是检查“跳跃开始”动画剩余多少时间。这是因为“跳跃开始”动画还剩下一定比例的时间,您可以安全地假设玩家在空中并准备过渡到“跳跃循环”动画状态。
-
要做到这一点,首先确保在“资源浏览器”中选择了“跳跃开始”动画,然后在“过渡规则”的“事件图”中右键单击并找到“时间剩余比率”函数。
让我们花点时间来谈谈“时间剩余比率”函数及其作用。该函数返回一个在0.0f
和1.0f
之间的浮点数,告诉您指定动画剩余多少时间。值0.0f
和1.0f
可以直接转换为百分比值,以便更容易考虑。在“跳跃开始”动画的情况下,您希望知道动画剩余的百分比是否小于 60%,以成功过渡到“跳跃循环”状态。这就是您现在要做的。
- 从“时间剩余比率”函数的“返回值”浮点输出参数中,从上下文敏感搜索菜单中搜索“小于比较操作”节点。由于您正在处理一个在
0.0f
和1.0f
之间的返回值,为了知道动画剩余的百分比是否小于 60%,您需要将这个返回值与0.6f
进行比较。最终结果如下:
图 11.52:在过渡到跳跃循环动画之前,您需要知道跳跃开始动画剩余多少时间
有了这个“过渡规则”,剩下的就是将“跳跃循环”动画添加到“跳跃循环”状态中。
- 在“移动”状态机中,双击“跳跃循环”状态以进入其图表。在“资源浏览器”中选择“跳跃循环”动画资产,单击并将其拖放到图表中。将其输出连接到“输出动画姿势”的“结果”输入,如下所示。默认设置的“播放跳跃循环”节点将保持不变。
图 11.53:跳跃循环动画连接到新状态的输出动画姿势
将“跳跃循环”动画放置在“跳跃循环”状态中后,您现在可以编译动画蓝图并进行 PIE。您会注意到移动和奔跑动画仍然存在,但当您尝试跳跃时会发生什么?玩家角色开始“跳跃开始”状态,并在空中播放“跳跃循环”动画。这很棒,状态机正在工作,但当玩家角色到达地面并不再在空中时会发生什么?玩家角色不会过渡回“移动”状态,这是有道理的,因为您还没有添加“跳跃结束”状态或“跳跃循环”和“跳跃结束”之间的过渡,以及从“跳跃结束”回到“移动”状态。您将在下一个活动中完成这些。请参见下面的示例,其中玩家角色被困在“跳跃循环”状态中:
图 11.54:玩家角色现在可以播放跳跃开始动画和跳跃循环动画,但无法过渡回默认移动状态
通过完成这个练习,您已成功使用Time Remaining Ratio
函数从JumpStart
状态过渡到JumpLoop
状态。这个函数允许您知道动画播放到哪个阶段,有了这个信息,状态机就可以过渡到JumpLoop
状态。玩家现在可以成功地从默认的Movement
状态过渡到JumpStart
状态,然后到JumpLoop
状态,导致一个有趣的问题。玩家现在被困在JumpLoop
状态,因为状态机没有包含回到Movement
状态的过渡。让我们在下一个活动中解决这个问题。
活动 11.04:完成移动和跳跃状态机
完成了一半的状态机,现在是时候添加跳跃结束的状态,以及允许您从JumpLoop
状态过渡到这个新状态的过渡规则,以及从这个新状态过渡回Movement
状态的过渡规则。
完成Movement
状态机的以下操作:
-
添加一个新的
Jump End
状态,从JumpLoop
过渡。将此状态命名为JumpEnd
。 -
将
JumpEnd
动画添加到新的JumpEnd
状态。 -
根据
JumpEnd
动画以及我们希望在JumpLoop
、JumpEnd
和Movement
状态之间快速过渡的方式,考虑修改动画的参数,就像你为JumpStart
动画所做的那样。循环动画
参数需要设置为False
,播放速率
参数需要设置为3.0
。 -
在
JumpLoop
状态到JumpEnd
状态添加一个过渡规则
,基于bIsInAir
变量。 -
根据
JumpEnd
动画的Time Remaining Ratio
函数,从JumpEnd
状态到Movement
状态添加一个过渡规则
。(查看JumpStart
到JumpLoop
的过渡规则)。
通过本次活动,您将拥有一个完全运作的移动状态机,允许玩家角色空闲、行走、冲刺,以及能够跳跃并在跳跃开始时正确地进行动画,以及在空中和着陆时进行动画。
预期输出如下:
图 11.55:玩家角色的空闲、行走、冲刺和跳跃动画
注意
可以在以下链接找到此活动的解决方案:packt.live/338jEBx
。
通过完成这个活动,您现在已经完成了玩家角色的移动状态机。通过添加剩余的JumpEnd
状态和从JumpLoop
状态过渡到该状态的过渡规则
,以及从JumpEnd
状态回到Movement
状态的过渡规则
,您成功地创建了您的第一个动画状态机。现在,您可以在地图上奔跑并跳上高处的平台,同时正确地进行动画并在移动和跳跃状态之间过渡。
总结
玩家移动混合空间已创建,玩家角色动画蓝图使用状态机从移动到跳跃的过渡,您已准备好进入下一章,在那里您将准备所需的动画插槽、动画剪辑,并更新动画蓝图,以使用角色的上半身进行投掷动画。
通过本章的练习和活动,您学会了如何创建一个 1D 混合空间,允许平滑地混合基于移动的动画,如空闲、行走和奔跑,使用玩家角色的速度来控制动画的混合。
另外,您还学会了如何将新的按键绑定集成到项目设置中,并在 C++中绑定这些按键,以启用角色的游戏机制,如冲刺和投掷。
最后,您学会了如何在角色动画蓝图中实现自己的动画状态机,以便玩家能够在移动动画之间进行过渡,跳跃的各种状态,然后再回到移动状态。有了所有这些逻辑,让我们在下一章继续创建资产和逻辑,允许玩家角色播放投掷动画,并设置敌人的基础类。
第十一章:动画混合和蒙太奇
概述
通过本章结束时,你将能够使用动画蒙太奇
工具来创建一个独特的投掷动画,使用你在第十章中导入的投掷
动画序列。通过这个蒙太奇,你将创建并使用动画插槽,允许你在玩家角色的动画蓝图中混合动画。你还将了解如何使用混合节点有效地混合角色的移动和投掷动画。
在完成玩家角色动画后,你将为敌人 AI 创建所需的类和资产,并学习更多关于材质和材质实例
,这将使这个敌人在游戏中具有独特的视觉颜色,以便可以进行区分。最后,敌人将准备好进入第十三章,敌人人工智能,在那里你将开始创建 AI 行为逻辑。
介绍
在上一章中,你通过在混合空间
中实现移动动画,并在动画蓝图中使用该混合空间
来根据玩家速度驱动动画,使玩家角色栩栩如生。然后,你能够基于玩家输入在 C++中实现功能,允许角色奔跑。最后,你利用动画蓝图内置的动画状态机来驱动角色的移动状态和跳跃状态,以实现在行走和跳跃之间流畅过渡。
随着玩家角色动画蓝图和状态机的工作,现在是时候通过实现角色的“投掷”动画来介绍动画蒙太奇和动画插槽了。在本章中,你将学习更多关于动画混合的知识,看看虚幻引擎如何通过创建动画蒙太奇
来处理多个动画的混合,并使用新的保存缓存姿势
和骨骼层叠混合
,以便玩家可以正确地将你在上一章中处理的移动动画与你将在本章实现的新投掷动画进行混合。
让我们首先学习一下什么是动画蒙太奇和动画插槽,以及它们如何用于角色动画。
动画混合、动画插槽和动画蒙太奇
动画混合是在骨骼网格上尽可能无缝地过渡多个动画之间的过程。你已经熟悉了动画混合的技术,因为你在第十一章中为玩家角色创建了一个混合空间
资产,其中角色在“空闲”、“行走”和“奔跑”动画之间平滑过渡。现在,你将通过探索和实现新的叠加技术来扩展这些知识,以将角色的移动动画与投掷动画结合起来。通过使用动画插槽
,你将把投掷动画发送到一组上半身骨骼和其子骨骼,以便允许移动和投掷动画同时应用而不会对其他动画产生负面影响。但首先,让我们更多地谈谈动画蒙太奇。
动画蒙太奇是一个非常强大的资产,它允许你将多个动画组合在一起,并将这些组合动画分割成所谓的部分。部分可以单独播放,按特定顺序播放,甚至循环播放。
动画蒙太奇也很有用,因为你可以通过蓝图或 C++来控制动画蒙太奇中的动画;这意味着你可以根据正在播放的动画部分或蒙太奇中调用的任何“通知”来调用逻辑、更新变量、复制数据等。在 C++中,有一个UAnimInstance
对象,你可以使用它来调用诸如UAnimInstance::Montage_Play
之类的函数,这允许你从 C++中访问和播放蒙太奇。
注意
这种方法将在第十四章“生成玩家投射物”中使用,当您开始为游戏添加细节时。关于动画和Notifies
在 Unreal Engine 4 中如何通过 C++处理的更多信息可以在docs.unrealengine.com/en-US/API/Runtime/Engine/Animation/AnimNotifies/UAnimNotifyState/index.html
找到。
您将在本章的第一个练习中了解更多关于Notifies
的内容,并且您将在第十四章“生成玩家投射物”中编写自己的通知状态。
下面的图片显示了动画蒙太奇的Persona
编辑器。然而,这将在练习 12.01“设置动画蒙太奇”中进一步拆分:
图 12.1:Persona 编辑器,在编辑动画蒙太奇时打开
就像在动画序列中一样,动画蒙太奇允许在动画的时间轴上触发Notifies
,这样可以触发声音、粒子效果和事件。Event
Notifies
将允许我们从蓝图或 C++中调用逻辑。Epic Games 在他们的文档中提供了一个武器重新加载Animation Montage
的示例,该示例分为reload start
、reload loop
和reload complete
的动画。通过拆分这些动画并应用Notifies
来触发sounds
和events
,开发人员可以完全控制reload loop
根据内部变量播放多长时间,以及在动画过程中播放任何额外的声音或效果。
最后,动画蒙太奇支持所谓的Anim Slots。Anim Slots 允许您对动画或一组动画进行分类,稍后可以在 Animation Blueprint 中引用,以允许基于插槽的独特混合行为。这意味着您可以定义一个 Anim Slot,稍后可以在 Animation Blueprint 中使用,以允许使用此插槽的动画以任何您想要的方式在基本移动动画的基础上混合;在我们的情况下,只影响玩家角色的上半身而不影响下半身。
让我们开始为玩家角色的Throw
动画创建Animation Montage
,这是第一个练习。
练习 12.01:设置动画蒙太奇
玩家角色的最后一件事是设置 Anim Slot,这将单独将此动画分类为上半身动画。您将在 Animation Blueprint 中使用此 Anim Slot,结合混合函数,允许玩家角色投掷投射物,同时在移动和跳跃时正确地对下半身进行动画处理。
通过这个练习结束时,玩家角色将能够仅使用上半身播放Throw
动画,而下半身仍将使用您在上一章中定义的movement animation
。
让我们开始为角色创建Animation Montage
,投掷并设置那里的 Anim Slot:
-
首先,导航到
/MainCharacter/Animation
目录,这是所有动画资产的位置。 -
现在,在内容浏览器中右键单击,悬停在可用下拉菜单中的
Animation
选项上。 -
然后,左键单击以选择从出现的附加下拉菜单中的
Animation Montage
选项。 -
就像创建其他基于动画的资产一样,比如
Blend Spaces
或Animation Blueprints
,Unreal Engine 会要求您为这个Animation Montage
分配一个Skeleton
对象。在这种情况下,选择MainCharacter_Skeleton
。 -
将新的
Animation Montage
命名为AM_Throw
。现在,双击打开蒙太奇:
图 12.2:您已成功创建了一个动画蒙太奇资产
打开“动画剪辑”资产时,您会看到类似的编辑器布局,就像打开“动画序列”时一样。有一个“预览”窗口,显示默认的 T 形主角骨架,但一旦您向这个剪辑添加动画,骨架将更新以反映这些变化。
通过完成这个练习,您已成功为“超级横向卷轴”项目创建了一个“动画剪辑”资产。现在是时候了解更多关于动画剪辑以及如何添加您需要的“投掷”动画和动画插槽,以便将“投掷”动画与现有角色移动动画混合。
动画剪辑
看一下下面的图:
图 12.3:动画剪辑 Persona 编辑器中的动画预览窗口
在“预览”窗口下方,您有主要剪辑时间轴,以及其他部分;让我们从上到下评估这些部分:
-
“剪辑”部分是可以添加一个或多个动画的动画集合。您还可以右键单击时间轴上的任何点以创建“部分”区域。
-
部分:如前所述,部分允许您设置单个动画序列的播放顺序以及部分是否应该循环。
为了投掷剪辑的目的,您不需要使用此功能,因为您只会在此剪辑中使用一个动画:
图 12.4:预览窗口和剪辑和部分区域
-
“元素定时”部分为您提供了剪辑的预览以及剪辑各个方面的顺序。通知的播放顺序,“剪辑”部分和其他元素将在这里进行可视化显示,以便您快速预览剪辑的工作方式。
-
“通知”使您能够在动画时间轴上添加点,然后通知其他系统执行操作或从蓝图和 C++中调用逻辑。通知选项,如“播放声音”或“播放粒子效果”,允许您在动画的特定时间播放声音或粒子。一个例子是在武器重新装填的动画中;您可以在动画的时间轴上添加通知,以在重新装填的精确时刻播放重新装填声音。在实现投掷投射物时,您将在项目的后续阶段使用这些“通知”:
图 12.5:元素定时和通知区域
现在您已经熟悉了动画剪辑的界面,您可以按照下一个练习将“投掷”动画添加到剪辑中。
练习 12.02:将投掷动画添加到剪辑中
现在您对动画剪辑是什么以及这些资产如何工作有了更好的理解,是时候将“投掷”动画添加到您在练习 12.01中创建的剪辑中了,设置动画剪辑。尽管您只会向此剪辑添加一个动画,但重要的是要强调您可以向剪辑添加多个独特的动画,然后播放。现在,让我们开始通过添加您在第十章中导入项目的“投掷”动画来开始:
在“资产浏览器”中找到“投掷”动画资产。然后,左键单击并将其拖放到“剪辑”部分下的时间轴上:
图 12.6:带有基于动画的资产的资产浏览器窗口
一旦将动画添加到动画剪辑中,预览窗口中的角色骨架将更新以反映此更改并开始播放动画:
图 12.7:玩家角色开始动画
现在投掷
动画已经添加到动画蒙太奇中,您可以继续创建动画槽
。
动画槽管理器
选项卡应该停靠在右侧的资产浏览器
选项卡旁边。如果您看不到动画槽管理器
选项卡,可以通过导航到顶部动画蒙太奇
编辑器窗口的工具栏中的窗口
选项卡来访问它。在那里,左键单击选择动画槽管理器
选项,窗口将出现。
完成此练习后,您已经将投掷
动画添加到了新的动画蒙太奇中,并且可以回放动画,以预览它在Persona
编辑器中的外观。
现在,您可以继续学习有关动画槽和动画槽管理器
的知识,然后在本章后面的部分中添加自己独特的动画槽,以便用于动画混合。
动画槽管理器
动画槽管理器
是您管理动画槽
的地方,正如其名称所示。从该选项卡中,您可以通过左键单击添加组
选项创建新的组
,并将其标记为Face
,以向其他人说明该组中的槽影响角色的面部。默认情况下,虚幻引擎为您提供了一个名为DefaultGroup
的组
和一个名为DefaultSlot
的动画槽
,该槽位于该组中。
让我们创建一个新的动画槽。
练习 12.03:添加新的动画槽
现在您对动画槽和动画槽管理器
有了更好的理解,您可以按照以下步骤创建一个名为上半身
的新动画槽。创建了这个新槽后,它可以在动画蓝图中使用和引用,以处理动画混合,这将在以后的练习中进行。
让我们通过以下步骤创建动画槽:
-
在
动画槽管理器
中,左键单击添加槽
选项。 -
在添加新槽时,虚幻将要求您给这个
动画槽
命名。将此槽命名为上半身
。动画槽的命名很重要,就像命名其他任何资产和参数一样,因为您稍后将在动画蓝图中引用此槽。
创建了动画槽后,现在可以更新用于投掷
蒙太奇的槽。
- 在
Montage
部分,有一个下拉菜单显示应用的动画槽
;默认情况下,它设置为DefaultGroup.DefaultSlot
。左键单击,然后从下拉菜单中选择DefaultGroup.Upper Body
:
图 12.8:新的动画槽将出现在下拉列表中
注意
更改动画槽
后,您可能会注意到玩家角色停止动画并返回到 T 形状。不用担心-如果发生这种情况,只需关闭动画蒙太奇
,然后重新打开它。重新打开后,角色将再次播放投掷
动画。
创建了您的动画槽
并放置在投掷
蒙太奇中后,现在是时候更新动画蓝图,以便玩家角色意识到这个槽,并根据它正确地进行动画。
-
导航到
/MainCharacter/Blueprints/
目录中的AnimBP_SuperSideScroller_MainCharacter
资产。 -
通过双击打开此资产并打开
动画图
。
完成此练习后,您已经使用动画槽管理器
在动画蒙太奇中创建了您的第一个动画槽。有了这个槽,现在可以在玩家角色动画蓝图中使用和引用它,以处理在上一章中实现的投掷
动画和移动动画之间所需的动画混合。在执行此操作之前,您需要了解有关动画蓝图中保存缓存姿势
节点的更多信息。
保存缓存姿势
在处理复杂动画和角色时,有时需要在多个地方引用状态机输出的姿势。如果你还没有注意到,你的Movement
状态机的输出姿势不能连接到多个其他节点。这就是Save Cached Pose
节点派上用场的地方;它允许你缓存或存储一个姿势,然后可以在多个地方引用。你需要使用它来设置上半身动画的新 Anim Slot。
让我们开始吧。
练习 12.04:保存 Movement 状态机的缓存姿势
为了有效地混合使用上一练习中创建的Upper Body Anim Slot
和已经存在的玩家角色的移动动画的Throw
动画,你需要能够在动画蓝图中引用Movement
状态机。为了实现这一点,按照以下步骤在动画蓝图中实现Save Cached Pose
节点:
- 在
Anim Graph
中,右键单击并搜索New Save Cached Pose
。将其命名为Movement Cache
:
图 12.9:姿势将在每帧评估一次,然后被缓存
- 现在,不要直接将你的
Movement
状态机连接到输出姿势,而是连接到缓存节点:
图 12.10:Movement 状态机正在被缓存
- 使用缓存的
Movement
状态机姿势,现在你只需要引用它。这可以通过搜索“使用缓存的姿势”节点来实现。
注意
所有缓存的姿势都会显示在上下文敏感菜单中。只需确保选择你在步骤 1中给它的名字的缓存姿势。
- 有了缓存姿势节点后,将其连接到
AnimGraph
的Output Pose
:
图 12.11:这与将 Movement 状态机直接连接到输出姿势相同
在步骤 4之后,你会注意到主角在最后一章之后会正确地进行动画并移动。这证明了Movement
状态机的缓存正在工作。下面的图片显示了玩家角色在动画蓝图的预览窗口中回到了Idle
动画。
现在,Movement
状态机的缓存工作正常,你将使用这个缓存来通过骨骼上的Anim Slot
混合动画:
图 12.12:主角正在按预期进行动画
完成这个练习后,你现在可以在动画蓝图中任何你想要的地方引用缓存的Movement
状态机姿势。有了这个便利,你现在可以使用缓存的姿势开始在缓存的移动姿势和Upper Body
Anim Slot 之间进行混合,使用一个叫做Layered blend per bone
的函数。
Layered blend per bone
你将在这里使用的节点是Layered blend per bone
。这个节点可以屏蔽角色骨骼上的一组骨骼,使动画忽略这些骨骼。
对于我们的玩家角色和Throw
动画,你将屏蔽下半身,以便只有上半身进行动画。目标是能够同时执行投掷和移动动画,并使这些动画混合在一起;否则,当你执行投掷时,移动动画会完全中断。
练习 12.05:使用上半身 Anim Slot 混合动画
“每个骨骼的分层混合”功能允许我们将“投掷”动画与您在上一章中实现的移动动画混合,并控制“投掷”动画对玩家角色骨骼的影响程度。
在这个练习中,您将使用“每个骨骼的分层混合”功能完全屏蔽角色的下半身,当播放“投掷”动画时,以便它不会影响角色下半身的移动动画。
让我们从添加“每个骨骼的分层混合”节点开始,并讨论其输入参数和设置:
- 在动画蓝图中,右键单击并在“上下文敏感”搜索中搜索“每个骨骼的分层混合”。
图 12.13显示了“每个骨骼的分层混合”节点及其参数。
-
第一个参数“基础姿势”是角色的基础姿势;在这种情况下,“移动”状态机的缓存姿势将是基础姿势。
-
第二个参数是您想要在“基础姿势”上叠加的“混合姿势 0”节点;请记住,选择“添加引脚”将创建额外的“混合姿势”和“混合权重”参数。现在,您只会使用一个“混合姿势”节点。
-
最后一个参数是“混合权重”,它是“混合姿势”对“基础姿势”的影响程度,范围从
0.0
到1.0
作为 alpha 值:
图 12.13:每个骨骼的分层混合节点
在连接任何内容到此节点之前,您需要向其属性中添加一个层。
- 左键单击选择节点并导航到“详细信息”。您需要左键单击“层设置”旁边的箭头,以找到此设置的第一个索引
0
。左键单击“分支过滤器”旁边的+
以创建新的过滤器。
这里再次有两个参数,即以下参数:
- “骨骼名称”:指定混合将发生的骨骼,并确定被屏蔽的骨骼的子层次结构。在本项目的主角骨架中,将“骨骼名称”设置为“脊柱”。图 12.14显示了“脊柱”骨及其子骨与主角的下半身不相关联。这可以在“骨架”资产
MainCharacter_Skeleton
中看到:
图 12.14:脊柱骨及其子骨与主角的上半身相关联
-
“混合深度”:骨骼及其子骨受动画影响的深度。值为
0
将不会影响所选骨骼的根子骨。 -
“网格空间旋转混合”:确定是否在“网格空间”或“本地空间”中混合骨骼旋转。“网格空间”旋转是指骨架网格的边界框作为其基本旋转,而“本地空间”旋转是指所讨论的骨骼名称的局部旋转。在这种情况下,我们希望旋转混合发生在网格空间中,因此我们将将此参数设置为 true。
混合传播到骨骼的所有子骨,以停止在特定骨骼上的混合,将它们添加到数组中,并将它们的混合深度值设为0
。最终结果如下:
图 12.15:您可以使用一个混合节点设置多个层
- 在“每个骨骼的分层混合”节点上设置好参数后,您可以将“移动缓存”缓存姿势连接到分层混合的“基础姿势”节点。确保将“每个骨骼的分层混合”节点的输出连接到动画蓝图的“输出姿势”:
图 12.16:将移动状态机的缓存姿势添加到每个骨骼的分层混合节点
现在是时候使用您之前创建的 Anim Slot,通过Layered blend per bone
节点仅过滤使用此插槽的动画了。
- 在
AnimGraph
中右键单击,搜索DefaultSlot
。左键单击选择Slot
节点并导航到Details
。在那里,您会找到Slot Name
属性。左键单击此下拉菜单,找到并选择DefaultGroup.Upper Body
插槽。
在更改Slot Name
属性时,Slot
节点将更新以表示这个新名称。Slot
节点需要一个源姿势,这将再次是对Movement
状态机的引用。这意味着您需要为Movement Cache
姿势创建另一个Use Cached Pose
节点。
- 将缓存的姿势连接到
Slot
节点的源:
图 12.17:通过 Anim Slot 过滤缓存的 Movement 姿势
- 现在剩下的就是将
Upper Body
插槽节点连接到Blend Pose 0
输入。然后,将Layered blend per bone
的最终姿势连接到Output Pose
动画蓝图的结果:
图 12.18:主角动画蓝图的最终设置
在主角动画蓝图中放置了 Anim Slot 和Layered blend per bone
节点后,您终于完成了主角的动画部分。
接下来,让我们简要讨论一下“投掷”动画的动画混合的重要性以及“投掷”动画将用于什么,然后再继续练习 12.06,在那里您将在游戏中预览“投掷”动画。
投掷动画
到目前为止,您已经投入了大量的工作,以确保“投掷”动画与之前章节中在动画蓝图中设置的“移动”动画正确融合。这一努力的主要原因是确保角色在执行多个动画时的视觉保真度。在接下来的练习和活动中,您将亲身体会到错误设置动画混合的视觉后果。
回到“投掷”动画,每款现代视频游戏都以某种形式实现动画混合,只要美术指导和游戏机制需要这样的功能。一个极好地运用动画的现代游戏系列的例子是由Naughty Dog开发的Uncharted系列。
如果您对这个系列不熟悉,您可以在这里观看最新版本的完整游戏玩法:www.youtube.com/watch?v=5evF_funE8A
。
Uncharted系列非常擅长使用成千上万的动画和混合技术,为玩家角色带来令人难以置信的真实感、重量感和动感,让您在玩游戏时感觉非常良好。虽然Super SideScroller项目不会像这样精致,但您正在学习制作视频游戏中不可思议动画所需的基础知识:
练习 12.06:预览投掷动画
在上一个练习中,您做了很多工作,通过使用Save Cached Pose
和Layered blend per bone
节点,允许玩家角色的Movement
动画和Throw
动画之间进行动画混合。执行以下步骤,在游戏中预览Throw
动画,并看到您劳动的成果:
-
导航到
/MainCharacter/Blueprints/
目录,并打开角色的BP_SuperSideScroller_MainCharacter
蓝图。 -
如果您还记得,在上一章中,您为投掷创建了名为
ThrowProjectile
的Input Action
。 -
在角色蓝图的“事件图”中,右键单击并在“上下文敏感”下拉搜索中搜索
ThrowProjectile
。用左键单击选择它,在图表中创建事件节点。
有了这个事件,您需要一个函数,允许您在玩家使用左鼠标按钮投掷时播放“动画蒙太奇”。
- 右键单击在“事件图”中搜索
Play Montage
。确保不要将其与类似的函数Play Anim Montage
混淆。
Play Montage
函数需要两个重要的输入:
-
Montage to Play
-
“在骨骼网格组件”
让我们首先处理“骨骼网格组件”。
- 玩家角色有一个“骨骼网格组件”,可以在标记为“网格”的组件选项卡中找到。左键单击并拖动一个
Get
引用到这个变量,并将其连接到此函数的In Skeletal Mesh Component
输入:
图 12.19:玩家角色的网格连接到 In Skeletal Mesh Component 输入
现在要做的最后一件事是告诉这个函数播放哪个蒙太奇。幸运的是,这个项目中只有一个蒙太奇存在:AM_Throw
。
-
在
Montage to Play
输入下的下拉菜单上左键单击,然后左键单击选择AM_Throw
。 -
最后,将
ThrowProjectile
事件的Pressed
执行输出连接到Play Montage
函数的执行输入引脚:
图 12.20:当玩家按下 ThrowProjectile 输入动作时,将播放 AM_Throw 蒙太奇
- 现在,当您点击左鼠标按钮时,玩家角色将播放投掷“动画蒙太奇”。
现在注意一下,您可以同时行走和奔跑,同时投掷,每个动画都会混合在一起,不会相互干扰:
图 12.21:玩家角色现在可以移动和投掷
不要担心重复使用左鼠标按钮动作播放Throw
蒙太奇时可能出现的任何错误;在后面的章节中,当您实现将要投掷的抛射物时,这些问题将得到解决。现在,您只想知道在Anim Slot
和Animation Blueprint
上所做的工作是否能够实现所需的动画混合结果。
让我们继续通过现在创建 C++类、蓝图和材质来设置敌人,以便在下一章中使用。
超级横向卷轴游戏敌人
当玩家角色在移动和执行Throw
动画时正确播放动画时,现在是时候谈论SuperSideScroller
游戏将呈现的敌人类型了。我们将有一个简单类型的敌人。
这个敌人将有一个基本的来回移动模式,不支持任何攻击;只有与玩家角色碰撞时才能造成伤害。
在下一个练习中,您将为第一种敌人类型设置基础敌人类的 C++,并配置敌人的蓝图和动画蓝图,为第十三章“敌人人工智能”做准备,在那里您将实现这个敌人的人工智能。为了效率和时间考虑,您将使用 Unreal Engine 4 在SideScroller
模板中已经提供的资产用于敌人。这意味着您将使用默认人偶资产的骨架、骨骼网格、动画和动画蓝图。让我们开始创建第一个敌人类。
练习 12.07:创建敌人基础 C++类
本练习的目标是从头开始创建一个新的敌人类,并在 第十三章 敌人人工智能 中准备好使用敌人。首先,按照以下步骤在 C++ 中创建一个新的敌人类:
-
在编辑器中,导航到
文件
并选择新建 C++ 类
来开始创建新的敌人类。 -
接下来,请确保在尝试搜索类之前,检查
选择父类
窗口提示的顶部的显示所有类
复选框。然后,搜索SuperSideScrollerCharacter
并 左键单击 选择它作为父类。 -
最后,您需要给这个类起一个名字并选择一个目录。将这个类命名为
EnemyBase
,并不要更改目录路径。准备好后,左键单击Create Class
按钮,让虚幻引擎为您创建新的类。
当您创建一个新类时,虚幻引擎会自动为您打开 Visual Studio,并准备好 .cpp
和 .h
文件。目前,您不会对代码进行任何更改,因此关闭 Visual Studio。
让我们为敌人资产在内容浏览器中创建文件夹结构。接下来。
- 回到虚幻引擎 4 编辑器,导航到内容浏览器,并创建一个名为
Enemy
的新文件夹:
图 12.22:通过右键单击现有文件夹并选择新文件夹来创建新文件夹
-
在
Enemy
文件夹中,创建另一个名为Blueprints
的文件夹,您将在其中创建并保存敌人的蓝图资产。 -
在
/Enemy/Blueprints
目录中,右键单击 并选择蓝图类
。从选择父类
中搜索您刚刚创建的新 C++ 类EnemyBase
,如图所示:
图 12.23:现在,新的 EnemyBase 类可供您创建蓝图
- 将其命名为
BP_Enemy
。
现在,您已经使用 EnemyBase
类作为父类为第一个敌人创建了 蓝图
,是时候处理 动画蓝图
了。您将使用虚幻引擎在 SideScroller
模板项目中提供给您的默认 动画蓝图
。按照下一个练习中的步骤创建现有 动画蓝图
的副本并将其移动到 /Enemy/Blueprints
目录。
练习 12.08:创建和应用敌人动画蓝图
在上一个练习中,您创建了使用 EnemyBase
类作为父类的第一个敌人的 蓝图
。在这个练习中,您将处理动画蓝图。
以下步骤将帮助您完成此练习:
-
导航到
/Mannequin/Animations
目录,并找到ThirdPerson_AnimBP
资产。 -
现在,复制
ThirdPerson_AnimBP
资产。有两种方法可以复制资产:
-
在内容浏览器中选择所需的资产,然后按 CTRL + W。
-
在内容浏览器中 右键单击 所需的资产,然后从下拉菜单中选择
复制
。
-
现在,左键单击 并拖动此重复的资产到
/Enemy/Blueprints
目录,并在释放 左键单击 鼠标按钮时选择移动选项。 -
将此重复的资产命名为
AnimBP_Enemy
。最好创建一个资产的副本,以便稍后根据需要进行修改,而不会影响原始资产的功能:
敌人 蓝图
和 动画蓝图
创建完成后,现在是时候更新敌人蓝图,以使用默认的 骨骼网格
人体模型和新的 动画蓝图
副本了。
-
导航到
/Enemy/Blueprints
并打开BP_Enemy
。 -
接下来,导航到
Mesh
组件并选择它以访问其详细信息
面板。首先,将SK_Mannequin
分配给骨骼网格
参数,如图所示:
图 12.24:您将使用默认的 SK_Mannequin 骨骼网格作为新敌人的角色
- 现在,您需要将“AnimBP_Enemy 动画蓝图”应用到
Mesh
组件上。导航到Mesh
组件的“详细信息”面板的“动画”类别,在“动画类”下,分配AnimBP_Enemy
:
图 12.25:将新的 AnimBP_Enemy 动画蓝图分配为敌人角色的动画类
- 最后,当在“预览”窗口中预览角色时,您会注意到角色网格的位置和旋转不正确。通过将
Mesh
组件的“变换”属性设置为以下内容来修复这个问题:
-
“位置”:(
X
=0.000000
,Y
=0.000000
,Z
=-90.000000
) -
“旋转”:(Roll=
0.000000
,Pitch=0
,Yaw=-90.000000
) -
“缩放”:(
X
=1.000000
,one
=1.000000
,Z
=1.000000
)
“变换”设置将如下所示:
图 12.26:这些是变换设置,以便您的角色被正确定位和旋转
以下图显示了迄今为止Mesh
组件的设置。请确保您的设置与此处显示的相匹配:
图 12.27:敌人角色的 Mesh 组件设置
这里要做的最后一件事是创建人体模型主要材质的“材质实例”,以便这个敌人可以拥有一个独特的颜色,有助于将其与其他敌人类型区分开来。
让我们首先了解更多关于材质和“材质实例”的知识。
材质和材质实例
在继续下一个练习之前,我们需要先简要讨论一下材质和“材质实例”是什么,然后您才能使用这些资产并将它们应用到新的敌人角色上。尽管本书更加关注使用虚幻引擎 4 进行游戏开发的技术方面,但您仍然需要知道材质和“材质实例”是什么以及它们在视频游戏中的使用方式。
注意
有关材质的更多信息,请参考以下 Epic Games 文档:docs.unrealengine.com/en-US/Engine/Rendering/Materials/index.html
。
材质是一种可以应用于网格的资产类型,然后控制网格在游戏中的外观的资产。 “材质”编辑器让您控制最终视觉结果的许多部分,包括对“纹理”、“自发光”和“高光”等参数的控制。以下图显示了应用了M_UE4Man_Body
材质的默认人体模型骨骼网格:
图 12.28:默认的人体模型骨骼网格应用了基本材质
“材质实例”是“材质”的扩展,您无法访问或控制“材质实例”派生的基本“材质”,但您可以控制“材质”创建者向您公开的参数。许多参数可以从“材质实例”内部向您公开以供使用。
Unreal Engine 在“侧向滚动”模板项目中为我们提供了一个Material Instance
的示例,名为M_UE4Man_ChestLogo
,位于/Mannequin/Character/Materials/
目录中。以下图片显示了基于父材质M_UE4Man_Body
给Material Instance
提供的一组暴露参数。要重点关注的最重要的参数是名为BodyColor
的Vector
参数。您将在下一个练习中使用这个参数来为敌人角色提供独特的颜色:
图 12.29:M_UE4Man_ChestLogo Material Instance 资产的参数列表
练习 12.09:创建并应用敌人材质实例
现在您已经基本了解了材质和材质实例是什么,是时候从M_UE4ManBody
资产创建您自己的Material Instance
了。通过这个Material Instance
,您将调整BodyColor
参数,为敌人角色提供独特的视觉表现。让我们从创建新的Material Instance
开始。
以下步骤将帮助您完成此练习:
-
导航到
/Mannequin/Character/Materials
目录,找到默认人体模型角色M_UE4ManBody
使用的“材质”。 -
可以通过右键单击“材质”资产
M_UE4Man_Body
,然后左键单击“创建材质实例”选项来创建Material Instance
。将此资产命名为MI_Enemy01
。
图 12.30:任何材质都可以用来创建材质实例
在Enemy
文件夹中创建一个名为Materials
的新文件夹。左键单击并将Material Instance
拖放到/Enemy/Materials
目录中,将资产移动到这个新文件夹中:
图 12.31:重命名 Material Instance MI_Enemy
- 双击
Material Instance
,在左侧找到Details
面板。在那里,您会找到一个名为BodyColor
的Vector Parameter
属性。确保复选框被选中以启用此参数,然后将其值更改为红色。现在,Material Instance
应该呈现红色,如图所示:
图 12.32:现在,敌人的材质是红色
- 保存
Material Instance
资产,并导航回BP_Enemy01
蓝图。选择Mesh
组件,并更新“元素 0”材料参数为MI_Enemy
:
图 12.33:将新的 Material Instance 资产 MI_Enemy 分配给 Mesh 组件的材料的元素 0
- 现在,第一种敌人类型在视觉上已经准备就绪,并且已经准备好适用于下一章的 AI 的适当的
Blueprint
和动画蓝图资产:
图 12.34:最终敌人角色设置
完成此练习后,您现在已经创建了一个Material Instance
并将其应用于敌人角色,使其具有独特的视觉表现。
让我们通过进行一个简短的活动来结束本章,这将帮助您更好地理解使用Layered blend per bone
节点来混合动画,这是在之前的练习中使用的。
活动 12.01:更新混合权重
在“练习 12.06”结束时,“预览投掷动画”,您能够混合移动动画和“投掷”动画,以便它们可以同时播放而不会对彼此产生负面影响。结果是玩家角色在行走或奔跑时能够正确执行动画,同时在上半身执行“投掷”动画。
在这个活动中,您将尝试使用Layered blend per bone
节点的混合偏差值和参数,以更好地理解动画混合的工作原理。
以下步骤将帮助您完成此活动:
- 更新
Layered blend per bone
节点的Blend Weights
输入参数,以确保Throw
动画的附加姿势与基础移动姿势完全不混合。尝试在这里使用值,如0.0f
和0.5f
,以比较动画中的差异。
注意
确保在完成后将此值返回为1.0f
,以免影响您在上一个练习中设置的混合。
-
更新
Layered blend per bone
节点的设置,以更改受混合影响的骨骼,以便整个角色的身体都受到混合的影响。从MainCharacter_Skeleton
资产的骨骼层次结构中的根骨骼开始是个好主意。 -
保持上一步的设置不变,向分支过滤器添加一个新的数组元素,并在这个新的数组元素中,添加骨骼名称和混合深度值“-1.0f”,这样只有角色的左腿在混合 Throw 动画时才能继续正确地播放移动动画。
注意
完成此活动后,请确保将Layered blend per bone
节点的设置返回到第一个练习结束时设置的值,以确保角色动画不会丢失任何进展。
预期输出如下:
图 12.35:显示整个角色身体受影响的输出
图 12.36:当混合 Throw 动画时,左腿继续正确地播放移动动画
图 12.37:角色的右腿在移动时播放 Throw 动画的结尾
注意
此活动的解决方案可在以下链接找到:packt.live/338jEBx
。
在结束本活动之前,请将Layered blend per bone
设置返回到练习 12.05“使用上半身动画插槽混合动画”的最后设置的值。如果您不将这些值恢复到原始设置,那么下一章节中即将进行的练习和活动中的动画结果将不同。您可以手动设置回原始值,或者参考以下链接中具有这些设置的文件:packt.live/2GKGMxM
。
完成此活动后,您现在对动画混合的工作原理和混合权重如何影响Layered blend per bone
节点上的附加姿势对基础姿势的影响有了更深入的理解。
注意
在这个项目中,您还没有使用许多动画混合技术,强烈建议您研究这些技术,首先查看docs.unrealengine.com/en-US/Engine/Animation/AnimationBlending/index.html
上的文档。
总结
通过使用 C++类、蓝图和材质设置敌人,您已经准备好进入下一章节,在那里您将利用虚幻引擎 4 中的行为树等系统为这个敌人创建 AI。
从本章的练习和活动中,您学会了如何创建一个“动画蒙太奇”,允许播放动画。您还学会了如何在这个蒙太奇中设置一个动画插槽,以便为玩家角色的上半身进行分类。
接下来,您将学习如何使用“使用缓存姿势”节点来缓存状态机的输出姿势,以便在更复杂的动画蓝图中可以引用这个姿势的多个实例。然后,通过学习“每骨层混合”功能,您可以使用动画插槽将基本移动姿势与“投掷”动画的附加层进行混合。
最后,您将通过创建 C++类、蓝图和其他资产来组建敌人的基础,以便为下一章做好准备。敌人准备就绪后,让我们继续创建敌人的人工智能,以便它可以与玩家进行互动。