概观
在本章中,您将了解Anim Notifies
和Anim States
,它们可以在动画蒙太奇中找到。您将使用 C++ 编写自己的Anim Notify
代码,并在Throw
动画蒙太奇中实现该通知。最后,您将了解视觉和听觉效果,以及这些效果如何在游戏中使用。
到本章结束时,您将能够在蓝图和 C++ 中玩动画蒙太奇,并知道如何使用 C++ 和UWorld
类将对象繁殖到游戏世界中。游戏中的这些元素将被赋予音频和视觉成分,作为一层额外的润色,你的SuperSideScroller
玩家角色将能够投掷摧毁敌人的投射物。
在上一章中,你通过创建一个行为树来让敌人从你创建的BP_AIPoints
演员中随机选择点,从而在敌人的人工智能方面取得了很大的进步。这给了SuperSideScroller
游戏更多的生命,因为你现在可以有多个敌人在你的游戏世界里移动。此外,您还学习了虚幻引擎 4 中可用的不同工具,这些工具一起用于制造各种复杂程度的人工智能。这些工具包括Navigation Mesh
、行为树和黑板。
现在你有敌人在你的关卡中跑来跑去,你需要允许玩家用你在上一章结束时开始创造的玩家投射物来击败这些敌人。
在本章中,您将学习如何使用UAnimNotify
类在Throw
动画蒙太奇的特定帧中生成玩家投射物。您还将学习如何将这个新的通知添加到蒙太奇本身,以及如何将一个新的Socket
添加到主角色骨骼中,投射物将从该骨骼中产生。最后,您将学习如何使用Particle Systems
和SoundCues
为游戏增加一层视觉和听觉的润色。
让我们从学习Anim Notifies
和Anim Notify States
开始这一章。之后,你将通过创建你自己的UAnimNotify
职业来弄脏你的手,这样你就可以在Throw
动画蒙太奇中产生玩家投射物。
当创建精致复杂的动画时,动画师和程序员需要有一种方法在动画中添加自定义事件,以允许额外的效果、层和功能发生。虚幻引擎 4 中的解决方案是使用Anim Notifies
和Anim Notify States
。
Anim Notify
和Anim Notify State
的主要区别在于Anim Notify State
拥有Anim Notify
没有的三个截然不同的事件。这些事件分别是Notify Begin
、Notify End
、Notify Tick
,都可以在蓝图或者 C++ 中使用。当涉及到这些事件时,虚幻引擎 4 会确保以下行为:
Notify State
永远从Notify Begin Event
开始。Notify State
永远以Notify End Event
结束。Notify Tick Event
将永远发生在Notify Begin
和Notify End
事件之间。
然而,Anim Notify
是一个简单得多的版本,它只使用一个函数Notify()
,允许程序员向 notify 本身添加功能。它以开火而忘记的心态工作,这意味着你不需要担心在Notify()
事件的开始、结束或中间的任何地方会发生什么。正是由于Anim Notify
的这种简单性,并且由于我们不需要Anim Notify State
中包含的事件,我们将使用Anim Notify
来为超级侧滚游戏生成玩家抛射物。
在继续下面的练习之前,您将在 C++ 中创建自己的自定义Anim Notify
,让我们简单讨论一下虚幻引擎 4 默认提供的现有Anim Notifies
的一些示例。默认Anim Notifies
状态的完整列表可以在下面的截图中看到:
图 14.1:虚幻引擎 4 中提供的默认动画通知的完整列表
本章后面会用到两个Anim Notifies
:Play Particle Effect
和Play Sound
。让我们更详细地讨论这两个问题,以便您在使用时熟悉它们:
-
Play Particle Effect
: ThePlay Particle Effect
notify, as the name suggests, allows you to spawn and play a particle system at a certain frame of your animation. As shown in the following screenshot, you have options to change the VFX being used, such as updating thelocation
,rotation
, andscale
settings of the particle. You can even attach the particle to a specifiedSocket Name
if you so choose:
图 14.2:播放粒子效果通知的详细信息面板,允许您自定义粒子
注意
视觉效果,简称 VFX,是任何游戏的关键元素。在虚幻引擎 4 中,视觉效果是使用编辑器中名为级联的工具创建的。自虚幻引擎 4.20 版本以来,一个名为尼亚加拉的新工具作为免费插件被引入,以提高 VFX 的制作质量和流水线。你可以在这里了解更多关于尼亚加拉的信息。
游戏中使用的一个非常常见的例子是,当玩家行走或奔跑时,使用这种类型的通知在他们的脚下产生污垢或其他效果。能够指定这些效果在动画的哪一帧产生是非常强大的,并且允许你为你的角色创建令人信服的效果。
-
Play Sound
: ThePlay Sound
notify allows you to play aSoundcue
orSoundwave
at a certain frame of your animation. As shown in the following screenshot, you have options to change the sound being used, update itsvolume
andpitch
values, and even have the sound follow the owner of the sound via attaching it to a specifiedSocket Name
:
图 14.3:播放声音通知的详细信息面板,允许您自定义声音
很像Play Particle Effect
notify 给出的例子,Play Sound
notify 也可以常用于在角色移动时播放脚步声。通过控制动画时间线上可以播放声音的确切位置,可以创建可信的声音效果。
虽然您不会使用Anim Notify States
,但至少知道默认情况下您可以使用的选项仍然很重要,如下图截图所示:
图 14.4:虚幻引擎 4 中提供给你的默认动漫通知状态的完整列表
注意
动画序列中不可用的两个Notify
状态是蒙太奇通知窗口和禁用根动作状态,如前面的截图所示。有关通知的更多信息,请参考以下文档:docs . unrealengine . com/en-US/Engine/Animation/sequence/Notifies/index . html。
现在您对Anim Notify
和Anim Notify State
更加熟悉了,让我们继续下一个练习,您将在 C++ 中创建自己的自定义Anim Notify
,您将使用它来生成玩家投射物。
SuperSideScroller
游戏中玩家角色将拥有的主要进攻能力是玩家可以向敌人投掷的弹丸。在前一章中,你设置了投射物的框架和基本功能,但是现在,玩家没有办法使用它。为了使产卵或投掷的投射物令人信服,您需要创建一个自定义的Anim Notify
,然后将其添加到Throw
动画蒙太奇中。这个Anim Notify
会让玩家知道是时候产卵了。
执行以下操作创建新的UAnimNotify
类:
-
在虚幻引擎 4 中,导航至
File
选项,左键单击选择New C++ Class
选项。 -
在
Choose Parent Class
对话窗口中,搜索AnimNotify
和*,左键单击*AnimNotify
选项。然后,左键单击Next
选项来命名新类。 -
命名这个新类
Anim_ProjectileNotify
。命名后,左键点击选择Create Class
选项,虚幻引擎 4 在 Visual Studio 中重新编译并热重加载新类。bOnce Visual Studio 打开后,你将同时拥有头文件Anim_ProjectileNotify.h
和源文件Anim_ProjectileNotify.cpp
。 -
The
UAnimNotify
base class has one function that needs to be implemented inside your class:virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation);
当在时间线上点击 notify 时,会自动调用该函数。通过重写该函数,您将能够向 notify 添加自己的逻辑。该功能还允许您访问拥有通知的
Skeletal Mesh
组件和当前正在播放的动画序列。 -
Next, let's add the override declaration of this function to the header file. In the header file
Anim_ProjectileNotify.h
, add the following code underneath theGENERATED_BODY()
:public: virtual void Notify(USkeletalMeshComponent* MeshComp,UAnimSequenceBase* Animation) override;
既然已经在头文件中添加了函数,那么就该在
Anim_ProjectileNotify
源文件内部定义函数了。 -
Inside the
Anim_ProjectileNotify.cpp
source file, define the function and add aUE_LOG()
call that prints the text"Throw Notify"
, as shown in the following code:void UAnim_ProjectileNotify::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) { UE_LOG(LogTemp, Warning, TEXT("Throw Notify")); }
现在,您只需使用这个
UE_LOG()
调试工具,就可以知道当您在下一个练习中将这个 notify 添加到Throw
动画蒙太奇时,这个函数正在被正确调用。
在本练习中,您通过添加以下函数创建了实现自己的AnimNotify
类所需的基础:
Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
在该功能中,您使用UE_LOG()
打印输出日志中的自定义文本"Throw Notify"
,以便您知道该通知工作正常。
在本章的后面,您将更新这个函数,以便它调用逻辑来生成玩家投射物,但是首先,让我们将新的通知添加到Throw
动画蒙太奇中。
现在您已经有了Anim_ProjectileNotify
通知,是时候将此通知添加到Throw
动画蒙太奇中,以便它实际上对您有用。
在本练习中,您将把Anim_ProjectileNotify
添加到Throw
蒙太奇的时间线中,在动画的确切帧处,您将期望投射体产生。
完成以下步骤来实现这一点:
-
Back inside Unreal Engine, navigate to the
Content Browser
interface and go to the/MainCharacter/Animation/
directory. Inside this directory, double-click theAM_Throw
asset to open theAnimation Montage
editor.在
Animation Montage
编辑器的最底部,你会找到动画的时间线。默认情况下,当动画播放时,您会观察到红色条将沿着时间线移动。 -
Left-click this
red
bar and manually move it to the 22ndframe
, as close as you can, as shown in the following screenshot:图 14.5:红色条允许您手动将通知放置在时间线上的任何位置
Throw
动画的第 22 帧是投掷中你期望投射物产生并被玩家投掷的准确时刻。以下截图显示了投掷动画的框架,如在Persona
内的编辑器中所见:图 14.6:玩家投射物应该诞生的确切时刻
-
Now that you know the position on the timeline that the notify should be played, you can now right-click on the thin
red
line within theNotifies
timeline.这将显示一个弹出窗口,您可以在其中添加
Notify
或Notify State
。在某些情况下,Notifies
时间线可能会崩溃,很难找到;只需左键点击Notifies
即可在折叠和展开之间切换。 -
选择
Add Notify
,从提供的选项中,找到并选择Anim Projectile Notify
。 -
After selecting to add
Anim Projectile Notify
to the Notifies timeline, you will see the following:图 14.7:动画 _ 项目通知成功添加到投掷动画蒙太奇
-
在
Throw
动画蒙太奇时间轴上Anim_ProjectileNotify
通知到位后,保存蒙太奇。 -
如果
Output Log
窗口不可见,请通过导航至Window
选项重新启用该窗口,并将鼠标悬停在该窗口上以查看Developer Tools
。找到Output Log
和选项,左键点击启用。 -
现在,使用
PIE
,一旦进入游戏,使用鼠标左键开始播放Throw
蒙太奇。
在动画中添加 notify 的位置,您将看到调试日志文本Throw Notify
出现在输出日志中。
大家可能还记得第 12 章、动画融合与蒙太奇中,给玩家角色蓝图BP_SuperSideScroller_MainCharacter
增加了Play Montage
功能。为了在虚幻引擎 4 的上下文中学习 C++,在接下来的练习中,您将把这个逻辑从蓝图移动到 C++。这是为了让玩家角色的基本行为不会过于依赖蓝图脚本。
完成本练习后,您已成功将自定义Anim Notify
类Anim_ProjectileNotify
添加到Throw
动画蒙太奇中。这个通知是在你期望从玩家手里扔出一枚炮弹的精确时刻添加的。由于您在第 12 章、动画混合和蒙太奇中为玩家角色添加了蓝图逻辑,因此当使用鼠标左键调用InputAction
事件ThrowProjectile
时,您可以播放此Throw
动画蒙太奇。在从在蓝图中播放投掷动画蒙太奇过渡到在 C++ 中播放蒙太奇之前,让我们进一步讨论播放动画蒙太奇。
正如您在第 12 章、动画混合和蒙太奇中所学的,这些项目对于允许动画师将单个动画序列组合成一个完整的蒙太奇非常有用。通过将蒙太奇分成自己独特的部分,并添加粒子和声音通知,动画师和动画程序员可以制作复杂的蒙太奇集,处理动画的所有不同方面。
但是一旦动画蒙太奇准备好了,我们如何在一个角色身上播放这个蒙太奇呢?您已经熟悉了第一种方法,即通过蓝图。
在蓝图中,Play Montage
功能可供您使用,如下图所示:
图 14.8:蓝图中的播放蒙太奇功能
您已经使用该功能播放了AM_Throw
动画蒙太奇。该功能要求必须在Skeletal Mesh
组件上播放蒙太奇,并且要求播放动画蒙太奇。
其余参数是可选的,这取决于蒙太奇的工作方式。让我们快速了解一下这些参数:
Play Rate
:参数Play Rate
可以增加或减少动画蒙太奇的播放速度。为了加快播放速度,您可以增加该值;否则,您将降低较慢播放速度的值。Starting Position
:Starting Position
参数允许您设置蒙太奇时间线的开始位置,以秒为单位,蒙太奇将从该位置开始播放。例如,在有 3 秒时间线的动画蒙太奇中,您可以选择在1.0f
位置开始蒙太奇,而不是在0.0f
位置。Starting Section
:参数Starting Section
可以让你告诉动画蒙太奇从特定的部分开始。根据蒙太奇的设置方式,您可以为蒙太奇的不同部分创建多个部分。例如,霰弹枪武器重装动画蒙太奇将包括一个用于重装的初始移动部分,一个用于实际子弹重装的环形部分,以及一个用于重新装备武器以便准备再次开火的最终部分。
谈到Play Montage
功能的输出,您有几个不同的选项:
On Completed
:动画蒙太奇播放完毕,完全融合出来后,调用On Completed
输出。On Blend Out
:当动画蒙太奇开始融合时,调用On Blend Out
输出。这可能发生在Blend Out Trigger Time
期间,或者如果蒙太奇提前结束。On Interrupted
:当蒙太奇开始融合时,由于该蒙太奇被另一个试图在同一骨架上播放的蒙太奇打断,调用On Interrupted
输出。On Notify Begin & On Notify End
:如果您使用的是动画蒙太奇中Notifies
类别下的Montage Notify
选项,则会调用On Notify Begin
和On Notify End
输出。Montage
Notify
的名称通过Notify Name
参数返回。
在 C++ 端,你只需要知道一件事,那就是UAnimInstance::Montage_Play()
函数。该功能需要播放动画蒙太奇、回放蒙太奇的播放速率、EMontagePlayReturnType
类型的值、用于确定播放蒙太奇的开始位置的float
值以及用于确定播放该蒙太奇是否应该停止或中断所有蒙太奇的Boolean
值。
虽然您不会更改EMontagePlayReturnType
的默认参数,即EMontagePlayReturnType::MontageLength
,但了解该枚举器存在的两个值仍然很重要:
-
Montage Length
:值Montage Length
返回蒙太奇本身的长度,以秒为单位。 -
Duration
: TheDuration
value returns the play duration of the montage, which is equal to the length of the montage, divided by the play rate.注意
有关
UAnimMontage
类的更多详细信息,请参考以下文档:
在下一个练习中,您将了解更多关于播放动画蒙太奇的 C++ 实现。
现在你已经通过蓝图和 C++ 更好地理解了在虚幻引擎 4 中播放动画蒙太奇,是时候将播放Throw
动画蒙太奇的逻辑从蓝图迁移到 C++ 了。这一变化背后的原因是因为蓝图逻辑作为占位符方法被放置到位,以便您可以预览Throw
蒙太奇。这本书是一个更侧重于游戏开发的 C++ 指南,因此,学习如何在代码中实现这个逻辑是很重要的。
让我们首先从蓝图中移除逻辑,然后继续在玩家角色类中用 C++ 重新创建逻辑。
以下步骤将帮助您完成本练习:
-
导航到玩家角色蓝图
BP_SuperSideScroller_MainCharacter
,可以在以下目录中找到:/MainCharacter/Blueprints/
。双击该资产打开。 -
Inside this Blueprint, you will find the
InputAction ThrowProjectile
event and thePlay Montage
function that you created to preview theThrow
Animation Montage, as shown in the following screenshot. Delete this logic and then recompile and save the player character Blueprint:图 14.9:玩家角色蓝图中不再需要这个占位符逻辑
-
现在,使用
PIE
并使用鼠标左键尝试与玩家角色投掷。你会观察到玩家角色不再播放Throw
动画蒙太奇。让我们通过在 C++ 中添加所需的逻辑来解决这个问题。 -
在 Visual Studio 中打开玩家角色的头文件,
SuperSideScroller_Player.h
。 -
The first thing you need to do is create a new variable for the player character that will be used for the
Throw
animation. Add the following code under thePrivate
access modifier:UPROPERTY(EditAnywhere) class UAnimMontage* ThrowMontage;
现在你有了一个代表
Throw
动画蒙太奇的变量,是时候在SuperSideScroller_Player.cpp
文件中添加播放蒙太奇的逻辑了。 -
Before you can make the call to
UAnimInstance::Montage_Play()
, you need to add the followinginclude
directory to the existing list at the top of the source file in order to have access to this function:#include "Animation/AnimInstance.h"
从第九章**视听元素中我们知道,玩家角色已经有了一个叫
ThrowProjectile
的功能,只要按下鼠标左键就会被调用。提醒一下,这是 C++ 中绑定发生的地方://Bind pressed action ThrowProjectile to your ThrowProjectile function PlayerInputComponent->BindAction("ThrowProjectile", IE_Pressed, this, &ASuperSideScroller_Player::ThrowProjectile);
-
Update
ThrowProjectile
so that it playsThrowMontage
, which you set up earlier in this exercise. Add the following code to theThrowProjectile()
function. Then, we can discuss what is happening here:void ASuperSideScroller_Player::ThrowProjectile() { if (ThrowMontage) { bool bIsMontagePlaying = GetMesh()->GetAnimInstance()-> Montage_IsPlaying(ThrowMontage); if (!bIsMontagePlaying) { GetMesh()->GetAnimInstance()->Montage_Play(ThrowMontage, 2.0f); } } }
第一行是检查
ThrowMontage
是否有效;如果我们没有分配有效的动画蒙太奇,继续逻辑是没有意义的,并且在进一步的函数调用中使用空对象也是危险的,因为它可能导致崩溃。接下来,我们将声明一个新的布尔变量,称为bIsMontagePlaying
,它决定了ThrowMontage
是否已经在玩家角色的骨骼网格上玩了。进行此检查是因为Throw
动画蒙太奇不应在已经播放时播放;如果玩家反复按下鼠标左键,将导致动画中断。接下来是
If
语句,检查ThrowMontage
是否有效,蒙太奇是否没有播放。只要满足这些条件,继续前进,播放动画蒙太奇是安全的。 -
在
If
语句中,您告诉玩家的骨骼网格以1.0f
的播放速率播放ThrowMontage
动画蒙太奇。使用1.0f
值,以便动画蒙太奇以其预期的速度回放。大于1.0f
的值会使蒙太奇回放更快,而小于1.0f
的值会使蒙太奇回放更慢。您所了解的其他参数,如开始位置或EMontagePlayReturnType
参数,可以留在它们的defaults.Head
处。回到虚幻引擎 4 编辑器中,像过去一样重新编译代码。 -
代码重新编译成功后,导航回玩家角色蓝图
BP_SuperSideScroller_MainCharacter
,可以在以下目录找到:/MainCharacter/Blueprints/
。双击该资产打开。 -
在玩家角色的
Details
面板中,你现在会看到你添加的Throw Montage
参数。 -
Left-click on the drop-down menu for the
Throw Montage
parameter to find theAM_Throw
montage. Left-click again on theAM_Throw
option to select it for this parameter. Please refer to the following screenshot to see how the variable should be set up:

图 14.10:现在,投掷蒙太奇被分配了 AM_Throw 蒙太奇
- Recompile and save the player character blueprint. Then, use
PIE
to spawn the player character and use the left mouse button to playThrow Montage
. The following screenshot shows this in action:

图 14.11:玩家角色现在可以再次执行投掷动画
通过完成本练习,您已经学习了如何为玩家角色添加Animation Montage
参数,以及如何在 C++ 中播放蒙太奇。除了在 C++ 中播放Throw
动画蒙太奇之外,您还添加了通过检查蒙太奇是否已经在播放来控制Throw
动画播放频率的功能。这样做,可以防止玩家滥发Throw
输入,导致动画中断或不完整播放。
注意
尝试将Animation Montage
的播放速率从1.0f
设置为2.0f
,并重新编译代码。观察提高动画的播放速率如何影响玩家对动画的观感。
当涉及到向游戏世界中产卵对象时,实际上是代表你的级别的World
对象处理所述对象的创建。您可以将UWorld
类对象视为代表您的级别的单个顶级对象。
UWorld
类可以做很多事情,例如从世界中产生和移除对象,检测级别何时被更改或流入/流出,甚至执行线跟踪来帮助对象间检测。为了这一章,我们将集中讨论生成对象。
UWorld
类有SpawnActor()
函数的多种变体,这取决于您想要如何生成对象,或者您在生成该对象的上下文中可以通过哪些参数访问。要考虑的三个一致参数如下:
-
UClass
:UClass
参数只是你想要在其中繁殖的对象的类。 -
FActorSpawnParameters
: This is a struct of variables that give the spawned object more context and references to what has spawned it. For a list of all of the variables included within this struct, please refer to this article from the Unreal Engine 4 Community Wiki: https://www.ue4community.wiki/Actor#Spawn让我们简单讨论一下
FActorSpawnParameters
中包含的一个更关键的变量:Owner
演员。Owner
是催生这个对象的演员,在玩家角色和投射物的情况下,明确引用玩家作为投射物的拥有者将非常重要。这背后的原因,尤其是在这个游戏的背景下,就是你不希望弹丸与它的Owner
发生碰撞;你想让这个投射物完全忽略拥有者,这样它只能和敌人或者关卡环境碰撞。 -
Transform
:向世界产卵一个物体时,世界需要知道这个演员的location
、rotation
、scale
属性才能产卵。在SpawnActor()
功能的一些模板中,需要传递完整的Transform
,而在其他模板中,Location
和Rotation
需要单独传递。
在继续生成玩家投射物之前,让我们在玩家角色的Skeleton
中设置Socket
位置,以便在Throw
动画期间投射物可以从玩家的手中生成。
为了生成玩家投射物,你需要确定投射物将在哪个Transform
中生成,同时主要关注Location
和Rotation
,而不是Scale
。
在本练习中,您将在玩家角色的Skeleton
上创建一个新的Socket
,然后您可以在代码中引用该新的Socket
,以便获得产生投射物的位置。
让我们开始吧:
-
在虚幻引擎 4 中,导航到
Content Browser
界面,找到/MainCharacter/Mesh/
目录。 -
In this directory, find the
Skeleton
asset; that is,MainCharacter_Skeleton.uasset
. Double-click to open thisSkeleton
.为了确定投射物应该在哪里产卵的最佳位置,我们需要添加
Throw
动画蒙太奇作为骨骼的预览动画。 -
在
Details
面板中的Animation
类别下,找到Preview Controller
参数并选择Use Specific Animation
选项。 -
Next, left-click on the drop-down menu to find and select the
AM_Throw
Animation Montage from the list of available animations.现在,玩家角色的
Skeleton
将开始预览Throw
动画蒙太奇,如下图截图所示:图 14.12:玩家角色预览投掷动画蒙太奇
如果你回忆起练习 14.02 ,在投掷蒙太奇中添加了通知,你在
Throw
动画的第 22 帧添加了Anim_ProjectileNotify
。 -
Using the timeline at the bottom of the
Skeleton
editor, move thered
bar to as close to the 22nd frame as you can. Please refer to the following screenshot:图 14.13:在前面的练习中添加了 Anim_ProjectileNotify 的第 22 帧
在
Throw
动画的第 22 帧,玩家角色应该如下所示:图 14.14:在投掷动画蒙太奇的第 22 帧,角色的手处于释放投射物的位置
如你所见,玩家角色将从他们的右手投掷弹丸,所以新的
Socket
应该附着在的右手上。让我们看看玩家角色的骨架层次,如下图所示:图 14.15:在玩家角色骨骼层次中找到的右手骨
-
从骨骼层次中,找到
RightHand
骨骼。这可以在RightShoulder
骨骼层次结构下找到。 -
Right-click on the
RightHand
bone and left-click theAdd Socket
option from the list of options that appear. Name this socketProjectileSocket
.另外,当添加新的
Socket
时,整个RightHand
的层次将扩展,新的插座将出现在底部。 -
With
ProjectileSocket
selected, use theTransform
widget gizmo to position thisSocket
at the following location:Location = (X=12.961717,Y=25.448450,Z=-7.120584)
最终结果应该如下所示:
图 14.16:世界空间中投掷动画第 22 帧的投影插座最终位置。
如果你的小控件看起来有点不同,那是因为上图显示的是世界空间中的插座位置,而不是本地空间。
-
现在
ProjectileSocket
已经定位在你想要的位置,保存MainCharacter_Skeleton
资产。
随着这个练习的完成,你现在知道玩家投射物将从哪里产生了。既然你在预览中使用了Throw
动画蒙太奇,并且使用了相同的第 22 帧动画,你就知道这个位置会根据Anim_ProjectileNotify
什么时候开火来修正。
现在,让我们继续在 C++ 中生成玩家投射体。
现在你已经有了ProjectileSocket
并且现在有了一个可以产生玩家投射物的位置,让我们添加产生玩家投射物所需的代码。
在本练习结束时,您将拥有准备生成投射体的功能,并且它将准备从Anim_ProjectileNotify
类调用。
请执行以下步骤:
-
从 Visual Studio 中,导航到
SuperSideScroller_Player.h
头文件。 -
You need a class reference variable to the
PlayerProjectile
class. You can do this using the variable template class type known asTSubclassOf
. Add the following code to the header file, under thePrivate
access modifier:UPROPERTY(EditAnywhere) TSubclassOf<class APlayerProjectile> PlayerProjectile;
现在你已经准备好了变量,是时候声明你将用来产生投射体的函数了。
-
在 void
ThrowProjectile()
函数和Public
访问修饰符的声明下添加以下函数声明:void SpawnProjectile();
-
Before preparing the definition of the
SpawnProjectile()
function, add the followinginclude
directories to the list of includes in theSuperSideScroller_Player.cpp
source file:#include "PlayerProjectile.h" #include "Engine/World.h" #include "Components/SphereComponent.h"
您需要包括
PlayerProjectile.h
,因为它是为了引用射弹类的碰撞成分而需要的。接下来,使用Engine/World.h
include 是使用SpawnActor()
功能和访问FActorSpawnParameters
结构所必需的。最后,你需要使用Components/SphereComponent.h
包含来更新玩家投射物的碰撞部分,这样它就会忽略玩家。 -
Next, create the definition of the
SpawnProjectile()
function at the bottom of theSuperSideScroller_Player.cpp
source file, as shown here:void ASuperSideScroller_Player::SpawnProjectile() { }
这个函数需要做的第一件事就是检查
PlayerProjectile
类变量是否有效。如果这个对象无效,那么继续尝试并派生它是没有意义的。 -
Update the
SpawnProjectile()
function so that it looks as follows:void ASuperSideScroller_Player::SpawnProjectile() { if(PlayerProjectile) { } }
现在,如果
PlayerProjectile
对象有效,你会想要获得玩家当前存在的UWorld
对象,并确保这个世界有效,然后继续。 -
Update the
SpawnProjectile()
function to the following:void ASuperSideScroller_Player::SpawnProjectile() { if(PlayerProjectile) { UWorld* World = GetWorld(); if (World) { } } }
此时,您已经进行了安全检查,以确保
PlayerProjectile
和UWorld
都有效,因此现在可以安全地尝试生成射弹。首先要做的是声明一个FactorSpawnParameters
类型的新变量,并将玩家指定为所有者。 -
Add the following code within the most recent
if
statement so that theSpawnProjectile()
function looks like this:void ASuperSideScroller_Player::SpawnProjectile() { if(PlayerProjectile) { UWorld* World = GetWorld(); if (World) { FActorSpawnParameters SpawnParams; SpawnParams.Owner = this; } } }
如您之前所知,来自
UWorld
对象的SpawnActor()
函数调用将需要FActorSpawnParameters
结构作为衍生对象初始化的一部分。在玩家投射物的情况下,可以使用this
关键字作为投射物拥有者的玩家角色类的参考。当你在投射物产生后更新它的碰撞时,这将在这个函数的后面派上用场。 -
Next, you need to handle the
Location
andRotation
parameters of theSpawnActor()
function. Add the following lines under the latest line,SpawnParams.Owner = this
:FVector SpawnLocation = this->GetMesh()- >GetSocketLocation(FName("ProjectileSocket")); FRotator Rotation = GetActorForwardVector().Rotation();
在第一行中,您声明了一个名为
SpawnLocation
的新FVector
变量。该向量使用您在上一练习中创建的ProjectileSocket
插座的Socket
位置。从GetMesh()
函数返回的Skeletal Mesh
组件包含一个名为GetSocketLocation()
的函数,该函数将使用传入的FName
返回插座的位置;在这种情况下,名称ProjectileSocket
。在第二行,您正在声明一个名为
Rotation
的新FRotator
变量。该值被设置为玩家的前进向量,转换成一个Rotator
容器。这将确保旋转,或者换句话说,玩家投射物将产生的方向,将在玩家的前面,并且它将远离玩家。现在,所有产生射弹所需的参数都准备好了。
-
Add the following line underneath the code from the previous step:
```cpp
APlayerProjectile* Projectile = World- >SpawnActor<APlayerProjectile>(PlayerProjectile, SpawnLocation, Rotation, SpawnParams);
```
`World->SpawnActor()`函数将返回一个你试图在其中繁殖的类的对象;在这种情况下,`APlayerProjectile`。这就是为什么你要在实际产卵前添加`APlayerProjectile* Projectile`。然后,传递`SpawnLocation`、`Rotation`和`SpawnParams`参数,以确保射弹在您想要的位置和方式下产卵。
- Finally, you can add the player character to the array of actors to ignore on the player projectile by adding the following lines of code:
```cpp
if (Projectile)
{
Projectile->CollisionComp-> MoveIgnoreActors.Add(SpawnParams.Owner);
}
```
现在你已经有了射弹的参考,这一行正在更新`CollisionComp`组件,这样玩家,或者`SpawnParams.Owner`,就被添加到了`MoveIgnoreActors`阵中。这个演员阵列在移动时会被投射物的碰撞所忽略,这是完美的,因为这个投射物不应该与投掷它的玩家发生碰撞。
- 返回编辑器重新编译新添加的代码。代码编译成功后,本练习就完成了。
随着这个练习的完成,你现在有了一个功能,可以生成玩家角色内部分配的玩家投射类。通过为投射体和世界的有效性添加安全检查,您可以确保如果产生了一个对象,它就是有效世界中的有效对象。
接下来,您为UWorld SpawnActor()
功能设置适当的location
、rotation
和FActorSpawnParameters
参数,以确保玩家投射物在正确的位置产生,基于前一练习中的插座位置,具有适当的方向,使其远离玩家,并以玩家角色作为其Owner
。
现在,是时候更新Anim_ProjectileNotify
源文件,让它生成投射物了。
你已经准备好了允许玩家投射物产生的功能,但是你还没有在任何地方调用这个功能。回到练习 14.01 、创建一个 UAnim 通知类,你创建了Anim_ProjectileNotify
类,而在练习 14.02 、将通知添加到投掷蒙太奇中,你将此通知添加到Throw
动画蒙太奇中。
现在是时候更新Uanim
Notify
类了,这样它就可以调用SpawnProjectile()
函数了。
为此,请执行以下操作:
-
In Visual Studio, open the
Anim_ProjectileNotify.cpp
source file.在源文件中,您有以下代码:
#include "Anim_ProjectileNotify.h" void UAnim_ProjectileNotify::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) { UE_LOG(LogTemp, Warning, TEXT("Throw Notify")); }
-
从
Notify()
功能中移除UE_LOG()
线。 -
Next, add the following
include
lines underneathAnim_ProjectileNotify.h
:#include "Components/SkeletalMeshComponent.h" #include "SuperSideScroller/SuperSideScroller_Player.h"
您需要包含
SuperSideScroller_Player.h
头文件,因为它是调用您在上一个练习中创建的SpawnProjectile()
函数所必需的。我们还包含了SkeletalMeshComponent.h
,因为我们将在Notify()
函数中引用这个组件,所以最好也包含在这里。Notify()
函数传入一个对拥有的Skeletal Mesh
的引用,标记为MeshComp
。您可以使用骨骼网格,通过使用GetOwner()
功能并将返回的演员转换到您的SuperSideScroller_Player
类来获取对玩家角色的引用。我们接下来要做这个。 -
在
Notify()
功能中,增加以下一行代码:ASuperSideScroller_Player* Player = Cast<ASuperSideScroller_Player>(MeshComp->GetOwner());
-
现在您已经有了对玩家的引用,在调用
SpawnProjectile()
函数之前,您需要添加Player
变量的有效性检查。在前一步的代码行后添加以下代码行:if (Player) { Player->SpawnProjectile(); }
-
Now that the
SpawnProjectile()
function is being called from theNotify()
function, return to the editor to recompile and hot-reload the code changes you have made.在你能够使用
PIE
跑来跑去并投掷玩家投射物之前,你需要分配上一个练习中的Player Projectile
变量。 -
在
Content Browser
界面,导航到/MainCharacter/Blueprints
目录找到BP_SuperSideScroller_MainCharacter
蓝图。双击打开蓝图。 -
在
Details
面板中,Throw Montage
参数下面,你会发现Player Projectile
参数。左键点击该参数的下拉选项,找到BP_PlayerProjectile
。在该选项上左键单击将其分配给Player Projectile
变量。 -
重新编译并保存
BP_SuperSideScroller_MainCharacter
蓝图。 -
Now, use
PIE
and use the left mouse button. The player character will play theThrow
animation and the player projectile will spawn.
请注意,射弹是从你创建的`ProjectileSocket`功能中衍生出来的,它会远离玩家。下面的截图显示了这一点:

图 14.17:玩家现在可以投掷玩家射弹了
完成这个练习后,玩家现在可以投掷玩家射弹了。玩家的投射物,在当前状态下,对敌人无效,只是在空中飞行。在Throw
动画蒙太奇、Anim_ProjectileNotify
类和玩家角色之间花费了大量的移动部件来让玩家投掷弹丸。
在接下来的练习中,你将更新玩家投射物,使其摧毁敌人,并发挥额外的效果,如粒子和声音。
到目前为止,在这一章中,我们已经把大量的焦点放在了游戏世界中的演员的培养或创造上;玩家角色使用UWorld
职业来产生投射物。虚幻引擎 4 和它的基础Actor
类有一个默认的功能,你可以用它来摧毁或移除游戏世界中的一个演员:
bool AActor::Destroy( bool bNetForce, bool bShouldModifyLevel )
在/Source/Runtime/Engine/Actor.cpp
目录中找到Actor.cpp
源文件,就可以在 Visual Studio 中找到这个功能的完整实现。这个功能存在于从Actor
类延伸出来的所有类中,在虚幻引擎 4 的情况下,它存在于所有可以在游戏世界中产生或放置的类中。更明确地说,EnemyBase
和PlayerProjectile
两个班都是班的孩子,因此可以被消灭。
进一步查看AActor::Destroy()
功能,您会发现下面一行:
World->DestroyActor( this, bNetForce, bShouldModifyLevel );
我们将不进一步详细讨论UWorld
类为了毁灭一个演员到底做了什么,但重要的是要强调这样一个事实,即UWorld
类负责创造和毁灭世界内部的演员。请随意深入挖掘源代码引擎代码,以找到更多关于UWorld
类如何处理演员的毁灭和繁殖的信息。
现在你有了更多关于虚幻引擎 4 如何处理游戏世界中演员的毁灭和移除的上下文,我们将自己为敌人角色实现这一点。
Super SideScroller
游戏的主要玩法是玩家在关卡周围移动,用弹丸消灭敌人。在项目的这一点上,你已经处理了玩家的移动并产生了玩家投射物。然而,射弹还不能消灭敌人。
为了使这个功能到位,我们将从给EnemyBase
类添加一些逻辑开始,这样它就知道如何处理它的破坏,并在它与玩家投射物碰撞时将其从游戏中移除。
完成以下步骤来实现这一点:
-
首先,导航到 Visual Studio,打开
EnemyBase.h
头文件。 -
In the header file, create the declaration of a new function called
DestroyEnemy()
under thePublic
access modifier, as shown here:public: void DestroyEnemy();
确保这个函数定义写在类定义的
GENERATED_BODY()
下面。 -
将这些更改保存到头文件中,打开
EnemyBase.cpp
源文件,以便添加该功能的实现。 -
Below the
#include
lines, add the following function definition:void AEnemyBase::DestroyEnemy() { }
目前,这个功能将非常简单。您所需要做的就是从基础
Actor
类中调用继承的Destroy()
函数。 -
更新
DestroyEnemy()
功能,使其看起来像这样:void AEnemyBase::DestroyEnemy() { Destroy(); }
-
这个函数完成后,保存源文件并返回编辑器,这样您就可以重新编译并热重新加载代码。
随着这个练习的完成,敌人角色现在有了一个功能,只要你选择,就可以轻松处理演员的毁灭。DestroyEnemy()
功能是可以公开访问的,这样就可以被其他类调用,这在以后处理玩家抛射物的销毁时会派上用场。
你之所以创建自己独特的摧毁敌人行动者的功能,是因为你将在本章的后面使用这个功能,当 VFX 和 SFX 被玩家投射物摧毁时,将它们添加到敌人中。
在继续讨论敌人毁灭的抛光元素之前,让我们在玩家投射类中实现一个类似的功能,这样它也可以被摧毁。
现在敌人角色可以通过你在上一个练习中实现的新DestroyEnemy()
功能来处理被摧毁的情况,是时候对玩家投射物进行同样的操作了。
在这个练习结束时,玩家投射物将有自己独特的功能来处理自己的毁灭和从游戏世界中移除。
让我们开始吧:
-
在 Visual Studio 中,打开播放器弹丸的头文件;也就是
PlayerProjectile.h
。 -
在
Public
访问修饰符下,添加如下函数声明:void ExplodeProjectile();
-
接下来,打开玩家投射物的源文件;也就是
PlayerProjectile.cpp
。 -
Underneath the void
APlayerProjectile::OnHit
function, add the definition of theExplodeProjectile()
function:void APlayerProjectile::ExplodeProjectile() { }
目前,该功能的工作方式与上一练习中的
DestroyEnemy()
功能相同。 -
将继承的
Destroy()
函数添加到新的ExplodeProjectile()
函数中,比如:void APlayerProjectile::ExplodeProjectile() { Destroy(); }
-
这个函数完成后,保存源文件并返回编辑器,这样您就可以重新编译并热重新加载代码。
随着这个练习的完成,玩家投射物现在有了一个功能,无论你选择什么时候,它都可以轻松处理演员的毁灭。你需要创建自己独特的函数来处理摧毁玩家投射物行动者的原因与你创建DestroyEnemy()
函数的原因是一样的——当玩家投射物与另一个行动者碰撞时,你将在本章的后面使用这个函数向玩家投射物添加 VFX 和 SFX。
现在你已经有了在玩家投射体和敌人角色中实现Destroy()
功能的经验,是时候把这两个元素放在一起了。
在下一个活动中,你将启用玩家投射物,以便在敌人角色碰撞时摧毁他们。
现在玩家投射物和敌人角色都可以处理被摧毁的情况,是时候多走一步,让玩家投射物在碰撞时摧毁敌人角色了。
为此,请执行以下步骤:
-
将
EnemyBase.h
头文件的#include
语句添加到PlayerProjectile.cpp
源文件的顶部。 -
在 void
APlayerProjectile::OnHit()
函数中,创建一个新的AEnemyBase*
类型的变量,并调用这个变量Enemy
。 -
将
APlayerProjectile::OnHit()
函数的OtherActor
参数转换为AEnemyBase*
类,并将Enemy
变量设置为该转换的结果。 -
使用
if()
语句检查Enemy
变量的有效性。 -
如果
Enemy
有效,从这个Enemy
调用DestroyEnemy()
功能。 -
在
if()
块之后,调用ExplodeProjectile()
功能。 -
保存对源文件的更改,并返回到虚幻引擎 4 编辑器。
-
Use
PIE
and then use the player projectile against an enemy to observe the results.预期产出如下:
图 14.18:投掷弹丸的玩家
当射弹击中敌人时,敌人角色被摧毁,如下图所示:
图 14.19:射弹和敌人被摧毁
这项活动完成后,玩家投射物和敌人角色在相互碰撞时可以被摧毁。此外,每当另一个演员触发其APlayerProjectile::OnHit()
功能时,玩家投射物将被摧毁。
至此,Super SideScroller
游戏的一个主要元素已经完成:玩家投射物产卵,敌人与投射物碰撞时被消灭。你可以观察到消灭这些演员非常简单,玩家也不太感兴趣。
这就是为什么,在本章即将到来的练习中,你将分别学习更多关于视觉和听觉效果,或者 VFX 和 SFX 的知识。你也将实施这些关于敌人角色和玩家投射物的元素。
现在,敌人角色和玩家投射物都可以被摧毁,让我们简单讨论一下什么是 VFX 和 SFX,以及它们将如何影响这个项目。
注意
这个活动的解决方案可以在:https://packt.live/338jEBx找到。
粒子系统等视觉效果和声音提示等声音效果在视频游戏中发挥着重要作用。它们在系统、游戏机制甚至基本动作的基础上增加了一定程度的润色,使这些元素变得更有趣或更令人愉悦。
让我们从理解视觉效果开始,然后是音频效果。
视觉效果(VFX)
在虚幻引擎 4 的背景下,视觉效果由所谓的粒子系统组成。粒子系统由发射器组成,发射器由模块组成。在这些模块中,您可以使用材质、网格和数学模块来控制发射器的外观和行为。最终的结果可能是任何事情,从火把,或雪落下,到雨,灰尘,等等。
注意
您可以在这里了解更多信息:https://docs . unrealengine . com/en-US/Resources/Showcases/Effects/index . html。
音效(SFX)
在虚幻引擎 4 的上下文中,音频效果由声波和声音提示的组合组成:
-
声波是
.wav
可以导入虚幻引擎 4 的音频格式文件。 -
Sound Cues combine Sound Wave audio files with other nodes such as Oscillator, Modulator, and Concatenator to create unique and complex sounds for your game.
注意
您可以在这里了解更多信息:https://docs . unrealengine . com/en-US/Engine/Audio/sound clues/node reference/index . html。
我们以 Valve 开发的游戏传送门 2 为例。
在传送门 2 中,玩家使用传送门枪发射两个传送门:一个橙色和一个蓝色。这些入口允许玩家穿越间隙,将物体从一个位置移动到另一个位置,并利用其他简单的机制来创建复杂的谜题。这些传送门的使用,发射传送门的音效,以及这些传送门的视觉 VFX 让游戏玩起来更加愉快。如果你对游戏不熟悉,请在这里观看完整的演练:https://www.youtube.com/watch?v=ZFqk8aj4-PA。
注意
关于声音和声音设计的重要性的进一步阅读,请参考以下 Gamasutra 文章:https://www . Gamasutra . com/view/news/318157/7 _ games _ worthy _ study _ for _ theory _ sound _ design . PHP。
在虚幻引擎 4 的背景下,VFX 最初是使用名为 Cascade 的工具创建的,艺术家可以结合使用materials
、static meshes
和math
为游戏世界创建有趣且令人信服的效果。本书不会深入探讨这个工具是如何工作的,但是你可以在这里找到关于 Cascade 的信息:https://www . UE4 community . wiki/Legacy/Introduction _ to _ Particles _ in _ UE4 - 2 - Cascade _ at _ a _ sketch。
在引擎的更新版本中,从 4.20 更新开始,有一个名为尼亚加拉的插件可以创建视觉效果。Niagara
与 Cascade 不同,它使用了一个类似于蓝图的系统,在这个系统中,您可以可视化地编写效果的行为脚本,而不是使用一组具有预定义行为的预设模块。你可以在这里找到更多关于尼亚加拉的信息。
在第九章、视听元素中,您了解到了更多关于音频以及音频在虚幻引擎 4 中的处理方式。现在需要知道的是虚幻引擎 4 使用.wav
文件格式将音频导入引擎。从那里,您可以直接使用.wav
文件,在编辑器中称为声波,或者您可以将这些资产转换为声音提示,这允许您在声波之上添加音频效果。
最后,在接下来的练习中,有一个重要的课程需要了解,这个课程叫做UGameplayStatics
。这是虚幻引擎中的一个静态类,可以从 C++ 和蓝图中使用,它提供了各种有用的游戏相关功能。在接下来的练习中,您将使用以下两个功能:
UGameplayStatics::SpawnEmitterAtLocation
UGameplayStatics:SpawnSoundAtLocation
这两种功能的工作方式非常相似;它们都需要一个World
上下文对象来产生效果,粒子系统或音频来产生效果,以及产生效果的位置。在下一个练习中,你将使用这些功能为敌人产生摧毁效果。
在本练习中,您将向本章和练习中包含的项目添加新内容。这包括粒子 VFX 和声音 SFX,以及它们所需的所有资产。然后,你将更新EnemyBase
类,这样它就可以使用音频和粒子系统参数来添加当敌人被玩家投射物摧毁时所需的抛光层。
在这个练习结束时,你将有一个敌人,当它与玩家的投射物碰撞时,会在视觉和听觉上被摧毁。
让我们开始吧:
-
首先,我们需要从
Action RPG
项目中迁移特定的资产,这可以在Unreal Engine Launcher
的Learn
选项卡中找到。 -
From
Epic Games Launcher
, navigate to theLearn
tab and, under theGames
category, you will findAction RPG
:注意
在本章后面的练习中,您将从 Action RPG 项目中获取额外的资产,因此您应该保持该项目打开,以避免重复打开该项目。
-
左键单击
Action RPG
游戏项目,然后左键单击Create Project
选项。 -
从这里,选择引擎版本 4.24,并选择将项目下载到哪个目录。然后,左键点击
Create
按钮开始安装项目。 -
一旦
Action RPG
项目下载完毕,导航至Epic Games Launcher
的Library
选项卡,找到My Projects
部分下的ActionRPG
。 -
双击
ActionRPG
项目,在虚幻引擎编辑器中打开。 -
在编辑器中,在
Content Browser
界面找到A_Guardian_Death_Cue
音频资产。右键点击该资产,选择Asset Actions
,然后选择Migrate
。 -
选择
Migrate
后,会出现A_Guardian_Death_Cue
中引用的所有资产。这包括所有音频类和声波文件。从Asset Report
对话窗口中选择OK
。 -
接下来,您需要导航到您的
Super SideScroller
项目的Content
文件夹,然后左键单击Select Folder
。 -
一旦迁移过程完成,您将在编辑器中收到通知,告知您迁移已成功完成。
-
Do the same migration steps for the
P_Goblin_Death
VFX asset. The two primary assets you are adding to the project are as follows:
```cpp
A_Guardian_Death_Cue
P_Goblin_Death
```
`P_Goblin_Death`粒子系统资源引用包含在`Effects`目录中的附加资源,如材料和纹理,而`A_Guardian_Death_Cue`引用包含在`Assets`目录中的附加声波资源。
- After migrating these folders into your
Content
directory, open the Unreal Engine 4 editor of yourSuperSideScroller
project to find the new folders included in your project'sContent Browser
.
你将用于摧毁敌人角色的粒子叫做`P_Goblin_Death`,可以在`/Effects/FX_Particle/`目录中找到。你用来摧毁敌方角色的声音叫做`A_Guardian_Death_Cue`,可以在`/img/Sounds/Creatures/Guardian/`目录中找到。现在您需要的资产已经导入到编辑器中,让我们继续代码。
- 打开 Visual Studio,导航到敌方基类的头文件;也就是
EnemyBase.h
。 - 添加以下
UPROPERTY()
变量。这将代表敌人被消灭时的粒子系统。确保这是在Public
访问修饰符
```cpp
UPROPERTY(EditAnywhere, BlueprintReadOnly)
class UParticleSystem* DeathEffect;
```
下声明的
- Add the following
UPROPERTY()
variable. This will represent the sound for when the enemy is destroyed. Make sure this is declared under thePublic
access modifier:
```cpp
UPROPERTY(EditAnywhere, BlueprintReadOnly)
class USoundBase* DeathSound;
```
定义了这两个属性之后,让我们继续前进,添加在敌人被消灭时产生和使用这些效果所需的逻辑。
- Inside the source file for the enemy base class,
EnemyBase.cpp
, add the following includes for theUGameplayStatics
andUWorld
classes:
```cpp
#include "Kismet/GameplayStatics.h"
#include "Engine/World.h"
```
当敌人被消灭时,你将使用`UGameplayStatics`和`UWorld`职业将声音和粒子系统繁殖到世界中。
- 在
AEnemyBase::DestroyEnemy()
功能中,你有一行代码:
```cpp
Destroy();
```
- Add the following line of code above the
Destroy()
function call:
```cpp
UWorld* World = GetWorld();
```
在尝试生成粒子系统或声音之前,有必要定义`UWorld`对象,因为需要`World`上下文对象。
- 接下来,使用
if()
语句检查刚刚定义的World
对象的有效性:
```cpp
if(World)
{
}
```
- Within the
if()
block, add the following code to check the validity of theDeathEffect
property, and then spawn this effect using theSpawnEmitterAtLocation
function fromUGameplayStatics
:
```cpp
if(DeathEffect)
{
UGameplayStatics::SpawnEmitterAtLocation(World, DeathEffect, GetActorTransform());
}
```
在尝试派生或操作对象之前,您应该确保对象是有效的,这一点怎么强调都不为过。通过这样做,您可以避免引擎崩溃。
- After the
if(DeathEffect)
block, perform the same validity check of theDeathSound
property and then spawn the sound using theUGameplayStatics::SpawnSoundAtLocation
function:
```cpp
if(DeathSound)
{
UGameplayStatics::SpawnSoundAtLocation(World, DeathSound, GetActorLocation());
}
```
在调用`Destroy()`函数之前,您需要检查`DeathEffect`和`DeathSound`属性是否都有效,如果有效,使用适当的`UGameplayStatics`函数生成这些效果。这确保了无论任何一个属性是否有效,敌人角色仍然会被摧毁。
- 现在
AEnemyBase::DestroyEnemy()
函数已经被更新来产生这些效果,返回到虚幻引擎 4 编辑器来编译和热重载这些代码变化。 - 在
Content Browser
界面内,导航至/Enemy/Blueprints/
目录。双击BP_Enemy
资产将其打开。 - 在敌人蓝图的
Details
面板中,你会发现Death Effect
和Death Sound
属性。在Death Effect
属性的下拉列表中左键单击,找到P_Goblin_Death
粒子系统。 - 接下来,在
Death Effect
参数下,左键单击Death Sound
属性下拉列表中的,找到A_Guardian_Death_Cue
声音提示。 - 现在这些参数已经更新并分配了正确的效果,编译并保存敌人蓝图。
- Using
PIE
, spawn the player character and throw a player projectile at an enemy. If an enemy is not present in your level, please add one. When the player projectile collides with the enemy, the VFX and SFX you added will play, as shown in the following screenshot:

图 14.20:现在,敌人在荣耀的火焰中爆炸并被摧毁
随着这个练习的完成,当敌人角色被玩家投掷物摧毁时,它会播放一个粒子系统和一个声音提示。这给游戏增加了一层很好的抛光,并且它让消灭敌人变得更令人满意。
在下一个练习中,您将为播放器投射体添加一个新的粒子系统和音频组件,以便它在空中飞行时看起来和听起来更有趣。
在当前状态下,玩家投射物按照预期的方式运行;它在空中飞行,与游戏世界中的物体碰撞,然后被摧毁。然而,从视觉上看,玩家投射物只是一个具有普通白色纹理的球。
在本练习中,您将通过添加粒子系统和音频组件来为玩家投掷物添加一层抛光,以便投掷物使用起来更愉快。
完成以下步骤来实现这一点:
-
Much like the previous exercises, we will need to migrate assets from the
Action RPG
project to ourSuper SideScroller
project. Please refer to Exercise 14.09, Adding Effects When the Enemy Is Destroyed, on how to install and migrate assets from theAction RPG
project.您要添加到项目中的两个主要资产如下:
P_Env_Fire_Grate_01 A_Ambient_Fire01_Cue
P_Env_Fire_Grate_01
粒子系统资产引用了包含在Effects
目录中的附加资产,例如材质和纹理,而A_Ambient_Fire01_Cue
引用了包含在Assets
目录中的附加声波和声音衰减资产。你将用于玩家投射物的粒子叫做
P_Env_Fire_Grate_01
,可以在/Effects/FX_Particle/
目录中找到。这与上一练习中P_Goblin_Death
VFX 使用的目录相同。你将用于玩家投射的声音叫做A_Ambient_Fire01_Cue
,可以在/img/Sounds/Ambient/
目录中找到。 -
在
Action RPG
项目的Content Browser
界面中右键单击这些资产中的每一个,选择Asset Actions
,然后选择Migrate
。 -
Make sure to choose the directory of the
Content
folder for yourSuper SideScroller
project before confirming the migration.现在所需的资产已经迁移到我们的项目中,让我们继续创建玩家投射类。
-
打开 Visual Studio,导航到玩家投射类的头文件;也就是
PlayerProjectile.h
。 -
在
Private
访问修饰符下,在UStaticMeshComponent* MeshComp
类组件的声明下,添加以下代码来声明玩家投射体的新音频组件:UPROPERTY(VisibleDefaultsOnly, Category = Sound) class UAudioComponent* ProjectileMovementSound;
-
Next, add the following code underneath the declaration of the audio component in order to declare a new particle system component:
UPROPERTY(VisibleDefaultsOnly, Category = Projectile) class UParticleSystemComponent* ProjectileEffect;
这些效果将成为玩家投射物的组成部分,而不是使用蓝图中可以定义的属性,比如在敌人角色类中。这是因为这些效果应该附加到射弹的碰撞部分,以便在投掷时随着射弹穿过水平面而移动。
-
With these two components declared in the header file, open the source file for the player projectile and add the following includes to the list of
include
lines at the top of the file:#include "Components/AudioComponent.h" #include "Engine/Classes/Particles/ParticleSystemComponent.h"
您需要引用音频组件和粒子系统类,以便使用
CreateDefaultSubobject
功能创建这些子对象,并将这些组件附加到RootComponent
。 -
添加以下行以创建
ProjectileMovementSound
组件的默认子对象,并将该组件附加到RootComponent
:ProjectileMovementSound = CreateDefaultSubobject<UAudioComponent> (TEXT("ProjectileMovementSound")); ProjectileMovementSound->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepWorldTransform);
-
接下来,添加以下行,以便为
ProjectileEffect
组件创建默认子对象,并将该组件附加到RootComponent
:ProjectileEffect = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("Projectile Effect")); ProjectileEffect->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepWorldTransform);
-
现在您已经创建、初始化了这两个组件并将其附加到
RootComponent
上,请返回到虚幻引擎 4 编辑器重新编译并热重新加载这些代码更改。 -
From the
Content Browser
interface, navigate to the/MainCharacter/Projectile/
directory. Find theBP_PlayerProjectile
asset and double-click it to open the Blueprint.
在`Components`选项卡中,您将找到使用前面的代码添加的两个新组件。观察这些组件是否连接到`CollisionComp`组件,也称为`RootComponent`。
- Left-click to select the
ProjectileEffect
component and, within theDetails
panel, assign theP_Env_Fire_Grate_01
VFX asset to this parameter, as shown in the following screenshot:

图 14.21:现在,您可以将 P_Env_fire_Grate_01 VFX 资产应用到之前添加的粒子系统组件中
- Before assigning the audio component, let's adjust the
Transform
of theProjectileEffect
VFX asset. Update theRotation
andScale
parameters of theTransform
for the VFX so that they match what is shown in the following screenshot:

图 14.22:粒子系统组件的更新变换,使其更好地适应射弹
- Navigate to the
Viewport
tab within the Blueprint to view these changes to theTransform
.ProjectileEffect
should look as follows:

图 14.23:现在,火 VFX 已经被适当地缩放和旋转
- 现在 VFX 已经设置好了,左键单击
ProjectileMovementSound
组件并将A_Ambient_Fire01_Cue
分配给该组件。 - Save and recompile the
BP_PlayerProjectile
Blueprint. UsePIE
and observe that when you throw the projectile, it now shows the VFX asset and plays the assigned sound:

图 14.24:玩家投掷物在空中飞行时现在有一个 VFX 和一个 SFX
完成这个练习后,玩家投射物现在有了一个 VFX 和一个 SFX,当它在空中飞行时可以一起玩。这些元素使射弹栩栩如生,使射弹使用起来更加有趣。
由于 VFX 和 SFX 是作为射弹的部件制造的,所以当射弹被摧毁时,它们也会被摧毁。
在下一个练习中,您将在Throw
动画蒙太奇中添加粒子通知和声音通知,以便在玩家投掷玩家投射物时提供更多的冲击力。
到目前为止,您已经通过 C++ 实现了游戏的波兰元素,这是一种有效的实现方式。为了丰富多彩,并扩展您对虚幻引擎 4 工具集的了解,本练习将指导您如何使用动画蒙太奇中的通知在动画中添加粒子系统和音频。我们开始吧!
与前面的练习非常相似,我们需要将资产从Action RPG
项目迁移到我们的Super SideScroller
项目。请参考练习 14.09 、消灭敌人时添加效果,了解如何从Action RPG
项目安装和迁移资产。请执行以下步骤:
-
Open the
ActionRPG
project and navigate to theContent Browser
interface.您要添加到项目中的两个主要资产如下:
P_Skill_001 A_Ability_FireballCast_Cue
P_Skill_001
粒子系统资产引用材质和纹理等包含在Effects
目录中的附加资产,而A_Ability_FireballCast_Cue
引用包含在Assets
目录中的附加声波资产。投掷弹丸时你将为玩家使用的粒子叫做
P_Skill_001
,可以在/Effects/FX_Particle/
目录中找到。这与之前练习中P_Goblin_Death
和P_Env_Fire_Grate_01
VFX 资产使用的目录相同。你用来破坏敌人角色的声音叫做A_Ambient_Fire01_Cue
,可以在/img/Sounds/Ambient/
目录中找到。 -
在
Action RPG
项目的Content Browser
界面中右键单击这些资产中的每一个,选择Asset Actions
,然后选择Migrate
。 -
Make sure to choose the directory of the
Content
folder for yourSuper SideScroller
project before confirming the migration.现在您需要的资产已经迁移到您的项目中,让我们继续向
AM_Throw
资产添加所需的通知。在继续本练习之前,请确保返回到您的Super SideScroller
项目。 -
从
Content Browser
界面,导航至/MainCharacter/Animation/
目录。找到AM_Throw
资产,双击打开。 -
在
Animation Montage
编辑器中心的预览窗口下方,找到Notifies
部分。这就是你在本章前面添加Anim_ProjectileNotify
的部分。 -
To the right of the
Notifies
track, you will find a+
sign that allows you to use additional notify tracks. Left-click to add a new track, as shown in the following screenshot:图 14.25:为了在添加多个通知时保持事情有条不紊,在时间线中添加多个轨道是很有用的
-
在与
Anim_ProjectileNotify
相同的帧中,右键单击您在上一步中创建的新轨迹内的。从Add Notify
列表中,左键单击选择Play Particle Effect
。 -
Once created, left-click to select the new notify and access its
Details
panel. InDetails
, add theP_Skill_001
VFX asset to theParticle System
parameter.在你添加了这个新的 VFX 之后,你会注意到 VFX 几乎被放在底部,玩家角色的脚在那里,但不是你想要的地方。这个 VFX 应该直接放在地板上,或者角色的底部。下面的截图展示了这个位置:
图 14.26:粒子通知的位置不在地面上
为了解决这个问题,你需要给玩家角色骨架增加一个新的
Socket
。 -
导航至
/MainCharacter/Mesh/
目录。双击MainCharacter_Skeleton
资产打开。 -
从左侧的
Skeleton
骨骼层次中,右键单击Hips
骨骼上的,左键单击选择Add Socket
选项。命名这个新插座EffectSocket
。 -
Left-click this socket from the hierarchy of bones in order to view its current location. By default, its location is set to the same position as the
Hips
bone. The following screenshot shows this location:

图 14.27:这个插座的默认位置在玩家骨架的中心
使用`Transform`小控件,移动`EffectSocket`的位置,使其位置设置如下:
```cpp
(X=0.000000,Y=100.000000,Z=0.000000)
```
这个位置会更靠近地面和玩家角色的脚。最终位置可以在下面的截图中看到:

图 14.28:将插座位置移动到玩家骨架的底部
- 现在你已经有了粒子通知的位置,回到
AM_Throw
动画蒙太奇。 - Within the
Details
panel of thePlay Particle Effect
notify, there is theSocket Name
parameter. UseEffectSocket
as the name.
注意
如果`EffectSocket`没有通过自动完成功能出现,关闭并重新打开动画蒙太奇。一旦重新打开,`EffectSocket`选项应该会出现。
- Lastly, the scale of the particle effect is a little too big, so adjust the scale of the projectile so that its value is as follows:
```cpp
(X=0.500000,Y=0.500000,Z=0.500000)
```
现在,当粒子效果通过此通知播放时,其位置和比例将是正确的,如下所示:

图 14.29:粒子现在在玩家角色骨架的底部播放
- 若要添加
Play Sound
通知,请在Notifies
时间线部分添加新的轨道;你现在应该总共有三个。 - On this new track, and at the same frame position as both the
Play Particle Effect
andAnim_ProjectileNotify
notifies, right-click and select thePlay Sound
notify from theAdd Notify
selection. The following screenshot shows where to find this notify:

图 14.30:播放声音通知您在本章前面已经了解到
- 接下来,左键点击选择
Play Sound
通知并进入其Details
面板。 - From the
Details
panel, find theSound
parameter and assignA_Ability_FireballCast_Cue
.
有了指定的声音,当`Throw`动画回放时,你会看到 VFX 的戏,你会听到声音。`Notifies`轨迹应如下所示:

图 14.31:投掷动画蒙太奇时间线上的最终通知设置
- 保存
AM_Throw
资产,使用PIE
投掷玩家弹丸。 - Now, when you throw the projectile, you will see the particle notify play the
P_Skill_001
VFX and you will hear theA_Ability_FireballCast_Cue
SFX. The result will look as follows:

图 14.32:现在,当玩家投掷投射物时,会打出强大的 VFX 和 SFX
完成最后一个练习后,当玩家投掷投射物时,玩家现在可以玩强大的 VFX 和 SFX。这给了投掷动画更多的力量,感觉像是玩家角色在使用大量的能量来投掷弹丸。
在接下来的最后一个活动中,你将使用你从最后几个练习中获得的知识,在玩家投射物被摧毁时,将 VFX 和 SFX 添加到其中。
在这最后一个活动中,你将使用你从给玩家投掷物和敌人角色添加 VFX 和 SFX 元素中获得的知识来为投掷物与物体碰撞时创建爆炸效果。我们增加这种额外爆炸效果的原因是为了在炮弹与环境物体碰撞时摧毁炮弹的基础上增加一层抛光。如果玩家的抛射物击中一个物体并消失而没有玩家的任何听觉或视觉反馈,这看起来会很尴尬和不合适。
您将向玩家投射物添加粒子系统和声音提示参数,并在投射物与对象碰撞时产生这些元素。
执行以下步骤以实现预期的输出:
-
在
PlayerProjectile.h
头文件中,添加一个新的粒子系统变量和一个新的声音基础变量。 -
命名粒子系统变量
DestroyEffect
,命名声音基础变量DestroySound
。 -
在
PlayerProjectile.cpp
源文件中,将UGameplayStatics
的包含添加到包含列表中。 -
更新
APlayerProjectile::ExplodeProjectile()
功能,使其现在同时生成DestroyEffect
和DestroySound
对象。返回到虚幻引擎 4 编辑器,重新编译新的 C++ 代码。在BP_PlayerProjectile
蓝图中,将默认情况下已经包含在您的项目中的P_Explosion
VFX 指定给射弹的Destroy Effect
参数。 -
将默认情况下已经包含在项目中的
Explosion_Cue
SFX 分配给投射体的Destroy Sound
参数。 -
保存并编译玩家投射物蓝图。
-
Use
PIE
to observe the new player projectile's destruction VFX and SFX.预期产出如下:
图 14.33:射弹 VFX 和 SFX
完成这项活动后,您现在有了在游戏中添加波兰元素的经验。您不仅通过 C++ 代码添加了这些元素,还通过虚幻引擎 4 中的其他工具添加了元素。在这一点上,你有足够的经验将粒子系统和音频添加到你的游戏中,而不必担心如何实现这些功能。
注意
这个活动的解决方案可以在:https://packt.live/338jEBx找到。
在这一章中,你学到了很多关于视觉和听觉效果在游戏开发世界中的重要性。使用 C++ 代码和通知的组合,您能够为玩家投射体和敌人角色碰撞带来游戏功能,并通过添加 VFX 和 SFX 为该功能增加一层润色。除此之外,你还在虚幻引擎 4 中学习了对象是如何产生和毁灭的。
此外,您还从蓝图和 C++ 中了解了动画蒙太奇是如何播放的。通过将播放Throw
动画蒙太奇的逻辑从蓝图迁移到 C++,您学习了这两种方法是如何工作的,以及如何在您的游戏中使用这两种实现。
通过使用 C++ 添加新的动画通知,您可以将此通知添加到Throw
动画蒙太奇中,这允许玩家生成您在上一章中创建的玩家投射体。通过使用UWorld->SpawnActor()
功能,并为玩家骨骼添加一个新的插座,您可以在Throw
动画的精确帧和您想要的精确位置生成玩家投射物。
最后,您学习了如何在投掷动画蒙太奇中使用Play Particle Effect
和Play Sound
通知将 VFX 和 SFX 添加到玩家投掷物中。这一章给了你机会去了解虚幻引擎 4 中存在的不同方法,当你在游戏中使用 VFX 和 SFX 的时候。
现在玩家可以投掷投射物并摧毁敌人角色,是时候为游戏实施最后一套机制了。在下一章中,你将创建玩家可以收集的收藏品,你还将为玩家创建一个能在短时间内提高玩家运动力学的动力。