原文:Unreal Engine 4 Blueprints Tutorial
作者:Tommy Tran
译者:Shuchang Liu
在本篇教程里,你将学会如何用蓝图系统创建玩家角色,设置输入,并编写角色通过触碰,收集道具的游戏逻辑。
蓝图是Unreal Engine 4的一套可视化脚本系统,通过蓝图可以快速制作游戏原型。你不再需要一行行的编写代码,取而代之的是可视化操作:拖拽节点,在UI里设置节点参数,给节点进行连线。
除了作为一款非常便捷的原型工具,蓝图也降低了非开发人员制作游戏逻辑的门槛。
在本篇教程,你将使用蓝图来:
- 设置垂直视角摄像机
- 创建具备基本移动的玩家控制器角色
- 设置玩家输入
- 创建可被角色触碰并收集的道具
注意:本篇教程假定你已了解Unreal Engine 4的基本操作界面。你也应该熟悉基本的蓝图概念,比如组件和节点。如果你需要复习以上内容,点击入门教程。
注意:本篇教程只是Unreal Engine 4系列教程的其中一篇:
- Part 1:入门
- Part 2:蓝图
- Part 3:材质
- Part 4:UI
- Part 5:制作简单游戏
- Part 6:动画
- Part 7:音频
- Part 8:粒子系统
- Part 9:AI
- Part 10:制作简单FPS游戏
起步入门
下载示例项目并解压。进入项目文件夹,双击BananaCollector.uproject打开项目。
注意:如果你看到了项目是由较早的引擎版本创建的提示,这很正常(因为引擎经常更新版本)。你可以选择以拷贝副本的形式打开,也可以直接转换项目版本打开。
你可以看到以下的场景。我们创建的角色会在场景内移动并收集道具。
为了方便起见,我已经给项目文件做了文件夹分类,如下图所示:
你可以点击红框处按钮来显示/隐藏侧边栏列表。
创建玩家
点击进入Content Browser界面的Blueprints文件夹。点击Add New按钮并选择Blueprint Class。
由于玩家需要能够获取输入,Pawn类比较适合。在弹出窗口选择创建Pawn,将其命名为BP_Player。
注意:选择Character类创建也可以,它还默认引入了移动组件。不过,既然我们想要自己动手实现移动,Pawn类已经足够了。
关联摄像机
摄像机是玩家观察游戏世界的方式。我们要创建一个垂直视角观察玩家角色的摄像机。
在Content Browser 双击BP_Player打开蓝图编辑器。
为了创建摄像机,在Components面板点击Add Component并选择Camera。
为了让摄像机视角自顶向下,我们需要先把它放在玩家角色上方。选中摄像机组件,再切换到Viewport页签。
按下W键激活移动操作杆,将摄像机移动到(-1100, 0, 2000)。或者,你可以在Location字段输入坐标点来移动摄像机。
如果摄像机被移出界面外,按下F键就可以重新定位看到摄像机。
接着,按下E键激活旋转操作杆,将摄像机沿Y轴旋转-60度。
玩家表示
我们用一个红色方块来表示玩家,所以需要使用Static Mesh组件进行展示。
首先,在Components面板左键点击空白处取消选中Camera组件。否则,新加的组件会成为摄像机的子节点。
点击Add Component并选择Static Mesh。
为了展示红色方块,选择Static Mesh组件,随后在Details页签Static Mesh属性点击下拉框,选择SM_Cube。
你在关卡场景里应该就能看到如下画面(如果看不到,你可以在Viewport窗口按下F键聚焦画面)
现在,是时候在关卡里动态生成玩家了。点击Compile按钮并回到主编辑器。
生成玩家
为了让玩家能够控制角色,你需要明确两件事:
- 玩家所要控制的Pawn类
- 角色应该在哪里生成
你可以通过创建Game Mode类(游戏模式类)来实现第一点。
创建游戏模式
游戏模式是一个类,用于控制玩家进入游戏的方式。比如,在多人游戏里,可以使用游戏模式,决定每个玩家在哪里生成。更重要的是,游戏模式决定了玩家使用哪个角色。
在Content Browser里点击进入Blueprints文件夹。点击Add New按钮并选择Blueprint Class。
从弹出窗口里选择Game Mode Base,并命名为GM_Tutorial。
现在,你需要指定哪个Pawn作为默认类。双击GM_Tutorial打开蓝图编辑器。
留意Details面板的Classes部分。点击Default Pawn Class属性的下拉框,并选择BP_Player。
其次,关卡还需要知道当前使用哪个游戏模式。你可以在World Settings里指明这一点。点击Compile并关闭蓝图编辑器。
每个关卡都有自己的设置。你可以通过Window\World Settings查看设置。同样地,也可以在Toolbar选择Settings\World Settings进行查看。
Details页签旁边就会出现World Settings页签。点击该页签的GameMode Override字段,选择GM_Tutorial。
现在可以看到Game Mode类已经修改成GM_Tutorial。
最后,我们还需要指定玩家的生成位置。通过在关卡里放置Player Start(玩家出生点)即可。
放置玩家出生点
在生成玩家的过程中,游戏模式会寻找关卡中是否有玩家出生点。如果有,游戏模式就会在该点生成玩家。
为了放置玩家出生点,在Modes面板搜索Player Start。左键拖拽 Player Start至Viewport面板。
你可以随意放置玩家出生点。放好后,点击Toolbar的Play按钮。玩家就会在出生点生成。
如果想要退出游戏,点击Toolbar的Stop按钮或按下Esc键皆可。如果你看不到鼠标指针,按下Shift+F1。
如果玩家不能控制移动,那其实算不上游戏,对吧?我们的下个任务就是设置输入控制。
设置输入
将某个按键指定成触发某个行为,称之为按键绑定。
在Unreal里,你可以通过按键触发event(事件)的方式来实现按键绑定。事件节点是一类当接收到特定行为时,触发执行的节点(比如在这个例子里,当你按下绑定按键时,触发执行某个事件节点)。当某个事件被触发时,任何跟事件节点连接的节点就会被执行。
这种按键绑定的方式非常有好处,因为这意味着你不需要硬编码按键对应的逻辑。
比如,你绑定了鼠标左键并命名为Shoot事件。任何有射击能力的Actor都可以使用Shoot事件,用于检测玩家何时按下了鼠标左键。如果你想修改射击对应绑定的按键,只要在输入设置里修改即可。
如果以硬编码方式实现,你需要遍历每个Actor,分别修改射击对应绑定的按键。
轴映射和操作映射
在Edit\Project Settings里可以看到输入设置。点击Engine部分的Input选项进入设置界面。
Bindings就是用于设置输入的部分。
Unreal提供了两类按键绑定方法:
- Action Mapping(操作映射):这类绑定只有两种状态:按下或者没按下。行为只会在按下或释放按键的时候触发。这类绑定适用于没有中间状态的行为,比如枪械射击。
- Axis Mapping(轴映射):这类绑定输出一个称之为Axis Value(下文有更多介绍)的数字。每帧都会触发对应的轴事件。这类绑定通常用于摇杆或者鼠标。
在这篇教程里,我们会使用Axis Mapping。
创建移动映射
首先,你需要创建两组Axis Mapping。这样就可以实现多按键触发同一个事件。
为了创建一组新的Axis Mapping,点击Axis Mappings右侧的+号。创建两组Axis Mapping,分别命名为MoveForward和MoveRight。
MoveForward负责前后移动。MoveRight负责左右移动。
我们需要把移动映射到四个按键上:W,A,S和D。现在只有两个插槽用于映射按键。通过点击每组映射文本框右侧的+号,添加映射插槽。
接着点击每组插槽的下拉框,将W和S键映射到MoveForward,A和D键映射到MoveRight。
接着,我们需要设置Scale字段。
Axis Value和Input Scale
在你设置Scale字段前,我们需要了解它是怎么跟axis values搭配起作用的。
Axis Value的输出数值取决于输入类型和使用方式。按钮点击会输出1。摇杆根据推动方向和力度,输出-1~1之间的数值。
你可以使用Axis Value控制角色的移动速度。比如,如果你把摇杆推到了最边上,Axis Value是1。如果你只推一半,Axis Value就是0.5。
将Axis Value跟某个速度变量进行相乘,你就能够调整摇杆的速度。
你也可以通过Axis Value判断出方向。如果你将角色速度与一个Axis Value正数相乘,得到的就是正数偏移。角色速度与一个Axis Value负数相乘,得到的就是负数偏移。将偏移值与角色位置相加,就能知道角色往哪个方向移动了。
由于键盘按键只能输出0或1的Axis Value,你可以使用Scale来将其转换成负数。通过Axis Value和Scale值相乘就可以做到这点。
如果正数(Axis Value)和负数(Scale)相乘,按键输出就是负数。
因此我们点击修改按键S和A的Scale字段文本为-1。
接着,有趣的部分来了:让角色动起来!关闭项目设置面板,双击BP_Player打开蓝图编辑器。
角色移动
首先,你需要获取移动映射的事件。在事件图表空白处点击右键打开节点列表。从弹出菜单中搜索MoveForward。添加处于Axis Events下的MoveForward节点。注意我们要添加的是Axis Events下的红色节点,而非Axis Values下的绿色节点。
添加MoveRight节点的步骤同上。
现在,我们需要设置下MoveForward节点。
使用变量
为了实现移动,我们需要指定角色的运动速度。最简单的方法是通过变量保存移动速度。
要新建变量,我们点击My Blueprint页签Variable部分的+号。
选中新建的变量,观察Details页签。将变量名重命名为MaxSpeed。随后,点击Variable Type的下拉框选择Float,将变量类型修改为Float。
接着,需要给变量设置默认值。在这之前,先点击Toolbar上的Compile按钮。
保持变量选中,在Details页签的Default Value区域,将MaxSpeed的默认值设为10。
接着,将MaxSpeed变量从My Blueprint页签拖拽至Event Graph。并选择弹出菜单的Get。
我们现在需要将MaxSpeed与Axis Value相乘来得出最终移动速度和方向。添加float * float节点,并连接Axis Value和MaxSpeed。
获取角色朝向
为了向前移动,首先需要知道角色的朝向。幸运地是,Unreal里有这样的节点。我们需要添加Get Actor Forward Vector节点。
接着,添加Add Movement Input节点。该节点通过获取方向和数值,将其转换成移动偏移值。我们按下图连接各个节点:
白线代表节点执行顺序。换言之,玩家进行操作,触发相应事件并执行InputAxis MoveForward节点,随后执行Add Movement Input节点。
Add Movement Input节点需要如下输入:
- Target:默认为self,在这里就是玩家角色自己(红色方块)。
- World Direction:目标移动方向,在这里就是玩家角色的朝向。
- Scale Value:玩家移动距离,在这里为Max Speed * Axis Value(值范围为-1~1)。
MoveRight事件的设置步骤同上,除了要把Get Actor Forward Vector节点替换为Get Actor Right Vector节点。试着不对照前面的操作指引,自己动手看看!
添加移动偏移
为了让角色真正动起来,我们还需要将Add Movement Input节点计算得出的偏移值,累加到角色当前位置上。
基本上我们的策略就是让游戏角色每帧移动一点点,所以我们需要在Event Tick事件添加运动节点,每帧进行调用。
现在看看Event Graph有没有Event Tick节点,它应该处于灰化状态,如果没有就创建对应节点。
为了获取偏移值,创建Consume Movement Input Vector节点。要累加偏移值到玩家角色上,还需要添加AddActorLocalOffset节点。随后如下图连接节点:
这样意味着在游戏每帧,你会获取角色的移动输入,并将其累加在角色当前位置上。
点击Compile,回到主编辑器并点击Play。你就能操控红色方块四处移动了!
这里有个小问题。高配机器有可能运行帧率更高。由于Event Tick事件每帧调用,那同等时间内运动节点执行的次数更多。导致的结果就是高配机器角色移动得更快,反之亦然。
为了避免这个问题,角色运动必须是帧率无关的。
注意:我已经做了按键绑定用于展示帧率有关会产生的影响。按下数字键0将帧率调成60,按下数字键1将重置帧率。控制角色四处移动,看看不同帧率对移动速度的影响。
帧率无关
帧率无关意味着不管帧率是多少,相同时间的游戏运行,会产生完全一致的结果。幸运地是,在Unreal里要做到这点非常容易。
退出游戏,并打开BP_Player。接着,观察Event Tick节点的Delta Seconds字段。
Delta Seconds为当前帧的Event Tick调用与上一帧Event Tick调用的时间间隔。通过将偏移值与Delta Seconds相乘,角色运动就是帧率无关的。
比如,你的角色最高速度是100。如果距离上一帧Event Tick已经过了1秒,则角色移动100个单元。如果只过了0.5秒,则角色运动50个单元。
如果角色运动是帧率相关的,那就会产生不过帧率高低,每帧固定移动100单元的结果。
为了将偏移值与Delta Seconds相乘,添加vector * float节点。之后,如下图进行连线:
由于每帧间隔时间很短,角色移动起来会很慢,所以可以将MaxSpeed的默认值调大至600。
至此恭喜你,成功解锁了帧率无关成就!
你可能注意到了,方块现在会穿透其他物体。为了解决这个问题,我们需要用上碰撞检测。
角色碰撞
为了能够互相碰撞,角色需要一个代表其碰撞区域(通常称之为碰撞体)的东西。你可以使用以下任一种:
- Collision mesh(碰撞网格):这个通常在网格导入时就会生成(如果你勾选了允许)。用户也可以用3D软件创建自定义碰撞网格。红色方块导入时就自动生成了碰撞网格。
- Collision component(碰撞组件):一般由三种形状:盒子,胶囊体和球体。你可以通过Components面板进行添加。通常用于简单碰撞。
下面就是一个人物和其碰撞体的例子。
当一个碰撞体碰到了另个碰撞体,碰撞就产生了。
设置碰撞
你可能会奇怪,方块有碰撞体,却没有检测到碰撞。当你移动方块时,Unreal只认为方块的根组件可产生碰撞。由于方块的根组件并没有任何碰撞体,所以就穿透了其他物体。
注意:一个root组件没有碰撞体的角色,同样可以挡住其他角色。但如果你移动这个角色,它是不会与任何物体产生碰撞的。
所以,为了使用碰撞网格,StaticMesh必须作为根组件存在。我们通过在Components面板左键拖拽StaticMesh至DefaultSceneRoot。释放鼠标,StaticMesh现在就成了根组件了。
最后还要做一件事情。切换到Event Graph页签,将AddActorLocalOffset节点的Sweep输入勾选为true。
简单来讲,AddActorLocalOffset会将角色从旧位置瞬移到新位置上。Sweep可以确保旧位置和新位置之间的物体都能与角色进行碰撞检测。
回到主编辑器并点击Play。现在方块能与关卡场景产生碰撞了!
创建道具
任何东西都能成为供玩家触碰拾取的道具。这里我们使用BP_Banana作为道具。
为了检测方块是否碰到了道具,我们需要一个在产生碰撞时,触发执行的事件节点。我们可以使用碰撞响应来生成事件。
碰撞响应也决定了角色之间的碰撞后行为。有以下三类碰撞响应:Ignore,Overlap和Block。以下是它们互相之间的作用结果:
虽然可以使用Overlap或Block的任一种,本篇教程只展示如何使用Overlap。
设置碰撞响应
退出游戏并打开BP_Banana。选择StaticMesh组件并观察Details面板,Collision部分可以设置碰撞响应。
如图所示,大部分设置都是灰置的。为了编辑它们,左键点击Collision Presets的下拉框,选择Custom选项。
现在,你需要指定道具和方块之间的碰撞响应。
组件有一项属性称之为对象类型。对象类型是一种给角色进行简单分类的方法。你可以在这里了解更多关于对象类型的内容。
由于方块的类型是WorldDynamic,我们需要修改该类型对象的碰撞响应。在Collision Responses设置下,左键点击WorldDynamic中间的勾选框,将碰撞响应改成Overlap。
碰撞处理
为了获取碰撞情况并进行处理,我们需要用到overlap事件。在Components面板右键点击StaticMesh。从弹出菜单中,选择Add Event\Add OnComponentBeginOverlap。
Event Graph界面就会出现OnComponentBeginOverlap (StaticMesh)节点。
最后,添加DestroyActor节点,并连接OnComponentBeginOverlap (StaticMesh)节点。顾名思义,该节点会将目标角色从游戏中移除。由于我们没有显式指定Target,所以它会销毁调用该节点的角色。
放置道具
关闭蓝图编辑器,并确保Content Browser打开了Blueprints文件夹。
通过左键拖拽 BP_Banana至Viewport界面来在关卡放置香蕉道具。
点击Play按钮,开始收集香蕉吧!
后续学习
你可以在这里下载完整项目。
你现在距离成为Unreal Engine专家又近了一步了。希望这篇教程没有难倒你。
如果你还想继续学习Unreal Engine 4引擎,点击下篇教程,我会进一步讲解Unreal Engine 4引擎的材质系统。