当前位置:   article > 正文

Unity 3D游戏开发+脚本编程完整指南:制作第一个游戏:3D滚球跑酷

Unity 3D游戏开发+脚本编程完整指南:制作第一个游戏:3D滚球跑酷
本节利用前面的知识来实现第一个较为完整的小游戏,如
1-21 所示。
图1-21 3D滚球跑酷游戏完成效果
图1-21 3D滚球跑酷游戏完成效果

1.3.1 游戏设计

1. 功能点分析 游戏中的小球会以恒定速度向前移动,而玩家控制着小球
左右移动来躲避跑道中的黄色障碍物。如果玩家能控制小球在
跑道上移动一定距离则视为玩家通过关卡,触碰到障碍物或从
跑道上掉落则视为失败。我们需要实现的功能点概括来说分为
主角的运动、摄像机的移动和过关与失败的检测等。
2. 场景搭建
01 创建项目。打开 Unity Hub 或者单独的 Unity ,初始模板
选择 3D ,如图 1-22 所示。建议使用 Unity 2018.3 以后的版本,这
里使用的 Unity 版本为 2019.2.13f1
图1-22 创建3D工程
图1-22 创建3D工程
02 创建场景内物体。在场景中新建一个 Cube (立方体)作
为跑道,将其长度改为 1000 ,宽度改为 8 (即立方体 z 轴和 x 轴的
scale ),然后将其位置沿 z 轴前移 480 ,如图 1-23 所示。
图1-23 创建跑道
图1-23 创建跑道
03 新建一个 Sphere (球体)作为玩家,重置它的初始位置
(选择 Transform 组件右上角菜单中的 Reset 选项),按住 Ctrl
拖曳球体的 y 轴,使其刚好移动到跑道上。
04 新建若干个 Cube (立方体)作为障碍物,用上面的方法
铺在跑道上面。为了便于区分,新建两个材质分别作为跑道和
障碍物的材质,调整好颜色后直接拖曳到物体上即可,如图 1-
24 所示。
图1-24 调整跑道和物体的颜色
图1-24 调整跑道和物体的颜色

小知识
按住 Ctrl 键拖曳物体的作用
按住 Ctrl 键拖曳物体会让其以一个固定值移动(可以在
Edit→Snap Settings 中修改这个固定值),调整物体的旋转和
缩放时也是同理。这个功能在搭建场景时可以很方便地对齐
物体的位置,特别是当物体为同一规格大小时。 Unity 中还有
很多类似的很方便的快捷键,在用到时会介绍。

1.3.2 功能实现

1. 主角的移动
之前的实例中已经提到过如何控制小球的移动,因此此处
不再赘述。与之前不同的是,在该案例的设计中,玩家只能控 制小球左右移动,因此只需要获取横向的输入即可,纵向移动
保持一个固定值。编辑好脚本后挂载在小球上,代码如下。
  1. public class Player : MonoBehaviour
  2. {
  3. public float speed;
  4. public float turnSpeed;
  5. void Update()
  6. {
  7. float x = Input.GetAxis("Horizontal");
  8. transform.Translate(x * turnSpeed * Time.deltaTime, 0, speed * Time.deltaTime);
  9. }
  10. }
2. 摄像机的移动
摄像机移动的方法可以分为两种。一种是像控制小球一样
为摄像机挂载控制脚本,使其与小球保持同步运动。另一种则
更为简单直接,即将摄像机设置为小球的子物体,此时摄像机
在没有其他代码控制的情况下会与小球保持相对静止,即随着
小球移动。这里选择第二种方法,当设置好父子关系后调整摄
像机到合适的高度和角度,如图 1-25 所示。
图1-25 将摄像机作为球体的子物体
图1-25 将摄像机作为球体的子物体
小提示
Unity 中的父子关系
父子关系是 Unity 中相当重要的一个概念,此处可以不用
深究,在第 2 章会详细说明。

1.3.3 游戏机制

1. 游戏失败
有两种情况会导致游戏失败,一种是碰到了障碍物,另一
种是小球从跑道边缘掉落。
碰撞到障碍物与吃金币实例中的原理类似,将障碍物碰撞
体上的 Is Trigger 选项勾选上,然后在障碍物脚本里的
OnTriggerEnter() 函数中检测碰撞。
不过游戏中的障碍物可能会有许多个,如果要一个个地分
别做上述修改显然很麻烦,还容易遗漏。因此正确的做法是把
其中一个障碍物设为 Prefab (预制体),后面添加的障碍物都以
这个 Prefab 为模板复制即可。
最后注意,要检测物理碰撞还需要在小球上添加 Rigidbody
组件。为了避免不必要的物理计算消耗,在这个游戏中完全用
代码控制小球的移动,物理系统仅做检测碰撞使用,因此小球
Rigidbody 组件上的 Is Kinematic 选项要勾选上。障碍物脚本里的
代码如下。
  1. public class Barrier : MonoBehaviour
  2. {
  3. private void OnTriggerEnter(Collider other)
  4. {
  5. // 防止其他物体与障碍物的碰撞被检测,我们只需要障碍物与小球的碰撞被检测到
  6. if (other.name == "Player")
  7. {
  8. Time.timeScale = 0;
  9. }
  10. }
  11. }
小知识
什么是预制体? Time.timeScale 是什么?
预制体简单来说就是一个事先定义好的游戏物体,之后
可以在游戏中反复使用。最简单的创建预制体的方法是直接
将场景内的物体拖曳到 Project 窗口中,这时在 Hierarchy (层
级)窗口中所有与预制体关联的物体名称都会以蓝色显示
(普通物体的名称是黑色)。关于预制体的内容会在第 2
详细说明。
Time.timeScale 表示游戏的运行时间倍率,设置为 0 即表
示游戏里的时间停滞, 1 即正常的时间流逝速度, 2 即两倍于
正常的时间流逝速度,以此类推。
小球从跑道边缘掉落时也视为游戏结束。但是由于是直接
用代码控制小球移动,与刚体有一点冲突,因此掉落部分的功
能同样用代码来实现。在 Player 脚本里添加如下代码。
  1. void Update()
  2. {
  3. // 省略的代码部分用省略号表示……
  4. // 一旦小球位置超出了跑道的范围则直接下落
  5. if (transform.position.x < -4 || transform.position.x > 4)
  6. {
  7. transform.Translate(0, -10 * Time.deltaTime, 0);
  8. }
  9. // 下落一定距离之后游戏结束
  10. if (transform.position.y < -20)
  11. {
  12. Time.timeScale = 0;
  13. }
  14. }
当游戏失败结束时应该允许玩家重新开始游戏,这里设置
键盘上的 R 键为重置游戏的按键,在按 R 键后即可重新加载当前
场景。在 Player 脚本里添加如下代码。
  1. using UnityEngine.SceneManagement;
  2. public class Player : MonoBehaviour
  3. {
  4. // 省略的代码部分用省略号表示……
  5. void Update()
  6. {
  7. if (Input.GetKeyDown(KeyCode.R))
  8. {
  9. SceneManager.LoadScene(0); // 重新加载场景
  10. Time.timeScale = 1; // 恢复正常的时间流速
  11. return;
  12. }
  13. // 省略的代码部分用省略号表示……
  14. }
  15. }
小提示
注意添加头部引用
SceneManager 这个类型是属于
UnityEngine.SceneManagement 的,因此要添加头部引用后才
能调用 SceneManager.LoadScene(0) 方法。这里参数 0 表示场景
的序号,由于游戏现在只有一个场景,因此表示加载当前场
景。

2. 游戏胜利
一般来说游戏都应该有一个最终目标,达成这个目标则视
为过关或者胜利。不过也不绝对,类似 Flappy Bird 这样的游戏
就没有最终目标。这里还是设置一个完成目标,即玩家跑了一
定距离就视为过关。
这里使用一个看不见的触发器作为决定距离的终点,确保
其范围能够覆盖跑道的宽度,当小球进入范围就表示游戏过
关,如图 1-26 所示。终点物体脚本的代码如下。

图1-26 在终点创建触发器
图1-26 在终点创建触发器

  1. public class End : MonoBehaviour
  2. {
  3. private void OnTriggerEnter(Collider other)
  4. {
  5. if (other.name == "Player")
  6. {
  7. Debug.Log("过关");
  8. Time.timeScale = 0; // 暂停游戏
  9. }
  10. }
  11. }
至此,这个小游戏的基本代码就完成了,之后会对其进行
适当的修改,使其更完整。

1.3.4 完成和完善游戏

1. 测试自己的游戏
这时候可以开始测试自己设置的关卡难度了,一个好的游
戏应当有一个合理的难度曲线。有一个小技巧可以提高这一步 的效率,即单击场景视窗右上角的坐标轴图标,让场景摄像机
迅速切换为对应轴方向的视角,而单击下面的 Persp Iso 则分别
代表切换摄像机为透视模式或正交模式,如图 1-27 所示。
图1-27 场景可切换为正交模式显示
图1-27 场景可切换为正交模式显示
小提示
场景摄像机不会影响实际游戏画面
场景摄像机指的是在 Scene (场景视窗)里的、仅在编
辑模式可用的摄像机。 Hierarchy 窗口中的摄像机决定在
Game 窗口里看到的实际游戏画面。注意不要将两者混淆。
这里可以切换场景摄像机为 y 轴方向正交,善用复制与按住
Ctrl 键拖曳功能搭建关卡。
2. 加入通关 UI Hierarchy 窗口中单击鼠标右键,通过选择 UI→Panel 选项
创建一个 UI 面板,并以 Panel 为父节点创建一个 Text 组件,在
Text 组件中输入过关的信息,同时调整字体大小、位置等设
置,如图 1-28 和图 1-29 所示。
图1-28 创建Text组件
图1-28 创建Text组件
图1-29 通关界面
图1-29 通关界面

小知识
只是创建了 Panel ,为什么自动添加了其他东西?
当直接创建任何 UI 下的组件时都会自动生成 Canvas
EventSystem 组件,这两个组件分别与 UI 的布局和交互相关,
暂时不做深究。完整的 UI 系统会在后续章节介绍。
接下来要做的是在游戏开始时隐藏 UI ,在小球触发终点物
体时再显示。终点物体的 End 脚本代码如下,同时注意修改 Panel 的名字为 EndUI
  1. public class End : MonoBehaviour
  2. {
  3. // 声明一个物体变量
  4. GameObject endUI;
  5. private void Start()
  6. {
  7. // 通过物体在场景中的名字来找到这个物体
  8. endUI = GameObject.Find("EndUI");
  9. // 在场景中隐藏这个物体
  10. endUI.SetActive(false);
  11. }
  12. private void OnTriggerEnter(Collider other)
  13. {
  14. if (other.name == "Player")
  15. {
  16. Debug.Log("过关");
  17. // 在场景中显示这个物体
  18. endUI.SetActive(true);
  19. Time.timeScale = 0; // 暂停游戏
  20. }
  21. }
  22. }
如此一来,当小球触碰到终点后, UI 就会显示出来,如图
1-30所示。
图1-30 到达终点时的显示效果
图1-30 到达终点时的显示效果

3. 加入摄像机运动效果
最后添加一个好玩的扩展功能:当控制小球左右移动时让
摄像机往对应方向倾斜。具体的做法会涉及一些 3D 数学知识,
会在后续章节中介绍。简单思路为:在 Player 脚本中使用获取的
横向输入,以此控制摄像机的倾斜角度。在 Player 中添加如下代
码,效果如图 1-31 所示
图1-31 摄像机随输入旋转的效果
图1-31 摄像机随输入旋转的效果

  1. void Update()
  2. {
  3. // 省略的代码部分用省略号表示……
  4. Transform c = Camera.main.transform;
  5. Quaternion cur = c.rotation;
  6. Quaternion target = cur * Quaternion.Euler(0, 0, x * 1.5f);
  7. Camera.main.transform.rotation = Quaternion.Slerp(cur, target, 0.5f);
  8. }
可以在此基础上加入更多细节,如音乐、音效和特效等。
合适的音乐和特效可以让简单的游戏更吸引人

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/105913
推荐阅读
相关标签
  

闽ICP备14008679号