前言
在Unity官方教程 2D UFO(上)中,我们学会了:
- 新建一个项目Project
- 从资源商店下载素材包
- 新建一个GameObject
- 给UFO添加刚体组件(Rigidbody 2D)和脚本(Script),让我们通过方向键或者WASD来控制UFO移动
我们会看到一个问题,UFO随便移动几步就会从平台掉下去,看也看不见追也追不着。我们可以给平台添加一圈围墙来防止这种情况的产生。那么这一节我们将学会以下内容:
你将学会操作什么?
- 如何制作围墙防止UFO从平台掉下去
- 如何让摄像机跟随UFO移动
- 添加“旋转小马达”——金币
- 制作预制件Prefab
- 实现UFO吃掉金币
- 添加计分板和游戏结束提示
- 如何发布游戏到本地
一、添加围墙防止UFO掉出平台
代入到现实生活里面,人和围墙之间会发生什么使得人不能直接穿越围墙到外面去?机智的A同学可能想到了:物理碰撞。Unity里要想拥有类似围墙的效果,其实可以通过给Background和Player分别添加碰撞器来实现。
把目光放回到Scene窗口,可以看到Background整个平台四条边的格子是特殊颜色和材质,这个意味着把墙设置在这4个区域是比较合适的。那么我们先来添加第一面墙吧!
选中Background,通过Add Component-->Physics 2D-->Box Collider 2D,就可以给平台添加一个碰撞器组件(墙是方形的所以选择Box)。
碰撞器默认是包裹在Background外围,在Scene窗口可以看到一圈原谅色在平台边缘。把Box Collider 2D组件的Offset内的X设置为-14.26,Size的X改为3.3,可以看到绿色碰撞器区域变成是在平台最左侧一列格子,这一面墙就添加好了。
有两种办法添加其余三面墙:
- 新增法:和添加第一面那样,Add Component-->Physics 2D-->Box Collider 2D
- 复制法:在已有的Box Collider 2D组件的右上角小齿轮里选择Copy Component,然后再点开一次,选择Paste Components As New
参考数值:
- 右:Offset,X为14.26,Y为0;Size,X为3.3,Y为31.64
- 上:Offset,X为0,Y为14.26;Size,X为31.64,Y为3.3
- 下:Offset,X为0,Y为-14.26;Size,X为31.64,Y为3.3
最后一步,给Player添加碰撞器Circle Collider 2D。添加之后双击Player可以快速定位到UFO,清晰地看到UFO外面那一圈原谅色。
修改Circle Collider 2D里面的Radius值可以调整碰撞区域,在这里设置为2.15是比较合适的。
这些都完成之后,运行游戏看看,现在UFO不会再飞出去了吧?(o゚▽゚)o
二、让摄像机跟随UFO移动
有丰富游戏经验的同学都知道,在游戏里面,主角总是处于画面中心的,换句话说就是摄像机应该是跟随主角移动,保持主角万众瞩目状态。很好,那怎么样才能让摄像机跟随UFO走起来呢?可能机智的A同学又想到了:把摄像机变成UFO的小弟(子对象),这样UFO移动,摄像机也会跟着变化。就听你一次,来试试看
然后运行游戏看看效果怎么样:
A同学????
的确,摄像机是跟随UFO移动了,但是变动地不止是位置,它的角度也跟随UFO变化了,所以就会如图一样出现天旋地转的情况。我们想要的效果只是想让摄像机的位置跟随UFO移动,不需要角度变化。这条路走不通,那么只能通过脚本了。
先把Main Camera从Player移出去,然后给摄像机添加一个脚本CameraController,脚本语言是C Sharp。
添加脚本之后记得先把脚本移动到Scripts文件夹,好习惯尽早养成。
脚本代码如下:
简单解读下代码含义:
- 首先定义了一个public的GameObject变量player,这个变量是用来告诉程序,摄像机会跟随哪个GameObject来移动
- 然后定义一个private的Vector3变量offset,这个变量用来记录游戏初始化的时候,摄像机和UFO之间的距离
- 在Start方法中,摄像机和UFO的位置差赋值给offset变量,有了这个固定值才能在后续UFO移动的过程中,UFO和摄像机的距离保持不变
- 最后在LateUpdate方法中,把摄像机的新位置变更为球体的新位置加上固定的offset偏移位置
保存脚本,回到Unity编辑器页面,我们会发现摄像机的脚本组件中,多了一个Player属性。
这就是刚才我们在脚本内定义的public属性player。
在Unity脚本里,public属性会在编辑器内显示,需要填充内容才可正常运行。
把UFO拖动到Player属性中,如图:
运行游戏,现在再撞墙,摄像机也不会翻跟斗了。
三、添加“旋转小马达”——金币(Pickup)
万事俱备,就只差一些金币让我们的主角UFO去碰撞和拾取了。为了让金币更加耀眼,为了UFO能看见,我们让金币可以自动旋转起来。
首先我们把Sprites内的Pickup拖到Hierarchy窗口,添加一个新的GameObject——Pickup。把Pickup的Sorting Layer改成Pickups之后,因为UFO的Player层级比较高,他俩都在同一个位置(原点),所以我们看不见新增的金币。为了方便查看金币,我们可以暂时把UFO隐藏起来。
在这里因为Pickup的名字和位置都刚好不需要修改,所以我没提到双标动作,但是一般添加GameObject之后最好是要改名和重置Transform的。
之后我们给这个金币添加一个自动旋转的脚本(记得放到Scripts文件夹),脚本名称为Rotator,脚本内容就一句代码,如下图:
简单地说,就是调用transform下的Rotate方法按指定的角度(围绕Z轴旋转45°)旋转一个物体。
放在Update()函数中的代码是以帧来执行的,因为我们需要金币的旋转是以秒来执行,所以需要乘以Time.deltaTime增量时间。
保存脚本,运行游戏,可以看到金币变成“旋转小马达”了(。◕ˇ∀ˇ◕)~
四、制作预制件Prefab
为了丰富游戏,我们可以做多个金币让UFO拾取。这个可以通过制作预制件(Prefab)来实现。那什么是Prefab呢?举个可能不是太贴切的例子,就是我们生活中的样本,比如某辆车的A系列的模型,根据这个模型可以生产出很多一模一样的车辆(金币)。然后只要修改这个模型的参数,那么这个修改就会作用到所有由该模型生产出来的车辆上。
由上可以总结出预制件的好处主要是:
一处修改,全部生效:修改Prefab的属性之后,所有使用这个Prefab的对象的属性也会跟着变化。
炒鸡方便的好吗?
制作Prefab的方法也很简单。不过根据资源管理原则,我们先做两件事
- 在Assets文件夹创建Prefabs文件夹来存放Prefab文件
- 在Hierarchy窗口下,通过Create-->Create Empty创建一个空的GameObject对象,重命名为Pickups,reset Transform,然后把Pickup拖到Pickups下成为它的子对象。后续新增的金币对象都放在其下,好方便管理。
把Pickup拖到Prefabs文件夹,一个预制件就做好了。可以看到Pickup这个对象的名字也变成蓝色了。
接下来有2个办法可以通过预制件制作其他金币。
- 选中Pickup对象,按CTRL+D可以复制出一个金币对象,按多少次就复制多少个(推荐这个方法)
- 把Prefabs文件夹里面的Pickup预制件拖到Pickups下就可以生成一个金币对象
在Scene窗口,鼠标左键拖动这些金币可以把它们随意放到自己觉得合适的地方。
我一共放了13个金币,中间位置留给UFO,同学可以把UFO显示出来了~
五、UFO拾取金币
接下来要实现UFO碰到金币的时候就拾取这个功能。关键词是“碰到”、“拾取”,那么它们分别意味着什么?
- 碰到,也就是碰撞检测(两者都存在碰撞组件才能进行检测)
- 拾取,UFO发现自己碰撞的是金币而不是墙的时候,让金币消失或者不显示就可以了。
很好,理清了思路,接下来就来看看怎么实现吧。
第1步:金币Pickup添加碰撞器
选中Pickups下的任意一个对象比如Pickup,通过Add Component-->Physics 2D-->Circle Collider 2D添加好碰撞器,然后双击Pickup定位放大,可以很清楚地看到金币周围的一圈绿色。
放大的情况下好调整碰撞器区域大小。在这里把Radius值设置为0.93是比较合理的,如果有强迫症的同学可以调整Offset值来调整中心点。
这样就给一个金币添加好了碰撞器了,可是我们还有12个金币也需要添加碰撞器怎么办?在这个添加好碰撞器的金币对象的Inspector窗口右上角有一个Apply按钮,点一下就可以把这个金币的设置同步到其他金币和预制件了。
第2步:编写脚本实现拾取功能
通过查官方API手册我们可以查到有个名为OnTriggerEnter的方法可以在UFO碰到金币的时候调用。另外前面也提到了,UFO是要判断碰撞到的东西的,因为不能什么东西都拾取,所以需要给拾取的金币添加一个标记(Tag)。
添加Tag的方法:
- 一共13个金币,每个金币都添加是很麻烦的事情,这时候可以直接修改预制件Prefab就可以全部生效了。
- 选中Prefabs内的Pickup,然后在Inspector的Tag里面选Add Tag,命名为PickUp(如果列表有PickUp那就无需再加了)
- 然后将预制件Pickup的Tag设置为“PickUp”
接下来就是编写脚本来实现拾取功能。打开UFO的PlayerController脚本,新增以下代码:
- OnTriggerEnter方法是检测物理发生碰撞的时候调用执行里面的代码,参数other变量表示UFO碰到的其他2D碰撞器
- 先调用CompareTag方法判断碰到的这个对象是否被标记了Tag"PickUp",如果是的话则调用SetActive把这个对象设置为未激活状态(不显示),也就是false。
编写完毕,保存脚本,回到Unity编辑器页面,运行游戏看看。
......机智的A同学大喊:哈哈哈哈,还是碰撞了,并没捡起来!
(尴尬.jpg)这是因为,我们没有把金币设置成碰撞触发器(Is Trigger)。把金币设置成触发器之后,在碰撞的时候才会执行脚本里面的OnTriggerEnter等方法。在预制件上勾选之后,再运行游戏,现在可以正常拾取金币了。
第3步:优化性能
现在看起来一切正常,好像并没有什么问题。但,实际上真的如此么?
- 在Unity的物理引擎中,静态的碰撞体(没有刚体)会被单独进行运算并保存在缓存中,节约计算资源优化性能。
- 所以如果我们移动或者旋转一个不带刚体的碰撞体(金币)的话,这个缓存会被重新计算生成,这会耗费一定的资源。
- 所以我们最好给金币添加上刚体(变成动态碰撞体),那么它的旋转不会导致缓存在每一帧都被刷新,一定程度上可以达到节约资源的目的。
那好,我们给Prefab Pickup 加上Rigidbody 2D,然后运行游戏试试。
就像天上掉下很多金色饺子一样,金币都受到了重力掉下来了。的确可以和之前UFO一样,把Rigidbody 2D的Gravity Scale改成0就不会掉下来,但这只能解决部分问题。我们希望的是金币不仅仅不受重力影响,也不受其他物理作用力的影响。而这可以通过修改Rigidbody 2D组件的Body Type为Kinematic来实现。
Kinematic:开启运动学,开启后不再受力的影响,只能通过Transform属性来操作。
六、添加计分板和结束提示
第1步:添加计分板
游戏完成了基本玩法了,但是我们还需要做一个计分板来鼓励玩家多多拾取金块。关键词,“分”和“计分板”,也就是说我们需要做两个事情:
- 定义一个变量来保存玩家的分数(捡取的金币数量)
- 把这个分数显示在UI上
那么我们来修改下PlayerController脚本,内容如下:
三行代码:定义一个变量count用来保存分数,在Start方法里count值初始化为0,最后拾取金币的时候,count数量+1。
count++是一种简写,效果等同于 count +=1,也等同于count = count +1
分数有了,还需要一个UI展示给玩家。保存脚本,回到Unity编辑器,创建一个UI Text。
改名为Count Text,重置下Transform,双击Count Text定位放大,鼠标左键把它拖到合适的位置,比如屏幕左上角。
Inspector的Text组件内的属性可以修改UI的展现。Text是默认文字内容,Font Size是字体大小,Color是字体颜色,可以自己试着修改。
接下来再次修改PlayerController:
- 最上面的命名空间,我们需要引入UnityEngine.UI,这样后面代码才会识别Text类型的变量
- 定义countText变量来保存UI Count Text
- 接着我们在Start方法中调用setCountText方法,来更新countText的内容
- 然后我们在每次UFO拾取金币的时候,也更新countText的内容
- 最后是我们定义的setCountText方法,里面调用的ToString方法是让int类型的count转换成字符串
保存脚本,回到Unity,把Count Text UI 拖入到Player里面的Count Text属性框里面。
做完这些运行游戏看看效果!
第2步:添加游戏结束提示
为了让游戏更加友好,在玩家拾取完毕所有金币之后,应该提示玩家通关了。首先我们也需要先创建一个文本UI(Win Text),字体18号,黄色,位置在UFO上方,这应该是个不错的选择。
回到PlayerController添加代码:
- 定义winText变量来保存UI Win Text
- 接着我们在Start方法中初始化winText为空字符串,因为游戏开始的时候这个内容是不显示的
- 最后我们在setCountText方法里面,判断当分数大于等于13的时候,就更新winText的内容为“You win !”
这个13是因为我的金币数量最多是13,同学可以根据自己的金币数量来修改这个数字。
保存脚本,回到Unity,把Win Text UI 拖入到Player里面的Win Text属性框里面,再运行游戏就可以看到效果了。
七、本地发布游戏
游戏制作完毕,可以发布游戏到本地了。呼(๑>ڡ<)☆不容易啊,不过最后一步最简单......保存场景,通过菜单栏File-->Build Settings打开发布页面。
- Platform选择第一项,PC,Mac&Linux Standalone
- 点击Add Open Scenes按钮,把当前场景添加进去并且勾选
- 点击Build按钮,选择存放路径,就可以完成发布了
找到保存路径下的exe文件双击运行就可以开始捡钱了!ヾ(◍°∇°◍)ノ゙