本文仅供个人记录和复习,不用于其他用途
如何查阅文档
在正式开始之前,先介绍一下如何查阅官方文档。首先就是按照 Help——Scripting Reference 的顺序打开文档:
如果你需要查找某一个类或者是方法,可以直接在搜索框中搜寻。
另外,每一个组件的右上方都有一个书本模样的图标,点击之后可以直接跳转到对应的文档中:
这里要说明一点,API 文档一般是包含了各种类的说明,而组件打开 Manual 文档则更多是介绍 U3D 中的各项功能:
我们无需通读整篇文档,可以根据需要一个个地学习,循序渐进。
初始化游戏环境
首先创建一个工程,保存当前场景。这里我是在 Assets 目录下创建了一个 Scenes 文件夹用于保存场景。由于这次只会用到一个场景,那么就将它取名为 main。
接下来创建一个地面,在 Hierarchy 窗口库中右键选择 3D Project —> Plane:
我们可以调整一下地面的坐标,把它重设为坐标系原点。
做完了地形,那么接下来就要导入游戏角色——球。创建一个球体,把它稍微往上移动一些,避免与地面重叠。由于地面默认的尺寸为 10*10,我们可以将它的 X 轴和 Z 轴放大两倍,这样地面就看起来大了不少。
在上图中,地面是深蓝色的。如果我们要为某一个物体添加颜色,那么就需要制作材质。创建一个新的文件夹,命名为 Materials,并在里面创建一个空材质。点击空材质,在属性栏中找到 Albedo 一项,选择你喜欢的颜色。
将材质与物体相连接,这样地面的颜色就改变了。连接的方法有很多种,比如直接拖动材质到场景视图中的地形上,或者是拖到 Hierarchy 窗口中的对象上,这两种方法是最简单的。
添加刚体组件
游戏的设定是用键盘操控一个小球,并用它来吃掉地面上的食物,从而获取分数。为了让小球具备物理属性,例如重力、碰撞体积、受力运动等等,我们就需要为其添加刚体组件。
添加的方法就是点击 Add Component,输入 Rigidbody 并添加。这时如果你再运行游戏,你就会发现小球从半空中掉落到了地面上,这说明小球已经受到了重力,同时还具备了碰撞体积。
既然小球已经落到了地面上,那么是时候让它滚动起来了。我们需要为小球创建一个脚本,并编写移动的逻辑:
|
|
代码不需要现在就理解,看看注释就好了。这里我简单的讲一下,该脚本是通过读取键盘的输入来控制小球的移动,像是WASD和方向键都是支持的。另外关于 force 变量,之所以要把它声明为 Public,是因为我想要在 Unity 的编辑器中调整这个力的倍数:
如上图所示,被声明为公共的成员属性都会标识在图像编辑器中,我们可以轻松地修改它的值。
运行游戏,小球已经能够自由地进行移动了。但问题又来了,小球在移动的时候,视角是不会动的,这样多少显得有些不好看。为了让视角运动起来,我们得设置摄像机的位置。
摄像机追随视角
我在上一章中的讲过,把一个物体设置为子对象,其位置会受到父对象的影响。那么,如果我们把主摄像机添加为小球的子对象,会发生什么呢?
如果你真的这么做了,并且运行了游戏,想必你的脑袋现在应该是晕乎乎的。没错,在小球滚动时,视角也会跟着它一起旋转。游戏在运行时,你可以观察一下场景视图,代表小球的坐标系是在不停地旋转的,所以这种方法是不可行的。
为了解决这个问题,我们需要创建一个脚本,用于控制摄像机与小球的距离,以达到跟随视角的效果:
|
|
代码很好理解,offset 代表了小球与摄像机的偏移量,根据小球的位置与偏移量便能算出摄像机应在的位置。你可能注意到了,我并没有对 playerTransform 进行初始化,所以我们要在编辑器中完成这一步:
将 Player 拖拽至 playerTransform 一栏,小球的位置便被赋给了 playerTransform,且会随着小球的位置进行改变(有点像指针)。做完这一步,摄像机的视角追随便完成了。
添加围墙
为了防止小球掉下去,我们得创建四面围墙,把小球挡在里面。具体步骤我就不说了,这里贴出围墙的部分参数:
添加食物
创建一个立方体,将其缩小为原来的两倍,并且设置偏转角度为(45, 45, 45):
为了方便我们移动立方体,我们可以在工具栏将 Local 设置为 Global,这样移动时物体的坐标系将会改为世界坐标系,我们在拖动时就很方便了:
创建预制体
由于我们需要很多个食物,且还要为它们添加脚本,一个个地创建显然是不明智的。Unity 提供了预制体,可以让我们快速地创建多个相同的对象:
创建步骤很简单,将你想要的物体从 Hierarchy 窗口中拖到 Project 窗口即可。此时,PickUp 就成为了一个预制体,要使用的话可以直接把它拖到场景视图中:
这样做有什么好处呢?好处当然有,比如你随意选择一个方块,修改它的缩放比:
你会发现你的场景变成了下面这样:
预制体的作用不仅仅是复制物体,如果某一物体的参数发生了改变,那么其他复制体也将受到影响,这一点对于数量多的物体非常适用。
最终结果如下:
另外,为了管理方便,你可以创建一个空的对象,将所有的复制体放到它的目录下,方便进行整体管理。
碰撞检测
为了让游戏展示出小球吃掉食物的效果,我们就得在小球与食物碰撞时销毁掉食物。那么问题来了,如何在 Untiy3D 中执行碰撞检测呢?我们要用到下面这个组件:
所有的实体都会附带一个 Collider 组件,这个组件意味碰撞器,用于管理物体的碰撞事件。让我们在 Player 脚本里编写一个新的方法:
|
|
OnCollisionEnter()
方法属于刚体类,当物体与其他实体发生碰撞时便会调用这个方法。传入的参数 collision 代表了与当前物体发生碰撞的实体。
打开控制台,运行游戏,控制台打印出了一条信息:
显然,当小球落下时,地面是最先与小球发生碰撞的,所以控制台打印出了这条信息。接着,我们控制小球与食物进行碰撞:
看到了吗,只要是与小球产生了碰撞的物体,控制台都会打印出它的名字。懂得了这个方法,我们就可以进行下一个步骤了:
如上图,物体的属性栏中会有一个 Tag 属性,我们点击方框,选择 Add Tag:
然后再添加新的标签名PickUp
。
最后,我们修改预制体的标签,这样所有的食物都有了一个共同的标签。
编写如下这段代码:
|
|
由于食物有了共同标签,那么我们就可以根据碰撞物体的名字来执行操作。对于被小球碰到食物,我们将其销毁:
触发检测
虽然我们已经做完了碰撞检测,但在游戏的过程中,每当小球碰到食物时,总是会被阻挡一下。按照我们预想的效果,食物是不应该有碰撞体积的,所以我们得换另外一种检测方法——触发检测。
说到触发检测,我就得先解释一下触发器。举个例子,当游戏人物走到大门前时,我们要让大门自动打开,那么大门前的这块区域便是触发器,而打开大门这一事件则是触发事件。触发器一般是没有实体的,但当游戏人物与之接触时,仍然可以触发预设的事件,而这个概念正是我们现在所需要的。
设置触发器很简单,找到 PickUp,将属性栏中的 Is Trigger 打上勾:
这样一来,碰撞器就转变成了触发器,而此时的食物也没有了碰撞体积:
修改代码,触发检测需要新添加一个方法:
|
|
与碰撞检测的代码类似,获取触发的物体标签,根据标签进行销毁。
分数显示与游戏胜利
要在游戏中显示得分的话,就得用到 uGUI 了。这里只是一个简单的应用,之后我会更新 uGUI 的使用教程。
在 Hierarchy 窗口中右键选择 UI -> Text,Unity 会自动创建一个 Canvas。将场景视图切换到 2D,双击 Canvas 下的 Text:
在属性栏中点击方框,按住 Alt 并选择左上角显示:
稍微拖动一下 Text,让它与边框隔开一些。这样,Text 便被调整到了画面的左上角:
为了显示分数,我们得在脚本中获取 Text 对象。新建成员变量 Score 和 text:
|
|
记得引入 UI 的头文件:
|
|
当小球每吃掉一个食物时,就要增加分数并修改文本:
|
|
另外,在游戏结束时也应该出现胜利的字样。让我们在 Canvas 中新建一个文本 WinText,并在属性框中修改其内容、文字大小、居中、颜色:
一般来说,胜利画面是最后才显示的,最开始时应该将其隐藏起来。Unity 中的每个物体名字旁都有个小框,取消掉勾之后物体就不会显示:
我们要在脚本中加上判断,当分数达到 9 时视为胜利。为了控制 WinText 的显示,我们需要创建一个空的游戏对象,并用 WinText 为其赋值:
|
|
胜利条件的判断如下:
|
|
最终代码如下:
|
|
别忘了,我们创建的 text 和 winText 都是没有初始化的,记得在编辑器中将两个 UI 对象拖过去赋值:
游戏最终效果如下:
至此,游戏的基本逻辑就完成了。