Unity 5.x游戏开发实战

978-7-115-45598-7
作者: Alan Thorn
译者: 李华峰
编辑: 胡俊英
分类: Unity

图书目录:

详情

本书的英文名是“Unity 5.x By Example”,可见本书是以示例见长,主要通过一系列丰富的示例来帮助读者掌握Unity 5.x技术,非常适合初学者学习。读者能够通过本书一步一步学习并实现游戏开发,掌握Unity的核心概念,学习如何进行游戏功能的编写,同时还将掌握不同的游戏构建技巧。

图书摘要

版权信息

书名:Unity 5.x游戏开发实战

ISBN:978-7-115-45598-7

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

• 著    [英] Alan Thorn

    译    李华峰

    责任编辑 胡俊英

• 人民邮电出版社出版发行  北京市丰台区成寿寺路11号

    邮编 100164  电子邮件 315@ptpress.com.cn

    网址 http://www.ptpress.com.cn

• 读者服务热线:(010)81055410

    反盗版热线:(010)81055315


Copyright ©2016 Packt Publishing. First published in the English language under the title

Unity 5.x By Example.

All rights reserved.

本书由英国Packt Publishing公司授权人民邮电出版社出版。未经出版者书面许可,对本书的任何部分不得以任何方式或任何手段复制和传播。

版权所有,侵权必究。


Unity是一个可以轻松创建各类型互动内容的多平台综合型游戏开发工具,是一个全面整合的专业游戏引擎。本书基于Unity 5.0及以上版本进行讲解,引导读者深度认识并掌握这一重要的游戏开发工具。

本书共分8章,通过4个典型的游戏项目来引导读者进行学习,每两章完成一个游戏案例,案例式的讲解模式更有利于读者快速提升实践能力。金币采集游戏开启了Unity开发之旅,随后的太空射击游戏进一步丰富了各类游戏设计技巧,之后又通过二维冒险游戏完整地呈现了Unity的强大功能,最后通过一个人工智能项目完整地将地形构建、导航等功能有机地整合到游戏当中。

本书几乎包含了学习Unity所需的所有内容,案例式的学习更有助于读者快速掌握开发技巧。本书非常适合那些没有Unity和游戏开发经验的读者,通过阅读本书,读者将掌握使用Unity进行游戏开发的核心技巧。如果读者对游戏开发和Unity本身有着浓厚的兴趣,那将对其学习提供无限助力,学习效果会更加出色。


Alan Thorn是一位屡获殊荣的作家、数学家,同时也是一位独立的视频游戏开发商,目前居住于英国伦敦。他创立了“Wax Lyrical Games”游戏开发工作室,并开发了一款广受好评的PC冒险游戏《维塔德男爵:灭绝的复仇》。Alan作为一名自由职业者,曾经服务于世界上一些大型的游戏公司。他曾经在欧洲最著名的机构讲授游戏开发,编写了9本游戏编程方面的图书,这其中就包括十分受欢迎的Teach Yourself Games Programming、Game Engine Design and Implementation和UDK Game Development。Alan还对计算、数学、制图学和哲学很感兴趣。关于他的“Wax Lyrical Games”公司的更多信息可以访问http://www.waxlyricalgames.com/获取。


Francesco Sapio毕业于意大利的罗马大学,在校期间获得了优异的成绩,现在正在攻读人工智能和机器人学的硕士学位。

他是一位Unity 3D的专家,同时也是一位熟练的游戏软件开发者,也是一位经验丰富的图形程序使用者。

最近,他刚刚编写了Unity UI Cookbook一书,专门教授读者如何使用Unity开发引人注目并且实用的图形用户界面。此外,他还是Unity Game Development Scripting一书的审校专家。Francesco还是一位音乐家和作曲家,尤其擅长为小电影和视频游戏配乐。近年来,他还从事了演员和舞蹈家的工作。目前,他还是罗马布兰卡乔剧院的特邀嘉宾。

此外,他还是一个非常活泼的人,曾经在罗马的意大利文化中心担任儿童娱乐志愿者。他还为高中和大学的学生讲授数学和音乐的私人课程。

Francesco热爱数学、哲学、逻辑学和破解难题,尤其是开发视频游戏,这一切都源自他对游戏设计和编程开发的热情。读者可以在领英上查看关于他的个人信息。

在这里我要深深感谢我的父母,是他们永无止尽的耐心、热情和支持一直伴随我到现在。而且我也要感谢家庭的所有成员,尤其是我的祖父母,在我的生命中,他们总是不断地用拉丁语“Ad Maiora”和“Per aspera ad astra”来鼓励我去将事情做得更好。

最后,我要对身边每一个我爱的人表示最大的谢意,尤其是我的女友,我非常感谢她为我所做的一切!


游戏是从现实通往梦境的一个通道,在我们每个人的成长过程中,游戏都扮演着一个不可或缺的角色。直到现在我依然忘不了,在上中学时,每当放学就偷偷跑到街边的游戏厅和同学大战《拳皇》的情景。那些一个个操作简单、画面朴实的游戏,却不知道实现了多少人儿时心中的英雄梦。

随着计算机技术的不断发展,一个个令人惊叹的游戏不断出现,从很早的《金庸群侠传》《星际争霸》《暗黑破坏神》一直到现在那些我也无法叫上名字的新游戏。各种新游戏不断出现,新游戏画面越来越精美,情节越来越丰富,这一切都得益于游戏开发引擎的不断进步。

可以毫不夸张地说,游戏开发者其实就是在扮演着“造梦者”的角色。这个职业在绝大多数人的眼中是极为神秘的,他们应该每天在高耸的大楼里面无休止地敲打着键盘,用那些普通人无法理解的代码创造着一个又一个的虚拟世界。

其实这是一个误解,很多游戏就是游戏开发者在家里利用业余时间完成的,有些开发者甚至可能只懂一点编程的知识。怎么样?听到这些是不是觉得很吃惊。其实每个人都可以成为游戏开发者,需要什么呢?只要有一个Unity 3D就足够了!

Unity 3D是什么呢?严格来说,这是一款游戏开发的引擎。关于它的详细描述,读者可以自行了解。如果要我来描述Unity 3D,只有两个词——简单、强大。如果你有一家公司、一个水平高超的开发团队及一笔不菲的开发基金,那你就可以用Unity 3D开发出一款脍炙人口的游戏。如果你什么都没有,那么凭着对游戏开发的热情,在家里,一个人从头学起,Unity同样可以帮助你开发出一款风靡世界的游戏!

本书的作者Alan Thorn是一位职业游戏开发者,他所开发的游戏《维塔德男爵:灭绝的复仇》受到了很多人的欢迎。难能可贵的是,他在本书编写中详细地分享了使用Unity开发游戏的案例过程。Alan Thorn绝对是一位与众不同的作家,在他的作品中,极少提到技术以外的问题,全书都是详实的案例,毫无赘言,所有案例准确细致,由浅入深。这一点让我在本书的翻译过程中受益匪浅。

感谢人民邮电出版社的编辑胡俊英,在本书编写的这段时间中她始终支持我的写作,她的鼓励和帮助引导我能顺利翻译完成全部书稿。最后感谢我的母亲,是她将我培养成人,并在人生的每一个关键阶段给我提供帮助;感谢我深爱的妻子及我可爱的儿子,感谢你们在我翻译本书的时候,给我无条件的理解和支持。

——李华峰


视频游戏作为一种文化现象,在过去半个世纪已经在世界范围内吸引并娱乐了数十亿人。视频游戏作为一种产业和文化,无论对于开发者还是艺术家来说,都是一个令人兴奋的所在。通过游戏,你的意愿、想法和工作可以去影响更多的人,并且以一种前所未有的方式来塑造和改变一代又一代的人。在最近的一段时间里,兴起了一股游戏开发的平民化运动,目标是游戏的开发过程要更简单,游戏本身要更容易、更广泛地被人们所接受,包括开发者在预算有限的情况下,在家里就能从事游戏开发的工作。推动这项运动的倡导者就是Unity引擎,这也是本书的主题。Unity引擎是一个计算机程序,它可以与你现有的资源途径(例如三维建模软件)协同工作,用来编写可以在多平台和设备,包括Windows、Mac、Linux、Android、iOS和Windows Phone上都能正常工作的视频游戏。使用Unity引擎,开发者可以导入现成的资源(例如音乐、贴图、3D模型等),然后将它们组装成一个有机的整体,通过一个全局的游戏逻辑形成一个游戏世界。Unity引擎是一个令人着迷的程序。最新版本的Unity可以免费下载和使用,而且它可以十分有效地和其他程序(例如GIMP和Blender)协同工作。本书着眼于Unity引擎以及如何使用它来开发一个好玩并且有趣的游戏。学习Unity并不需要什么基础,但是必须对编程语言有一定的了解(如JavaScript、ActionScript、C、C++、Java或者C#)。现在我们以章节的形式来看看本书中都涵盖了哪些内容。

本书将在实践中探讨Unity引擎的使用,通过具体的游戏实例,一步步地带你进行游戏开发。本书共有8章,重点讲解了4个迥然不同的案例,每两章完成一个案例。接下来,我们来看看这些案例的具体内容。

第1章以第一人称视角金币采集游戏开始了我们的Unity开发之旅。如果你此前对Unity完全不了解并且准备开始你的第一个游戏,那么这就是一个绝佳的切入点。

第2章紧接第1章的内容,并完成了这个游戏。这里假设你已经完成了第1章的内容,并且完成了整个的项目,就为下一章的开始做了铺垫。

第3章标志着第二个项目的开始,我们将专注于一个太空射击游戏的开发。在这里,我们创建一个游戏,在游戏中玩家将要面对迎面而来的敌人。

第4章完成了整个的太空射击项目,这一章的内容紧随上一章,并为项目添加最后的加工。

第5章展示了二维游戏和UI功能的风采。在这里,我们将通过开发一个依靠二维物理属性的侧视图游戏来探讨Unity强大的二维功能。

第6章完成上一章开始的二维冒险游戏,为此进行了最后的完善,并根据总体游戏逻辑将各个环节连接在一起。通过这个实例我们可以很清晰地了解游戏中的各个部分和各个方面是如何有机地形成一个整体的。

第7章着重研究人工智能,创造了一个可以巡逻、追逐并攻击的敌人,而且也实现了这些敌人的导航功能。

第8章结束了上一章的人工智能项目,和本书的其他内容成为一个有机的整体。在这个项目中,我们将会看到如何使用有限状态机来实现一个强大的人工智能,这个人工智能可以被应用在各种情况下。

本书几乎包含了学习Unity的所有内容。每一章都提供了注重实用性的真实Unity开发案例,并提供了可以下载和使用的配套文件。除了本书和你的学习兴趣之外,你还需要下载一个最新版本的Unity软件。本书在编写的时候,Unity的最新版是5.3.1。这个软件的个人版(personal edition)可以免费下载和使用,下载地址为https://unity3d.com/ 。除了Unity之外,如果创建道具、人物角色,或者其他的3D资源,可能需要3D建模软件和动画软件,例如3DS Max、Maya或者Blender等,可能还会需要图像编辑软件,例如Photoshop和GIMP等。Blender是一款免费的软件,可以从http://www.blender.org/ 下载。GIMP也是一款免费的软件,可以从https://www.gimp.org/下载。

本书十分适合那些没有Unity和游戏开发经验,但却希望将游戏开发作为一种爱好或者职业的读者。你也许已经有了一些与游戏开发无关的程序编写或者脚本编辑基本知识,例如C、C++、C#、Java、JavaScript、ActionScript、Python或者其他面向对象编程语言的编程能力。此外,你至少应该要有一些基本的关于游戏开发的重要概念。例如,本书假设你已经知道了3D模型、贴图、音频文件、可执行文件等基础性知识。本书会对这些概念进行简单的讲解,但是不会深入。本书将专注于Unity作为软件开发游戏的功能,每一章都是一篇可以深入研究的教程,并提供了一个可以试玩和扩展的游戏。

本书中会有很多的文本样式,这些样式用来区分不同种类的信息。下面给出了这些样式的一些实例以及它们含义的解释。

代码段将按照如下格式显示:

using UnityEngine;
using System.Collections;

public class Coin : MonoBehaviour
{
  // Use this for initialization
  void Start () {}

  // Update is called once per frame
  void Update () {}
}

当代码块中的特定部分需要注意时,相关的行或者条目会被设置为粗体:

using UnityEngine;
using System.Collections;

public class Coin : MonoBehaviour
 {
  // Use this for initialization
  void Start () {
     Debug.Log ("Object Created");
  }

  // Update is called once per frame
  void Update () {

  }
}

新名词和重要的词汇,例如“菜单”“对话框”等将会以粗体显示,显示格式如同“你将需要创建一个新项目”。

 

警告或重要的注释出现在这样的括号里。

 

 

提示和技巧出现在这样的括号里。

我们欢迎读者的反馈意见。无论你对本书有什么想法,喜欢或者不喜欢哪些内容,都可以告诉我们。这些反馈意见对我们创作出对大家真正有所帮助的作品至关重要。

你可以将一般的反馈以电子邮件的方式发送到feedback@packtpub.com ,并在邮件主题中包含书名。

如果你在某一方面很有造诣,并且愿意著书或参与合著,可以参考我们的作者指南www.packtpub.com/authors。

现在你已经是Packt图书的尊贵读者了,我们会尽力帮读者充分利用手中的图书。

可以使用账号在http://www.packtpub.com 下载本书的样例代码。如果是通过其他途径购买的本书,则可以访问网址http://www.packtpub.com/support 进行注册,这些文件会直接发送到你的邮箱中。

可以按照如下步骤下载代码文件。

1.使用邮箱地址和密码在我们的网站登录或者注册。

2.将鼠标移动到顶部的“SUPPORT”选项卡上。

3.单击“Code Downloads & Errata”。

4.在查找对话框中输入本书的名字。

5.选中要下载代码文件的书名。

6.从下拉列表框中选中购买本书的途径。

7.单击“Code Download”。

当文件下载之后,需要使用如下软件的最新版来对这个文件进行解压。

Windows操作系统下的WinRAR / 7-Zip

Mac操作系统下的Zipeg / iZip / UnRarX

Linux操作系统下的7-Zip / PeaZip

我们同样提供了使用本书的PDF彩图版,彩色的图片可以更有效地帮助读者理解书中的内容,可以从下面的这个地址下载本书的彩图版https://www.packtpub.com/sites/default/ files/downloads/Unity5xByExample_ColorImages.pdf

虽然我们已尽力确保本书内容正确,但错误仍在所难免。如果读者在书中发现任何文字或者代码错误,就请将这些错误提交给我们,以便帮助我们改进本书的后续版本,避免其他读者产生不必要的误解。如果读者发现了错误,请访问http://www.packtpub.com/ submit-errata,选择相应图书,单击errata submission form链接,然后填写具体的错误信息即可。勘误一经核实,读者的提交将被接受,此勘误将被上传到本公司网站或添加到现有勘误表中。读者可以通过在http://www.packtpub.com/support 上选择书名来查看本书所有的勘误表。

版权问题是每一个媒体都要面对的问题。Packt非常重视版权的保护。如果读者发现我们的作品在互联网上以任何形式被非法复制,请立即告知我们相关网址或网站名称,以便我们采取措施。

请把可疑盗版材料的链接发到copyright@packtpub.com。

非常感谢你帮助我们保护作者的权益。

如果你对本书有任何方面的疑问,可以通过questions@packpub.com 联系我们,我们将尽最大的努力解决。


本章将会以一个十分有趣的采集类游戏作为Unity的入门。即使你以前从来都没有使用过Unity这个游戏引擎,也无需有任何的担心。我们的教程将会一步一步地来指导你熟悉Unity。到了下一章结束的时候,你就可以利用所学的内容构造出一个比较简单但是功能相当完善的游戏。这将是一个非常重要的过程,因为在从始至终的开发中,你将会了解到整个游戏的实现流程。

在本章中将会学习到如下内容:

现在就来开始游戏的开发之旅吧。首先,这是一个第一人称视角的游戏,玩家必须在规定时间内通过不断地移动去采集所有的金币。如果玩家在时间耗尽时还没有采集到所有金币,则视为玩家失败,游戏结束。如果玩家在时间耗尽之前就将全部的金币采集完毕,则视为玩家胜利。这款游戏的方向控制采用标准“WASD”的模式,按下“W”键向前运动,按下“A”键向左运动,按下“S”键向右运动,按下“D”键向后运动。视角的变换由鼠标控制,当走到金币的位置时就可以完成金币的采集。如图1.1所示,采用Unity编辑器实现对游戏行为的设计。开发这个游戏的最大优势就在于,通过整个的开发过程,可以了解到Unity的全部关键特性,同时无需使用任何额外的软件来产生游戏所需的资源(Asset),例如贴图(Textures)、网格(Meshes)以及材质(Materials)等。

图1.1 金币采集游戏(最终完成效果)

 

本章和下一章介绍了这个金币采集游戏的开发过程,关于这个金币采集游戏的完整代码,可以在本书的配套文件Chapter01/CollectionGame目录下找到。

当在Unity中创建一个新游戏时,如现在的金币采集游戏,都需要先创建一个新的项目。一般来说,Unity中的“项目”这个词就等同于“游戏”。在Unity中有两种方法来创建一个新的项目,但其实这两者并没有太大的差别。在打开的Unity的图形化编辑界面中,可以看到一个场景或者关卡,从应用菜单上依次选择“File | New Project”,如图1.2所示。这时Unity会询问是否对当前已经打开的项目进行保存,这时需要根据自己的实际情况来选择“Yes”或者“No”。在选择完“New Project”以后,Unity将会出现一个项目创建向导,根据这个向导的要求,就可以一步一步完成项目的创建工作了。

图1.2 使用主菜单来创建一个新的项目

如果是第一次启动Unity,则会看到一个欢迎的对话框,如图1.3所示。在这个对话框中,可以通过选择“NEW PROJECT”按钮来创建新的项目。

图1.3 Unity的欢迎界面

选择“NEW PROJECT”创建向导之后,Unity就会按照指定的一些基本设置来创建一个新的项目。此处需要填写项目的名称(例如CollectionGame),然后在电脑上选择一个用来保存自动生成项目文件的文件夹。最后,注意3D和2D的这两个按钮,当单击3D按钮时,这意味着要创建的是一个三维游戏;如果单击的是2D按钮,这意味着将要创建一个二维的游戏,所有这些步骤都完成之后,需要单击“Create project”按钮来完成整个项目的生成过程,如图1.4所示。

图1.4 创建一个新的项目

现在Unity已经创建了一个空白的新项目。这里就是开发一个新游戏的起点,在这个新创建的项目中并不包含任何的东西,没有任何的网格,贴图或者其他资源。这一点只需要检查一下位于Unity编辑器界面下方的项目(Project)面板区域就可以确定。这个项目(Project)面板中会显示项目文件夹中的全部资源,同时它也对应着一个本地驱动器上的文件夹,而这个文件夹就是在之前项目向导中所创建的。这个文件夹如图1.5所示,现在也是空的。在这个游戏的开发过程中,还会使用到这个项目(Project)面板中的更多功能。

图1.5 位于Unity开发界面的底部的项目(Project)面板

 

如果现在使用的Unity开发界面的布局与图1.5所示的有些不同,那么可以选择将用户开发界面的布局进行重置,恢复为默认布局。只需要单击开发界面右上角的布局下拉菜单,然后再选择“Default”,见图1.6。

图1.6 切换成默认的开发界面布局

可以在Windows操作系统下的资源管理器或者Mac操作系统中的Finder直接查看到项目文件夹中的内容。要做到这一点,只需要在Unity编辑器上的项目(Project)面板中单击鼠标右键,这时就会显示出一个内容菜单,然后在这个菜单上选择选项“Show in Explorer”,这个过程如图1.7所示。

图1.7 使用项目(Project)面板显示项目文件夹

单击“Show in Explorer”可以在默认的系统文件浏览器中对文件夹中的内容进行浏览,如图1.8所示。以这种方式查看文件,可以更容易地实现对文件的检查、清点或者备份操作。但是,不要在Windows操作系统下的资源管理器或者Mac操作系统中的Finder中对文件夹中的内容进行修改,尤其是不要进行移动、重命名或者删除文件的操作,这样操作可能会导致Unity项目损坏。如果进行文件的删除和移动操作,就在Unity编辑器中的项目(Project)面板中进行。只有这样操作,Unity才会同时对它的元数据进行更新,以确保项目能继续正常工作。

图1.8 从操作系统的文件浏览器中查看项目(Project)面板的内容

 

如果是从操作系统的文件浏览器中查看项目文件夹,就可以看到一些在项目(Project)面板中看不到的文件和文件夹,例如Library文件夹、Project Settings文件夹或者Temp文件夹。这些文件夹中保存的都是项目的元数据。这些文件并不是项目直接的组成部分,但是却包含一些额外的设置和首选项信息,这些信息保证了Unity能正常运行。注意,不要试图对这些文件夹和其中的文件进行修改或者编辑。

资源是游戏开发中的原材料,也就是组成游戏的模块。Unity中的资源包括网格(或者3D模型)、贴图、各种音乐和音效、场景,其中网格包括各种人物、道具、植物、建筑物等;贴图包括各种JPEG文件和PNG文件,这些图片决定了网格的外观;音乐和音效用来提高游戏真实性和气氛;场景(Scene)是一个网格、纹理、音乐、音效等协同工作而组成的独立系统。因此可以这样说,游戏不能没有资源,否则这些游戏看起来空虚而又无趣。基于这个原因,金币采集游戏也同样需要资源。毕竟这个游戏需要一个可以在其中进行移动和采集操作的环境。

Unity只是一个游戏开发引擎,而并非是一个资源开发软件。这意味着在游戏中需要的资源(例如各种人物、道具),通常是由一些设计者使用其他软件开发出来的。然后,这些设计者将这些制作好的资源导出,并传递给Unity。Unity则负责将这些资源有机地组合到一个游戏系统中。这些用来设计3D模型资源的第三方软件包括Blender(这是一款免费的软件)、Maya或者3DMAX。而对于纹理的创建,可以选择Photoshop或者GIMP(这也是一款免费的软件)。至于音效的制作,可以选择使用Audacity(这款软件同样是免费的)。当然除了这些软件以外,还有更多的选择。这些软件的具体内容并不在本书的范围内。通常,Unity引擎会认为已经有了可以导入到游戏中的资源。例如,在金币采集的游戏中将使用Unity所提供的资源。现在,将这些资源导入到这个项目中,为此,需要首先选择菜单栏上的“Assets”选项,然后在下拉菜单上选中“Import Package”,然后如图1.9所示依次选中“Characters”“ParticleSystems”“Environment”和“Prototyping”等选项。

图1.9 使用资源包导入(Import Package)菜单完成资源导入操作

当在菜单中进行一个资源包的导入操作时,都会出现一个导入操作的对话框。在这个对话框中无需进行任何改动,保留里面设置的默认值,然后单击“Import”按钮即可。这个过程如图1.10所示。

图1.10 选择需要导入的资源

默认情况下,Unity会从资源包(一个资源的库文件)中的所有文件解压缩到当前项目中。在导入操作结束后,各种各样的大量资源和数据将会添加到项目中,以后可以随时使用这些文件,它们都是原来文件的副本。所以,无论对项目中的哪个文件进行更改,都不会影响到原来文件。这些文件包括各种模型、声音、贴图等。图1.11所示为在Unity项目(Project)面板的编辑器中列出的这些文件。

图1.11 从项目(Project)面板中对导入的资源进行浏览

 

当在应用菜单上依次选择了“Assets | Import”之后,如果什么资源包都没有,那么可以从Unity的主页https:// Unity3d.com/上下载和安装这些资源包。打开这个主页之后,首先选择“Additional Downloads”,然后在如图1.12所示的页面上选择“Standard Assets”。

图1.12 标准资源包(Standard Assets)的下载

然而导入的资源并没有存在于游戏之中,没有出现在屏幕上,也不能做任何事情!它们只是被简单地添加到了项目(Project)面板,其实这些资源现在更像是一个库或者说是资源的仓库,可以从其中进行选择来完成游戏的建立。到目前为止,这些资源已经被内置到了Unity之中,在之后的章节中将继续使用它们,来完善金币采集游戏的功能。如果想了解关于每个资源的详细信息,可以使用鼠标单击选中这个资源,这时关于这个资源的详细信息就会出现在Untiy编辑器右侧的检查(Inspector)面板中。检查(Inspector)面板是一个位于开发界面右侧的属性编辑器,它是上下文敏感的,并会自动切换为显示所选对象的属性,如图1.13所示。

图1.13 检查(Inspector)面板显示了当前选中物体的所有属性

现在已经创建了一个Unity的项目,并且利用Unity的标准资源包导入了一个很大的资源库,包括一些建筑类网格,例如墙、地板、天花板、楼梯等,将使用这些资源建立第一个关卡(level)。记住,在Unity中,一个场景(Scene)往往也就意味着一个关卡。场景和关卡这两个词汇在这里是没有区别的,都是指一个三维空间,也就是游戏发生的时空。现在来创建一个金币采集游戏的场景,首先从应用程序菜单依次选择“File | New”,或者也可以在键盘上按下“Ctrl + N”组合键。当完成了这个操作之后,一个新的空白的场景就被创建好了。可以在占据了Unity开发界面最大部分的场景(Scene)选项卡中看到场景的预览,如图1.14所示。

图1.14 场景(Scene)选项卡中给出了一个3D世界的预览

 

如图1.14所示,在Unity中的场景(Scene)选项卡以外的其他选项卡也是可见的,并且可以使用的。这些选项卡包括游戏(Game)选项卡和动画(Animator)选项卡。有时,选项卡的数目可能会更多。不过现在可以忽略除了场景以外的其他所有选项卡。场景(Scene)选项卡是为了能够在游戏的开发中简单快速地查看一个关卡。

每一个新的场景都是空的,严格来说几乎是空的。默认情况下,每一个新的场景都是由两个对象开始的,首先添加一个用来照亮其他任意对象的光源对象(Light),然后是一个用来从特定角度对场景中的内容进行显示和渲染用的摄像机(Camera)。可以使用开发界面(见图1.15)左侧的层次(Hierarchy)面板来查看所有在场景中对象的完整列表。这个面板上显示了场景中所有对象的名称。在Unity中,游戏对象的含义就是在场景中存在的单一、独立的东西,无论它们是可见的还是不可见的,例如网格、光源、摄像机、道具等。因此,只有从层次(Hierarchy)面板才能看到游戏场景中的一切。

图1.15 层次(Hierarchy)面板

 

也可以通过在层次(Hierarchy)面板中单击游戏中对象的名称来选中它们。

接下来为这个场景添加一个地面(Floor)。要知道,游戏中的玩家总得站在一些东西上面。可以使用第三方的建模软件(如Maya、3DS Max或者Blender)来创建一个地面网格。不过,在之前导入过的标准资源包中就含有可以使用的地面网格,这是相当方便的。这些网格都是原型软件包(Prototyping Package)的一部分。如果想通过项目(Project)面板完成对这些资源的访问,可以双击打开“Standard Assets”文件夹,然后访问“Prototyping | Prefabs”文件夹。如图1.16所示,可以看到这些对象,并从检查(Inspector)面板中对它们进行预览。

图1.16 包含了大量用于快速建立场景的网格的标准资源包/原型包

 

你也可以在应用程序菜单上依次选择GameObject|3D Object|Plane,这样可以更加方便地向场景(Scene)中添加地面(Floor)。不过,加进来的这个地面看起来一片灰暗,给人一种枯燥无味的感觉。当然,这个地面的外观是可改变的。正如我们在后面即将看到的一样,Unity中允许对物体外观进行改变。不过,在本书中,我们将从项目面板(Project panel)中的标准资源包里找到一个专门的地面网格(floor mesh)来使用。

图1.16所示的名为“FloorPrototype64x01x64”的网格十分适合作为一个地面,现在只需要简单地将项目(Project)面板(Panel)中的对象拖曳到场景(Scene)视图中,然后释放鼠标即可完成这个网格的添加工作,如图1.17所示。当在进行这个操作时要注意,在三维空间中新添加了网格之后,场景(Scene)视图中发生的变化,同时也要注意到这个网格的名称也出现在了层次(Hierarchy)面板的列表中。

图1.17 将网格资源从项目(Project)面板中拖动到场景视图中

至此,现在项目(Project)面板上的网格资源已经被实例化为一个场景中的物体。这意味着一个项目(Project)面板上的副本已经作为一个独立的游戏对象加入到了场景之中。地面的实例(或者对象)依附于项目(Project)面板中的“floor”资源。但是反过来,这些资源却并不依附这些实例。这意味着当在场景中删除这些实例(或者游戏对象)时,这些资源并不会被删除。反之,当删除了这些资源时,场景中的这些实例就会被删除。如果场景中需要更多的地面,就可以多次从项目(Project)面板中向场景(Scene)视图拖动“floor”资源。每进行一次这种操作,都会在场景中添加一个单独的游戏物体。虽然这些游戏物体都依附于同一个“floor”资源,但是它们相互之间都是独立的(见图1.18)。

图1.18 向场景中添加多个“floor”网格

实际上,并不需要这么多重复的地面,所以先把它们删除。删除这些地面的方法是先在场景(Scene)视图中逐个单击这些“floor”对象,然后在键盘上按下删除键。另外记住,也可以通过单击层次(Hierarchy)面板上这些游戏物体的名字,然后按下删除键。不管使用的是哪一种方法,如今在场景中都只剩下了唯一一个“floor”对象。现在还有一个问题,那就是这个“floor”对象的名字问题。在层次(Hierarchy)面板上可以清楚地看到这个“floor”对象的名字是“FloorPrototype64x01x64”,这个名字很长,意义又不够明确,而且调用的时候又不方便。因此应该将这个名字改为一个更容易管理的,而且意义更明确的,这样才能使工作更加有条理,也更容易管理。Unity中有很多种可以将对象重命名的方法,首先选中对象,然后在其检查(Inspector)面板中的名称字段处输入新的名字。这里将这个对象重命名为“WorldFloor”,如图1.19所示。

图1.19 对“floor”网格进行重命名

到现在为止,一个地面网格已经建立好了,但是仅仅这一个游戏对象还是很无趣的,还需要向其中添加更多的内容,例如建筑物、楼梯、柱子或者更多的地面。否则,玩家在这个游戏中就没有能进行探索的世界了。在开始创建之前,首先要先确认现在的地面处在了整个世界的中心位置。场景中的任何一个位置都有一个唯一的坐标,这个坐标(x,y,z)值指的是距离世界中心点(原点)的距离。在检查(Inspector)面板中可以看到当前选中对象的坐标。事实上,在Unity中,一个对象的位置(Position)、角度(Rotation)和尺寸(Scale)同属于一个名为变换(Transform)的类别。位置表示的是一个对象距离3个坐标轴的距离。角度表示一个对象绕着它的中心轴旋转的角度。尺寸表示一个对象应该缩小到多小或者扩大到多大。默认的尺寸指的就是对象正常的大小,尺寸为2表示扩大到原来两倍的大小,尺寸为0.5表示缩小到原来的一半,以此类推。这样,一个对象的位置、角度、尺寸共同构成了它的变换属性。如果想改变一个选定对象的位置,可以在xyz位置字段输入新的值。例如将一个对象移动到游戏世界的中心,就可以输入(0,0,0),如图1.20所示。

图1.20 将一个对象移动到游戏世界的中心点

如上所示,通过输入适当的值,当然这个值要在系统许可的范围内,就可以为对象指定准确的位置。不过,使用鼠标来直接移动对象往往更为直观。现在就来完成这个操作,首先添加第二个floor对象,这个floor对象的位置要远离第一个floor对象。先从项目(Project)面板上拖曳一个floor对象到场景中,然后单击这个新的floor场景以选中它,然后按下键盘上的“W”键,或者单击编辑器界面顶端的变换工具图标来切换到变换工具。变换工具允许对场景中的对象进行重新定位,如图1.21所示。

图1.21 打开变换工具

当选定一个游戏对象,并且打开变换工具以后,一个标识(Gizmo)就出现在游戏对象的中心部分,在场景(Scene)选项卡中可以看到标识(Gizmo)其实是由3个不同颜色的彩色轴组成的。其中,红颜色的轴代表x轴,绿颜色的轴代表y轴,蓝颜色的轴代表z轴。如果想移动一个游戏对象,首先要将光标悬停在这3个轴中的一个轴(或者两个轴之间的平面)上面,然后单击并按住鼠标,同时向目标方向拖动。可以反复地进行这个操作,将游戏对象移动到指定的位置。现在就用这个方法来拖动第二个floor对象,使其远离第一个floor对象,如图1.22所示。

图1.22 使用标识(Gizmo)来完成对一个游戏对象的变换操作

也可以使用鼠标来完成对游戏对象的转动和缩放。在键盘上按下“E”键可以启动旋转工具,按下“R”键可以启动缩放工具,或者也可以在编辑器界面的上方单击它们对应的工具栏图标来启动这些工具。当工具启动之后,一个标识(Gizmo)就出现在这个对象的中心部分,可以通过单击和拖动鼠标来将这个对象旋转到指定角度,或者缩放到指定尺寸,如图1.23所示。

图1.23 旋转和缩放工具的选择

在Unity中进行开发时,使用键盘和鼠标对物体进行快速的移动、旋转和缩放是非常重要的。所以,最好习惯使用键盘的快捷键来完成这些操作,而不是总用鼠标去单击工具栏。不过,除了要对游戏对象进行移动、旋转和缩放以外,可能还需要频繁地变换自己的视角,这样才能从不同的位置、角度和视角对这个世界进行观察。这意味着必须要经常改变场景中的摄像机。当要看清楚一个游戏对象时,就会需要进行放大或缩小的操作。如果想准确地将游戏对象对齐并结合在一起,就需要改变观察的视角。如果想完成这些操作,就需要充分地将鼠标和键盘结合在一起使用。

如果想靠近或者远离正在观察的对象,只需要向上或者向下滑动鼠标滚轮,就可以实现放大或者缩小目标对象,过程如图1.24所示。

图1.24 放大或者缩小操作

按住鼠标的中键然后将鼠标向适当的方向移动,如向上移动,这样就可以将场景(Scene)视图向上移动。同样,向另外3个方向移动鼠标也可以将场景(Scene)视图向这3个方向移动。或者也可以在应用工具栏中(或者使用键盘上的Q快捷键)激活移动工具,如图1.25所示。当这个移动工具处于激活状态的时候,就可以十分简单地单击并拖动整个场景(Scene)视图。注意,这个操作并不会改变场景的大小,而只是沿着向左、向右或者向上、向下的方向滑动摄像机。

图1.25 激活移动工具

有时在构建关卡时,可能会找不到所需要的对象。例如,此时摄像机可能正在关注一个与目标完全不同的地方,这个地方并不是所要单击或者看到的。如果遇到了这种情况,必须要改变相机的视角来找到目标对象。如果将这个目标对象处在整个场景的中心,就必须不断地进行改变位置和旋转视图的操作。但是如果要自动地完成这一切,可以有一种更简单的方法,那就是从层次(Hierarchy)面板上选中这个对象的名字。然后,按下键盘上的“F”键,也可以直接在层次(Hierarchy)面板上直接双击来完成这个操作,如图1.26所示。

图1.26 一个选定的对象

当选中了一个游戏对象之后,可能会经常性地对这些对象进行旋转操作,以便能从所有的重要视角来对其进行查看,只需要单击鼠标,并在拖动的同时按住键盘上的“Alt”键,就可以完成视角的转动,如图1.27所示。

图1.27 绕着一个选中的游戏对象进行旋转

最后,在场景(Scene)视图中使用第一人称视角控制是十分实用的,像在玩一款第一人称视角的游戏一样,这将有助于你以一种身临其境的方式来体验整个现场。如果想实现这个功能,可以在按下鼠标右键的同时使用键盘上的“W”“A”“S”“D”来控制前进、后退或者左右转动。使用鼠标来控制头的方向。另外,还可以在运动的同时按下“Shift”键来提高运动的速度,如图1.28所示。

图1.28 使用第一人称视角控制

这里之所以选择对各种进行变换和导航控制的方法进行学习,就在于掌握了这些知识之后,你就可以以任何方式来定位和移动目标对象,也可以从任何的位置和角度来查看整个世界。如果想快速地建立一个高品质的游戏关卡,做到这一点是十分重要的。所有的这些以及一些其他的控制方式都将贯穿于这本书的场景创建和Unity开发之中。

现在已经成功地实现了对象的属性变换和在场景视图中的导航。接下来可以开始完成金币采集游戏的第一个关卡了。首先将空间中的两个floor网格对象分开,在它们之间留一个大的裂缝,将来会在这个裂缝上创建一个桥梁。玩家将可以通过这个桥梁往返于两个如同岛屿一样的floor网格对象之间。可以使用变换(Translate)工具(W)来移动物体,如图1.29所示。

图1.29 将两个floor网格对象分隔成独立的岛屿

 

如果创建更多的floor对象,可以使用之前用过的方法,从项目(Project)面板处拖动一个网格资源到场景视图中。另外,也可以将在场景视图中选中的对象进行复制,复制的方法是在选中目标对象之后,同时按下键盘上的“Ctrl+D”组合键,这两种方法都可以达到目的。

接下来,要向场景中添加一些道具(Props)和障碍物(Obstacles),首先向floor网格对象上添加一些房屋(House)对象,可以按照“Assets | Standard Assets | Prototyping | Prefabs”这个目录找到房屋对象(HousePrototype16x16x24),如图1.30所示。

图1.30 向场景中添加房屋道具

当将一个房屋拖动到场景中之后,这个房屋可能会与地面的底部完美的对齐,但是也有可能不会,如果对齐了,那你的运气还真是不错。但是我们不可能每次都有这么好的运气,一个专业的游戏开发者应该依靠自身的技能,而不是运气。不过别担心,在Unity中,可以轻松地使用顶点捕捉(Vertex Snapping)来将任意的两个网格对象进行对齐。这个功能的工作原理是使两个游戏中的对象通过将它们的顶点定位到同一个位置来实现对齐操作。

这里以图1.31为例,在这张图中房子歪歪扭扭地坐落在floor对象之上,我们很自然地希望将它和floor对象进行水平对齐或者角对齐。要实现这个效果,我们首先要选择房子(House)对象,这一点可以通过单击或者在层次面板中选择这个对象来完成,要注意进行选中操作的应该是要移动的那个对象,而不是那个用来对齐的参照物(此处特指floor对象)。

图1.31 使用顶点捕捉(Vertex Snapping)来对没有对齐的物体进行调整

接下来,激活变换工具(W),并且按下键盘上的“V”键,同时移动光标,观察选中网格中最近顶点的标识(Gizmo)光标,如图1.32所示。Unity会要求选择一个进行捕捉操作的源顶点。

图1.32 按下“V”键来激活顶点捕捉(Vertex Snapping)

按下键盘上的“V”键,同时把光标移动到房屋底部角的位置,然后单击并从角拖动到floor网格的角。然后房屋将会完成与floor网格角对角的顶点捕捉对齐,当完成了这种对齐之后,释放“V”键,此时两个网格的顶点已经对齐了,如图1.33所示。

图1.33 通过顶点对齐两个网格

现在,可以使用原型包(Prototyping Package)中的网格资源来搭建一个完整的场景了。通过向场景中拖放一些道具,并使用变换、旋转以及缩放操作,就可以实现对这些游戏对象的定位、排放和转动。使用顶点捕捉,可以按自己所愿将所有游戏对象进行对齐。多练习一些这种操作,使用这些工具和资源可以完成图1.34所示的场景安排。

图1.34 构建一个完整的关卡

现在已经完成关卡中基本建筑的模型导入和布局摆放了,只使用很少的几个网格资源和一些基本工具就完成了这些操作。不过,这些工具的功能却是相当强大的,将这些工具组合操作,可以让游戏世界变得丰富多彩,甚至以假乱真。不过,这里还遗漏了一点很重要的事情,那就是光。仔细观察图1.34,这里所有的物体看起来都是单调的,没有光亮、阴影。这是因为场景中并没有配置合适的照明系统,虽然在游戏创建的时候已经默认自带了一个光源对象(Light),但是这个光源对象(Light)现在并没有起什么作用。

现在这个场景中还没有天空,接下来就为这个金币采集游戏添加上一个天空(Sky)效果,单击场景(Scene)视图顶部工具栏的下拉菜单,然后从下拉菜单中选中“skybox”以启用天空效果。这里的天空盒(Skybox)指的就是一个包围了整个场景的大型立方体,这个立方体的每个内部的侧面都有一个贴图(图像),这些贴图连续在一起,看起来就如同现实中的天空一样。这里选用了“skybox”,就在场景(Scene)视图中显示了一个如图1.35所示的默认的天空。

图1.35 启用天空效果

虽然天空效果的启用使得整个场景看起来好多了,但是它却仍然缺乏一个合适的照明系统。场景中的游戏对象缺乏光亮和阴影,为了改善这一点,首先要打开场景(Scene)视图上方(见图1.36)的照明图标开关,确保照明系统是启用的。注意,这个开关只是决定照明系统是否在场景(Scene)视图中起作用,而决定不了在最后的游戏中照明系统是否起作用。

图1.36 在场景(Scene)视图中启用场景照明系统

启用了照明系统之后,就会明显地看到场景与之前有了很大的差异,现在的场景看起来真实多了。也可以选择层次(Hierarchy)面板上的“Directional Light”来进行旋转操作,以确定场景中的灯光效果。众所周知,一天中的不同时间里,光线的强度和角度都是不同的,现在就可以利用这种操作来实现不同时间中光线的变化。这样将会改变场景的渲染方式,如图1.37所示。

图1.37 转动场景中的方向光(Directional Light)模拟一天中时间的变化

如果要撤销之前对方向光(Directional Light)做的任何操作,可以按下键盘上的“Ctrl + Z”组合键。为了做好最终及最佳的照明效果,需要把场景中所有不能移动的游戏对象(例如墙壁、地板、桌子、椅子、天花板、草、山、塔等)都标记为静态的。对于Unity来说,意味着标记为静态的游戏对象在整个的游戏过程中,无论发生什么,都是永远不会被移动的。通过将游戏对象标记为静态,就可以帮助Unity在对游戏渲染和照明处理时进行优化。现在只需要在场景中选择所有不能移动的对象(这包括了目前为止游戏中的所有对象),然后在对象的检查(Inspector)面板中选中静态(Static)复选框。注意,无需分别地设置静态属性。在为多个对象设置静态属性时,只需要在选择这些对象的,同时按下Shift键就能选中所有对象,然后就可以一次性地在对象的检查(Inspector)面板中为这些对象设置属性,如图1.38所示。

图1.38 为多个不能移动的对象设置静态选项以改善照明和系统性能

当为一个几何图形的游戏对象勾选了静态(Static)复选框之后,Unity将会自动为场景中的光线效果进行计算,这些效果包括阴影、间接照明等。通过计算得到了一些被称为“GI缓存”的数据,这些数据中包含了光的传播路线,向Unity指示了光线应该如何在场景中进行传播和反射,才能更真实地模拟现实世界。即便如此,之前勾选了静态复选框的游戏对象还是没有阴影,这将使游戏看起来很不真实。产生这种情况的原因是大部分的网格对象都有一个产生阴影(Cast Shadows)的选项,而这个选项默认是禁用的。现在来消除这个缺陷,首先选中场景中的所有网格对象,然后在检查(Inspector)面板上单击“Mesh Renderer”组件并选中“Cast Shadows”复选框,当完成这个操作后,所有的网格对象就有如图1.39所示的阴影效果了。

图1.39 从“Mesh Renderer”组件上启用阴影效果

现在的网格对象都已经产生了阴影,下面就此开发过程做个小结。到目前为止,已经创建了一个新的Unity项目,使用网格对象填充了整个游戏场景,而且使用方向光(Directional Light)成功地完成了对场景的照明。如果使用第一人称视角模式在这个游戏环境中进行一番游历,那将会是非常棒的体验。好了,我们来看看接下来的操作吧!

至此,已经使用Unity自带原型包(Prototyping Package)完成了金币采集游戏环境的构建。现在的游戏环境包含了两个主要的岛屿,岛屿上有一些建筑物,两个独立的岛屿通过一个石桥连接在一起,如图1.40所示。可能你的布局与图1.40看起来有一些不同,不过也是非常不错的。

图1.40 目前已经包含了两个岛屿区域的场景

总的来看,场景还是完成得不错,现在需要把这个场景保存一下。可以选择按下键盘上的“Ctrl + S”组合键,或者也可以如图1.41所示,从应用程序菜单上选择“File | Save Scene”选项。如果是第一次对场景进行保存,Unity会弹出一个“Save”对话框,提示为这个场景起一个名字(这里本书将这个场景起名为“level__01”)。

图1.41 对场景进行保存

完成这个场景的保存之后,它就成为了当前项目的场景资源,同时也如图1.42一样出现在了项目(Project)面板中。这意味着当前的场景已经不再像以前是一个临时拼凑在一起的组合了,而是一个真正不可分割的整体。注意,对一个场景进行保存与对一个项目进行保存并不一样,比如在应用程序菜单上就分别有“Save Scene”和“Save Project”两个不同的选项。记住,一个项目是一些文件和文件夹的集合,它包含了游戏中的场景和资源。相比之下,一个场景是项目中的一个资源,外观就像是一个完整的3D地图。这个场景中包含了很多网格、纹理和声音。因此,保存一个项目是指将包括场景在其中的各种文件和资源的配置进行保存。而对场景的保存,只是将指定场景中关卡的变化进行保存。

图1.42 保存的scenes被作为资源添加到项目中

 

从图1.42可以看出,已经将场景保存到了一个名为“scenes”的文件夹中。可以在项目(Project)面板上的任意空白区域单击鼠标右键来为自己的项目创建一个文件夹。另外,依次单击应用程序菜单上的“Assets | Create | Folder”,就可以轻易地使用鼠标拖放来完成对文件夹中的资源进行移动操作。

现在的这个关卡中并没有任何可以玩的东西。它只是一个使用编辑器开发出来的静止的、了无生趣的且与外界毫无交互行为的三维环境。现在给游戏添加可玩性,允许玩家通过控制键盘上的“W”“A”“S”“D”以第一人称视角的方式在这个游戏世界中进行环游和探索。为了实现这个目标,需要向场景中添加一个第一人称视角角色控制器(First-person Character Controller),它是Unity中自带的资源,本身已经包含了实现第一人称视角角色控制的所有功能。依次打开“Standard Assets | Characters | FirstPerson Character | Prefabs”文件夹,然后从项目(Project)面板拖动“FPSController”资源到场景中,如图1.43所示。

图1.43 向场景中添加第一人称视角角色控制器

当添加了第一人称视角角色控制器以后,就可以单击Unity工具栏上的“Play”按钮以第一人称视角模式来进行游戏,如图1.44所示。

图1.44 单击工具栏上的“Play”按钮对Unity中的场景进行测试

按下“Play”按钮之后,Unity就可以从场景(Scene)选项卡切换到游戏(Game)选项卡,正如所看到的,场景(Scene)选项卡是以一个开发者的视角来观看整个场景的,此时可以对场景进行编辑、制作和设计。相比之下,游戏(Game)选项卡是以一个游戏者的视角来查看整个场景的,只能进行游戏和测试。在游戏者的视角中,只能从主摄像机的角度来查看所有的场景。当激活了游戏(Game)选项卡,并启动“Play”模式后,可以使用游戏默认的控制方式开始这个游戏的测试工作。第一人称视角控制器使用键盘上的“W”“A”“S”“D”4个键来控制运动,使用鼠标来控制视角,如图1.45所示。

图1.45 在游戏(Game)选项卡中完成对关卡的测试

 

即使处于“Play”模式中,也可以随时切换到场景(Scene)选项卡中,甚至可以对场景进行编辑和修改,对场景中的对象进行移动和删除。但是要注意的是,所有,在“Play”模式期间进行的改动,在“Play”模式结束之后都会自动复原,也就是这些改动都不会被保留。这是一种特意的设计,它允许在游戏过程中对属性进行编辑,从而可以观察产生的影响,并对问题进行调试,但是却并不真正地对场景进行改动。

现在的关卡中已经可以实现以第一人称视角模式行走了。当结束游戏时,可以再一次单击“Play”按钮,或者按下键盘上的“Ctrl+P”组合键,之后就返回到场景(Scene)选项卡中了。

 

Unity中还提供了用来暂停和恢复游戏的“Toggle-Pause”按钮。

到现在为止,你应该已经注意到了,当在关卡中以第一人称视角控制器进行游戏的时候,在下面的控制台(Console)窗口中不断地输出一些信息。默认情况下,这个窗口位于Unity编辑器的下方,就在项目(Project)面板旁边,也可以通过在菜单栏上依次选中“Window|Console”人为地打开这个窗口。控制台(Console)窗口会将所有遇到的错误或者警告以消息的形式显示出来。错误都会以红颜色标记,警告都会以黄颜色进行标记,而普通的信息则会以默认的灰色显示。有时,一条消息只会显示一次,而有时却会显示很多次,如图1.46所示。

图1.46 控制台(Console)窗口输出信息、警告和错误

正如之前提到过的,在控制台(Console)窗口中可以输出3种不同类型的消息:信息、警告和错误。信息是指基于当前工作的项目给出的最佳建议和意见。警告是指在代码或者场景中出现了一些不太严重的问题,如果不改正,则可能会导致游戏中出现意想不到的行为以及不佳的游戏性能。错误消息描述了需要立即引起注意的代码和场景中的错误。有时,这些错误导致游戏根本无法工作,或者在运行的时候发生错误,从而引起游戏崩溃或者失去响应。此时控制台(Console)窗口就显得十分有用了,因为它可以实现对游戏进行调试。图1.46中就给出了一个关于“Audio Listener”重复的问题。

“Audio Listener”是一个连接到摄像机对象的组件。默认情况下,每一个摄像机都有一个“Audio Listener”组件,它像是一个耳朵,在摄像机所在的位置接收场景中的各种声音。但是,Unity中并不支持在同一场景中使用多个处于激活状态的“Audio Listener”。这也就意味着在同一地点同一时间只能听到一个声音。那么问题就产生了,因为现在的场景中已经包含了两个摄像机,其中一个是在创建场景时自动产生的,另外一个是添加的第一人称视角控制器中自带的。若想确认这一点,只需要在层次(Hierarchy)面板上选中第一人称视角控制器对象,之后单击它名字旁边的三角形图标,这样就会在这个物体的下方显示更多的对象,这些对象都是第一人称视角控制器的组成部分,如图1.47所示。

图1.47 找到第一人称视角控制器中的摄像机

选中位于“FPSController”下方展开之后显示的“FirstPersonCharacter”对象,如图1.47所示。“FirstPersonCharacter”对象是“FPSController”对象的一个子对象。这一点从层次(Hierarchy)面板上的“FPSController”中包含了“FirstPersonCharacter”就可以看出来。子对象会继承它们上一级对象的变换(Transformation)属性。这表示当在场景中对某些对象进行移动或者旋转操作时,所做的变换同样会影响到它的所有子对象。在检查(Inspector)面板中,可以看到这个对象包含一个“Audio Listener”组件,如图1.48所示。

图1.48 包含了“Audio Listener”组件的第一人称视角控制器游戏对象

也可以选择将“Audio Listener”组件从FPSController对象上移除,但是这样做,游戏者在以第一人称视角进行游戏时就再也听不到声音了。所以,选择将场景创建之初自带的摄像机删除。只需要在层次(Hierarchy)面板上选中图1.49所示的摄像机,然后按下键盘上的“Delete”键即可完成删除操作。这样就可以消除在游戏进行时产生的关于“Audio Listener”的警告。现在可以开始游戏的试玩了!

图1.49 删除摄像机对象

到目前为止,游戏已经取得了不错的进展,已经可以以第一人称视角的模式在整个游戏环境中行走和探索了。不过,这个游戏环境还有很多值得改进的地方。例如,floor网格现在就如图1.50所示的那样孤零零地悬浮在游戏世界的空中,没有任何的支撑。更离谱的

图1.50 整个世界地面悬浮在空中,没有任何的支撑

是,当走出了地面边缘的时候,就会掉下一个无底的深渊。所以现在需要在地面下面添加一片水域,这样场景的环境就更加完整了。

为了向这个游戏中添加水资源,可以使用另一种Unity中预置的资源。在项目(Project)面板中依次打开“Standard Assets | Environment | Water | Water | Prefabs”文件夹,然后将“WaterProDaytime”资源从项目(Project)面板中拖动到场景中,如图1.51所示,这是一个圆形的对象,但是实际上的尺寸比所需要的小得多。

图1.51 向环境中添加水资源

在向场景中添加了水资源预设体(Prefab)之后,将其放置在地平面的下面,然后使用“Scale”工具(R)来变大水资源预设体(Prefab)的平面(X,Z)大小,一直扩大到遥远的地平线为止。这样就使得地面网格看起来好像是一望无际的大海之中的一个小孤岛,完成后的效果如图1.52所示。

图1.52 环境中水资源面积的调整以及最后的大小

现在,在一个游戏(Game)选项卡中开始另一个测试。在工具栏上按下“Play”按钮,然后以第一人称视角模式来完成角色的导航。如图1.53所示,可以看到关卡中的水面,当然,在水面上是无法行走的,同样也不能在这里面游泳和潜水。如果真的走到了水面上,就会掉进去,好像这些水根本就不存在一样。现在的水只是一个装饰,仅仅起了一个使场景看起来更真实的作用。

图1.53 以FPS模式对有水的环境进行测试

现在场景中的水是虚无缥缈的,看得见却摸不着,但玩家可以轻易地从这些水中穿过,就好像它们是不存在的一样。Unity现在并不会将这些水当作固态或者半固态的物体来对待。通过给物体添加上一个盒子碰撞体(Box Collider )组件,就可以使其具有固体的性质,关于碰撞体(Collider)和物理属性(Physics)的内容,将会在第3章中进行更详细的讲解。现在,简单快速地给场景中的水对象添加上一个固体的属性,只需要在层次(Hierarchy)面板上将水对象选中,然后再在应用程序菜单上依次选中“Component | Physics | Box Collider”,如图1.54所示。向一个选中的对象上添加一个组件会改变这个对象本身的表现。即便如此,也千万不要有添加越多组件,对象越完美的错误观点。其实应该在保证功能的前提下,为一个对象添加尽量少的附件,这种策略可以使工作流变得简单、整洁,并具有更好的性能优化。

图1.54 向水资源对象添加一个盒子碰撞体(Box Collider)

当向水资源中添加了一个盒子碰撞体(Box Collider)之后,就会在网格周围出现一个绿色的笼状网格。这个绿色的笼状网格大概表示了水资源对象的体积和形状,也就是说Unity认为这个区域就是一个固体,如图1.55所示。

图1.55 盒子碰撞体(Box Collider)的近似体积

现在当控制游戏中的角色走到水面上去时,就会发现角色会在水面上移动,而不会掉下去。当然,更真实的效果应该是这个角色能在水中游泳,虽然现在的“水上漂”有点假,总比掉下去要好多了。想让角色能在水中像真的一样游泳,需要更多的工作,这里暂不涉及。如果需要将盒子碰撞体的功能从水中去掉,返回到初始的虚无状态,则首先选中水对象,然后单击它的“Box Collider”组件右上方的齿轮图标,然后选中下面菜单中的“Remove Component”选项,如图1.56所示。

图1.56 移除一个组件

至此,该游戏已经有了很多功能,例如一个完整的环境、一个第一人称视角控制器、一片大海。不过,本章设计的是一个金币采集游戏,但是现在这个游戏场景中还没有任何可以采集的金币。为了实现这些功能,需要编写一些C#脚本,这些脚本要到下一章才会看到。然而可以创建一些金币对象,如将一个圆柱体(Cylinder)对象变形成一个金币。下面先来创建一个圆柱体对象,操作方法是从应用程序菜单处依次选择“GameObject | 3D Object | Cylinder”,如图1.57所示。

图1.57 创建一个圆柱体(Cylinder)对象

最初,这个圆柱体(Cylinder)对象看起来一点也不像是一个金币。不过它的外形是很容易改变的,只需要按着Z轴将圆柱体(Cylinder)变薄即可。切换到“Scale”工具(R),然后向内减小圆柱体(Cylinder)的长度,如图1.58所示。

图1.58 将圆柱体(Cylinder)对象变成一个金币形状

在改变了金币的大小和形状之后,它自带的碰撞体的大小与金币的体积就完全不一样了。碰撞体的体积要比金币大很多,如图1.58所示。默认情况下,圆柱体(Cylinder)自带的是一个胶囊型(Capsule)碰撞体,而不是之前见过的盒状碰撞体。可以在金币被选中之后,从检查(Inspector)面板处来改变“Radius”属性的值,这将改变胶囊碰撞体(Capsule Collider)的大小,如图1.59所示。将这个值调小,使得胶囊碰撞体的大小与金币更加接近。或者,选择将胶囊碰撞体(Capsule Collider)完全删除,然后再添加一个盒子碰撞体(Box Collider )来代替。这两种方法都是可行的,可以根据实际情况进行选择。在下一章的代码中将会使用到碰撞体,它将用来检测玩家与金币之间的接触。

图1.59 调整金币的胶囊碰撞体(Capsule Collider)尺寸

至此,已经将金币的形状和结构设计好了。接下来的一章还要对金币很多方面进行改进。例如,需要给金币添加一个有金属光泽的外观,还要使这个金币可以被采集。本章仅仅使用了Unity自带的基本工具,就产生了一个形状看起来很像金币的对象。

现在已经为金币采集游戏的开发打下了良好的基础,关于这个游戏的功能部分将会在下一章中完成和实现。现在,读者已经掌握了如何从零开始创建一个Unity项目,并使用网格、贴图和场景来充实这个项目。另外,读者也已经了解了如何为游戏创建一个场景,以及如何使用各种资源来完善游戏的功能。这些资源包括水、第一人称视角控制器和原型资源库等。在下一章中,将为金币添加可采集的特性,并为游戏添加一系列的逻辑,使得游戏有输或者赢的结局。


这一章将会在第1章建立好的游戏基础上继续进行。在这个金币采集游戏中,玩家可以第一人称视角模式在整个游戏环境中进行漫游,在游戏规定时间到达之前,寻找并采集到所有的金币。如果在游戏规定时间结束前,玩家就已经完成了所有金币的采集工作,则视为游戏胜利。反之,如果到游戏规定时间结束时,玩家并没有完成所有金币的采集工作,则视为游戏失败。到目前为止,已经在这个项目中添加了一个完整的环境,环境中包括地面、道具、水、一个第一人称视角的控制器,及一个看起来已经具有金币形状但还不能被采集的金币对象。

本章继续完善这个游戏,最后将实现金币可以被玩家采集,系统中还有一个可以检测游戏进行时间的计时器。从本质上来说,这一章中的主要工作是为游戏添加逻辑和规则。为了实现这一目标,需要使用C#编写一些代码,所以本章的学习需要对编程有一些基础。这本书主要是Unity的功能介绍以及如何使用Unity游戏开发引擎,但是程序的编写并不在本书的范围内,因此,假设本书的读者已经有了编写代码的能力。总体而言,本章将涉及的主题如下:

在上一章结束时,通过将一个初始的圆柱形(Cylinder)对象进行不均匀的缩放,从而产生了一个基本的金币对象。依次单击应用程序菜单上的“GameObject | 3D Object | Cylinder”可以创建该对象,结果如图2.1所示。从概念上来说,金币对象是游戏逻辑中的一个基本单位,玩家在游戏时间耗尽之前要尽力地去采集这些金币。这意味着这些金币并不能仅仅是被看到就行了,其主要目的是实现游戏的功能,玩家是否收集到了金币将意味不同的游戏结果。因此,现在的金币对象还缺少两个重要的方面。首先,这个金币看起来很阴暗,而且颜色还是灰色的,它完全无法吸引玩家的注意力。再者,这个金币对象无法被玩家采集。不过,玩家可以穿过金币,就好像什么都没有发生一样。

图2.1 到目前为止的金币对象

 

本章和下一章中讲到的金币采集游戏的完整项目程序可以在本书配套文件的Chapter02/Collection Game文件夹中找到。

这一节将集中讲述如何使用材质(Material)改善金币的外观。一个材质其实就是一个算法(或者说指令集),它指定了金币的外观所呈现的样式。一个材质并不仅仅指物体在颜色方面看起来怎样,它同样定义了物体表面是光滑的还是粗糙的,光照射到物体上之后是发生镜反射还是漫反射。这一点必须要搞清楚才能明白为什么贴图和材质是两种不同的东西。贴图是指一个加载到计算机存储器中的图像文件,可以通过它的UV贴图将一个三维物体覆盖上。相反的,材质定义了一个或者多个贴图是如何结合在一起,并应用到了一个对象外观的塑造上的。如果需要在项目中创建一个新的材质资源,就可以在项目(Project)面板的空白位置单击鼠标右键,然后在弹出的上下文菜单中,选择“Create | Material”,如图2.2所示。或者在应用程序菜单处依次选中“Assets | Create | Material”。

图2.2 创建一个材质

 

有时一个材质也可以被称作“Shader”。如果需要,就可以使用着色器语言(Shader Language)来创建一个自定义的材质。当然也可以使用Unity中自带的一些插件,例如“Shader Forge”等。

当成功地创建了一个新的材质之后,接下来就要给这个材质起一个名字。此处将这个材质命名为“mat_GoldCoin”。这个名字的前半部分为“mat”,这样能让大家一下子就知道这是一个材质资源。现在只需要在文本编辑区域输入新的名字,即可完成对这个材质的重命名。以后也可随时在这个材质上双击鼠标,来完成对名字的修改,如图2.3所示。

图2.3 为一个材质重命名

接下来,如果还没有选中这个材质资源,那么就在项目(Project)面板上选中它,这时其所有属性都会在对象检查(Inspector)面板处显示出来。这里的属性数量很多。注意,可以在这个对象的Inspector面板底部看到这个材质基于当前设定的预览图,当在检查(Inspector)面板处修改了材质设定,下面的预览图也会及时地根据调整进行更新,如图2.4所示。

图2.4 从对象检查(Inspector)面板处对材质的属性进行修改

下面为金币创造一个金光闪闪的材质。首先需要指定着色器(Shader)的类型,这是因为这个类型决定了其他可用的参数。着色器(Shader)的类型决定了系统将采用哪种算法来计算对象的阴影。有很多种类型供选择,但是最经常使用的类型就是“Standard”或者“Standard(Specular setup)”。对于游戏中使用的金币,可以将着色器(Shader)的类型设定为“Standard”,如图2.5所示。

图2.5 设定材质的着色器(Shader)类型

此时,在预览(Preview)面板中显示的是阴暗的灰色,这与所需要的相差太多了。现在需要的是一个金色,首先指定反射(Albedo)的属性。单击反射(Albedo)右侧的色板,然后会弹出一个颜色选择器,在其中选出所需要的金色。这时,反射(Albedo)右侧的色板也会实时根据材质的变化更新,如图2.6所示。

图2.6 将反射(Albedo)的值指定金色

现在,金币的材质比以前好多了,但是需要的游戏对象是金币,也就是说,它们看起来应该是金光闪闪的。为了将这个效果添加到材质库中,可单击并滑动对象检查(Inspector)面板处“Metallic”属性右侧的滑动条,将值设定为1。这个值设为1表明会将材质设定为一个金属表面,而不是一个像布或者头发之类的漫反射面。同样,此时的预览会如同图2.7所示的一样,及时地根据设定的更改进行更新。

图2.7 创建一个金属材质

现在已经创建好了一个新的金属材质,可以在预览窗口中看到它逼真的效果了。如果需要,还可以在预览窗口中更改材质预览时的形状。默认的情况下,材质是以一个球形展示出来的,不过也可以以其他的基本形状来预览,例如正方体、圆柱体、圆环等。这样有助于在不同的条件下对材质进行预览。可以通过单击预览面板上方的“几何图形”按钮来更改对象的形状(见图2.8)。

图2.8 对一个对象上的材质进行预览

当为金币准备好了材质之后,就可以通过拖动的方式将这个材质直接应用到场景中的网格上。先将做好的金币材质应用到金币上,从项目(Project)面板选中刚做好的材质,然后拖动到场景中的金币对象上。当将材质拖动到金币上释放鼠标时,金币就呈现出金属效果了。这个过程如图2.9所示。

图2.9 为金币设置材质

我们已经成功地为金币添加材质了,也可以通过在场景选中金币对象来查看它所使用的材质了。同样,也可以从检查(Inspector)面板窗口来查看“Mesh Renderer”组件。当使用摄像机时,“Mesh Renderer”组件可以保证一个网格对象是可见的。组件“Mesh Renderer”包含一个材质(Materials)区域,该区域中列出了分配给这个对象的全部材质。在材质(Materials)区域选中材质的名字之后,Unity就会自动地从项目(Project)面板上选中材质,这样就可以快速简单地对材质进行定位,如图2.10所示,组件“Mesh Renderer”列出了分配给一个对象的所有材质。

图2.10 组件“Mesh Renderer”列出了分配给一个对象的所有材质

 

一个网格对象有时会拥有多个材质,对象的每一个面都分配不同的材质。不过为了获得最佳的游戏性能,最好在满足需求的基础上使用尽可能少的材质。如果可能,尽量让多个游戏对象都使用同一个材质,这样做可以很明显地提高游戏的性能。如果想要获得关于游戏渲染性能优化方面的更多内容,可访问http://docs. Unity3d.com/Manual/OptimizingGraphics Performance. html的在线文档。

已经完成可以用来收集的金币的材质了,它看起来很漂亮,不过,并没有彻底地完成金币部分。当玩家接触到金币时,它并不会消失,而且也没有机制来记录玩家到底采集到了多少金币。接下来就需要开始编写程序了。

为游戏定义逻辑、规则和行为的时候,往往需要使用到脚本。如果想将那些静态的、无生命的场景和对象转换成为可以进行交互的环境和对象,那么开发人员就需要编写代码。这些代码定义了这些物体在遇到了指定情况之后,应该做出什么样的反应。金币采集游戏也需要编写代码才能实现所有的功能。这个游戏需要实现3个主要的 功能:

在Unity中并没有包含一个能实现上述功能的模块。所以必须自己来编写一些代码来实现这些功能。Unity中支持两种语言,即UnityScript语言(有时候称之为JavaScript语言)和C#语言。这两种语言功能都很强大,但是本书中主要采用的是C#语言。这是因为从发展的趋势来看,JavaScript的使用率将会逐渐下降。现在开始对这个主要功能进行编程。首先在项目(Project)面板的空白区域单击鼠标右键,然后在弹出的上下文菜单中依次选择“Create | C# Script”,就可以创建一个新的脚本文件。另外,也可以从应用程序菜单处依次选择“Assets | Create | C# Script”来创建一个新的脚本文件,如图2.11所示。

图2.11 创建一个新的C#脚本

当创建了脚本之后,需要为脚本起一个描述性的名字。本书起的名字为“Coin.cs”。在Unity中,每一个脚本文件都对应一个与其同名的类。因此,“Coin.cs”文件对应的就是“Coin”类。这个“Coin”类将封装一个金币的所有行为,并最终会附加到场景中的金币上,如图2.12所示。

图2.12 为脚本文件命名

在对象检查(Inspector)面板中双击Coin.cs,就可以使用MonoDevelop打开这个文件。MonoDevelop是一款Unity自带的第三方IDE应用,它可以实现对游戏中的代码进行编写和修改。当一个文件在MonoDevelop中打开之后,它的内容就会如代码示例2.1所示的在MonoDevelop中显示出来。

代码示例2.1:

using UnityEngine;
using System.Collections;

public class Coin : MonoBehaviour
{
  // Use this for initialization
  void Start () {}

  // Update is called once per frame
  void Update () {}
}

 代码示例的下载

可以使用自己的账号从http://www.packtpub.com .处下载本书的代码示例。无论你在哪里购买的本书,都可以访问http://www.packtpub.com/support 并进行注册,书中的资源也可以通过电子邮件的形式发送给你。

可以按照如下步骤来下载这些文件:

  • 使用电子邮箱地址在页面上注册,如果已经注 册过了,那么直接登录即可;

  • 找到并使用鼠标单击位于页面顶端的“SUPPORT”;

  • 单击“Code Downloads & Errata”;

  • 在搜索search框中输入要下载资源的书的名字;

  • 选择要下载资源的书;

  • 在下拉菜单中选中购买本书的地点;

  • 单击“Code Download”。

将这些文件下载了之后,要确定解压缩软件已经更新到了最新的版本:

  • WinRAR / 7-Zip for Windows;

  • Zipeg / iZip / UnRarX for Mac;

  • 7-Zip / PeaZip for Linux。

默认情况下,所有新创建的类都派生自“MonoBehavior”类,这个类中定义了一些对所有组件都通用的函数。“Coin”类具有两个自动生成的函数,也就是Start()和Update()。这些函数都是由Unity自动调用的事件。当游戏对象(关联了这个游戏脚本)在场景中创建时,就会调用Start()函数。Update()函数会在每一帧被附加了游戏脚本的对象中调用一次。Start()函数主要用来实现代码的初始化,Update()函数主要用来实现那些随着事件推移的行为,例如运动和变化。现在,将新创建的脚本文件与场景中的金币对象进行管理,可以从项目(Project)面板处将“Coin.cs”文件拖曳到金币对象上。当完成以后,一个新的金币组件就被添加到了游戏对象上。这意味着这个脚本已经关联到了游戏对象上,如图2.13所示,一个关联了脚本的游戏对象。

图2.13 一个关联了脚本的游戏对象

当一个脚本与一个游戏对象关联到一起之后,这个脚本就作为这个游戏对象的一个组件而存在。一个脚本文件可以添加到多个游戏对象上,甚至可以被多次添加到同一个游戏对象上。每个组件都代表着一个单独而且不同的类的实例化。当一个脚本以这种方式添加进来之后,Unity会自动地调用它的函数,例如Start()和Update()。可以在Start()函数中加入一个Debug.Log语句来确认脚本是否能正常工作,这个语句在场景中的游戏对象被创建时在命令行窗口输出一个调试信息。查看如下所示的代码示例2.2。

代码示例2.2:

using UnityEngine;
using System.Collections;

public class Coin : MonoBehaviour
 {
  // start()是初始化函数
  void Start () {
    Debug.Log ("Object Created");
  }

  // Update在每一帧调用一次
  void Update () {

  }
}

如果按下工具栏上的“Play”键,或者按下键盘上的“Ctrl + P”组合键,来运行这个向游戏对象上添加了前面的那个脚本的游戏,就会在控制台窗口中看到一条内容为“Object Created”的信息,每当这个类进行实例化的时候,都会输出一次(见图2.14)。

图2.14 在控制台窗口处输出消息

现在已经为Coin类创建了最基本的脚本,并且已经将这个脚本成功附加到了金币对象上。接下来,继续编写一些函数,这些函数将会记录采集过的金币信息。

如果整个场景里只有一个金币,那么这个金币采集游戏就有些名不符实了。本游戏的核心设计思路就是在一个关卡中应该包含很多个金币,玩家应该在系统计时结束前收集完这些金币。现在,如果想要知道玩家是否将所有的金币都收集齐了,首先得先知道这个场景中总共有多少个金币。毕竟如果不知道金币的总量,也就无法知道是否已经将所有的金币收集全了。所以,第一个任务就是通过Coin类来轻松地知道任意时刻金币的总数。下面给出实现这一功能的Coin类,具体的如代码示例2.3所示。

代码示例2.3:

//-------------------------
using UnityEngine;
using System.Collections;
//-------------------------
public class Coin : MonoBehaviour 
{
  //-------------------------
  //追踪scene中金币数量
  public static int CoinCount = 0;
  //-------------------------
  // start()是初始化函数
  void Start () 
{
    //创建游戏对象, 增加金币的数量
    ++Coin.CoinCount;
  }
  //-------------------------
  //当游戏对象被销毁时调用OnDestroy函数
  void OnDestroy()
  {
    //减少金币数量
 --Coin.CoinCount;

    //检查剩余金币数量
    if(Coin.CoinCount <= 0)
    {
      //玩家获得胜利
    }
  }
  //-------------------------
}
//-------------------------

下面就代码示例2.3进行以下几点总结。

代码示例2.3中包含了一个变量CoinCount,利用这个变量可以知道金币的总量。查询这个值就可以轻松地获得当前金币的总量。现在已经完成整个金币采集功能工作的第一步了。

前面已经设计了一个金币计数变量,通过这个变量就可以随时地获知当前场景中金币的数量。不过,现在游戏仍然没有实现金币采集功能。下面就来完善这个功能。当玩家接触到金币后,也就是说,当玩家穿过金币或者碰撞到金币的时候,就可以看作这个金币被成功采集了。

如果想要确定玩家是否与金币发生接触,就需估算出玩家和金币的体积,这样才能检测出两个物体在空间中是否出现重叠。这是Unity通过碰撞体(Collider)来实现的。碰撞体(Collider)是一种附加在网格上的特殊物理对象,当两个游戏对象在空间上有重叠时,碰撞体就会检测到。FPSController对象(第一人称视角控制器)上已经自带了碰撞体对象,它使得控制器更像是一个人的身体。在场景中选中FPSController对象(第一人称视角控制器),然后就可以看到围绕在主摄像头周围的绿色线框,它是胶囊形状的。如图2.15所示,“Character Controller”具有一个和玩家身体形状相仿的碰撞体。

图2.15 “Character Controller”具有一个和玩家身体形状相仿的碰撞体

FPSController 中包含了一个“Character Controller”组件,这个组件包含“Radius”“Height”和“Center”等选项,规定了场景中角色的物理属性。如图2.16所示,FPSController 中已经包含一个“Character Controller”,在此游戏中,这些选项的值都不需要修改,保留默认值即可。

图2.16 FPSController中的“Character Controller”

金币游戏对象中只包含了一个胶囊碰撞体(Capsule Collider)组件,这是当初在创建圆柱体对象时系统自动添加上的,它与场景中的金币的大小差不多,在它的“Character Controller”组件中没有添加任何的属性和动作,这是很正常的,因为与FPSController这种会动的运动物体不同,金币是一个不会动的静态物体。如图2.17所示,包含了胶囊碰撞体(Capsule Collider)组件的圆柱体对象(Cylinder)。

图2.17 包含了胶囊碰撞体(Capsule Collider)组件的圆柱体对象

对于现在这个游戏,仍然为金币游戏对象选择使用胶囊碰撞体(Capsule Collider)组件,如果希望改用一个不同的碰撞体,比如一个盒子或者球形,来代替这个默认组件,就需要首先将金币上任何已有的碰撞体组件移除。具体操作为:单击位于游戏检查(Inspector)面板中组件右上角的齿轮图标,然后从上下文菜单中选中“Remove Component”,如图2.18所示。

图2.18 从一个游戏对象上将指定组件移除

接下来,选中一个游戏对象,然后通过应用程序菜单依次选中“Component | Physics”,然后在弹出的菜单中选中一个合适形状,如图2.19所示。

图2.19 为选中的游戏对象添加一个组件

不论使用哪种类型的碰撞器,这里都还有一点小问题。那就是当在进行游戏的时候,如果想要穿过金币,就会发现无法成功,反而会被金币挡在那里。这是因为现在的金币是一个固态的物理对象,所以FPSController对象是无法穿金币而过的。但是本游戏可不是这么设计的,金币不应该是一个游戏中的“拦路石”,而是一种可以被采集的对象,这就是说当玩家从金币中穿过时,就可以采集到金币,同时金币也会在场景中消失。操作方法为:选中金币对象,然后在它的游戏对象检查(Inspector)面板中的胶囊碰撞体(Capsule Collider)组件中选中“Is Trigger”复选框。这个“Is Trigger”选项适用于所有的碰撞体类型,主要用来检测与其他碰撞体是否发生碰撞,并且在发生碰撞时允许它们通过,如图2.20所示。

图2.20 选中“Is Trigger”复选框允许对象穿过其他的碰撞体

此时,在玩游戏时,FPSController就可以轻松地穿过场景中的所有金币。然而,玩家接触到金币之后,金币并不会消失,同样也不会被玩家采集到。要完善这些功能,需要向“Coin.cs”文件中添加更多的代码。首先添加一个OnTriggerEnter()函数,当游戏对象,例如游戏玩家,进入了一个碰撞体时,就会自动地调用。现在,添加一句Debug.Log语句,当玩家进入到一个碰撞体时,就会输出一条调试消息,这只是为了进行测试,代码示例2.4如下所示。

代码示例2.4:

//-------------------------
using UnityEngine;
using System.Collections;
//-------------------------
public class Coin : MonoBehaviour
{
  //-------------------------
  public static int CoinCount = 0;
  //-------------------------
  // start()是初始化函数
  void Start () {
    //创建游戏对象, 增加金币的数量
    ++Coin.CoinCount;
  }
  //-------------------------
  void OnTriggerEnter(Collider Col)
  {
    Debug.Log ("Entered Collider");
  }
  //-------------------------
  //当游戏对象被销毁时调用OnDestroy函数
  void OnDestroy()
  {
    //减少金币的数量
    --Coin.CoinCount;

    //检查剩余的金币
    if(Coin.CoinCount <= 0)
    {
      //玩家胜利
    }
  }
  //-------------------------
}
//-------------------------

 

关于OnTriggerEnter()函数的更多详细信息,可以访问Unity的在线文档,访问地址为http://docs.Unity 3d.com/ ScriptReference/MonoBehaviour.OnTriggerEnter.html

首先单击工具栏上的“Play”按键来测试样例2.4中的代码。当穿过一个金币时,就会触发这个函数并展示一个信息。不过,这里还是存在一个问题,到底是什么触发了这个函数呢?很明显一定有什么东西穿过了金币,但是到底是什么呢?是玩家,敌人又或者是一个掉落的砖头,还是其他的什么东西?为了区分开这些内容,需要使用“Tag”。“Tag”可以为场景中的指定物体打上特殊的标记或者标签,这样就可以在代码中轻松将玩家和其他对象区分开。要知道在本游戏设计中,只有玩家才可以收集金币。所以,需要为玩家对象添加一个名为“Player”的“Tag”。要做到这一点,需要首先在场景中选中这个对象,然后在对象检查(Inspector)面板中选中Tag下拉列表框,在Tag的下拉列表中选中“Player”。这样就将FPSController标记为“Player”对象,如图2.21所示。

图2.21 为FPSController添加一个“Player”标签

FPSController已经被标记为了Player。接下来就按照代码示例2.5所示重新定义Coin.cs类。现在这个类已经可以完成金币的采集工作,当玩家接触到金币后,金币就会消失,同时将金币的数量减1。

代码示例2.5:

//-------------------------
using UnityEngine;
using System.Collections;
//-------------------------
public class Coin : MonoBehaviour
{
  //-------------------------
  public static int CoinCount = 0;
  //-------------------------
  // start()是初始化函数
  void Start () {
    //创建游戏对象, 增加金币的数量
    ++Coin.CoinCount;
  }
  //-------------------------
  void OnTriggerEnter(Collider Col)
  {
    //如果玩家采集到了金币,就销毁这个对象
    if(Col.CompareTag("Player"))
      Destroy(gameObject);
  }
  //-------------------------
  //当游戏对象被销毁时调用OnDestroy函数
  void OnDestroy()
  {
    //减少金币的数量
    --Coin.CoinCount;

    //检查剩余的金币
    if(Coin.CoinCount <= 0)
    {
      //玩家胜利
    }
  }
  //-------------------------
}
//-------------------------

下面对代码示例2.5进行如下总结。

至此,已经创建好了第一个可以工作的金币了。现在游戏中的玩家就可以走近并采集这个金币,然后这个金币就会被从场景中移除。不过,这个场景中不应该只包含一个金币,而是很多个金币。可以通过对这个已经做好的金币进行复制来解决这个问题,当然要将复制好的金币放置到其他不同的地方。其实还有更好的解决办法,接下来将进行介绍。

现在已经搞定了金币的基本功能,但是整个场景中还需要更多的金币。上一节提出了一个简单的解决方案,但是问题是如果这么做了,当需要对金币的属性进行改变时,那么将不得不逐个对所有金币进行修改。为了避免这种繁琐的重复性工作,可以使用Unity中的预设体(Prefabs)。预设体功能可以将一个场景中的对象转换为一个项目(Project)面板上的资源。预设体通常是场景实例化中最经常需要的资源。这样做的优势就在于,当对资源进行更改的时候,可以将修改自动地应用在一个场景甚至多个场景中所有的实例上。

这样将会使得在使用自定义的资源进行工作时变得更简单,所以现在先来将金币设置为预设体。首先选中场景中的金币对象,然后将它拖曳到项目(Project)面板中。当完成了这个步骤之后,一个新的预设体就做好了。场景中的对象就会升级成为一个预设体的实例。但是这也意味着,一旦这个资源从项目(Project)面板上删除,所有该资源对应的实例也就会变得无效,如图2.22所示。

图2.22 创建一个金币的预设体

现在预设体已经成功创建了,可以从项目(Project)面板中向场景中拖曳预设体来创建更多的金币实例了。每一个金币的实例都和初始的预设体资源相关联,当对预设体资源做出改动时,这些改变会立刻在所有的实例上生效。按照这个思路,向游戏中添加足够的金币对象。图2.23所示为设计的金币分布。

图2.23 向关卡中添加金币的预设体

这里又产生了一个新的问题,即如何才能将一个预设体的实例变回一个独立的游戏对象,而不再关联到预设体资源。这是非常有用的,例如有些时候,可能需要利用预设体来实例出一些对象,但是又不希望这些对象和预设体关联在一起。这一点也很容易实现,首先在场景中选中一个预设体的实例,然后在应用程序菜单中选中“GameObject | Break Prefab Instance”,如图2.24所示。

图2.24 “Break Prefab Instance”功能

 

当向场景中添加了一个预设体的实例并对其进行了改动之后,如果想将这个改动应用到预设体资源中,就可以选中对象,然后在应用程序菜单中选中“Game Object | Apply Changes to Prefab”。

现在已经拥有一个具备场景和金币的关卡了。凭借新添加进来的Coin.cs脚本,现在的金币也可以被正常计数和采集了。不过对于玩家来说,现在的关卡中仍然没有挑战性,因为他们既赢不了,也输不了。在这个游戏中,玩家没有需要达成的目标。因此时间限制也就成为了游戏中的一个重要条件:它定义了玩家胜利和失败的关键。也就是说,在游戏时间结束前,玩家采集到了所有的金币,就可以看作玩家胜利;玩家没有采集到所有的金币,则玩家失败。所以需要为这个关卡创建一个倒计时的计数器。首先选择菜单栏上的“GameObject | Create Empty”创建一个新的空游戏对象,并为其起名为“Level Timer”,如图2.25所示。

图2.25 为计时(Timer)对象重命名

 

记住,游戏开始时,玩家是看不到任何空游戏对象的,因为这些对象身上没有任何的网格渲染组件。但是这些对象在用来创建一些不直接对应到物理的和可见实体上的功能和行为时,就显得特别有用了,例如定时器、管理器和游戏逻辑控制器之类。

接下来,创建一个名为“Timer.cs”的新脚本文件,然后将这个脚本添加到场景中的LevelTimer 对象上。完成这个操作以后,在这个场景中,定时器就开始起作用了。不过需要确定的是,这个定时脚本仅仅被添加到了场景中的一个物体上,而不是多个物体上。否则,在同一场景中可能就会出现多个相互矛盾的定时器。可以使用层次(Hierarchy)面板来查找整个场景中特定类型的组件。首先在层次(Hierarchy)查找框中输入“t:Timer”,然后按下键盘上的回车键开始查找。这样将会查找到场景中所有带有定时器类型组件的对象,并将在层次(Hierarchy)面板中显示查找的结果。另外,在这个层次(Hierarchy)面板中只会显示匹配的结果。在查找时输入的“t”表示这次查找的条件是类型,如图2.26所示。

图2.26 查找具有特定类型组件的对象

如果在查找过程中停止这次查找,并返回层次(Hierarchy)面板的初始状态,则可以通过单击查找对话框右侧的“×”号图标按钮来实现。不过这个按钮很难发现,如图2.27所示。

图2.27 取消查找

如果希望Timer脚本生效,那么必须在里面编写相应的代码。下面的代码示例2.6给出了Timer.cs中的完整代码。如果读者之前从未在Unity中进行过系统的编码,那么这段代码将会是非常有学习价值的。这段代码给出了很多十分重要的特点,代码中的注释给出了详细的说明。

代码示例2.6:

//-------------------------
using UnityEngine;
using System.Collections;
//-------------------------
public class Timer : MonoBehaviour
{
  //-------------------------
  //完成关卡的时间 (单位为秒)
  public float MaxTime = 60f;
  //-------------------------
  /倒计时
  [SerializeField]
  private float CountDown = 0;
  //-------------------------
  // start()是初始化函数
  void Start () 
  {
    CountDown = MaxTime;
  }
  //-------------------------
  // 每一帧都会调用update
 void Update () 
  {
    //时间减少
    CountDown -= Time.deltaTime;

    //当时间耗尽之后重启关卡t
    if(CountDown <= 0)
    {
      //重置金币的数量
      Coin.CoinCount=0;
      Application.LoadLevel(Application.loadedLevel);
    }
  }
  //-------------------------
}
//-------------------------

现在对上面的代码进行如下总结。

 

除了在每一帧都会被调用的Update()函数,Unity中还支持两个相关的函数,即FixedUpdate()和LateUpdate()。FixedUpdate()主要用来实现物理编码,稍后将看到,这个函数在每一帧都会被调用相同的次数。LateUpdate()函数会被每个活动(Active)的对象在每一帧调用一次,但是LateUpdate()函数的调用总是发生在Update()事件之后。因此,它总是发生在Update()周期之后,使它成为一个后期Update。在本书的后面将介绍这个后期Update()存在的特殊用途。如果想了解更多的关于FixedUpdate()函数功能,可以访问http://docs.Unity3d. com/ScriptReference/MonoBehaviour.Fixed Update.html.在线文档。同样,如果想了解更多的关于LateUpdate函数功能,可以访问http://docs.Unity3d.com/ScriptReference/ MonoBehaviour.LateUpdate.html.在线文档。

完成Timer脚本的创建工作之后,在场景中选中LevelTimer对象。从对象的检查(Inspector)面板中就可以看到玩家在当前关卡完成任务所允许的最大时间限制(以秒为单位),如图2.28所示。这里将时间设置为60秒。这意味着玩家需要在关卡开始之后的60秒之内完成所有金币的采集工作。如果定时器中的时间耗尽,那么这个关卡将会重启。

图2.28 设定当前关卡的时间限制

现在有了一个具备倒计时功能的完整关卡,可以完成金币的采集,而且定时器也可以耗尽时间。总的来说,现在的游戏已经初具规模了,不过这里还有一个更深入的问题,后面内容将进行讲解。

至此,金币采集游戏几乎要完成了。金币可以被采集了,计时器也开始工作了,但是胜利的条件却没有得到妥善的处理。当玩家在计时器中的时间耗尽之前采集到所有的金币,游戏中并没有任何胜利的提示。相反倒计时仍然会继续,就好像胜利的条件完全没有达到一样。下面来解决这个问题,首先,当游戏者在游戏中获得胜利之后,应该删除计时器对象,以防止倒计时继续进行,并给出一些视觉上的效果来提示玩家该关卡已经完成。在这种情况下,可以添加一些焰火(Fireworks)。从Unity 5的例子系统包(Particle System Packages)即可添加焰火,操作方法为:依次选中“Standard Assets | ParticleSystems | Prefabs folder”,然后将焰火粒子系统(Fireworks Particle System)拖曳到场景中,可以继续添加2到3个焰火粒子系统,如图2.29所示。

图2.29 添加两个焰火预设体

默认情况下,所有的焰火粒子系统都会在关卡一开始时执行。可以通过在工具栏上单击“Play”按钮来测试这个游戏。不过这并不是所希望的效果,我们所设计的效果应该是当游戏者达成胜利条件以后,才执行这个焰火的特效。在场景中选中焰火粒子系统对象,然后在对象的 Inspector 面板上取消组件“Particle System”上单选框“Play On Awake”的选中状态,取消“Play On Awake”的功能,如图2.30所示。

图2.30 取消“Play On Awake”功能

取消“Play On Awake”之后,粒子系统就不会在关卡开始的时候自动执行了。这是正确的,但是这些粒子系统在整个游戏中都不会执行了,应该在合适的时候启动这些粒子系统,通过代码的编写就可以实现这一点。在进行编码之前,首先将全部的焰火对象都赋予一个合适的标签。这样做的原因是,在代码中需要在场景中查找全部的焰火对象,然后触发对应的事件。为了将焰火对象和其他对象区分开,可以使用Tag属性。所以,首先创建一个新的焰火Tag,然后将它们分配给场景中的焰火对象。在这一章开始的时候就为了检测是否与硬币发生碰撞为游戏中的角色添加过Tag属性,如图2.31所示。

图2.31 为焰火对象添加Tag

现在所有的焰火对象都已经被添加了Tag了,接下来可以重新定义Coin.cs脚本类来处理场景中的胜利条件,如下面的代码示例2.7所示。

代码示例2.7:

//-------------------------
using UnityEngine;
using System.Collections;
//-------------------------
public class Coin : MonoBehaviour
{
  //-------------------------
  public static int CoinCount = 0;
  //-------------------------
  // 初始化函数
  void Awake () 
  {
    //创建对象并增加金币的数量 
    ++Coin.CoinCount;
  }
  //-------------------------
  void OnTriggerEnter(Collider Col)
  {
    //当玩家采集到金币之后要销毁这个金币对象
    if(Col.CompareTag("Player"))
      Destroy(gameObject);
  }
  //-------------------------
  void OnDestroy()
  {
    --Coin.CoinCount;

    //检查剩余金币的数量
    if(Coin.CoinCount <= 0)
    {
      //玩家收集完全部金币,游戏胜利
      /销毁计时器对象,启动庆祝焰火
 GameObject Timer = GameObject.Find("LevelTimer");
 Destroy(Timer);

 GameObject[] FireworkSystems = 
 GameObject.FindGameObjectsWithTag("Fireworks");
 foreach(GameObject GO in FireworkSystems)
 GO.GetComponent().Play();
    }
  }
  //-------------------------
}
//-------------------------

下面对代码示例2.7进行总结。

 

需要注意的是,要尽可能避免使用GameObject. Find()这个函数,它的执行效率很低。相对而言,FindGameObjects WithTag()函数是一个更好的选择。在这里介绍Game Object.Find的目的主要是为了让读者知道这个函数的存在,在有些时候需要使用这个函数来查找一些没有使用特定Tag的对象。

 

如前所述,Unity中的每一个对象都是一些相关组件的集合。例如,一个标准立方体(使用菜单GameObject | 3D Object | Cube所创建的),就是由Transform组件、Mesh Filter组件、Mesh Renderer组件以及Box Collider组件所组成的。这些组件共同决定了立方体的性质。

在脚本中使用函数GetComponent()可以获得一个目标组件的引用,这样就可以直接访问目标组件的公共属性。前面代码中的OnDestroy()函数就使用GetComponent()函数获得了“Particle System”组件的引用,这个函数是一个十分重要而且有用的函数,关于GetComponent()函数的更多详细信息,可以访问http://docs.Unity3d.com/ Script Reference/GameObject. GetComponent.html上的Unity在线文档。

至此,已经完成第一个Unity游戏了!现在是时候来运行一下这个游戏了。Unity中游戏的测试过程首先是单击工具栏上的“Play”按钮,然后以游戏者的视觉角度来进行游戏,以此来查看该游戏能否正常工作。除了进行游戏以外,还可以使用调试模式,在整个游戏进行过程中时刻观察着对象Inspector面板处所有公共和私有变量值的变化,以此来保证在游戏过程中所有变量的值都不会超出正常范围。激活调试模式的方法是,单击对象检查(Inspector)面板右上角的菜单图标,并从出现的上下文菜单中选中“Debug”选项,如图2.32所示。

图2.32 从检查(Inspector)面板处激活调试模式

激活调试模式以后,检查(Inspector)面板的变量和组件的外观可能发生改变,通常情况下,可以一个更详细和更准确的方式来查看这些变量,另外,还可以看到所有的私有变量。图2.33所示的就是在调试模式下看到的变换(Transform)组件。

图2.33 在调试(Debug)模式下查看到的变换(Transform)组件

在游戏运行时,另一个十分有用的工具是Stats面板,可以通过在工具栏上单击Stats按钮来将游戏(Game)选项卡切换到统计(Stats)选项卡,如图2.34所示。

图2.34 从游戏(Game)选项卡切换到统计(Stats)选项卡

游戏(Game)选项卡只有在Play模式下才可以使用,在这种模式下,它会详细地显示游戏中的所有关键性能数据统计,例如帧速率(FPS)和内存使用情况。这些数据可以诊断任何可能会影响到游戏的问题。FPS表示每秒中帧(时间单元或者周期)的数量,当然这里并没有办法说什么值是好的FPS值,什么值是差的FPS值,或者某个值是最为合适的FPS值。但是通常认为较高的FPS值要好于较低的FPS值,这是因为较高的FPS值意味着游戏在一秒完成了更多的周期。如果FPS值小于20甚至15,那么很有可能是游戏出现了延迟,因此要花费更多的时间来完成一个周期。很多游戏内部和外部的变量都会影响到FPS。内部因素包括场景中光源的数量、网格对象的顶点密度、指令的数目、代码的复杂度。外部因素包括计算机硬件配置、当前正在运行的程序和进程的数量及硬盘空间的大小等。

简而言之,如果FPS值比较低,那么就意味着在游戏中存在一些需要注意的问题。这个问题的解决需要考虑到很多方面,例如网格是不是过于复杂,它们上面是否有过多的顶点,贴图是不是太大了,游戏中是不是加入了太多的音效等。图2.35所示为一个正常运行的金币采集游戏示例图,这个完整的游戏可以在本书的配套文件中的“Chapter02/End”文件夹中找到。

图2.35 对金币采集游戏进行测试

现在是时候来构建(Build)游戏了,构建(Build)是指将游戏进行编译和打包,使得游戏成为一个独立的,可以自执行的形式,玩家在没有Unity编辑器的情形下就可以运行这个游戏。通常情况下,在开发游戏时,就应该考虑到这个游戏的运行环境(例如Windows、iOS、Android及其他),要知道这是在设计阶段就应该决定的,而不是在开发结束时才想到的问题。虽然我们经常听说Unity是一种“一次开发,随处运行”的工具,但其实这个口号可能会带来负面的效果,尽管它宣称在游戏做好了之后,就可以像在桌面系统上一样轻松地在任何平台上运行这个游戏。

不幸的是,问题并非如此简单,能够在桌面系统运行良好的游戏,不一定能在手机上完美运行。反之亦然,主要是由于这些系统硬件之间和行业标准之间存在着巨大差异。考虑到这些差异,我们把主要精力放在Windows和Mac等桌面平台上,暂时不考虑手机和游戏机等平台。如果想创建一个在桌面平台上运行的游戏,可以在菜单栏上依次选择“File | Build Settings”,如图2.36所示。

图2.36 选择“Build Settings”

之后,“Build Settings”对话框就会显示出来,这个界面中包含了3个主要的区域,“Scenes In Build”区域中列出了在进行游戏构建时会包括的所有场景,注意是所有的场景,并不考虑这个场景是否真的可以被玩家访问。总之,如果在游戏中需要一个场景,那么这个场景必须出现在列表中,最开始的时候,这个列表是空的,如图2.37所示。

图2.37 “Build Settings”对话框

可以从项目(Project)面板上将场景资源拖曳到“Scenes In Build”列表中,这样就可以轻松地将场景添加进来。对于金币采集游戏,需要将“Level_01 scene”添加到列表中。当场景添加进来以后,Unity中就会自动为其分配一个编号,这个编号的值取决于它在列表中的顺序。0代表最高的场景,1代表紧随其后的那个场景,以此类推。最高的场景就是开始的场景,也就是说,当游戏在生成之后运行时,会从编号为0的scene开始。因此,scene0通常也就是游戏的序篇或者介绍场景,如图2.38所示,在“Build Settings”对话框中添加了一个关卡。

图2.38 向“Build Settings”中添加一个关卡

接下来,在“Build Settings”对话框的左侧的“Platform”列表框中选择游戏运行的目标平台。对于桌面系统来说,应该选择“PC, Mac & Linux Standalone”,这也是默认的选项,然后在右侧的“Target Platform”下拉列表框中选择Windows、Linux,或者Mac OS X中的一项,这要取决于所使用的系统,如图2.39所示。

图2.39 选中一个目标平台

如果之前在多个平台上对游戏进行过测试,或者在其他的平台例如Android和iOS上进行过测试,就可以使用左下角的“Switch Platform”按钮。当在Platform中选择了一个选项之后,这个按钮就有可能被激活了。如果这个按钮被激活了,那么可以单击“Switch Platform”按钮来向Unity确认,如图2.40所示,Unity会花费一点时间来将资源配制成可以在选中平台上运行的程序。

图2.40 切换平台

在首次进行构建之前,想要查看一些与玩家相关的重要构建参数,例如游戏分辨率、游戏质量设置、可执行文件图标、相关信息以及其他设置,可以在“Build”对话框中选中“Player Settings”对游戏玩家设置进行调整。现在已经在对象检查(Inspector)面板中显示“Player Settings”了。同样,也可以在菜单栏上选中“Edit | Project Settings | Player”来完成这个操作,如图2.41所示。

图2.41 访问“Player Settings”选项

在“Player Settings”选项中,可以设置“Company Name”和“Product Name”这两个参数,它们将会成为内置在这个可执行文件中的信息。同样如果需要,还可以为这个游戏指定一个图标,为鼠标光标指定一个图像。对于这个采集游戏,后面的两项设置将不进行设定,如图2.42所示。

图2.42 为游戏设置厂商名字和产品名字

“Resolution and Presentation”选项卡也是十分重要的,因为在这里可以设置游戏屏幕的大小以及在游戏启动的时候是否同时出现分辨率(Resolution)对话框。在这个选项卡中,可以看到“Default Is Full Screen”选项已经处于被选中状态,这意味着游戏将会以一个全屏幕的形式运行,而不是在一个较小的可移动的窗口中运行。此外,将“DisPlay Resolution Dialog”下拉列表框设置为“Enabled”,如图2.43所示。设置完成后,在游戏开始时就会出现一个选项屏幕,允许用户对目标分辨率和屏幕大小以及一些自定义控制进行选择。如果构建最终版本,则需要禁用这个选项,按照自定义的屏幕选项设置进行游戏。不过,对于构建测试版本,分辨率对话框是一个十分有用的功能,它可以实现在各种不同大小的屏幕中测试游戏。

图2.43 启用“Resolution”对话框

现在已经为第一个游戏的编译构建做好了准备。接下来,单击“Build Settings”对话框中的“Build”按钮,或者从应用程序菜单上依次选择“File | Build & Run”。之后Unity会弹出一个保存对话框,在这个保存对话框中需要指定生成文件在硬盘上的存储位置,选择一个位置,然后单击“Save”,构建的过程就完成了。有时这个过程会产生错误,错误信息以红色的字体显示在控制台窗口。这是有可能会发生的,例如,试图将文件保存到一个只读硬盘区域,或者空间不足,又或者没有在该计算机上的管理权限等。不过,如果游戏在编辑器中能正常运行,一般在构建过程中也都会成功,如图2.44所示。

图2.44 构建并运行这个游戏

当构建过程结束时,Unity会在指定位置创建一个新的文件。如果是Windows操作系统,产生的就是图2.45所示的一个可执行文件和一个文件夹。注意,这两者都是必要的,而且两者是相互依存的。如果希望其他人在不安装Unity的情况下就可以进行这个游戏,就需要将可执行文件和相关的文件夹都发给用户。

图2.45 Unity构建产生的文件

在游戏运行中,“Resolution”对话框将会显示,这里假设已经在“Player Settings”中将“DisPlay Resolution Dialog”选项设置为“Enabled”(见图2.46)。用户可以选择游戏分辨率、游戏质量、输出显示器以及对玩家控制进行配置。

图2.46 从Resolution对话框做好游戏的准备

当单击“Play”按钮时,游戏就会在默认的全屏模式下开始运行了。游戏现在已经完成了,而且构建成功了。现在可以将这个游戏发给自己的朋友和家人们进行测试了!效果如图2.47所示。

图2.47 在全屏模式下运行金币采集游戏

当不想再玩这个游戏时如何才能退出呢?现在这个游戏中并没有Quit按钮或者主菜单。在Windows操作系统中,需要按下键盘上的“Alt + F4”组合键。如果是在Mac系统中,可以按下“Cmd + Q”组合键。如果是Ubuntu,按下“Ctrl + Q”组合键。

到现在为止,已经完成了第一个Unity工程——金币采集游戏。读者已经见识了Unity中大量的特性和功能,包括关卡的编辑与设计、预设体、粒子系统、网格、组件、脚本文件以及构建设置。当然这些特性和功能还需要进一步进行扩展和深入,现在已经将这些内容融合到了金币采集游戏中。接下来,需要继续一个完全不同的游戏,在这个游戏中会再次使用到这些功能和特性,并且将介绍一些全新的功能。简而言之,下一章,我们将会从一个Unity的初学者过渡成为一个Unity中等程度的使用者。


相关图书

Unity游戏开发入门经典(第4版)
Unity游戏开发入门经典(第4版)
Unity  3D游戏开发技术详解与典型案例
Unity 3D游戏开发技术详解与典型案例
从零开始:快速入门Unity 3D游戏开发
从零开始:快速入门Unity 3D游戏开发
Unity 3D脚本编程与游戏开发
Unity 3D脚本编程与游戏开发
AR开发权威指南:基于AR Foundation
AR开发权威指南:基于AR Foundation
VR与AR开发高级教程:基于Unity(第2版)
VR与AR开发高级教程:基于Unity(第2版)

相关文章

相关课程