Unity 2D 入门

原文:Introduction to Unity 2D
作者:Sean Duffy
译者:kmyhy

3/15/17 更新说明: 升级至 Unity 5.5。

Unity 是一个非常流行和强大的游戏引擎,它支持众多的平台和设备。尽管 3D 游戏最近火热,大部分手机游戏、控制台和桌面游戏仍然是以 2D 方式呈现的,因此学习用 Unity 编写 2D 游戏仍然非常重要。

在本教程中,你将编写一个 2D 太空登陆游戏, 并学到如下技能:

  1. 如何使用精灵和相机。
  2. 如何使用物理 2D 组件处理碰撞和玩法。
  3. 如何创建 2D 动画和状态。
  4. 如何使用图层和精灵的 order。

如果你还没安装 unity 5,请从 Unity 官网下载它。

注意:如果你是第一次接触 Unity,你可以看看我们的《Intro to Unity 教程》 ,以便加快你的学习进度。

开始

从这里下载本教程的开始项目,解压缩,用 Unity 打开 LowGravityLander-Start 项目。

在项目窗口中,打开 Scenes 文件夹下的Lander-Start 场景。你会在 Game 视图中看到:

开始项目已经能够运行,但你还需要解决几个问题才能真正完成它。

准备发射(当在着陆点着陆时更是危机四伏)!让我们开始吧!

注意:对于 Unity 2D 游戏,Unity 编辑器会自动处于 2D 模式。当你创建新项目时,可以选择 2D 或 3D 模式:


在开始项目中已经设置好这个选项了。

精灵

通过 Unity 强大的 2D 引擎和内置编辑器,我们很容易使用精灵。

要向游戏中添加精灵,从项目文件夹中将它拖到你的场景视图即可。这真的很简单,打开场景视图,然后从 Sprites 文件夹中,将 playership 精灵图片拖到你的场景视图。

在结构视图中,点击 Unity 为你创建的 playership 游戏对象,在检视器中查看它的属性。注意 Unity 会自动添加一个 Sprite Renderer 组件到游戏对象中,这个组件包含了你的 playership 图片:

OK!Sprite Renderer 允许你将图片用做 2D/3D 场景中的精灵。

从结构视图中删除 playership 游戏对象。

Sprite Modes

在 Assets/Sprites 文件夹中选中一个精灵图片。在检视器中,你可以有三种不同的模式来使用这个精灵:

  • Single: 只有一张图片的精灵。
  • Multiple: 使用多张图片的精灵,比如动画或精灵表单(spritesheet,同一个角色图片由多个图片组成)。
  • Polygon: 自定义精灵的多边形形状,你可以用各种原始形状来创建,比如:三角形、方块、五边形、六边形等等。

一个精灵表单是一张包含多个更小图片的单张图片,比如:

使用精灵表单的原因是游戏中用到的每一张图片都会占用一次绘制动作。当精灵图片非常多时,这会产生非常大的负担,而且你的游戏会变得复杂臃肿,导致潜在的问题。

通过精灵表单,你可以在一次绘制中绘制多个精灵图,提升游戏性能。当然,在精灵表单中如何组织这些精灵图是一个学问,那就是另一篇教程的事情了。

精灵编辑

将多张图片放到一张图片是很有用的,这样就可以用于动画或者允许对象拥有多个动作。通过 Unity 的 2D 精灵表单编辑器,能够轻易管理这些精灵表单。

在这个游戏中你将用到两个精灵表单:一个用于登陆舱的推进器动画,一个用于爆炸效果。这两个动画都由多个播放帧组成,你可以用精灵编辑器编辑和分割它们。

explosion-spritesheet.png 是用于爆炸效果的,它已经切分好了, 但 thruster-spritesheet.png 图片仍然需要处理,这是你接下来的工作。

在项目窗口的 Sprites 文件夹中点击 thruster-spritesheet.png。在检视器中,它的 Sprite Mode 已经是 Multiple 了(如果不是,请修改为 Multiple 并点击 Apply)。

然后,点击 Sprite Editor:

弹出一个窗口,显示了自动切分成多个帧的精灵表单(下图中的数字是方便演示而添加的,不是截图中的内容):

点击窗口左上角的 Slice,你会看到默认的切分动作是 Automatic:

Automatic 表示 Unity 会自动搜索并切分你的精灵表单,以它自己的方式。但你也可以以 cell size 和 cell count 方式来切分你的精灵表单。

选择 cell size 将允许你以像素单位的方式来指定每一帧的大小。

在精灵编辑器中,点击 Slice 菜单下面的 Grid by Cell Size:

在 Pixel Size,将 X 设为 9,Y 设为 32。将 Pivot 设为 Center,其它值保持为 0,然后点击 Slice:

点击精灵编辑器窗口的 Apply,将修改应用到精灵表单:

这就完成了——你可以关闭精灵编辑器了。推进器的精灵表单已经就绪。

将精灵赋给登陆舱

现在,还不能在游戏中看到登陆舱。因为它还没有被添加上任何 Sprite Renderer组件。不会有任何壮观的着陆场景——或者坠毁效果!——如果登陆舱甚至不能在屏幕上看到的话。

要解决这个问题,点击结构视图中的 Lander 游戏对象。在检视器中,点击 Add Component,在搜索栏中输入 Sprite Renderer。然后,选中 Sprite Renderer 组件,在组件属性中点击 Sprite 右边的圆圈,选择 playership 精灵图片:

将 Order in Layer 设置为 1。

接下来的工作是设置起落架的精灵图。
在 Lander 游戏对象下方,选择 LanderFeet 游戏对象,然后点击 Sprite Renderer 组件中 Sprite 选择器右边的小圆圈。然后从 Select Sprite 窗口中选择 lander-feet 图片:

点击 Play;你可以在游戏视图中查看你的登陆舱了。通过 WASD 或者箭头键在屏幕上移动:

2D 镜头和单位像素

Unity 2D 项目默认带有一个正交视图相机。

在 2D 游戏中,通常你会用这种相机而不是透视视图相机。接下来你会了解正交视图和透视视图的区别。

下图显示了 Lander 项目中的默认的相机设置:

注意,Projection (投影)属性现在是 Orthographic(正交)。

在项目窗口中,选择 playership 精灵图,然后在检视器中查看它的 Import Settings。在 Pixels Per Unit(单位像素)属性中,当前默认为 100:

这里的 100 是什么意思呢?

术语:单位像素

在 Unity 中“单位”一词不一定和屏幕像素对应。相反,你的对象的大小只是相对于其它对象的,它可以是任意大小,比如:1 个单位 = 1 米。对于精灵图片,Unity 用“单位像素”来定义它们以“单位”计算的大小。

假设有一张精灵图,是一张 500 像素宽度的图片。下表显示了当绘制这个精灵时,在不同的缩放系数以及不同单位像素时,它的 x 轴上的宽度的变化:

还是没看懂?下面会对这个计算过程进行说明:

假设有一个游戏,使用了静态相机来全屏显示背景,就好像电脑桌面上的墙纸。

backdrop.png 的高是 2048,默认单位像素是 100。如果你稍微心算一下,就会知道在结构视图中的游戏对象 backdrop 的高度是 20.48 个单位。

当然,正交相机的 Size 属性会将屏幕高度折半,因backdrop 游戏对象的真实高度应当也经过直角转换,即 10.24:

当然,你不需要修改项目中的相机,因为当前的 Size 为 5 ,对于这个游戏中的移动相机来说刚刚好。

星系

在精灵的 Import 设置中有一个 Max Size 属性,允许你指定精灵的最大尺寸,单位为像素。你可以根据目标平台来修改这个设置。

放大一下背景是淡蓝色星系的场景视图。注意它有一点模糊;当你导入一张精灵图片时,Max Size 属性默认是2048。Unity 会将图片缩小以适应默认的纹理尺寸,这会导致图片质量下降。

要解决这个问题,在项目窗口中选中这张 backdrop 图片,勾选 Override for PC、Mac & Linux Standalone,然后将 Max Size 修改为 4096。点击 Apply,然后 Unity 会花几秒钟重新导入这张图片到场景视图中。你会发现背景突然变得清晰明锐了:

将 Max Size 设为 4096 将告诉 Unity 用 4096x4096 大小的纹理贴图,这样你就可以看出原图的细节显示。

但是,这确实会付出一些代价。查看下图中的检视器的的预览区域;背景贴图的大小现在是 4 M,而原来是 1 M。

纹理贴图的大小增加后,会使它的内存暴增 4 倍。

值得注意的是,根据 Unity 所支持的平台的不同,可能会有针对其它平台的 Override 设置。如果你准备将游戏编译到这些平台时,你可以使用这些 Override 设置,从而在不同的平台上使用不同的大小和格式。

注意:4096x4096 是十分大的图片文件了,尽可能避免使用这么大的文件,尤其是对于手机游戏来说。这个项目中只是为了演示才使用这么大的图片。

贴图

你还可以修改贴图的格式:

你可能想修改某些贴图的格式,以提升图像质量,或者压缩它们的大小,但这要么会增加图片在内存中的占用,要么会降低图片的保真度。最好是理解每个参数的作用,尝试修改它们并比较贴图最终的尺寸和质量。

将 Use Crunch Compression 设置为 50% 的压缩时间会长一点,但文件尺寸会变得最小,当然你后面仍然可以调整它。

将 backdrop 的 Import 设置改回之前的内容,然后再修改 Format 和 Crunch Compression 设置,然后点击 Apply。

在开发你自己的游戏时,你可以尝试不同的压缩率以在最小大小和质量之间找到一个结合点。

2D 碰撞和物理

Unity 中你可以像在 3D 游戏中一样修改 2D 物理引擎的重力。对于新项目 Unity 默认将重力设置为地球重力,也就是 9.80665 m/s2。但如果你将飞船降落在月球上,而不是地球上,则重力就应当是地球重力的 16.6%,也就是 1.62519 m/s2

注意:在开始项目中,重力被设置为 -1,以便你更容易起飞和测试游戏。

要修改游戏的重力,点击 Edit / Project Settings / Physics 2D 然后用 Physics2DSettings 检视器面板将重力的 Y 值从 -1 修改为 -1.62519:

点击 Play,运行游戏,四处飞一下,看看重力对你的飞船的移动有什么影响:

碰撞

如果你曾经试过引导登陆舱,那么你也可能碰到过一两块岩石了。这是因为 Unity 的 2D 碰撞系统生效了。

每个会受重力和其它物体影响到的对象,都需要拥有一个 2D 碰撞体组件和一个 2D 刚体组件。

在结构视图中选中 Lander 这个游戏对象;你会看到它带有有一个 2D 刚体和一个 2D 多边形碰撞体组件。在一个精灵上添加一个 2D 刚体组件将让它接受 Unity 2D 物理系统的管理。

物理组件的简单介绍

一个 2D 刚体组件表示重力将对这个精灵产生作用,你可以在脚本中通过力来控制这张图片。如果你想让这个精灵受其它对象影响并和其它对象发生碰撞,你还需要添加一个 2D 碰撞体。添加碰撞体组件将使精灵能够响应和其它精灵发生的碰撞。

多边形 2D 碰撞体相对于其它简单的碰撞体,比如盒子碰撞体或圆形碰撞体来说,要耗费更多的性能,但它能和其它物体发生更精确的碰撞。尽可能地使用最简单的碰撞体能够确保你达到最佳性能。

碰撞多边形

在你的飞船上尝试一下碰撞体,从结构视图中选择 Lander 游戏对象,在 Polygon 2D Collider 中点击 Edit Collider:

在场景视图中,将鼠标至于碰撞体的边沿;当手柄出现,你可以移动碰撞体的端点;也可以添加或删除端点,从而改变碰撞体的形状:

现在,将 Lander 的碰撞体恢复原样。

注意:在 Lander 游戏对象附属的 Lander.cs 脚本中,我们用 OnCollisionEnter2D 去处理和其它对象的碰撞。如果碰撞力超过某个设定值,登陆舱就会坠毁。

你的降落坐垫也需要一个碰撞体;不然的话在着陆的时候你的飞船会直接落下。

在结构视图中,双击 LanderObjective 游戏对象,让降落坐垫居中显示。在检视器中,点击 Add Component,选择 Box Collider 2D 组件:

Unity 会为 LanderObjective 游戏对象添加一个盒子 2D 碰撞体组件,并自动将碰撞体的大小设为和精灵图的大小一样。很好!

对于刚体和 2D 碰撞体组件,有几点需要注意:

  • 如果你想在移动物体时应用变形组件,而不是只有重力能够作用它,可以将刚体的 body type 设为 Kinematic。要保持让它们受 Unity 重力控制,使用 Dynamic。如果要让它们根本不可移动,设为 Static。
  • 还可以修改刚体组件的质量,线性阻力、角阻力和其它物理属性。
  • 碰撞体可以用于 Trigger 模式;在这种模式下,它们不会和其它物体发生物理碰撞,而是允许你用代码在所有 MonoBehaviour 脚本中都有效的 OnTriggerEnter2D() 方法来响应事件。
  • 要在你的脚本代码中处理碰撞事件,可以用 OnCollisionEnter2D() 方法。这个方法在所有 MonoBehaviour 脚本中都是可用的。
  • 可以为碰撞体分配一个可选的 Physics2D 材质,以控制反弹属性或摩擦属性。

注意:当游戏中只有几个对象的时候,你可能没有注意到,如果屏幕上有数以百计的对象时都参与物理作用时,用更简单的碰撞体形状将大大提升游戏性能。

如果有大量对象发生碰撞时,你可能不得不重新审视一下使用多边形碰撞体组件的策略。

登陆舱动画

你的登录器还不算完,因为还缺少一个看得见的推进器向上助推的效果。现在推进器已经有了,但看不出它喷火的效果。

Unity 动画 101

要为游戏对象增加动画效果,需要为这个对象添加一个包含所需动画的 Animator 组件。这个组件需要引用一个 Animation Controller,这个 Controller 定义了将要使用的动画剪辑,以及这些剪辑的控制方式,以及其它“发烧级”特效,比如混合和动画的过渡。

推进器的动画控制器

在结构视图中,展开 Lander 游戏对象,显示出 4 个下级对象。选择 ThrusterMain 游戏对象,你会看到已经有一个 Animator 组件在上面了,但它还没有对应的动画控制器:

仍然在 ThrusterMain 游戏对象,点击 Animation 编辑器标签。如果在编辑器主窗口中看不见这个标签,请点击 Window 菜单,然后选择 Animation:

点击 Create 按钮,创建一个动画剪辑:

名字输入 ThrusterAnim,位置选择 Assets/Animations 文件夹。

你会在项目窗口的 Animations 文件夹看到 2 个新的动画资源。ThrusterAnim 这个动画剪辑中保存了推进器的动画,ThrusterMain 则是控制这个动画的动画控制器:

在动画窗口,你会看到一个时间轴;在时间轴上,你可以对每个推进器的图片帧进行排序或添加。

点击 Add Property ,属性类型选择 Sprite Renderer/Sprite:

你的编辑器现在看起来是这个样子:

在项目窗口,点击 Sprites 文件夹,展开 truster-spritesheet.png 图片。选中 4 个切片图,然后拖到动画编辑器的 ThrusterMain : Sprite 时间线。

动画帧在时间线中重叠在一起,你可以根据需要重新安排。首先从最右边的图片开始;点击这张图片,将它向右拖,置于 0:05 秒处:

选中最后一帧,用 Delete 键删除它。

点击动画窗口的 Record 按钮一次,关闭这个剪辑的记录模式,防止意外修改到这个动画:

接下来配置动画控制器。

Lander.cs 脚本当前的 Animation参数设置为 true 或 false,表明玩家是否点火了推进器。动画控制器应该负责计算这些参数并允许某些状态被修改。

在项目窗口,点击 Animations 子文件夹,双击 ThrusterMain.controller。这会打开动画编辑器,当你在 ThrusterMain 游戏对象上创建动画剪辑时,Unity 会自动添加这个控制器。

现在推进器动画正在持续运行。

正确地说,推进器动画只应当在玩家当前已经点火了推进器才播放。

右击动画编辑器中的网格区域,然后选择 Create State/Empty:

在检视器中,将新状态命名为 NoThrust。这是动画在玩家没有任何输入时的默认状态:

从 Entry 处,Animator 会走到 NoThrust 并停下来,直到布尔值变成 true。为了改变动画状态,你必须添加一个 transition 连接。

在 Entry 状态上右击,选择 Make Transition。再点击 NoThrust 状态,这会从 Entry 添加一个箭头指向 NoThrust。右键点击 NoThrust,选择 Set As Layer Default State。NoThrust 会变成如下图所示的橙色:

橙色表明这个状态将会是播放时的第一个状态。

在 Animator 编辑器中,点击 Parameters 标签中的 + 按钮,创建一个新的 Bool 型参数,命名为 ApplyingThrust:

右击 NoThrust,点击 Make Transition,然后点击 ThrusterAnim。这会创建一个转换,允许在两个状态之间改变。执行同样步骤,创建一个从 TrhusterAnim 到 NoThrust 的转换:

点击从 NoThrust 到 ThrusterAnim 之间的转换线条,在检视器中点击 +,添加一个条件。这个选项只对条件 ApplyingThrust 有效。

从下拉框中选择 true。也就是说只有 ApplyingThrust 为 true 时,动画才会变成 TrusterAnim 状态。

现在编辑从 ThrusterAnim 到 NoThrust 之间的转换,同样适用 ApplyingThrust 条件,但这次将条件设为 false:

完成后的动画控制器变成这个样子:

在 Animator 编辑器中,你可以调整动画回放速度为一个合适的值。点击 ThrusterAnim 状态,在检视器中,修改 Speed 属性为 1.5:

推进器动画应该对用户按下扳机进行即时响应。点击两条转换箭头(NoThrust 和 ThrusterAnim 之间的两条),在检视器中,将转换有关的设置修改为 0。反选 Has Exit Time 和 Fixed Duration:

最后,你需要将相同的动画和控制器应用到左右推进器。在结构视图中,选择 ThrusterLeft 和 ThrusterRight,将 ThrusterMain.controller 从项目窗口的 Animations 文件夹拖到 Animator 组件的 Controller 属性上:

点击 Play,运行游戏;用 WASD 或箭头键试一下你的推进器吧!

休斯顿,起飞吧!

精灵的排序和图层

如果精灵不进行排序的话,2D 引擎的事情不能算完。Unity 允许你使用图层系统和图层顺序来进行精灵的排序。

点击 Play,再次运行游戏;拿出你吃奶的力气去碰撞旁边的大石头吧!观察编辑器中的场景视图,当 Restart 按钮显示时,有一些石头会消失在幕布图片之后:

这是因为渲染引擎无法得知精灵的摆放顺序。所有精灵,除了飞船之外,都用的是默认的图层顺序 0。

要解决这个问题,需要使用图层和图层排序系统来分隔精灵。Unity 会按照指定的图层顺序来将精灵们绘制在图层上。对于每个图层,Unity 会按照精灵在图层中的序号依序绘制。

点击 Edit 菜单,然后点击 Project Settings,选择 Tags & Layers。展开 Sorting Layers 一节。

点击 +,添加 3 个新的图层:

  • Background
  • Rocks
  • Player

点击并拖动每个图层旁边的句柄,将他们的顺序设置为如下所示。你的图层顺序决定了 Unity 的绘制这些图层中的精灵的顺序:

从结构视图中点击 Backdrop;在 Sprite Render 组件中点击 Sorting Layer 下拉框,然后选择列表中的 Background:

展开 Rocks 游戏对象,选中所有下级的 rock 游戏对象。在检视器中,将这些对象的Sorting Layer 统统设置为 Rocks:

由于场景中的岩石是前后交叠的,很方便用它们来演示同一图层中的精灵的 Order in Layer 属性的用法。

如果你不为 Rocks 图层中的每个岩石分配一个排序值,你会发现在游戏中,岩石会随机地从其它岩石上“弹出”。这是因为 Unity 无法以同一的顺序绘制岩石,因为它们在图层中的顺序都是0。

找到交叠在一起的岩石,将位于较前面的岩石指定一个更大的 Order in Layer 值。

修改 Lander 及其子对象,以及 Pickups 下面的所有 Fuel 游戏对象的 Sprite Renderer Sorting Layer 属性为 Player。这将确保它们绘制在所有东西之前。

但有一个问题。对于推进器动画该怎么办(而且,登陆舱的脚架正常情况下应当隐藏在登陆舱下面)?如果我们不指定它们的 Order in Layer 数值,我们会看到一些奇怪的现象。

将 Lander 的 Order in Layer 属性修改为 2。选中 Thruster 的所有子对象和 LanderFeet 对象,设置它们的 Order in Layer 为 1。

当登陆舱碰到登录平台时,平台会微微下沉,表示你已经着陆。登录平台和岩石精灵是彼此交叠的,为了使结果看起来正确,你必须将登录平台排在岩石的后面。

将 LanderObjective 精灵设置为 Rocks 图层,然后指定它的 Order in Layer 为 0。

设置 LanderObjective 下方的岩石的 Order in Layer 值设置为 1:

最后,选中 Prefabs 文件夹中的 Explosion 预制件,将它的 Sorting Layer 设置为 Player:

点击 Player,试一下你的飞行技巧,点火推进器,落到着陆平台上——注意不要在一个反向上推得过猛,以便撞到岩石上!

结束

你可以从这里下载本教程完整的示例项目。

你已经学习了 Unity 的大部分重要的 2D 设计功能,你已经有一个好玩的重力着陆游戏去“秀”一下了!

如果想进一步学习 Unity 的 2D 工具和功能,你可以看一下官方的 Unity 2D 游戏制作文档。

希望你喜欢这个教程——请在下面的评论中留言或提问。我期望与你交流!:]

阅读更多

没有更多推荐了,返回首页