- Unreal Engine 4 系列教程 Part 10:制作简
- Unreal Engine 4 系列教程 Part 5:制作简单
- Unreal Engine 4 系列教程 Part 4:UI教程
- Unreal Engine 4 系列教程 Part 3:材质教程
- Unreal Engine 4 系列教程 Part 9:AI教程
- Unreal Engine 4 系列教程 Part 2:蓝图教程
- Unreal Engine 4 系列教程 Part 7:音频教程
- Unreal Engine 4 系列教程 Part 1:入门
- Unreal Engine 4 系列教程 Part 6:动画教程
- Unreal Engine 4 系列教程 Part 8:粒子系统
原文:How to Create a Simple FPS in Unreal Engine 4
作者:Tommy Tran
译者:Shuchang Liu
在本篇教程中,将学习创建一个简单的第一人称视角射击游戏。你将学会如何创建一个持枪的第一人称角色,并实现射击其他Actor。
第一人称视角射击游戏(FPS)是一类玩家以游戏角色视角进行射击体验的游戏。FPS游戏非常热门,不乏使命召唤和战地等大作。
Unreal引擎最开始就是为FPS游戏量身打造的引擎,所以用Unreal引擎制作FPS游戏也是理所当然的事。在本篇教程中,你将学会:
-
创建能够四处移动的第一人称角色
-
创建一把枪,绑定在角色身上
-
使用直线追踪(大家熟知的射线追踪)发射子弹
-
对Actor扣除伤害
注意:本篇教程只是Unreal Engine 4系列教程的其中一篇:
起步入门
下载示例项目并解压。进入项目文件夹,双击BlockBreaker.uproject打开项目,我们能看到以下场景:
![](https://img.haomeiwen.com/i1797904/896929af0802b13e.png)
绿色墙上包含着多个目标,当目标受到伤害时会变红。一旦血量值降为零,目标就会消失。红色按钮可以重置所有的目标。
首先,我们要创建玩家角色。
创建玩家角色
打开Blueprints文件夹并创建一个新的Blueprint Class类,选择Character作为父类,并将其命名为BP_Player。
![](https://img.haomeiwen.com/i1797904/2ea4fccb9d1050fd.png)
Character本身是Pawn的一种,额外多了一些其他功能,比如CharacterMovement组件。
![](https://img.haomeiwen.com/i1797904/356a815e4862cb0b.png)
该组件会自动处理如走动跑跳等移动功能,我们只要简单调用对应函数就可以移动角色。我们也可以在该组件设置走路速度,起跳速度等变量。
在实现移动功能前,Character需要知道玩家的按键情况,因为我们先将移动映射到W,A,S和D键上。
注意:如果你还不熟悉关于键位映射的有关内容,请查看蓝图教程。键位映射是一种定义键位执行特定行为的方法。
创建移动映射
选择Edit\Project Settings,打开Input设置。
创建两个名为MoveForward和MoveRight的轴映射。MoveForward控制前后移动,MoveRight控制左右移动。
![](https://img.haomeiwen.com/i1797904/818764a9f7d6b150.png)
对于MoveForward,将按键改为W,随后,创建多一个键位插槽,将其设置为S,并将Scale改为-1.0。
![](https://img.haomeiwen.com/i1797904/242a4c8c962211e3.png)
随后,我们会将Scale值跟角色朝向向量相乘,当Scale值是正数时,向量方向朝前,当Scale值是负数时,向量方向朝后。通过得出的向量结果,我们就可以让角色朝前朝后移动了。
![](https://img.haomeiwen.com/i1797904/3c60d01463dae502.png)
接着,我们要对左右移动做同样的设置,将MoveRight设为D,新建键位插槽设为A,Scale值设为-1.0。
![](https://img.haomeiwen.com/i1797904/cfa8126887023d46.png)
现在我们设置好了键位映射,就可以用它们来进行移动了。
实现移动
打开BP_Player并打开Event Graph,添加MoveForward事件节点(在Axis Events分类下)。即使没有按任何按键,该事件也会每帧调用。
![](https://img.haomeiwen.com/i1797904/4aa7c6aad9982ec3.png)
该事件会输出Axis Value,也即刚才所设置的Scale值。当按下W时,输出1,当按下S时,输出-1。如果不按任何按键,输出0。
接着,我们要让角色进行移动,添加Add Movement Input,进行如下连接:
![](https://img.haomeiwen.com/i1797904/e44752e32e8db3b7.png)
Add Movement Input节点会用一个向量与Scale Value字段相乘,这样就能将向量转换到对应方向。由于我们用了Character类,CharacterMovement组件会将Pawn往对应方向移动向量距离。
现在,我们需要指定移动方向。我们希望角色往正面朝向移动,所以可以使用Get Actor Forward Vector节点,该节点返回一个正面朝向向量,创建节点如图下一样连接:
![](https://img.haomeiwen.com/i1797904/ad6a33e6a4bf9a3c.png)
小结:
- MoveForward节点会每帧输出Axis Value,当按下W时输出1,当按下S时输出-1,什么都不按,输出0
- Add Movement Input节点将玩家朝向向量与Scale Value相乘,使得不同按键控制输出不同方向的向量。什么都不按,意味着向量并没有方向,角色原地不动
- CharacterMovement组件获得Add Movement Input节点的输出,驱动角色朝指定方向移动
MoveRight按以上步骤操作,不过记得将Get Actor Forward Vector节点改为Get Actor Right Vector节点。
![](https://img.haomeiwen.com/i1797904/af42e02fd4ec106d.png)
在测试移动功能前,我们还要设置下Game Mode里的默认Pawn。
设置默认Pawn
点击Compile并回到主编辑器,打开World Settings面板并找到Game Mode设置,将Default Pawn Class改为BP_Player。
![](https://img.haomeiwen.com/i1797904/990f13349977794a.png)
注意:如果你的主编辑器面板还没有World Settings面板,在Toolbar选择Settings\World Settings调出面板。
现在运行游戏你就能控制BP_Player了,按下Play并使用W,S,A和D来进行移动。
![](https://img.haomeiwen.com/i1797904/9235b1e20044ec1c.gif)
我们接着创建输入映射来观察四周。
创建观察映射
打开Project Settings,再创建两个轴映射,分别命名为LookHorizontal和LookVertical。
![](https://img.haomeiwen.com/i1797904/559c1ec8d92018ca.png)
将LookHorizontal的键位改为Mouse X。
![](https://img.haomeiwen.com/i1797904/dffb8bf2a60d2f0c.png)
这样当鼠标向右滑动时会输出正数,反之亦然。
接着,将LookVertical的键位改为Mouse Y。
![](https://img.haomeiwen.com/i1797904/9dc15b7d523af080.png)
这样当鼠标向上滑动时会输出正数,反之亦然。
现在,我们要写点逻辑来实现转动视角。
实现转动视角
如果一个Pawn上没有Camera组件,Unreal会自动为你创建一个摄像机。默认情况下,摄像机会使用控制器的旋转。
注意:如果你想了解更多关于控制器的内容,可以查看AI部分教程。
虽然控制器并没有物理实体,它仍旧有自己的旋转。这意味着我们可以让角色和摄像机面向不同方向。比如,在第三人称游戏里,角色和摄像机并不总是处于同一方向。
![](https://img.haomeiwen.com/i1797904/b2d530ed81187e48.gif)
要在第一人称视角里转动摄像机,我们所要做的就是修改控制器的旋转。
打开BP_Player并创建LookHorizontal事件。
![](https://img.haomeiwen.com/i1797904/52b7c4fbf63859cf.png)
要让摄像机看向左边或右边,我们需要调整控制器的偏航角(Yaw),创建Add Controller Yaw Input节点并进行如下连接:
![](https://img.haomeiwen.com/i1797904/d767f69f05b42d27.png)
现在,当你水平移动鼠标时,控制器会向左或向右转动,由于摄像机使用了控制器的旋转,摄像机也会跟着转动。
重复以上步骤实现LookVertical,不过记得将Add Controller Yaw Input改为Add Controller Pitch Input节点。
![](https://img.haomeiwen.com/i1797904/759ac759e51ae427.png)
如果我们现在就运行测试游戏,会发现上下转动的反转的,也就是说,当我们鼠标向上滑动,摄像机是向下转动的。
如果想改成非反转控制,将Axis Value剩余-1,这样就能对Axis Value取反,控制器的转动也会反转过来。
![](https://img.haomeiwen.com/i1797904/7cb1e70dcbee39e2.png)
点击Compile并按下Play运行游戏,使用鼠标来转动视角吧。
![](https://img.haomeiwen.com/i1797904/0473e3a8363a95c9.gif)
现在移动和视角转动都实现了,是时候搞把枪了!
创建枪支
你还记得当创建蓝图类时,我们可以指定一个父类吧?好吧,其实也可以指定自己的蓝图类作为父类。当我们有多个不同类型的物体,拥有同样的函数或属性时,就会发现很有用处。
举例来说,当我们有多种类型的汽车。我们可以创建一个Car类,包含比如速度和颜色等变量。然后可以再创建其他类(子类),使用Car类作为父类。每个子类都会包含同样的变量。现在我们就能轻易地创建不同速度和颜色变量的汽车了。
![](https://img.haomeiwen.com/i1797904/af8f0b14a17d2a6a.png)
我们可以使用相同方式来创建枪支。因为我们首先要创建一个基类。
创建枪支基类
回到主编辑器并创建Actor类型的Blueprint Class,将其命名为BP_BaseGun并打开。
接着,我们要创建一些定义枪械参数的变量,创建如下float类型变量:
- MaxBulletDistance:子弹最远飞行距离
- Damage:子弹伤害
- FireRate:子弹发射间隔(秒)
![](https://img.haomeiwen.com/i1797904/2c78ddd1dc5ab66c.png)
注意:每个变量的默认值都是0,对本例来说没什么问题。然而,如果你希望新的枪支类有别的默认值,你需要在BP_BaseGun设置下。
现在,我们需要给枪支一个物理外观,添加Static Mesh组件并命名为GunMesh。
![](https://img.haomeiwen.com/i1797904/070f8fac031f933a.png)
先别急着给Static Mesh组件设置网格,我们会在创建枪械子类时再做这件事。
创建枪械子类
点击Compile并返回主编辑器。要创建子类,我们要在右键点击BP_BaseGun,从弹出菜单选中Create Child Blueprint Class。
![](https://img.haomeiwen.com/i1797904/884f0e052fd00a0e.gif)
将其命名为BP_Rifle并双击打开,然后打开Class Default设置以下变量:
- MaxBulletDistance: 5000
- Damage: 2
- FireRate: 0.1
![](https://img.haomeiwen.com/i1797904/a54b9aa93dca9da4.png)
这意味着每颗子弹能最远飞行5000单位的距离。如果子弹命中Actor,能对其造成2点伤害。当持续开火射击时,射击间隔不少于0.1秒。
接着,我们需要指定这把枪的网格,选中GunMesh组件,并将其Static Mesh设置为SM_Rifle。
![](https://img.haomeiwen.com/i1797904/5590438079ca5346.png)
这把枪现在就完成了,点击Compile并关闭BP_Rifle。
接着,我们要创建自己的摄像机组件了。这样能够更好地控制摄像机位置,我们还可以将枪支跟摄像机绑定在一起,这样枪支就能始终保持在摄像机的正面了。
创建摄像机
打开BP_Player并创建摄像机组件,将其命名为FpsCamera。
![](https://img.haomeiwen.com/i1797904/7f40fe5b066763fc.png)
摄像机的默认位置有点偏低,会让玩家感觉太矮,将FpsCamera的位置改为(0, 0, 90)。
![](https://img.haomeiwen.com/i1797904/12c57bdf488f3905.png)
默认情况下,摄像机组件并不使用控制器的旋转。要修正这点,在Details面板启用Camera Settings\Use Pawn Control Rotation。
![](https://img.haomeiwen.com/i1797904/63b56879ef7ed638.png)
接着,我们需要定义枪支的位置。
定义枪支位置
要定义枪支位置,我们可以使用Scene组件。这个组件非常适合用来定义位置,因为它只包含一个Transform。首先确保选中了FpsCamera,然后再创建Scene组件,组件就会附着在摄像机节点下,将组件命名为GunLocation。
![](https://img.haomeiwen.com/i1797904/6ad5721839ad2408.png)
通过在FpsCamera放置GunLocation,枪支就会相对于摄像机保持同一位置,这样枪支就能始终保持在镜头前方。
接着,将GunLocation的位置改为(30, 14, -12),让其处于相对摄像机靠前一侧位置。
![](https://img.haomeiwen.com/i1797904/3142f9a3e60eac50.png)
随后,将旋转设为(0, 0, -95)。当枪支在这个位置时,看起来就像在瞄准屏幕中间。
![](https://img.haomeiwen.com/i1797904/8552e495fe52cb61.png)
现在,我们需要生成枪支并将其绑定在GunLocation位置上。
生成并绑定枪支
找到Event BeginPlay并创建Spawn Actor From Class节点,将Class设为BP_Rifle。
![](https://img.haomeiwen.com/i1797904/0c7308b524b9b83f.png)
由于我们需要用到枪支,先创建变量存储其引用。创建BP_BaseGun类型变量,将其命名为EquippedGun。
这里要注意新建变量不要设为BP_Rifle类型的,因为玩家应该能够使用各种类型的枪支,而不单只是来福枪,否则如果生成了其他种类的枪支,就不能存储在BP_Rifle类型变量上了。
接着,将Spawn Actor From Class的Return Value引脚设为EquippedGun。
![](https://img.haomeiwen.com/i1797904/2f1cd31cbd102d2f.png)
要绑定枪支的位置,我们需要用上AttachToComponent组件。创建组件并将Location Rule和Rotation Rule设为Snap to Target。这样就能让枪支拥有跟其父类一样的位置和旋转。
![](https://img.haomeiwen.com/i1797904/30044d8b17a4adf8.png)
接着,创建GunLocation引用并进行如下连线:
![](https://img.haomeiwen.com/i1797904/4c2741f46dd50ff1.png)
小结:
- 当BP_Player生成时,它会连带生成BP_Rifle实例
- EquippedGun字段会持有BP_Rifle引用
- AttachToComponent节点会将枪支设置在GunLocation位置。
点击Compile并按下Play运行游戏。现在当玩家生成时,枪支也会一同生成,显示在摄像机的前面。
![](https://img.haomeiwen.com/i1797904/30d236d8fb0cf6ea.gif)
现在有趣的地方来了:射击子弹!要检测子弹是否打中东西,我们要用上射线检测(line trace)。
射击子弹
射线检测是一个包含开始点和结束点(两点成线)的函数,它会检测这条线上的每个点,看是否碰到其他物体。在游戏中,这是用于检测子弹是否打中东西的最普遍做法。
由于射击是属于枪支的特性,射击函数应该设计在枪支类里,而不是角色类。打开BP_BaseGun并创建名为Shoot的函数。
随后,创建两个Vector输入,分别命名为StartLocation和EndLocation。它们分别为射线检测的开始点和结束点(通过BP_Player传入)。
![](https://img.haomeiwen.com/i1797904/73b04488122dbddc.png)
我们可以使用LineTraceByChannel节点来执行射线检测。这个节点会使用可视力(Visibility)或者摄像机(Camera)碰撞通道来进行碰撞检测。创建节点,进行如下连线:
![](https://img.haomeiwen.com/i1797904/51db7614dd587855.png)
接着,我们需要检测射线是否碰撞到任何东西。创建Branch并进行如下连线:
![](https://img.haomeiwen.com/i1797904/06eb3dcc9723afe7.png)
如果检测到碰撞,Return Value会输出true,反之亦然。
为了在子弹击中物体时给予玩家视觉反馈,我们可以使用粒子特效。
生成子弹撞击粒子
首先,我们需要获得射线碰撞的位置。拖拽Out Hit引脚到图表空白处,从弹出菜单中,选择Break Hit Result。
![](https://img.haomeiwen.com/i1797904/b94185cc948f5ffa.png)
这样我们能得到一个跟射线检测结果相关的,具有多种引脚的节点。
创建Spawn Emitter at Location节点,并将Emitter Template设为PS_BulletImpact。随后,连接其Location与Break Hit Result的Location。
![](https://img.haomeiwen.com/i1797904/c705137860dd608f.png)
以下是目前的函数连线:
![](https://img.haomeiwen.com/i1797904/efb6271b784bbc21.png)
小结:
- 当Shoot函数执行时,它首先会执行一个射线检测
- 如果检测到碰撞,Spawn Emitter at Location节点会在碰撞位置生成粒子特效PS_BulletImpact。
现在射击逻辑已经完成了,再写下调用逻辑即可。
调用射击函数
首先,我们需要创建射击的按键映射。点击Compile并打开Project Settings。创建一个新的Axis Mapping并命名为Shoot,将其按键设为Left Mouse Button,然后关闭Project Settings。
![](https://img.haomeiwen.com/i1797904/eec81d48d1ecdf7b.png)
随后,打开BP_Player并创建Shoot事件。
![](https://img.haomeiwen.com/i1797904/6fdeb6d413e7d767.png)
为了检测玩家是否按下Shoot键,我们需要检测Axis Value是否等于1。创建如下高亮节点:
![](https://img.haomeiwen.com/i1797904/377de24af650ec90.png)
接着,创建EquippedGun引用节点,并调用它的Shoot函数。
![](https://img.haomeiwen.com/i1797904/23f0374b0efe2398.png)
现在,我们需要计算下射线检测的起始点和结束点。
计算射线检测的位置
在很多FPS游戏里,子弹其实是从摄像机而不是从枪里发射出来的,这是因为摄像机天然就是对准射击准心的。所以如果从摄像机发射子弹,就可以保证其一定会往准心飞行。
注意:也有些游戏确实是从枪里发射子弹的。然而,它要求一些额外计算来保证子弹向准心方向射击。
创建FpsCamera引用并将其与GetWorldLocation连接。
![](https://img.haomeiwen.com/i1797904/0c7e4724a9b56fe0.png)
现在我们还需要结束点位置。记得枪支有个MaxBulletDistance变量吧,这意味着结束点位置应该在距离摄像机MaxBulletDistance单位远的地方。因此要计算出结束点位置,添加如下高亮节点:
![](https://img.haomeiwen.com/i1797904/78fd591f37317800.png)
随后,像下图一样连接所有节点:
![](https://img.haomeiwen.com/i1797904/da43ed649ecc2ae3.png)
小结:
- 当玩家按下或按住鼠标左键时,枪支会从摄像机处开始发射子弹。
- 子弹会向前飞行MaxBulletDistance距离
点击Compile并按下Play运行游戏,按住鼠标左键开始发射子弹吧!
![](https://img.haomeiwen.com/i1797904/f171ec87ba211c13.gif)
现在,枪支是每帧都在射击的,射速实在是有点太快了,所以下一步要降低枪支的开火速度。
降低开火速度
首先,我们需要一个变量检测玩家是否正在射击。打开BP_Player创建boolean类型变量,命名为CanShoot,将其默认值设为true。如果CanShoot等于true,说明玩家正在射击。
下面将Branch部分图表改成下图:
![](https://img.haomeiwen.com/i1797904/2614298cd66aea15.png)
现在,玩家只有在按下Shoot键且CanShoot等于true时才能进行射击了。
接着,添加如下高亮节点:
![](https://img.haomeiwen.com/i1797904/83dead676c79ebbd.png)
调整修改小结:
- 玩家只有在按住鼠标左键且CanShoot等于true时才能进行射击
- 一旦玩家射出一颗子弹,CanShoot就会设为false,防止马上射出第二颗子弹
- CanShoot会在隔FireRate秒时间后重置为true
点击Compile并关闭BP_Player,按下Play运行游戏测试下枪支的射速吧!
![](https://img.haomeiwen.com/i1797904/e71a45ff4f25b609.gif)
实现受击
在Unreal里,每个Actor都能受击。然而,Actor要对受击伤害做出什么处理是可以自由定义的。
比如,当战斗中的游戏角色当受击时,会扣除血量。然而,像气球一类物体是没有血量概念的。取而代之的,我们会编写逻辑让气球在受击时爆炸。
在我们处理Actor受击时,我们首先要施加一个“伤害”。打开BP_BaseGun并在Shoot函数后添加Apply Damage节点。
![](https://img.haomeiwen.com/i1797904/70a83a7f503f258c.png)
接着,我们需要指定要施加伤害的Actor,在本例中,就是射线检测到的Actor。连接Damaged Actor与Break Hit Result的Hit Actor引脚。
![](https://img.haomeiwen.com/i1797904/8efdd20dc42a43b5.jpg)
最后,我们需要指定要受击伤害数值,创建Damage变量引用,并与Base Damage相连。
![](https://img.haomeiwen.com/i1797904/077a6427723d0037.png)
现在,当我们调用Shoot函数时,它就会对射线检测到的物体进行伤害。点击Compile并关闭BP_BaseGun。
现在我们需要处理每种Actor对于受击伤害的反馈。
处理受击
首先,我们需要处理目标获得伤害数据,打开BP_Target并创建Event AnyDamage事件节点,这个节点会在受到伤害且其数值不为零时触发执行。
![](https://img.haomeiwen.com/i1797904/fae2bac88b3efe6c.png)
随后,调用TakeDamage函数并连接Damage引脚。这个函数会将目标的Health变量减去Damage数值,并更新目标的颜色。
![](https://img.haomeiwen.com/i1797904/c5385c8cf2dd1984.png)
现在,当目标受到伤害时,它就会扣除血量了。点击Compile并关闭BP_Target。
接着,我们需要处理按钮对伤害的反馈。打开BP_ResetButton并创建Event AnyDamage。随后,调用ResetTargets函数。
![](https://img.haomeiwen.com/i1797904/d75524b90b0928a3.png)
这个函数会在按钮受击时调用并重置所有目标的状态。点击Compile并关闭BP_ResetButton。
按下Play运行游戏开始射击目标。如果你想要重置所有目标,就朝按钮射击。
![](https://img.haomeiwen.com/i1797904/1cc51ef8c9b6c8d9.gif)
后续学习
你可以在这里下载完整项目。
虽然本篇教程中所制作是一个非常简单的FPS游戏,你可以在此基础上进一步扩展,试着创建更多具有不用射速和伤害的枪械,也可以尝试添加装弹功能!
以上就是Unreal Engine 4 系列教程的全部内容了。不过别担心,我们后续还会制作更多关于Unreal Engine 4的教程。现在我们开始在招募新的Unreal Engine教程团队了,希望可以为社区带来更多精彩的Unreal Engine系列教程。希望到时可以看到你们的身影!:]
网友评论