学习内容
- 连续向前移动玩家
- 生成玩家必须避开的障碍物
- 随机化障碍以创造变化
- 创建一个在玩家遇到障碍物时显示的重启按钮
入门
下载启动项目并解压缩。转到项目文件夹并打开InfiniteMatrix.uproject。
注意:如果您收到一条消息说项目是使用早期版本的虚幻编辑器创建的,那没关系(引擎经常更新)。您可以选择打开副本的选项,也可以选择就地转换的选项。
按Play测试运动控制。您可以通过移动鼠标进行垂直和水平移动。
你要做的第一件事就是让玩家不断前进。
让角色向前移动
导航到Blueprints文件夹并打开BP_Player。
要将Player向前移动,您将在每帧的Player位置上添加一个偏移量。
首先,您需要创建一个变量来定义玩家向前移动的速度。创建一个名为ForwardSpeed的Float变量并将其默认值设置为2000。
接下来,确保您在 Event Graph 中,然后找到Event Tick节点。创建以下设置:
通过将ForwardSpeed与Delta Seconds相乘,您可以获得与帧速率无关的结果。
注意:如果您不熟悉帧速率独立性,请阅读我们的蓝图教程。我们将在“帧率独立性”部分介绍它。接下来,您将使用此结果沿单个轴移动玩家。
沿单轴移动
要移动Player,请创建一个AddActorWorldOffset节点。通过左键单击复选框将Sweep设置为true。
如果您尝试将Float结果连接到Delta Location输入,Unreal 会自动将其转换为Vector。
但是,这会将 Float 值放入 Vector 的 X、Y 和 Z 分量中。对于这个游戏,向前移动应该只沿着X 轴。幸运的是,您可以将 Vector 拆分为三个Float组件。
确保AddActorWorldOffset节点的Delta Location引脚没有连接。右键单击Delta Location引脚并选择Split Struct Pin。
最后,像这样连接所有东西:
让我们回顾一下:
- 每一帧,游戏都会将ForwardSpeed和Delta Seconds相乘以获得与帧速率无关的结果
- AddActorWorldOffset将使用结果沿X 轴移动玩家
- 由于启用了Sweep,如果有任何阻挡,玩家将停止前进
单击编译,然后返回主编辑器。如果您按Play,您将穿过隧道。
您可以创建自动生成隧道的蓝图,而不是手动放置隧道。
创建隧道生成器
转到 Content Browser 并确保您位于Blueprints文件夹中。创建一个以Actor作为父类的新蓝图类。将其命名为BP_TunnelSpawner,然后打开它。
由于游戏会不断地生成隧道,因此最好创建一个生成函数。转到 My Blueprint 面板并创建一个名为SpawnTunnel的新函数。此功能的目的是在提供的位置生成隧道。
要将位置传递给函数,函数需要一个输入参数。当您调用该函数时,这些将显示为输入引脚。
它们还将在函数的Entry节点上显示为输出引脚。
让我们继续创建一个输入参数。确保您位于SpawnTunnel函数的图表中。选择Entry节点,然后转到 Details 面板。单击输入部分旁边的+号。
将输入参数重命名为SpawnLocation并将其类型更改为Vector。
要生成隧道,请添加Spawn Actor From Class节点。单击位于Class引脚右侧的下拉菜单,然后选择BP_Tunnel。
要设置生成位置,请右键单击Spawn Transform引脚并选择Split Struct Pin。之后,将Spawn Actor From Class节点链接到Entry节点,如下所示:
现在,无论何时调用SpawnTunnel函数,它都会在提供的位置生成一个BP_Tunnel实例。
让我们测试一下!
测试隧道生成器
切换到 Event Graph 并找到Event BeginPlay节点。添加一个SpawnTunnel节点并将其连接到Event BeginPlay节点。
在SpawnTunnel节点上,将Spawn Location设置为(2000, 0, 500)。
现在,当游戏开始时,它会在玩家上方生成一条隧道。单击编译,然后返回主编辑器。
首先,从关卡中删除BP_Tunnel 。通过左键单击World Outliner 中的BP_Tunnel来执行此操作。然后,按Delete键将其从关卡中删除。
接下来,转到内容浏览器。左键单击并将BP_TunnelSpawner拖到 视口中。这会将它的一个实例添加到关卡中。
如果您按下Play,游戏将在玩家上方和远离玩家的地方生成一条隧道。
完成测试后,返回BP_TunnelSpawner。将SpawnTunnel节点的Spawn Location重置为(0, 0, 0)。
之后,单击编译,然后返回主编辑器。
在下一部分中,您将为BP_Tunnel设置功能。
设置隧道蓝图
BP_Tunnel将负责两件事。首先是检测游戏何时应该生成新隧道。为此,您将创建一个触发区。一旦触发,BP_Tunnel将告诉BP_TunnelSpawner生成一个新隧道。通过这样做,您可以创造出无尽隧道的错觉。
它要做的第二件事是定义一个生成点。BP_TunnelSpawner将使用该点作为下一个生成位置。
让我们从创建触发区域开始。
创建触发区
打开BP_Tunnel,然后转到 Components 面板。添加一个Box Collision组件并将其命名为TriggerZone。
目前碰撞区域很小。转到详细信息面板并找到Shape部分。将Box Extent属性设置为(32, 500, 500)。
接下来,将Location属性设置为(2532, 0, 0)。这会将TriggerZone放置在隧道网格的末端。这意味着只有在玩家到达隧道尽头时才会生成新隧道。
现在,是时候创建生成点了
创建生成点
要定义生成点的位置,可以使用Scene组件。这些组件非常适合定义位置,因为它们只包含一个变换。它们在视口中也可见,因此您可以看到生成点的位置。
转到“组件”面板并确保您没有选择任何内容。添加一个Scene组件并将其重命名为SpawnPoint。
隧道网格在X 轴上的长度为2500 个单位,因此应该是连接点所在的位置。转到 Details 面板并将Location属性设置为(2500, 0, 0)。
接下来要做的是创建一个在SpawnPoint生成隧道的函数。
在隧道中生成新的生产点
单击编译,然后切换到BP_TunnelSpawner。
下一个BP_Tunnel应该在最远隧道的SpawnPoint生成。通过这样做,隧道将始终继续。
由于最远的隧道始终是最后生成的隧道,因此您可以轻松获得对它的引用。
打开SpawnTunnel的图表。右键单击Spawn Actor From Class节点的Return Value引脚。选择提升为变量并将变量重命名为NewestTunnel。
现在,您将始终参考最远的隧道。
接下来,创建一个新函数并将其命名为SpawnTunnelAtSpawnPoint。创建以下图表:
此设置将获得最新的隧道及其SpawnPoint组件的位置。然后它将在此位置生成一个新隧道。
为了让BP_Tunnel与BP_TunnelSpawner通信,它需要一个引用。如果没有通信,BP_TunnelSpawner将不知道何时生成下一个隧道。
创建对 Tunnel Spawner 的引用
单击编译,然后关闭SpawnTunnelAtSpawnPoint图。之后,切换到BP_Tunnel。
添加一个新变量并将其命名为TunnelSpawner。将其变量类型设置为BP_TunnelSpawner\Object Reference。
单击编译,然后切换回BP_TunnelSpawner。
打开SpawnTunnel的图形并添加指示的节点:
现在,每个隧道都会引用BP_TunnelSpawner。
接下来,您将告诉BP_TunnelSpawner在玩家进入TriggerZone时生成下一个隧道。
编写触发区脚本
单击编译,然后切换到BP_Tunnel。
转到组件面板并右键单击TriggerZone。选择添加事件\添加 OnComponentBeginOverlap。这会将以下节点添加到您的事件图表中:
只要另一个Actor与TriggerZone重叠,该节点就会执行。
首先,您应该检查与TriggerZone重叠的Actor是否是玩家。
左键单击并拖动Other Actor引脚。释放左键单击空白区域并从菜单中选择Cast to BP_Player 。
注意:由于隧道在另一条隧道的末端产生,它会触发该隧道的TriggerZone。如果Other Actor是隧道,则强制转换为 BP_Player将阻止任何其他节点执行。
接下来,在Cast to BP_Player节点之后添加指示的节点:
让我们一步一步来:
- 当一个Actor与TriggerZone重叠时,将执行On Component Begin Overlap (TriggerZone)节点
- Cast to BP_Player节点检查重叠的 Actor 是否是玩家
- 如果是玩家,那么BP_TunnelSpawner将生成一个新隧道。它的位置将位于最后生成的隧道的SpawnPoint组件。
- 由于旧隧道不再使用,游戏使用DestroyActor节点将其移除
单击Compile,返回主编辑器,然后按Play。一旦你到达隧道的尽头,游戏将产生一个新的。
虽然游戏是无休止地产生隧道,但看起来并不无止境。您可以通过始终显示一些隧道来缓解这种情况。稍后,当您将其与障碍物结合使用时,玩家将无法看到生成的隧道。
产生更多隧道
首先要做的是创建一个产生一定数量隧道的函数。
打开BP_TunnelSpawner并创建一个名为SpawnInitialTunnels的新函数。
要生成指定数量的隧道,您可以使用ForLoop节点。该节点将执行连接的节点指定的次数。添加一个ForLoop节点并将其连接到Entry节点。
要使ForLoop节点执行n次,您需要将Last Index设置为n – 1。
在本教程中,您将生成三个隧道。要执行三个循环,请将Last Index值设置为2。
注意:如果您不设置First Index或Last Index字段,它们将默认为0
游戏开始时,玩家应始终从隧道中开始。为此,您可以在玩家的位置生成第一个隧道。
产生第一条隧道
要确定第一个隧道是否已生成,您可以检查是否设置了NewestTunnel。如果未设置,则表示第一条隧道尚未生成。这是因为NewestTunnel仅在游戏生成隧道后设置。
要执行此检查,请在ForLoop节点之后添加一个IsValid节点(带有问号图标的节点)。
接下来,获取对NewestTunnel的引用并将其连接到IsValid节点的Input Object引脚。
如果未设置NewestTunnel ,则将执行Is Not Valid pin,反之亦然。
添加以下内容并将其连接到IsValid节点的Is Not Valid引脚:
此设置将在玩家 Pawn 的位置生成一个隧道。
接下来,您将生成后续隧道。
产生后续隧道
添加一个SpawnTunnelAtSpawnPoint节点并将其连接到IsValid节点的Is Valid引脚。
这是最终的图表:
概括:
- ForLoop节点一共会执行3次
- 在第一个循环中,它将在玩家的位置生成一条隧道
- 在随后的循环中,它将在最新隧道的SpawnPoint处生成一个隧道
接下来,转到事件图表并删除SpawnTunnel节点。之后,在Event BeginPlay之后添加SpawnInitialTunnels节点。
现在,当游戏开始时,它将产生三个隧道。
单击Compile,返回主编辑器,然后按Play。隧道现在更长了!
游戏目前不是很有挑战性,所以让我们添加一些障碍。
制造障碍
以下是您将用作障碍物的网格:
打开BP_Tunnel并转到 Components 面板。添加一个静态网格组件并将其命名为WallMesh。
转到 Details 面板并将其Static Mesh属性更改为SM_Hole_01。
接下来,将其Location属性设置为(2470, 0, 0)。这会将其放置在隧道的尽头。
为了让游戏更有趣,墙壁也会旋转。添加一个新的Float变量并将其命名为RotateSpeed。将默认值设置为30。
切换到 Event Graph 并找到Event Tick节点。创建以下设置:
这将使WallMesh以所提供的量旋转每一帧。
单击编译,然后返回主编辑器。按播放查看旋转的墙壁。
让我们通过在墙壁上添加一些变化来增加趣味。
创建墙壁变化
无需为每个变体创建新蓝图,您只需随机化WallMesh 即可。
打开BP_Tunnel并创建一个名为RandomizeWall的新函数。之后,创建以下图表:
顾名思义,Set Static Mesh节点会将WallMesh设置为提供的网格。
要制作静态网格列表,您可以使用Select节点。
左键单击并拖动New Mesh引脚。释放左键单击空白区域,然后添加一个Select节点。
Select节点允许您设置选项列表。Index输入确定Select节点输出的选项。
由于有四个墙网格可用,您需要再创建两个选项引脚。您可以通过右键单击Select节点并选择Add Option Pin来执行此操作。这样做直到你有四个选项引脚。
接下来,将每个选项设置为以下内容:
- 选项 0: SM_Hole_01
- 选项 1: SM_Hole_02
- 选项 2: SM_Hole_03
- 选项 3: SM_Hole_04
现在,让我们选择一个随机选项。
随机化墙
您可以使用Range 节点中的 Random Integer来获取随机数。该节点将返回一个>= Min和 <= Max的值。
在 Range节点中添加一个Random Integer,并将其连接到Select节点的Index引脚。
将最大值设置为3。这将为您提供四个可能的数字:0、1、2 和 3。
要创建更多随机化,让我们向WallMesh添加随机旋转。在Set Static Mesh节点之后添加以下内容:
这将为WallMesh添加0到360度之间的随机旋转。
这是最终的图表:
概括:
- Select节点提供网格列表
- 使用Random Integer in Range节点选择随机网格
- Set Static Mesh节点将WallMesh设置为选定的网格
- AddLocalRotation节点向WallMesh添加随机旋转偏移
单击编译,然后关闭RandomizeWall图。
切换到BP_TunnelSpawner并打开SpawnTunnel图。添加突出显示的节点:
现在,每当隧道生成时,它都会有一个随机的墙体网格。
关闭SpawnTunnel图,然后单击Compile。返回主编辑器并按播放查看所有墙的变化!
如果撞到墙,您将停止前进。但是,如果您四处走动并穿过一个洞,您将再次开始向前移动。
下一步是在玩家与墙壁碰撞时禁用向前移动。
处理墙壁碰撞
要启用或禁用向前移动,您可以使用布尔变量。它们只有两种状态:true和false。
打开BP_Player,然后创建一个名为IsDead的新布尔变量。
接下来,转到Event Tick节点并创建一个Branch节点。
然后,获取对IsDead的引用并将其连接到Branch节点的Condition引脚。
将Event Tick节点连接到Branch节点。然后,将Branch节点的False引脚连接到AddActorWorldOffset节点。
现在,只要IsDead设置为true,玩家就会停止前进。
接下来,让我们设置玩家撞墙时的IsDead变量。
设置 IsDead 变量
单击编译,然后切换到BP_Tunnel。在 Components 面板中,右键单击WallMesh并选择Add Event\Add OnComponentHit。这会将以下节点添加到您的事件图表中:
每当另一个Actor与WallMesh碰撞时,该节点就会执行。
首先,您需要检查与WallMesh碰撞的Actor是否是玩家。
左键单击并拖动Other Actor引脚。释放左键单击空白区域并从菜单中选择Cast to BP_Player 。
接下来,左键单击并将Cast的BP_Player引脚拖动到 BP_Player节点。释放左键单击空白区域,然后添加Set Is Dead节点。
通过左键单击复选框将IsDead设置为true。
单击编译,然后返回主编辑器。按播放并尝试撞墙。如果你移动到一个洞,你将不再穿过它。
在下一节中,您将在玩家撞墙时显示重新启动按钮。
显示重启按钮
您将显示的小部件名为WBP_Restart。您可以在UI文件夹中找到它。这是它的样子:
要显示或隐藏小部件,您需要对其进行引用。打开BP_Player,然后创建一个名为 RestartWidget 的新变量。将变量类型更改为WBP_Restart\Object Reference。
接下来,转到 Event Graph 并找到Event BeginPlay节点。
添加一个Create Widget节点并将Class值设置为WBP_Restart。
之后,添加一个Set Restart Widget节点,然后像这样连接所有内容:
现在,当玩家生成时,它将创建一个WBP_Restart实例。下一步是创建一个显示此实例的函数。
创建显示功能
创建一个新函数并将其命名为DisplayRestart。完成后,创建以下图表:
概括:
- Add to Viewport将在屏幕上显示RestartWidget
- Set Input Mode UI Only 将限制玩家与 UI 的交互。这样玩家就不能在他们死的时候四处走动。
- 顾名思义,Set Show Mouse Cursor只是显示鼠标光标
要显示重启按钮,您只需在播放器与墙壁碰撞后调用DisplayRestart即可。
调用显示函数
关闭DisplayRestart图形,然后单击Compile。
切换到BP_Tunnel,然后找到On Component Hit (WallMesh)节点。
将DisplayRestart节点添加到节点链的末尾。
单击编译,然后关闭BP_Tunnel。返回主编辑器并按Play。如果撞到墙,会出现重启按钮。
最后一步是当玩家点击按钮时重新开始游戏。
重启游戏
游戏重启时需要做两件事:
- 重置Player。这包括从屏幕上删除重新启动按钮。
- 重生隧道。这样玩家就从隧道的起点开始。
让我们从重置Player开始。
重置Player
打开BP_Player,然后创建一个名为RestartGame的新函数。创建以下图表:
概括:
- Set Is Dead将IsDead设置为false。这重新启用了向前运动。
- Remove From Parent从屏幕上删除RestartWidget
- Set Input Mode Game Only重新启用游戏输入,以便玩家可以四处移动
- Set Show Mouse Cursor隐藏鼠标光标
接下来,让我们重生隧道。
重生隧道
单击编译,然后关闭BP_Player。
打开BP_TunnelSpawner并确保您位于SpawnInitialTunnels图中。
首先,您需要在生成新隧道之前移除现有隧道。
在Entry节点之后添加一个Sequence节点。将Then 1引脚连接到ForLoop节点。
注意: Sequence节点按顺序执行其输出。这是一种垂直组织图表的好方法,尤其是因为节点链可能会变得很长。
接下来,创建以下节点:
此设置将获取所有现有隧道并将其从游戏中移除。
最后,将Sequence节点的Then 0引脚连接到Get All Actors of Class节点。这将确保在生成点过程之前移除隧道。
这是最终的图表:
最后要做的是处理按钮单击。
处理按钮点击
单击编译,然后关闭BP_TunnelSpawner。
转到内容浏览器并导航到UI文件夹。双击WBP_Restart将其打开。
选择RestartButton,然后转到 Details 面板。转到事件部分,然后单击OnClicked旁边的按钮。
这将创建一个名为On Clicked (RestartButton)的节点。该节点将在玩家点击RestartButton时执行。
重新创建以下内容:
概括:
- Get Owning Player Pawn返回玩家当前控制的 Pawn
- Cast to BP_Player检查 Pawn 是否属于BP_Player类
- 如果是,它将调用RestartGame函数。此功能重置Player并隐藏重新启动按钮。
- Get All Actors of Class and Get返回BP_TunnelSpawner然后调用SpawnInitialTunnels。此功能将删除现有隧道并生成新隧道。
注意:您可能想知道为什么我使用Get All Actors Of Class而不是使用对BP_TunnelSpawner的引用。主要原因是因为BP_Tunnel与WBP_Restart没有关系。对于像这样的简单游戏,执行上述方法比找出存储引用的位置更容易。
单击编译,然后关闭蓝图编辑器。按Play测试重启按钮!