Unreal Engine 4 系列教程 Part 2:蓝图教程

原文:Unreal Engine 4 Blueprints Tutorial
作者:Tommy Tran
译者:Shuchang Liu

在本篇教程里,你将学会如何用蓝图系统创建玩家角色,设置输入,并编写角色通过触碰,收集道具的游戏逻辑。

蓝图是Unreal Engine 4的一套可视化脚本系统,通过蓝图可以快速制作游戏原型。你不再需要一行行的编写代码,取而代之的是可视化操作:拖拽节点,在UI里设置节点参数,给节点进行连线。

除了作为一款非常便捷的原型工具,蓝图也降低了非开发人员制作游戏逻辑的门槛。

在本篇教程,你将使用蓝图来:

  • 设置垂直视角摄像机
  • 创建具备基本移动的玩家控制器角色
  • 设置玩家输入
  • 创建可被角色触碰并收集的道具

注意:本篇教程假定你已了解Unreal Engine 4的基本操作界面。你也应该熟悉基本的蓝图概念,比如组件和节点。如果你需要复习以上内容,点击入门教程

注意:本篇教程只是Unreal Engine 4系列教程的其中一篇:

起步入门

下载示例项目并解压。进入项目文件夹,双击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按钮并回到主编辑器。

生成玩家

为了让玩家能够控制角色,你需要明确两件事:

  1. 玩家所要控制的Pawn类
  2. 角色应该在哪里生成

你可以通过创建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,分别命名为MoveForwardMoveRight

MoveForward负责前后移动。MoveRight负责左右移动。

我们需要把移动映射到四个按键上:WASD。现在只有两个插槽用于映射按键。通过点击每组映射文本框右侧的+号,添加映射插槽。

接着点击每组插槽的下拉框,将WS键映射到MoveForwardAD键映射到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)相乘,按键输出就是负数。

因此我们点击修改按键SAScale字段文本为-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

我们现在需要将MaxSpeedAxis Value相乘来得出最终移动速度和方向。添加float * float节点,并连接Axis ValueMaxSpeed

获取角色朝向

为了向前移动,首先需要知道角色的朝向。幸运地是,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作为道具。

为了检测方块是否碰到了道具,我们需要一个在产生碰撞时,触发执行的事件节点。我们可以使用碰撞响应来生成事件。

碰撞响应也决定了角色之间的碰撞后行为。有以下三类碰撞响应:IgnoreOverlapBlock。以下是它们互相之间的作用结果:

虽然可以使用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引擎的材质系统。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349