Unity-2018-入门指南-全-
Unity 2018 入门指南(全)
原文:
zh.annas-archive.org/md5/0be53531cdbfde6920a8d24b44936790译者:飞龙
前言
随着游戏无处不在以及几乎每个行业都使用游戏化,人们想要发现如何使用最先进的发展软件的愿望从未如此强烈。现在有越来越多的软件工具可供开发者使用,以帮助他们为游戏机、网页、桌面计算机和移动设备创建惊人的游戏。游戏引擎是这些工具中最强大的之一。Unity 3D 游戏引擎是精英游戏引擎之一。它被大型游戏工作室和独立开发者用来创建流行的 2D 和 3D 游戏。由于有免费版本,以及 Unity 的最新版本,现在是开始使用 Unity 的最佳时机。
《Unity 2018 入门,第三版》涵盖了目前最受欢迎的游戏引擎之一。这本书将引导你完成创建 3D 游戏的全过程,从下载 Unity 游戏引擎到发布你的游戏。你将享受本书中一些激动人心的主题的覆盖,包括玩家控制的角色和动画。无论你是刚开始作为游戏开发者,还是对 Unity 或其他游戏引擎有经验,这本书都将为你提供使用 Unity 2018 开发游戏的导游。通过清晰的解释、技巧和丰富的截图,你将获得开发游戏的详细步骤。
这本书采用实践动手的方式来学习 Unity 2018。随着你逐章前进,你将构建一个名为 Cucumber Beetle 的 3D 交互式游戏。在创建游戏的过程中,你将学习 Unity 2018 的关键特性,包括创建游戏环境、动画角色、脚本编写等。所有网格、模型、纹理、动画和其他资源都可以在本书的网站上找到。
当你完成这本书中的课程后,你将自信地开始使用 Unity 2018 来创建你自己的游戏。
这本书面向的对象是谁
这本书是为那些对 Unity 新手或者那些在 Unity 2018 之前版本有一些经验的人所写。如果你想了解 Unity 2018,想要复习一下,或者只是想看看如何使用顶级游戏引擎开发游戏,这本书就是为你准备的。
这本书涵盖的内容
第一章,下载和安装 Unity,概述了游戏引擎,然后深入探讨了 Unity 的起源,游戏引擎的现状,以及它是如何发展到成为今天使用最广泛的游戏引擎之一的。突出了 Unity 的能力和特性,并提供了下载和安装它的说明。
第二章,Unity 界面,检查 Unity 的主要视图、窗口、布局和工具栏。本章涵盖的界面组件是使用最频繁的。
第三章,设计游戏,涵盖了本书特色游戏——黄瓜甲虫的设计。游戏设计包括游戏玩法、游戏机制、玩家角色、非玩家角色、游戏资产、动画等。使用屏幕原型和叙述来记录游戏设计。
第四章,创建我们的地形,介绍了游戏地形的创建和定制。介绍了造型工具,并将水和植被特征添加到游戏环境中。
第五章,灯光、相机和阴影,探讨了 Unity 中的相机和照明。本章从查看相机开始,包括视角、视锥体和 Skyboxes。还涵盖了使用多个相机包括迷你地图的使用。还探讨了不同类型的照明、反射探针和阴影。
第六章,为我们的游戏创建和导入 3D 对象,专注于使游戏环境更加健壮,并向游戏场景添加树木和其他对象。本章还检查了使用 Unity 原生建模工具创建 3D 对象的必要步骤。从 Unity 资产商店和为黄瓜甲虫游戏专门准备的 3D 资产中添加了资产到游戏中。
第七章,实现我们的玩家角色,包含了游戏中的玩家角色——黄瓜人。角色被导入,并审查了控制和动画。到本章结束时,游戏将准备好在游戏模式中进行测试。
第八章,实现我们的非玩家角色,解释了非玩家角色——黄瓜甲虫。审查了甲虫的 11 种动画,并对非玩家角色的动画控制器进行了修改。此外,还将编写脚本以控制非玩家角色。在本章中,还向游戏世界添加了黄瓜地、黄瓜和樱桃。
第九章,添加抬头显示,涵盖了游戏中的抬头显示(HUD)的设计、开发和集成。使用画布创建文本和图形,提供视觉指示,如得分、健康和额外信息,以帮助玩家在游戏过程中保持对局势的了解。还实现了迷你地图。
第十章,编写我们的得分系统脚本,探讨了游戏得分系统的设计、脚本编写和实现。这包括为游戏 HUD 的关键屏幕组件提供逐帧更新。
第十一章,脚本胜利与失败,深入探讨了游戏胜利和失败条件的设计和脚本编写。脚本将更新以管理 Cucumber Man 的生命值,提供逐帧的屏幕更新,并在生命值耗尽时确保玩家生命丢失。还涵盖了角色生命和重生。
第十二章,为我们的游戏添加音频和视觉效果,展示了在游戏中添加音频和视觉效果的计划和实施,以帮助增强整体游戏体验。具体来说,音频被添加到战斗系统的关键事件中,并使用 Unity 的粒子系统添加了几个特殊效果到游戏中。
第十三章,优化我们的游戏以进行部署,讨论了优化和部署。探讨了诊断 Unity 游戏性能问题的步骤,以及如何优化脚本和图形渲染。解释了 Unity 构建过程,以及如何创建独立玩家和如何将游戏部署到多个平台。
第十四章,虚拟现实,探讨了 Unity 在虚拟现实方面的能力。提供了虚拟现实简介,包括硬件要求。您将学习如何使用 Unity 游戏引擎创建虚拟现实游戏。
要充分利用本书
您不需要有编程经验、与游戏引擎合作的经验或 Unity 的知识,就可以从本书中受益。没有对知识或经验做出任何假设。
唯一软件要求是下载并安装 Unity 游戏引擎。这些步骤在书中详细说明,因此您开始阅读之前不需要任何软件。
下载示例代码文件
您可以从www.packtpub.com的账户下载本书的示例代码文件。如果您在其他地方购买了此书,您可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。
您可以通过以下步骤下载代码文件:
-
在www.packtpub.com上登录或注册。
-
选择“支持”选项卡。
-
点击“代码下载与勘误”。
-
在搜索框中输入书籍名称,并遵循屏幕上的说明。
文件下载后,请确保使用最新版本解压缩或提取文件夹:
-
WinRAR/7-Zip for Windows
-
Zipeg/iZip/UnRarX for Mac
-
7-Zip/PeaZip for Linux
本书代码包也托管在 GitHub 上,地址为github.com/PacktPublishing/Getting-Started-with-Unity-2018-Third-Edition。如果代码有更新,它将在现有的 GitHub 仓库中更新。
我们还提供其他代码包,这些代码包来自我们丰富的图书和视频目录,可在github.com/PacktPublishing/找到。查看它们吧!
下载彩色图像
我们还提供包含本书中使用的截图/图表的彩色图像的 PDF 文件。您可以从这里下载:www.packtpub.com/sites/default/files/downloads/GettingStartedwithUnity2018ThirdEdition_ColorImages.pdf.
使用的约定
本书使用了多种文本约定。
CodeInText:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称。以下是一个示例:“确保在项目面板中选择了Skybox文件夹。”
代码块设置如下:
public AudioSource audioSource;
public AudioClip eating;
public AudioClip attack;
public AudioClip die;
粗体:表示新术语、重要单词或屏幕上出现的单词。例如,菜单或对话框中的单词在文本中显示如下。以下是一个示例:“从HUD_canvas中选择填充选项。”
警告或重要注意事项看起来像这样。
技巧和窍门看起来像这样。
联系我们
我们始终欢迎读者的反馈。
一般反馈:通过feedback@packtpub.com发送电子邮件,并在邮件主题中提及书籍标题。如果您对本书的任何方面有疑问,请通过questions@packtpub.com发送电子邮件给我们。
勘误:尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果您在这本书中发现了错误,我们将不胜感激,如果您能向我们报告,我们将非常感谢。请访问www.packtpub.com/submit-errata,选择您的书籍,点击勘误提交表单链接,并输入详细信息。
盗版:如果您在互联网上以任何形式发现我们作品的非法副本,如果您能提供位置地址或网站名称,我们将不胜感激。请通过copyright@packtpub.com与我们联系,并提供材料的链接。
如果您想成为一名作者:如果您在某个领域有专业知识,并且对撰写或参与书籍感兴趣,请访问 authors.packtpub.com.
评论
请留下评论。一旦您阅读并使用过这本书,为什么不在您购买它的网站上留下评论呢?潜在读者可以查看并使用您的客观意见来做出购买决定,Packt 公司可以了解您对我们产品的看法,我们的作者也可以看到他们对书籍的反馈。谢谢!
想了解更多关于 Packt 的信息,请访问 packtpub.com.
第一章:下载和安装 Unity
在本章中,你将了解游戏引擎,并查看四种不同类型,然后再专注于 Unity。一旦打下这个基础,我们就会开始深入探讨 Unity 的起源,了解游戏引擎的现状,以及它是如何发展成为今天使用最多的顶级游戏引擎之一的。我们将突出 Unity 的功能和特性。然后,我们将回顾开发 Unity 以及运行 Unity 游戏所需的系统要求。最后,我们将下载并安装 Unity。
本章的主题包括:
-
游戏引擎概述
-
Unity——过去、现在和未来
-
Unity 的优势
-
系统要求
-
下载 Unity
-
安装 Unity
游戏引擎概述
可以将游戏引擎定义为提供你开发部署视频游戏所需功能的一套工具。对于游戏引擎,没有单一的行业标准定义。这很大程度上归因于它们的多样性和用途。通常,游戏引擎至少具备以下功能集:
-
2D 和/或 3D 图形设计工具
-
动画
-
资产管理——创建和导入游戏资产的能力
-
音频支持
-
跨平台部署——游戏可以制作适用于多个平台(如桌面、移动和游戏机)
-
图形用户界面
-
网络——支持多人游戏
-
物理
-
支持一种或多种语言的脚本编写
游戏引擎的基本概念是它们提供了一套强大的工具来处理游戏开发中的大部分繁琐工作,使开发者能够专注于游戏的美学和游戏玩法。在电子游戏的早期,每个游戏都是从头开始编写的,没有游戏引擎的库或功能。为了避免为每个游戏重新发明轮子,游戏引擎开始出现,这使得游戏工作室能够更容易地制作游戏。
游戏引擎不是可以创建任何可想象游戏的通用软件套件。它们高度专业化,尽管非常灵活,但旨在针对特定范围的游戏类型。例如,Codemasters 的 Ego 游戏技术引擎主要用于创建赛车游戏;Wildfire Games 的 Pyrogenesis 用于创建实时策略(RTS)游戏;Nival Interactive 的 Silent Storm 引擎主要用于回合制战术游戏;Naomi Takahashi 的 ONScripter 用于创建视觉小说和第一人称冒险游戏。
针对特定游戏类型的游戏引擎
可用的游戏引擎种类繁多;其中许多是免费的,一些是开源的,还有一些是专有软件。为你的游戏项目选择正确的游戏引擎是开发前的关键步骤。并非每个游戏引擎都适用于你的游戏,也没有单一的游戏引擎适用于所有游戏。幸运的是,我们有大量可供选择。
在决定为特定的游戏项目选择哪个游戏引擎时,请考虑你的游戏所属的主要游戏类型的典型特征。
我们使用“主要类型”这个短语,因为许多现代游戏模糊了类型界限,并融合了两个或更多类型的特点。这种类型模糊可以导致新的和创新的游戏。
第一人称射击(FPS)
这个游戏类型有许多成功的作品;以下是一些例子:
-
战场
-
生化奇兵
-
边缘之地
-
召唤师
-
命运
-
恶魔城
-
半条命
-
霍尔
-
左 4 死
-
守望先锋
-
红色警戒
第一人称射击(FPS)游戏旨在让玩家沉浸在游戏世界中。玩家扮演一个角色,为了达到理想的沉浸感,动画、音频和图形质量至关重要。特别关注角色的手臂和武器:

这些游戏通常具有以下特点:
-
大型 3D 分段游戏世界(室内和室外)
-
角色主要步行移动
-
一些车辆使用
-
标准摄像机和瞄准控制
-
逼真的动画
-
大量逼真的手持物体(武器)库存
-
非玩家角色(NPCs)具有逼真的人工智能
-
单人和多人模式
第三人称游戏
第三人称游戏是玩家角色在游戏场景中几乎或完全可见的游戏。这个类型包括第三人称射击(TPS)和第三人称动作/冒险。这意味着需要投入相当大的精力来关注角色的外观和动画。这些游戏基于第三人称角色视角,如下所示:

这里有一些更成功和受欢迎的第三人称游戏:
-
死亡空间
-
战争机器
-
贪婪城市
-
波斯王子
-
红色警戒
-
生化危机
-
美国海军陆战队
-
突袭细胞
-
无主之地
这些游戏通常具有以下特点,除了上一节中列出的 FPS 游戏的特点:
-
重视玩家角色
-
跟随玩家的摄像机
-
玩家控制的角色动作序列
-
全身动画
-
角色和摄像机旋转
其他游戏类型
还有大量其他游戏类型,如射击、平台、赛车、格斗、策略、战争、模拟和益智。识别特定游戏类型困难在于游戏类型分类的多种方式。例如,你可以有一个既是大型多人在线游戏(MMOG)的 TPS 游戏,因为它包含大量益智元素,所以它可以被添加到益智游戏类型中。
这不是需要过分担心的事情。重要的是能够识别出你游戏的关键组成部分,这样你就可以为你的项目选择最佳的可用游戏引擎。
可用的 3D 游戏引擎
在本节中,我们将简要回顾一些领先的游戏引擎,以给你一个关于可用性和它们功能的感觉。
由于 Unity 游戏引擎将在本章后面介绍,并且贯穿整本书,因此本节没有涉及。
CryENGINE
CryENGINE 由 Crytek 开发。有趣的是,这个游戏引擎最初是为了为显卡制造商 Nvidia 制作一个游戏演示而创建的,由于这个演示取得了巨大成功,游戏(孤岛惊魂)被全面投入生产,并成为商业上的成功。这个游戏引擎本身也非常成功。
该引擎及其完整源代码都是免费提供的。对于商业使用 CryENGINE,如版税等,没有财务义务。这个引擎能够提供高质量的视觉效果和出色的性能,你可以为以下平台开发游戏:
-
Linux PC
-
Oculus Rift
-
PlayStation 4
-
Windows PC
-
Xbox One
关于 CryENGINE 的更多信息,我推荐 Packt Publishing 出版的《精通 CryENGINE》:www.packtpub.com/game-development/mastering-cryengine。
Lumberyard
Lumberyard 是 Amazon Web Services(AWS)平台的一部分,在本书出版时仍处于测试阶段。这是一个基于 CryENGINE 的免费 AAA 游戏引擎。Lumberyard 的独特卖点在于没有其他游戏引擎提供与 Amazon Web Services 和 Twitch 的深度集成。
AAA(发音为“三 A”),指的是那些拥有极其庞大的生产和营销预算的游戏。
使用这个引擎,你可以为以下平台开发:
-
Android
-
HTC Vive
-
iOS
-
Oculus Rift
-
OSVR
-
PC
-
PlayStation 4
-
PlayStation VR
-
Xbox One
关于 AWS Lumberyard 的更多信息,我推荐 Packt Publishing 出版的《学习 AWS Lumberyard 游戏开发》:www.packtpub.com/game-development/learning-aws-lumberyard-game-development。
微软的 XNA 游戏工作室
微软的 XNA 游戏工作室是一套基于微软 .NET 框架的工具。它是免费提供的。如果你计划使用这个工具,有一些重新分发限制需要你审查。
使用 XNA,你可以为以下平台开发:
-
Windows PC
-
Windows Phone
-
Xbox 360
关于 XNA 的更多信息,我推荐 Packt Publishing 出版的《Microsoft XNA 4.0 游戏开发食谱》:www.packtpub.com/game-development/microsoft-xna-40-game-development-cookbook。
Unreal 游戏引擎
Epic Games 开发的 Unreal 游戏引擎最初是一款近 20 年前的第一人称射击游戏(FPS)。从那时起,Unreal 游戏引擎经历了显著的发展,现在是一个免费提供的 AAA 游戏引擎。你可以使用 C++ 或蓝图(一个可视化脚本系统)来用 Unreal 开发游戏。
Unreal 随带了一些模板,使得入门变得非常容易。这些模板包括:
-
2D 文件滚动器
-
第一人称
-
飞行
-
拼图
-
滚动
-
侧滚动
-
第三人称
-
俯视角
-
双摇杆射击游戏
-
车辆
-
车辆高级
使用 Unreal,你可以为以下平台进行开发:
-
Android
-
Daydream
-
HTML 5
-
iOS
-
Linux
-
macOS
-
Nintendo Switch
-
Oculus Rift
-
PlayStation 4
-
PlayStation VR
-
Samsung Gear VR
-
Steam
-
Viveport
-
Windows PC
-
Xbox One
关于 Unreal 的更多信息,我推荐 Packt 出版的《Unreal Engine: Game Development from A to Z》:www.packtpub.com/game-development/unreal-engine-game-development-z。
Unity – 过去、现在和未来
Unity 游戏引擎由 David Helgason、Nicolas Francis 和 Joachim Ante 在丹麦哥本哈根于 2004 年创建。他们创建游戏引擎是为了开发游戏,但最终专注于游戏引擎。以下是其发布历史的简要概述。
如果你在本节中遇到不熟悉的术语或功能,不要担心,我们将在后续章节中介绍它们。
版本 1.0 - 2005
此初始版本可用于开发针对 macOS X 操作系统的项目。Unity 1.0 的主要功能包括:
-
一些文档
-
变换脚本接口
-
Alpha 映射着色器
-
颜色编码的控制台警告和错误
-
完全沙盒化的网络播放器
版本 1.1 支持 Microsoft Windows 和网络浏览器作为分发平台。它还包括对 C/C++ 外部插件的支持。
版本 2.0 - 2007
版本 2.0 几个令人印象深刻的功能,包括对为 Windows 平台制作的项目更好的支持。此版本还改善了跨平台的网络播放器兼容性。
引擎更新包括更好的图形处理性能,支持微软的 DirectX 和 OpenGL。在此版本中,还引入了地形引擎,以及实时软阴影和网络功能。
Unity 资产服务器也在 2.0 版本中引入。这是一个用于在 Unity 项目上工作的团队的资产和版本控制系统。
Unity 2.0 的主要功能包括:
-
地形引擎
-
视频播放
-
DirectX 9.0 渲染器
-
网络多人游戏
-
实时动态阴影
-
游戏 GUI
-
网络播放器流和压缩
-
Unity 资产服务器
-
物理增强
-
脚本增强
-
水效果
此后,还有增量发布。最值得注意的是,2009 年发布了 2.5 版本,包括跨平台支持,这样 Unity 游戏引擎就可以在 Mac 和 Windows 计算机上运行。
版本 3.0 - 2010
3.0 版本的发布包括大量新功能和改进,以及编辑器、渲染、iOS 相关、物理、脚本和网络的大量错误修复。
Unity 3.0 的主要功能包括:
-
支持 Android 操作系统
-
改进的标准资产包
-
改进的编辑器
-
新的图形功能
-
资产管道改进
-
音频改进
-
物理改进
-
脚本功能文档
版本 3.5 代表了巨大的更新,并于 2011 年发布。它包括对 Flash 部署的支持。其他关键更新包括:
-
Shuriken 粒子系统
-
内置路径查找
-
升级了遮挡剔除
-
新的细节级别
-
线性空间照明
版本 4.0 - 2012
版本 4.0 于 2012 年发布,使游戏开发者能够创建令人惊叹的游戏玩法和动画。为此版本的游戏引擎的主要更新包括:
-
一个名为 Mecanim 的新动画系统
-
实时阴影(适用于所有平台)
-
支持 DirectX 11 渲染
-
更新了 Shuriken 粒子系统,包括对世界碰撞的支持
-
添加了新的部署平台:
-
Linux
-
Adobe Flash
-
-
支持跨平台动态字体
版本 5.0 - 2015
Unity 5.0 以免费个人版启动。此版本提供给收入或资金低于 10 万美元的任何人。也没有收取版税,这使得在没有初始游戏引擎技术成本的情况下进入游戏行业成为可能。使用免费版本,发布的游戏包含一个不可定制的启动画面。
版本 5.0 的关键特性包括:
-
3D 物理性能改进
-
动画系统更新
-
WebGL 预览
-
使用 HDR 反射探针的视觉保真度增强
-
音频混音器
-
实时全局照明
-
基于物理的标准着色器
版本 2017 - 2017
2017 年,Unity Technologies 宣布,他们将从增量数字版本转向发布年份的版本。因此,2017 年的主要版本是 2017 版,从那时起,该年度的所有次要版本都遵循语义版本控制。
语义版本控制是一种正式的软件版本控制约定,它使用一个三部分版本标识模式:主要.minor.patch。
实际上,2017 年的第一个版本是 2017.1,发布于 2017 年 7 月。以下是 Unity 2017 引入的一些主要特性:
-
时间轴——一个用于创建电影内容的可视化工具
-
Cinemachine——一个高级相机系统
-
后处理——你可以应用过滤器、控件等功能的特性
-
Unity Teams——一个基于云的新协作服务
-
改进的图形和平台支持
-
粒子系统改进
-
渐进式光映射改进
-
2D 改进
-
动画改进
-
实时阴影改进
版本 2018 – 2018
Unity 2018 的发布预计在 2018 年春季。游戏引擎将有一些令人兴奋的增强,特别是图形方面的重点。
下面是预期对 Unity 2018 的变更亮点:
-
可脚本渲染管线(SRP):将提供新的 SRP API,通过 C# 脚本提供对渲染管线的有限控制。Unity 2018 将包括两个渲染管线(轻量级和高清),可用于使用、复制或修改。
-
升级了后处理堆栈:新的后处理堆栈版本包括自动体积混合,以及增强的自定义效果控制。
-
着色器图:创建着色器的可视化界面。
-
集成 IDE:MonoDevelop 将不再作为脚本集成开发环境与 Unity 一起发货。Windows 用户将收到 Visual Studio 2017,Mac 用户将收到 Visual Studio for Mac。Visual Studio 是一个更强大的开发环境。
-
C#作业系统:此系统将使开发者能够创建更安全的多线程代码,并提高性能。
Unity 的优势
在 2D 和 3D 游戏开发中,有如此多的游戏引擎选项可用,你可能会想知道为什么你应该选择 Unity。2005 年的首次发布让我们了解了一个优秀的游戏引擎,在过去十年中,它通过新功能、支持和改进不断得到提升。每次新版本发布,开发者都会获得更多功能,以帮助他们开发令人惊叹的游戏。
Unity Technologies,Unity 的创造者,是一家致力于游戏引擎持续改进的成熟公司。随着新的处理技术和平台的出现,Unity 的支持也不会落后。与所有 Unity 功能一样,所有新的功能都有优秀的官方文档相伴。
能够使用无需支付版税的免费游戏引擎,这是一个游戏规则的改变。个人、爱好者和小型工作室可以使用与大型工作室相同功能强大的游戏引擎来制作游戏。此外,强大的功能集和 Unity 社区是使 Unity 成为您游戏开发项目的正确选择的其他因素。
Unity 功能
在本节中,我们将列出 Unity 游戏引擎的核心功能。我们将在随后的章节中详细探讨它们。Unity 可用于开发以下设备的应用程序,这些设备涵盖了控制台、桌面、移动、AR、电视、VR 和网页:
-
Android/Android TV
-
Daydream
-
Facebook Gameroom
-
Fire OS
-
Gear VR
-
Google Cardboard
-
iOS/tvOS
-
Linux
-
macOS
-
Microsoft Hololens
-
Nintendo 3DS/Nintendo Switch
-
Oculus Rift
-
PlayStation 4/PlayStation Vita/PlayStation VR
-
三星 SMART TV
-
Steam OS/Steam VR
-
Tizen
-
WebGL
-
Wii Universe(Unity 2018 不再支持)
-
Windows/Windows Phone/Windows Store Apps
-
Xbox One
编辑器
Unity 的编辑器是主要的游戏引擎界面,你将在其中度过大部分时间。以下是 Unity 编辑器的关键功能:
-
可在 Mac 和 Windows PC 上使用
-
2D 和 3D 场景设计工具
-
即时播放模式
-
强大的动画工具
-
时间轴工具用于创建电影序列
-
Cinemachine 工具用于智能相机
-
人工智能(AI)路径查找工具
-
可扩展——有大量可用的插件
-
粒子系统
-
支持 C#和 JavaScript
-
支持单人和多人游戏
-
包含协作工具
图形
Unity 游戏引擎的图形功能令人印象深刻。我们能够在游戏引擎中使用几何形状、网格、纹理和材质原生创建游戏对象。我们还可以使用从外部、专业软件工具(包括 Maya、3DS Max 和 Blender)导入的高质量图形。高级光照和阴影控制以及细节级别功能为我们的游戏增添了重要的真实感。
关键图形功能包括:
-
实时渲染
-
全局照明
-
基于物理的着色
-
原生图形 API,用于更快的渲染
-
多种光照技术
Unity 社区
Unity 拥有最活跃的社区之一。社区成员分享内容并帮助解答有关使用 Unity 进行开发的问题。Unity Technologies 推崇这个社区。除了官方教程外,您还可以找到大量博客、教程和视频,帮助您开始 Unity 开发,以及学习如何使用您的游戏完成特定操作。
Unity 资产商店包括 Unity Technologies 和 Unity 社区提供的免费和付费资产。可用的资产包括模型、艺术作品、动画、脚本和工具。资产商店具有直观的界面,使查找所需内容变得容易。我们将在第六章“为我们的游戏创建和导入 3D 对象”中探讨资产商店。
Unity 定期举办现场活动,让您可以直接从 Unity Technologies 了解 Unity,并与同行设计师和开发者建立联系。Unity Unite 活动每年在美国、亚洲和欧洲举办。您可以在以下位置了解过去和未来的 Unite 活动:unite.unity.com/。
系统要求
系统要求分为两部分。第一部分指的是您需要哪些硬件和软件才能使用 Unity 进行游戏开发。第二部分指的是您需要哪些硬件和软件来运行在 Unity 中开发的游戏。虽然这些要求可能会变化,但截至 Unity 2017,这些要求在接下来的两个部分中详细说明。
开发系统要求
您可以使用 Mac 或 Windows 计算机进行 Unity 开发。Mac 必须运行 macOS X 10.9 或更高版本。对于 Windows 计算机,您必须拥有 Windows 7 SP1+、Windows 8 或 Windows 10。
Windows XP 和 Windows Vista 不受官方支持。
您的开发计算机上的图形处理单元(GPU)必须具有 DX9(着色器模型 3.0)或 DX11,并具有 9.3 功能级别的功能。
根据您要针对的发行平台,还有其他特定要求。请参考以下表格以获取更多信息。
| 如果您正在为... | 您必须具有... |
|---|---|
| Android |
-
Android SDK
-
Java 开发工具包
|
| iOS |
|---|
-
macOS X 10.9.4 或更高版本
-
Xcode 7.0 或更高版本
|
| WebGL | 以下之一:
-
macOS X 10.9+
-
Windows 7 SP1+(64 位)
|
| Windows Store | Windows 8.1(64 位)以及以下之一(根据适用情况):
| IL2CPP(用于编译.NET 程序集) | 带有 C++编译器功能的 Visual Studio |
| 通用 Windows 平台 (UWP) | Visual Studio 2015 或更高版本 Windows 10 SDK |
| Windows 8.1/Windows Phone 8.1 | Visual Studio 2013 或更高版本 Windows 8.1 SDK |
|
播放系统要求
对于在您开发的设备上玩 Unity 游戏的用户来说,要求并不多。通常,拥有硬件和操作系统都最新的设备将带来最佳的游玩体验。
基本要求如下详细说明:
| 设备/平台 | 要求 |
|---|---|
| Android |
-
支持 NEON 的 ARMv7(Cortex)CPU 或 Atom CPU
-
OpenGL ES 2.0 或更高版本
-
OS 4.1 或更高版本
|
| 桌面 | CPU
- 必须支持 SSE2 指令集
图形卡选项
-
DX9(着色器模型 3.0)
-
DX11 具有 9.3 功能级别的功能
操作系统选项
-
macOS X 10.9+
-
SteamOS+
-
Ubuntu 12.04+
-
Windows XP SP2+
|
| iOS | iOS 7.0 或更高版本 |
|---|
| WebGL | 以下浏览器之一的最新版本
-
Chrome
-
Edge
-
Firefox
-
Safari
|
| Windows Phone | Windows Phone 8.1 或更高版本 |
|---|
下载 Unity
获取 Unity 相对简单。首先访问 Unity 网站unity3d.com。在右上角,如图所示,您将看到一个获取 Unity 的链接:

该链接将带您到store.unity.com页面,您可以在个人计划选项中点击尝试个人链接。
您将被带到下载 Unity 个人版页面。您应该查看该页面的信息,以确保您符合个人计划的要求。
根据您的系统,您可能会被提示选择 Windows 或 macOS X 按钮。如果遇到此选择,请选择适合您计算机的适当操作系统。
一旦您确认您的资格,下载安装程序按钮将被启用。
安装程序下载非常快。它只是一个安装程序,而不是游戏引擎本身。我们将在下一节中回顾安装过程。
安装 Unity
在上一节中,您已下载了 Unity 安装程序。找到该文件,可能位于您的下载文件夹中,然后启动程序。
对于剩余的步骤,以及在这本书的整个过程中,我们将使用在 macOS 上运行的 Unity。安装和使用 Unity 的步骤和过程对两个系统都是相同的;界面在外观上可能略有不同。
一旦您启动下载助手,您的计算机可能会提示您进行安全提示:

一旦您接受安全警告,并点击打开按钮,您将看到简介面板。点击继续按钮。
接下来,您将看到软件许可协议面板(此处未显示)。您可以阅读、打印和保存软件许可协议。点击继续按钮以继续。这将弹出一个窗口询问您是否同意软件许可协议的条款。如果您同意,请点击同意按钮。
下一个步骤是 Unity 组件选择屏幕。默认情况下,所有组件都将被选中。您将看到安装这些组件在您的计算机上需要多少空间以及您还剩下多少空间。您可以禁用您知道马上不需要的组件,稍后再安装它们。或者,如果您不介意空间问题,您可以安装所有组件:

为了跟随本书的教程,您至少需要选择以下组件:
-
Unity 2017.1(或界面中显示的后续版本)
-
标准资产
此外,以下组件也强烈推荐:
-
文档
-
示例项目
接下来,您将选择一个安装目标并点击继续按钮。
下载助手将开始下载您之前标识的组件(此处未显示)。如果您有较慢的互联网连接,这可能需要一段时间。
现在是创建您的免费 Unity ID 的好时机。如果您在浏览器中仍然打开了 Unity,您将在页面的右上角看到人物图标。如果您看不到该图标,您可以重新访问unity3d.com。当您点击该图标时,您将看到一个带有创建 Unity ID 按钮的新界面。点击该按钮并填写注册表单:

您需要您的账户来使用 Unity 资产商店,即使是获取免费内容也是如此。
一旦所有组件都已下载并安装,下载助手将通知您。
当您首次启动 Unity 时,您需要选择一个许可。现在 Unity 已成功安装在您的计算机上,您可以使用它了。
摘要
在本章中,我们探讨了游戏引擎,并查看了四个不同的游戏引擎以支持我们对游戏引擎的基础知识。我们还探讨了 Unity 的历史以及它是如何从一个版本发展到下一个版本的。此外,我们还发现了 Unity 的关键功能和特性。涵盖了开发 Unity 游戏所需的 Unity 的系统要求,以及运行它们所需的系统要求。最后,我们下载并安装了 Unity。
在第二章“Unity 界面”中,我们将提供 Unity 编辑器用户界面的详细信息。您将了解各种视图、窗口、布局和变换工具。
第二章:Unity 界面
在第一章“下载和安装 Unity”中,我们探讨了游戏引擎,并快速浏览了四个替代游戏引擎,以帮助我们更好地欣赏 Unity。我们回顾了 Unity 的历史以及它是如何从一个版本发展到下一个版本的。此外,我们还发现了 Unity 的关键功能和特性。我们涵盖了开发 Unity 游戏所需的系统要求和运行它们所需的系统要求。最后,我们下载并安装了 Unity。
在本章中,我们将检查 Unity 的主要视图和窗口;我们还将涵盖布局和工具栏。本章涵盖的界面组件是使用最频繁的。随着新功能和工具的引入,后续章节将涵盖更多界面组件。
具体来说,我们将涵盖以下组件:
-
屏幕空间
-
菜单
-
场景视图
-
游戏视图
-
项目窗口
-
层级窗口
-
检查器窗口
-
工具栏
-
布局
Unity 可以在 Windows PC 和 Mac 上运行。本章及全书使用的屏幕图像均来自在 Mac 上运行的 Unity。提供的截图与您在 Windows PC 上运行 Unity 时看到的界面可能会有细微差别。
屏幕空间
当我们第一次启动 Unity 时,我们可能会被界面上所有的区域、标签、菜单和按钮所吓到。Unity 是一个功能丰富的游戏引擎,因此我们应该期待有更多的组件供我们交互。如果我们把界面分解成独立的组件,我们可以独立检查每个组件,从而全面了解整个界面。
正如您所看到的,我们已经确定了界面的六个主要区域。我们将在后续章节中逐一检查这些区域。您很快就会了解到,这个界面是可以定制的。以下截图显示了 Unity 用户界面的默认配置。

Unity 游戏引擎用户界面的概述。
在下一节中,我们将涵盖前一个截图所示的所有以下组件:
-
菜单
-
场景视图
-
游戏视图
-
项目窗口
-
层级窗口
-
检查器窗口
-
工具栏
-
布局
当这本书出版时,Unity 2017 是最新版本,因此截图反映了 Unity 2017 的界面。Unity 2018 处于测试版,因此本章中提供了信息框中的界面差异。
菜单
如图中所示,Unity 编辑器的主菜单栏包含八个下拉选项。在本节中,我们将简要回顾每个菜单选项。更多细节将在后续章节中提供,随着我们开始开发我们的Cucumber Beetle游戏:

Unity 的菜单是上下文相关的。这意味着只有与当前选中对象相关的菜单项才会被启用。其他不适用的菜单项将显示为灰色而不是黑色,并且不可选择。
从 Unity 2018 开始,还有一个额外的顶部菜单是 Mobile Input。此菜单项允许您切换移动输入的开启和关闭。
Unity
如此显示的 Unity 菜单项为我们提供了访问 Unity 信息、我们的软件许可、显示选项、模块信息和访问预设的功能:

选择 Unity | 关于 Unity... 菜单选项可以访问您正在运行的引擎版本。还有其他信息,但您可能只会使用此菜单选项来检查您的 Unity 版本。
Unity | Preferences... 选项会弹出 Unity 预设对话框窗口。该界面有七个侧标签:常规、外部工具、颜色、按键、GI 缓存、2D 和缓存服务器。随着您在 Unity 中经验的积累,我们鼓励您熟悉它们。我们将在第十三章 优化我们的游戏以部署中使用外部工具标签。
Unity | 模块 选项为您提供正在运行的播放引擎列表以及任何 Unity 扩展。
您可以通过选择 Unity | 退出 菜单选项来退出 Unity 游戏引擎。
在 Unity 2018 中,Unity 菜单项不存在。该功能将移至帮助菜单。
文件
Unity 的文件菜单包括访问您的游戏场景和项目。我们将在整个游戏开发过程中使用这些功能。如您在以下屏幕截图中所见,我们还可以访问构建设置...。我们将在第十三章 优化我们的游戏以部署中探讨此功能。

编辑
Edit 菜单具有与标准编辑器类似的功能,而不仅仅是游戏引擎。例如,标准的剪切、复制、粘贴、删除、撤销和重做选项都在那里。此外,快捷键与软件行业标准一致。
如您从以下屏幕截图中所见,这里还有额外的功能可供访问。这里有播放、暂停和单步执行命令。我们还可以登录和注销我们的 Unity 账户:

Edit | Project Settings 选项为我们提供了访问输入、标签和图层、音频、时间、播放器、物理、2D 物理、质量、图形、网络、编辑器和脚本执行顺序的功能。在大多数情况下,选择这些选项之一将打开或聚焦键盘控制到特定的功能。
资产
我们将在本书中广泛使用 Assets 菜单功能,从第四章 创建我们的地形 开始。资产是我们可以在游戏中使用的事物的表示。例如,包括音频文件、艺术文件和 3D 模型。Unity 中可以使用几种类型的资产。正如您可以从以下屏幕截图中看到的那样,我们能够创建、导入和导出资产:

随着您通过本书并开始开发您的游戏,您将越来越熟悉这一系列功能。
GameObject
GameObject 菜单为我们提供了创建和操作 GameObject 的能力。在 Unity 中,GameObject 是我们在游戏中使用的事物,例如灯光、摄像机、3D 对象、树木、角色、汽车等等。正如您所看到的,我们可以创建一个空的 GameObject,以及一个空的子 GameObject:

在本书中,我们将全面地使用 GameObject 菜单项。在此阶段,重要的是要知道这是您创建 GameObject 以及对它们进行一些操作的地方。
组件
在上一节中,我们提到 GameObject 只是 事物。实际上,只有当我们向它们添加组件时,它们才变得有意义。组件是 Unity 中的一个重要概念,随着我们游戏开发的进展,我们将大量使用它们。组件为我们的 GameObject 实现了功能。
以下屏幕截图显示了各种组件类别。这是在 Unity 中创建组件的一种方法:

窗口
窗口菜单选项提供了访问许多额外功能的方法。正如您所看到的,这里有一个最小化选项,可以将主 Unity 编辑器窗口最小化。缩放选项切换全屏和缩放视图:

布局选项提供了访问各种编辑器布局、保存或删除布局的功能。布局将在本章后面更深入地介绍。
以下表格提供了通过窗口菜单项可用的剩余选项的简要描述。随着您通过本书的进展,您将获得这些窗口的实践经验:
| 窗口选项 | 描述 |
|---|---|
| 服务 | 访问集成服务:广告、分析、云构建、协作、性能报告、应用内购买和多玩家。 |
| 场景 | 将焦点集中在场景视图中。如果尚未打开,则打开窗口。更多详细信息将在本章后面提供。 |
| 游戏 | 将焦点集中在游戏视图中。如果尚未打开,则打开窗口。更多详细信息将在本章后面提供。 |
| 检查器 | 将焦点集中在检查器窗口上。如果尚未打开,则打开窗口。更多详细信息将在本章后面提供。 |
| 层级 | 将焦点置于层级窗口。如果窗口尚未打开,则打开它。本章后面将提供更多详细信息。 |
| 项目 | 将焦点置于项目窗口。如果窗口尚未打开,则打开它。本章后面将提供更多详细信息。 |
| 动画 | 将焦点置于动画窗口。如果窗口尚未打开,则打开它。在第七章 实现我们的玩家角色 中提供了更多详细信息。 |
| 性能分析器 | 将焦点置于性能分析器窗口。如果窗口尚未打开,则打开它。在第十三章 优化我们的游戏以部署 中提供了更多详细信息。 |
| 音频混音器 | 将焦点置于音频混音器窗口。如果窗口尚未打开,则打开它。 |
| 资产商店 | 将焦点置于资产商店窗口。如果窗口尚未打开,则打开它。 |
| 版本控制 | Unity 为大多数流行的版本控制系统提供功能。 |
| 协作历史 | 如果您使用的是集成协作工具,您可以通过此处访问您项目更改的历史记录。 |
| 动画师 | 将焦点置于动画师窗口。如果窗口尚未打开,则打开它。 |
| 动画师参数 | 将焦点置于动画师参数窗口。如果窗口尚未打开,则打开它。 |
| 精灵打包器 | 将焦点置于精灵打包器窗口。如果窗口尚未打开,则打开它。为了使用此功能,您需要在项目设置中启用旧版精灵打包。 |
| 实验 | 将焦点置于实验窗口。如果窗口尚未打开,则打开它。默认情况下,Look Dev 实验功能可用。更多实验功能可以在 Unity 资产商店中找到。 |
| 测试运行器 | 将焦点置于实验窗口。如果窗口尚未打开,则打开它。这是一个在编辑和播放模式下对您的代码进行测试的工具。也可以测试构建。 |
| 时间轴编辑器 | 将焦点置于时间轴编辑器窗口。如果窗口尚未打开,则打开它。这是一个上下文菜单项。 |
| 灯光 | 访问灯光窗口和灯光资源管理器窗口。灯光将在第五章 灯光、相机和阴影 中介绍。 |
| 次要渲染 | 此功能允许您选择和编辑对象的绘制方式。使用遮挡剔除,只有当前相机可视范围内的对象,且未被其他对象遮挡的对象,才会被渲染。 |
| 帧调试器 | 此功能允许您逐帧遍历游戏,以便您可以看到给定帧上的绘制调用。 |
| 导航 | Unity 的导航系统使我们能够实现与 NPC(非玩家角色)移动相关的人工智能。我们将在第八章 实现我们的非玩家角色 中介绍这个概念。 |
| 物理调试器 | 将焦点置于物理调试器窗口。如果窗口未打开,则打开窗口。在此,我们可以切换几个与物理相关的组件,以帮助调试游戏中的物理问题。 |
| 控制台 | 将焦点置于控制台窗口。如果窗口未打开,则打开窗口。控制台窗口显示警告和错误。在游戏过程中,您也可以在此处输出数据,这是一种常见的内部测试方法。 |
一些窗口将以标签的形式打开。您可以拖放窗口使其成为标签。您还可以将标签拖放到空白区域以使其成为自己的窗口。
帮助
如以下截图所示,您可以通过帮助菜单选择访问 Unity 的文档和脚本手册:

通过帮助菜单提供的附加功能如下:
| 帮助内容 | 描述 |
|---|---|
| Unity 服务 | 此菜单选项将带您访问一个 Unity 页面,该页面作为获取集成 Unity 服务信息的入口。 |
| Unity 论坛 | 此菜单选项将带您访问 Unity 论坛网站,该网站包含多个不同的 Unity 论坛,并具有搜索功能。 |
| Unity 问答 | 此菜单选项将带您访问 Unity 问答网页。在这里,您可以就 Unity 开发提出新问题,并搜索以前的问题和答案。 |
| Unity 反馈 | 搜索以前的反馈或直接向 Unity 输入您的反馈。 |
| 检查更新 | 这有助于确保您拥有 Unity 游戏引擎的最新版本。 |
| 下载 Beta... | 这将带您访问 Unity 测试计划的网页。 |
| 发布说明 | 这是一个快速链接到 Unity 当前版本发布说明的网页。 |
| 软件许可 | 这提供了 Unity 使用的软件组件的法律信息。 |
| 报告一个 Bug... | 这将打开 Unity 错误报告器,允许您输入错误报告。 |
Unity 2018 中的附加帮助菜单选项将包括:
-
关于 Unity
-
管理许可
-
重置包到默认设置
-
故障排除
场景视图
参考本章的第一张截图,场景视图通常位于 Unity 编辑器的中央,可能位于游戏视图标签旁边。
场景视图是您将在其中花费大部分时间来制作游戏的地方。您将在这里构建和操作游戏关卡。您在此视图中执行的一些操作包括添加和修改游戏角色、灯光、摄像机以及与您的游戏相关的其他对象。
如以下截图所示,场景视图在界面的顶部有一组控件。这被称为控制栏:

以下是对这些控件的总览:
| 图标 | 控制 | 描述 |
|---|---|---|
![]() |
渲染模式 | 影响场景视图中事物外观的各种绘图选项。 |
![]() |
2D/3D | 这是一个在 2D 和 3D 视图之间的切换。 |
![]() |
灯光 | 切换灯光的开启和关闭。 |
![]() |
音频 | 切换音频的开启和关闭。 |
![]() |
特效 | 此控制包含对 Skybox、雾、光晕、动画材质和图像效果的切换。 |
![]() |
仪器 | 此控制提供了大量选项,用于显示某些对象的方式。 |
![]() |
搜索 | 这个强大的搜索功能使你能够在场景视图中过滤对象。 |
游戏视图
游戏视图允许你在编辑器中工作时预览你的游戏。在这个视图中,你不会操作你的游戏,但可以预览它。
你还可以玩游戏。要启动游戏模式,你点击位于工具栏顶部中央的播放按钮:

你可以在游戏模式下更改你的游戏。你在游戏模式下所做的任何更改都是临时的。一旦你退出游戏模式,这些更改将自动撤销。
如以下截图所示,游戏视图在界面的顶部有一组控制按钮。这被称为控制栏:

下表包括上述控制栏的每个组件。每个组件都提供了图像、控件名称和描述:
| 图标 | 控件 | 描述 |
|---|---|---|
![]() |
显示 | 如果你场景中有多个相机,你可以更改视图到特定的相机。 |
|
| 比例 | 可用的宽高比包括:
-
低分辨率宽高比
-
自由比例
-
5:4
-
4:3
-
3:2
-
16:10
-
16:9
-
独立(1024x768)
|
![]() |
缩放滑块 | 此滑块允许你放大到特定区域以获得更详细的视图。 |
|---|---|---|
![]() |
在播放时最大化 | 此控件是一个切换按钮,允许你在游戏模式下将游戏视图最大化到编辑器的全尺寸。 |
![]() |
静音音频 | 此切换按钮允许你在游戏模式下静音游戏音频。 |
![]() |
统计信息 | 此控件切换统计信息的叠加开启和关闭。 |
![]() |
仪器 | 这个强大的搜索功能使你能够在游戏视图中过滤对象。 |
项目窗口
项目窗口是您将经常使用的窗口,并提供快速访问项目中的对象。如图所示,项目窗口在左侧以分层视图组织。选择左侧的文件夹将显示右侧面板中的内容。
该面板在顶部显示面包屑,并在右下角有一个滑块来控制图标的大小:

项目窗口右上角的创建下拉菜单为您提供快速访问在当前文件夹中创建游戏资源。
创建下拉菜单的右侧是一个搜索栏,后面跟着三个图标。前两个图标分别是按类型搜索和按标签搜索。第三个图标允许您保存搜索。
本节提供的项目窗口内容是代表性的,但不属于本书的游戏。
层级窗口
层级窗口列出了当前场景中的所有 GameObject。这与项目窗口中显示的内容不同,因为游戏项目包含一个或多个场景,并且项目中的不是每个对象都在场景中。如图所示,层级窗口中的一些项目左侧有一个三角形。单击三角形将展开内容,显示子对象。将 GameObject 相互关联或从属化是重要的,并且您将在开始向游戏添加 GameObject 时接触到这一点:

层级窗口右上角的创建下拉菜单为您提供快速访问创建游戏资源。
创建下拉菜单的右侧是一个搜索栏。在搜索栏下方和右侧是一个菜单图标。单击该图标将显示以下选项列表:

如上图菜单所示,它是上下文相关的,因此并非所有项目都会始终启用。
检查器窗口
检查器窗口是我们检查 GameObject 的地方。您会记得 GameObject 由多个组件组成,而这些组件是使 GameObject 在我们的游戏中变得有价值的原因。
此示例截图显示了一个具有 11 个组件的 ThirdPersonController GameObject。每个组件左侧的灰色三角形允许您展开组件并对该组件的属性进行任何所需的更改:

当您在场景视图中选择一个 GameObject、层级窗口或项目窗口时,检查器窗口将显示所选 GameObject 的组件和属性。每个组件都有不同的属性集。
在检查器窗口的底部,当选择了一个 GameObject 时,您将看到添加组件按钮。单击此按钮允许您向当前 GameObject 添加组件。
工具栏
这里所示的是 Unity 的工具栏,位于编辑器界面的顶部。它横跨窗口的整个宽度,间距取决于当前宽度。

工具栏可以从理论上组织成以下类别:变换工具、 Gizmo 切换、播放按钮、云和账户按钮,以及图层和布局。我们在游戏视图部分介绍了播放按钮;其余的工具栏类别如下详细说明。
变换工具
变换工具是一组在场景视图中操纵 GameObject 的五个基本工具。每个工具都由一个图标表示:

第一个按钮是手工具或视图工具。当选择此工具时,场景视图中的光标将变成一只手。这让我们知道我们处于哪种模式。使用此工具选择后,我们可以用鼠标滚动来放大和缩小场景。如果你点击左鼠标按钮,你能够绕场景移动。点击右鼠标按钮,你能够根据光标当前位置四处查看。
如果你在一个 PC 上按住 Alt 键或在 Mac 上按住 Option 键,然后点击左鼠标按钮,你可以围绕当前区域旋转。按下相同的键和右鼠标按钮允许你在场景中放大和缩小。
第二个按钮是平移工具,形状为一个四箭头。当选择一个对象时,点击平移工具;对象将有三个 Gizmo,每个轴一个。点击并拖动这些 Gizmo 中的任何一个将沿相应轴移动对象,如下面的截图所示:

使用变换平移工具的立方体
第三个变换工具是旋转工具,看起来像两个旋转的箭头。这个工具允许我们沿任何轴(x、y 或 z)旋转对象。这个工具不是用线和箭头 Gizmo 实例化的,而是用三个彩色环表示,每个轴一个。点击一个环并拖动它将沿该轴旋转对象,如下面的截图所示:

使用变换旋转工具的立方体
第四个变换工具是缩放工具,它由线和块形 Gizmo 表示。与其他变换工具一样,每个轴都有一个 Gizmo。点击并拖动这些 Gizmo 中的一个会增加或减少对象沿选定轴的尺寸。例如,你可以使立方体更宽、更窄、更高或更短。如果你想保持纵横比,你可以点击中心正方形而不是红色、蓝色或绿色的正方形。现在,当你点击并拖动时,你的对象将以完美的纵横比增长或缩小,如下面的截图所示:

使用变换缩放工具的立方体
最后一个变换工具是矩形工具,它由一个带有交叉点的矩形表示。矩形工具可以用来在场景视图中移动、调整大小和旋转对象。因此,这是一个多功能工具,它还具有您可以直接使用检查器视图编辑的相应属性。请看以下截图:

使用变换矩形工具的立方体
Unity 2018 将引入第六个变换工具,允许移动、旋转和缩放选定的对象。
工具切换
如此所示,工具栏上有两个工具切换,称为变换工具切换:

变换工具切换:中心与全局
第一个是中心与枢轴之间的切换。第二个切换是全局与局部之间的切换。
云和账户按钮
此类别中有三个按钮,如图所示:

工具栏:Collab、云和动作按钮
第一个按钮是 Collab 或协作按钮。如果您使用的是集成协作工具,您可以在此处访问项目更改的历史记录。
第二个按钮是云按钮。这个按钮只是打开 Unity 服务窗口。
第三个按钮是账户按钮。这是一个下拉按钮,允许您访问您的 Unity 账户。
层和布局
工具栏上按钮的最后部分包括层下拉按钮和布局下拉按钮。这些按钮如图所示,位于工具栏的右端:

工具栏:层和布局下拉按钮
选择层下拉按钮允许您选择您想要查看、不查看和锁定哪些层。此外,如您所见,您有权编辑层:

编辑层包括创建用户定义的层,您可以重新排序和重命名这些层。
布局下拉菜单显示了以下选项集:

布局将在下一节中介绍。
布局
与 Unity 一起工作的美妙之处之一是您可以自定义用户界面的布局方式。您可以使用 2x3、4 分割、高或宽的预定义布局之一,或者您可以创建自己的布局。布局指的是 Unity 中各种视图在屏幕上的排列方式。
要更改布局,我们只需点击位于 Unity 界面右上角远端处的布局按钮。让我们逐一查看每个布局以了解其差异。
第一个布局是 2x3 布局。此布局提供了很好的排列,场景和游戏视图位于左侧,层次和项目视图位于中间,右侧是一个完整的检查器视图,如图所示:

4 分割布局提供了对同一场景的四种不同视图,如下面的截图所示。这是检查您游戏中光照和着色实现的好方法。我们将在本书的后面部分讨论光照和着色。4 分割布局如下所示:

高布局提供了高但不是宽的视角来查看场景视图,其他视图位于右侧,如下面的截图所示:

宽布局提供了场景视图的宽视角,其他视图位于底部和右侧,如下面的截图所示:

默认布局是宽布局的一种变体。区别在于,使用默认布局时,层次结构视图位于左侧,如下面的截图所示:

您可以在任何时候切换布局,而不会影响您的游戏。大多数 Unity 开发者不会只在一个布局下工作。不同的布局提供不同的好处,适用于不同的任务。您还可以通过拖动视图或窗口的任何边框来修改任何布局。如果您想保存布局,对当前布局进行任何更改,然后选择布局按钮并选择保存布局。您将被提示输入名称。
摘要
在本章中,我们检查了 Unity 用户界面的主要组件。这包括场景和游戏视图。我们还查看了项目、层次结构和检查器窗口。然后我们回顾了各种布局以及工具栏。最后,我们确定将在后续章节中介绍新的功能和工具时覆盖额外的界面组件。
在下一章中,我们将开始设计我们的3D 黄瓜甲虫游戏。这个设计将指导我们在本书的其余部分开发游戏。
第三章:游戏设计
在第二章《Unity 界面》中,我们考察了 Unity 的用户界面,并特别关注了最常用的组件,包括:菜单、场景视图、游戏视图、项目窗口、层次结构窗口、检查器窗口、工具栏和布局。熟悉 Unity 的界面让我们有信心继续使用游戏引擎,并在引入创建游戏所需的新功能时探索额外的界面组件。
在本章中,我们将设计我们的游戏“黄瓜甲虫”,以便我们可以制定一个开发计划。我们的游戏设计将包括我们希望在游戏中拥有的所有功能、玩家角色、非玩家角色、游戏资产、动画等等。我们将使用屏幕草图以及叙事来记录我们的游戏设计。在过程中,我们将探讨与使用 Unity 进行游戏相关的概念。
具体来说,在本章中,我们将探讨以下概念:
-
游戏概念
-
游戏角色
-
游戏玩法
-
难度平衡
-
项目组织
游戏概念
为什么设计我们的游戏,而不是直接开始开发?这个问题源于对开发游戏的兴奋,尤其是使用 Unity 游戏引擎。所有游戏都始于一个想法。这个想法被转化为设计,而这个设计是开发和最终游戏的基础。
游戏的设计就像房子的蓝图。在没有蓝图的情况下建造房子是不可想象的,同样,在没有设计的情况下开发游戏也是一个同样糟糕的想法。这样做的原因是为了节省时间和挫折感。对于更大的项目,浪费的时间也意味着不必要的资金支出。想象一下,你雇佣了一个由十二名开发者、动画师和艺术家组成的项目团队。如果你分享了你的游戏想法,他们会有足够的依据去进行吗?他们会做出伟大的事情,但不会有一套连贯的游戏组件吗?我们通过我们的游戏设计所做的,就是在开始时尽可能多地记录下来,以便开发过程具有目的性。毫无疑问,你将在开发过程中不断修改你的游戏设计,因此有一个强大的基础开始至关重要。
我们的游戏设计将成为我们游戏外观的基础,玩家的目标是什么,游戏玩法将是什么,支持用户操作、动画、音频、人工智能和胜利条件。这需要考虑很多因素,也强调了将游戏想法转化为游戏设计的重要性。
在本书中,我们将涵盖一系列组件,然而,在本节中,我们将涵盖以下列出的组件:
-
游戏想法
-
输入控制
-
胜利与失败
游戏想法
我们Cucumber Beetle游戏的基本概念是它将是一款 3D 游戏,以黄瓜人为玩家角色。这个角色必须保护黄瓜地,抵御黄瓜甲虫的攻击。这些甲虫一心想要摧毁黄瓜地,以及任何挡在他们路上的东西,包括我们的黄瓜人。
黄瓜人将从樱桃树上收获樱桃,并将它们投向黄瓜甲虫。这将是我们黄瓜人对抗黄瓜甲虫的唯一防御形式,除了逃跑。
输入控制
考虑玩家如何与我们的游戏互动是很重要的。玩家将使用标准控制集控制黄瓜人。玩家期望游戏中的用户控制将遵循行业标准。因此,我们默认的用户输入控制集,如这里所示,将包括键盘和鼠标:

我们将配置和编程我们的游戏,以便用户从键盘输入与以下表格中的键和动作配对相匹配:
| 键盘输入 | 动作 |
|---|---|
| 上箭头 | 向前/上移动 |
| 下箭头 | 向后/下移动 |
| 左箭头 | 向左移动 |
| 右箭头 | 向右移动 |
| W | 向前/上移动 |
| S | 向后/下移动 |
| A | 向左移动 |
| D | 向右移动 |
| 空格键 | 跳跃 |
鼠标也将是用户输入的一个重要来源。我们将使用以下表格中所示的两个组件,利用鼠标实现:
| 鼠标输入 | 动作 |
|---|---|
| 鼠标移动 | 旋转角色 |
| 左键 | 投掷樱桃 |
左鼠标按钮将是我们操作按钮。我们需要确保只有当玩家剩下一个或多个樱桃时,才会投掷樱桃。
获胜与失败
我们获胜的条件是当所有黄瓜甲虫都被消灭时。玩家可能会以两种不同的方式输掉游戏。第一种失败条件是当所有黄瓜都被黄瓜甲虫吃掉时。第二种失败条件是如果玩家用完了生命值。
通过这个简短的描述,你可以知道将会有很多事情需要跟踪,包括以下内容:
-
黄瓜甲虫数量
-
黄瓜数量
-
黄瓜人生命值数量
游戏角色
我们的游戏将使用几个游戏对象,但只有两个游戏角色。第一个游戏角色是黄瓜人,将由玩家控制。第二个游戏角色是黄瓜甲虫。这是一个非玩家角色,由人工智能控制。让我们更详细地看看这两个角色。
黄瓜人
玩家将以黄瓜人的身份玩我们的游戏,这是游戏的主角。这是一个我们将导入用于游戏的角色。它使用 Maya 创建的,Maya 是一款专业的 3D 建模和动画软件套件,以及 Photoshop。以下是我们的黄瓜人的样子:

那么,黄瓜人玩家角色能做什么呢?我们已经知道,我们将能够通过键盘和鼠标输入的组合在我们的游戏环境中移动他。我们还知道,空格键将使他跳跃,而左鼠标按钮,我们的动作按钮,将使他射击樱桃。
因为黄瓜人是由人类玩家控制的,所以它被称为玩家角色。
我们将为黄瓜人实现以下动画:
-
空闲:当角色没有被玩家移动时,将播放这个动画。
-
行走:当玩家使黄瓜人行走时,无论方向如何,都会播放这个动画。
-
奔跑:这个动画与行走动画类似。它更快,可以快速覆盖距离。
-
跳跃:每当按下空格键时,我们将实现跳跃。
-
死亡:如果我们的角色被黄瓜甲虫克服,我们将播放这个动画。
-
投掷:这是将使黄瓜人投掷樱桃的动画。
黄瓜人可以做的其他动作是收集樱桃。我们将通过自动收集系统来实现这一点。每当黄瓜人接触到樱桃树时,我们将增加他拥有的樱桃数量。
我们将在第七章实现我们的玩家角色中实现黄瓜人。
黄瓜甲虫
我们游戏中的反派将是黄瓜甲虫。我们将控制我们想要在游戏中放置多少个黄瓜甲虫以及它们的位置。我们还将通过人工智能控制它们的行为。黄瓜甲虫角色是使用 Maya 和 Photoshop 创建的,与创建黄瓜人的相同软件对。以下是我们的黄瓜甲虫的图片:

黄瓜甲虫会寻找并消耗黄瓜。我们将确定黄瓜被消耗所需的时间。正如您在前面的插图中所见,有黄瓜甲虫在其正常状态下的图像,所有六条腿都着地,以及站立的状态。站立姿势将在它攻击黄瓜人时使用。
因为黄瓜甲虫是由人工智能控制的,而不是由人类玩家控制,所以它被称为非玩家角色。
我们将为黄瓜甲虫实现以下动画:
-
当在地面上时:
-
空闲:当黄瓜甲虫不在搜索、进食或攻击时,将播放这个动画。
-
地面行走:当黄瓜甲虫行走时,将播放这个动画。
-
进食:当黄瓜甲虫找到黄瓜并正在吃它时,将播放这个动画。甲虫比黄瓜小,所以需要多咬几口。
-
地面攻击:黄瓜甲虫将用其触角攻击黄瓜人。
-
站立后空闲:这个动画将黄瓜甲虫从地面状态变为站立并保持空闲。
-
地面死亡动画: 当黄瓜人将其黄瓜甲虫击败在地面上时,将播放此动画。
-
-
当站立时:
-
行走: 我们将赋予我们的黄瓜甲虫用它们的后两条腿行走的本领
-
奔跑: 黄瓜甲虫将能够用它们的后两条腿向黄瓜人奔跑
-
站立攻击: 当站立时,黄瓜甲虫将能够用它们的前四条腿攻击黄瓜人
-
站立死亡动画: 当黄瓜人将其黄瓜甲虫击败在站立位置时,将播放此动画。
-
我们需要仔细规划和脚本编写来创建所需的黄瓜甲虫行为。黄瓜甲虫的数量和位置是我们需要做出的决定。
我们将在第八章“实现我们的非玩家角色”中实现黄瓜甲虫。
游戏玩法
游戏将从玩家位于游戏世界中心开始。由玩家控制的黄瓜人需要防御黄瓜地免受黄瓜甲虫的侵害。为了击退黄瓜甲虫,黄瓜人需要从附近的樱桃树上收集樱桃。目标是击败所有黄瓜甲虫,在黄瓜地中的黄瓜全部被吃掉之前。
让我们看看我们将如何实现这一切。本节涵盖了以下游戏玩法组件:
-
游戏世界布局
-
开始条件
-
积分系统
-
头戴式显示器
游戏世界布局
将在第四章“创建我们的地形”中创建我们的游戏世界,然后在第五章“灯光、摄像机和阴影”中添加摄像机和灯光效果。我们将创建一个基本的户外环境,由一个大岛和周围的水组成。水将用于美学和边界目的。我们不会使用水;毕竟,黄瓜不能游泳。
这里是我们游戏世界形状的模拟图:

前面的模拟图中说明了四个基本要素:
-
水域: 这将仅用于我们的游戏世界边界。
-
出生点: 有五个出生点,我们在失去生命后可以将我们的玩家角色随机放置到其中一个。
-
樱桃树: 我们将在游戏世界中放置四个较大的樱桃树簇。
-
黄瓜地: 在模拟图中没有标记,但由绿色涂鸦区域表示。这代表了大片黄瓜地大致的种植或放置位置。
我们模拟图中没有表示的是黄瓜甲虫的放置。我们将通过脚本随机放置甲虫。我们将在第八章“实现我们的非玩家角色”中这样做。
开始条件
当我们的游戏首次启动时,我们将设置几个起始条件。以下是一份这些条件的清单:
-
黄瓜人生成点
-
黄瓜甲虫的数量和位置
-
黄瓜人持有的樱桃数量
-
黄瓜的数量和位置
让我们逐一查看这些起始条件。
第一个起始条件是黄瓜人的生成位置。正如你在我们早期的原型中看到的,游戏中有五个可能的生成点。我们将编写一个脚本,每次随机生成玩家。虽然只有五个位置,但这确实为游戏增加了足够的随机性,使其成为一个有趣且具有挑战性的组成部分。
黄瓜甲虫的数量和位置是第二个起始条件。这是一对重要的决策。如果我们决策不当,游戏可能会变得过于简单或过于困难,在这两种情况下,游戏都不会足够具有挑战性,以至于让人玩起来觉得有趣。我们将在本章后面的标题为难度平衡的部分更详细地探讨这个问题。
黄瓜人持有的樱桃数量是我们的第三个起始条件。黄瓜人将开始时没有樱桃,每次重生时也将没有樱桃。因此,如果黄瓜人有 319 个樱桃并被一个或多个黄瓜甲虫击败,黄瓜人将带着零樱桃重生,失去他之前拥有的 319 个樱桃。
我们的第四个和最后一个起始条件是黄瓜的数量和位置。这是一组重要的考虑因素,与黄瓜甲虫的位置和数量相关。我们将在本章后面的标题为难度平衡的部分检查我们关于这个问题的决策的可能性和可能的结果。
积分系统
到目前为止,我们已经确定我们将跟踪游戏中的几个组件。这些组件在此列出,并列出我们将在脚本中使用的变量名:
-
黄瓜甲虫数量:
numberOfCucumberBeetles
-
黄瓜数量:
numberOfCucumbers
-
黄瓜人生命值数量:
livesLeft
根据我们之前关于游戏结束条件的决定,我们可以应用以下数学检查来确定游戏是否结束以及结果如何。以下表格列出了每个游戏结束条件及其结果:
| 条件编号 | 游戏结束条件 | 结果 |
|---|---|---|
| 1 | numberOfCucumbers == 0 |
黄瓜甲虫获胜 |
| 2 | numberOfCucumberBeetles == 0 |
黄瓜人获胜 |
| 3 | livesLeft == 0 |
黄瓜甲虫获胜 |
为了实现三个游戏结束条件,我们知道我们必须跟踪甲虫、黄瓜和生命值。这是不可选择的。在我们有灵活性的地方是我们向用户显示的内容。我们将在下一节抬头显示中做出这个决定。
由于我们正在跟踪涉及数字的关键信息,这使得我们很容易实现点数系统。例如,每当消灭一个黄瓜甲虫时,我们可以给玩家 50 分。我们也可以在黄瓜人被黄瓜甲虫咬一口时扣分。
我们将在“黄瓜甲虫”游戏中实施的点数系统将包括两种类型的事件,一种是增加分数的事件,另一种是减少分数的事件。以下是我们的点数系统详情表:
| 游戏内事件 | 分数 |
|---|---|
| 游戏开始 | + 1,500 |
| 黄瓜人摘樱桃 | + 5 |
| 黄瓜人用樱桃击中地面上的黄瓜甲虫 | + 10 |
| 黄瓜人消灭地面上的黄瓜甲虫 | + 25 |
| 黄瓜人用樱桃击中站立的黄瓜甲虫 | + 50 |
| 黄瓜人消灭站立的黄瓜甲虫 | +100 |
| 黄瓜人失去生命值 | - 500 |
如前表所示,我们将玩家的初始分数设为 1,500 分,每次生命值损失时减去 500 分。这将确保我们的分数不会变成负数,并且减少我们在脚本中需要检查的事项。这也为玩家每次保住的生命值提供奖励。
抬头显示
我们决定在游戏过程中跟踪一些信息,这些信息在计算分数和游戏结束时具有价值。玩家会希望看到这些信息,因为它往往能提供动力,并增加游戏的乐趣。因此,我们将为玩家创建一个抬头显示(HUD),并动态更新游戏中的数据。
抬头显示(HUD)是一个始终显示在屏幕上的信息视觉层。
这里是我们“黄瓜甲虫”游戏中抬头显示(HUD)的模拟图:

如您所见,我们的抬头显示(HUD)有六个组成部分。每个部分都进行了详细说明:
-
健康值:这包括一个文本标签和一个健康计。
-
生命值:位于“健康”文本标签下方的图标表示剩余的生命值。我们不会为这个图标添加标签,因为其功能对玩家来说应该是显而易见的。我们将使用黄瓜人的图像作为图标。
-
得分:得分将在屏幕右上角显示。
-
樱桃:我们将直接在得分下方显示樱桃的数量。
-
黄瓜:抬头显示(HUD)的左下角包含两个组件。第一个组件是游戏中剩余的黄瓜数量。
-
黄瓜甲虫:当前游戏中剩余的黄瓜甲虫数量将在抬头显示(HUD)的左下角显示,位于黄瓜数量下方。
难度平衡
在确定游戏难度时需要考虑很多因素。如果游戏太难,玩家可能会失去兴趣;如果游戏太简单,可能不会吸引目标受众。有些游戏为用户提供可选的难度级别。其他游戏有多个级别,每个级别难度逐渐增加。为了达到我们期望的难度平衡,我们必须应对几个问题。
在本节中,我们将首先探讨难度平衡问题,然后介绍我们的实施计划。
难度平衡问题
在我们的游戏设计中,有很多关于游戏的问题需要考虑。对本节中问题的回顾将帮助我们理解即使是像我们这样的简单游戏也必须应对哪些问题才能达到期望的难度平衡。
这一系列问题与我们游戏中难度的整体实施相关:
-
我们是否应该提供不同难度的选择,由玩家决定?
-
应该有多少个不同的难度级别?
-
难度级别应该叫什么名字?
-
每个难度级别具体会有哪些不同?
-
-
我们是否应该有多个游戏级别,每个级别难度逐渐增加?
-
游戏应该有多少不同的难度级别?
-
游戏级别应该叫什么名字?
-
每个游戏级别具体会有哪些不同?
-
考虑以下关于我们游戏中胡瓜甲虫的问题:
-
应该有多少只胡瓜甲虫?
-
胡瓜甲虫应该有多被动或多主动?
-
胡瓜甲虫在什么距离内应该意识到胡瓜人?
-
胡瓜甲虫在寻找胡瓜时应有多有效?
-
每次攻击中,胡瓜甲虫应该对胡瓜人造成多少伤害?
-
胡瓜甲虫在死亡前能承受多少伤害?
-
胡瓜甲虫是否应该进行沟通并相互协助进行协同攻击?
接下来的一系列问题涉及我们的可玩角色,胡瓜人:
-
角色应该有多少条命?
-
玩家从一只胡瓜甲虫的攻击中会受到多少伤害?
-
角色在死亡前能承受多少伤害?
-
角色是否能够跑得比胡瓜甲虫快?
-
角色应该持有多少樱桃?如果是的话,应该有多少?
-
玩家应该在哪个位置复活?在一个安全区域?
-
当角色失去生命时,它们是否应该在没有樱桃的情况下复活?
-
当角色复活时,应该恢复满血吗?
我们还必须在游戏中考虑胡瓜和樱桃。以下是关于我们将要实现的游戏资产的一些问题。
胡瓜:
-
一只胡瓜甲虫需要咬多少次才能吃掉一个胡瓜?
-
游戏中应该有多少个胡瓜?
-
胡瓜在游戏环境中的位置应该在哪里?
樱桃:
-
玩家应该以多快的速度收集樱桃?
-
黄瓜人最多能有多少樱桃?
-
樱桃会对黄瓜甲虫造成多少伤害?
-
游戏中会有多少樱桃可用?
如您所见,我们需要回答一些问题作为我们设计的一部分。其中一些问题可能看起来重复,因为它们与游戏中的多个组件相关。
实施计划
根据上一节提出的问题,我们必须给出一些答案。所以,让我们在这里做吧。这组答案将作为我们难度平衡的实施计划。
我们的第一组决策集中在整体游戏概念上。以下是这些决策:
-
实现一个游戏关卡。
-
为用户提供三个游戏难度设置。设置名称如下:
-
我是小黄瓜(最简单)
-
我能应付(中等)
-
来吧!(最难)
-
既然我们决定创建三个游戏关卡,我们必须确定它们将如何不同。这可以通过使用矩阵轻松管理。当我们填写矩阵时,我们将能够记录大多数之前列出的问题的答案。以下是我们将称之为的难度实施矩阵:
| 组件 | 我是小黄瓜 | 我能应付 | 来吧! |
|---|---|---|---|
| 黄瓜甲虫数量 | X | X * 2 | X * 5 |
| 黄瓜甲虫(对黄瓜人)造成的伤害 | 每秒减少 5 个健康点 | 每秒减少 10 个健康点 | 每秒减少 20 个健康点 |
| 黄瓜甲虫(对黄瓜)造成的伤害 | 每秒减少 5 分 | 每秒减少 7 分 | 每秒减少 9 分 |
| 黄瓜甲虫起始健康值 | X | X * 2 | X * 5 |
| 黄瓜人起始健康值 | X | X * .75 | X * .5 |
| 黄瓜起始健康值 | 300 | 400 | 500 |
| 黄瓜数量 | X | X * .75 | X * .5 |
| 樱桃收集速率 | 每秒 1 个樱桃 | 每两秒 1 个樱桃 | 每三秒 1 个樱桃 |
| 黄瓜人能持有的最大樱桃数 | 99 | 75 | 50 |
| 樱桃(对黄瓜甲虫)造成的伤害 | X | X * .75 | X * .50 |
还有一组决策将不会根据用户选择的难度级别而改变。以下是一份这些决策的清单:
-
黄瓜甲虫的攻击性不会改变。我们将编写脚本,如果它们意识到黄瓜人,就会攻击他。
-
我们将为黄瓜甲虫设定一个非常小的视野区域,这使得黄瓜人可以轻松地绕过它们,也许更重要的是,跑得比它们快。
-
黄瓜甲虫将能够轻松地找到黄瓜。
-
黄瓜甲虫不会相互沟通。因此,不会有协调攻击。这并不意味着一个以上的黄瓜甲虫不能同时攻击黄瓜人。所有在黄瓜人范围内的黄瓜甲虫都会攻击。
-
黄瓜人将开始时有三个生命。
-
我们不会限制玩家收集樱桃的数量,但它们都会在重生过程中作为一部分丢失。
-
重生将在游戏中先前确定的五个重生点之间随机进行。
-
黄瓜人重生后将拥有满血量。
-
黄瓜将在游戏中的黄瓜地内随机放置。
-
樱桃树和樱桃的数量不会随着不同难度级别而变化。
项目组织
我们的项目将包括大量的资产。到目前为止,我们已经确定并详细说明了我们计划使用的黄瓜人、玩家角色、黄瓜甲虫(非玩家角色)。我们还将使用樱桃树、樱桃和黄瓜地。让我们在下一节中简要看看这些,自定义资产。
自定义资产
我们已经使用 Maya 和 Photoshop 创建了一些自定义资产用于我们的游戏。第一套资产是两种黄瓜地形式,如下图中所示:

我们将使用这些黄瓜地来覆盖我们游戏环境中的大片区域,如本章前面所述。我们有两种不同的地块形状,以帮助创建一个真实且非重复的游戏环境外观。我们还将旋转地块,以增加地面覆盖的整体视觉变化。
我们的游戏中还将提供定制的黄瓜。虽然游戏中可能有数百个,但我们将使用一个基础黄瓜,如图所示,并在整个游戏环境中复制它。我们可以修改大小和旋转,以确保看起来有很多独特的黄瓜:

我们下一个自定义资产是一棵樱桃树。我们的基础树如图所示,具有独特的形状。我们可以改变这棵树的大小和旋转,以创建看起来有很多独特树木的视觉效果。当然,这并不是一棵真实的樱桃树,但它将适合我们的目的:

虽然我们的樱桃树上长有樱桃,但它们都是树的一部分,而不是单独的对象。因此,我们的玩家角色可以从樱桃树上收集樱桃,但不会有视觉提示表明樱桃已经被摘下。我们将更新我们的 HUD 来提供这种提示。对于向黄瓜甲虫投掷樱桃,我们将使用以下图片中的樱桃:

标准资产
除了为这款游戏创建的自定义资源外,我们还将使用许多 Unity 的标准资源,包括纹理、材质和 3D 对象。我们将在第四章,创建我们的地形中开始使用纹理和材质。我们将在第六章,为我们的游戏创建和导入 3D 对象中使用 Unity 的本地工具创建我们自己的 3D 资源,并导入额外的资源。
组织结构
如果我们没有组织计划的计划,我们的项目可能会迅速失控。Unity 对项目组件有很好的搜索支持,但我们更希望能够快速导航到我们的资源。以下是我们开始创建游戏时将构建的基本结构:

摘要
在本章中,我们完全设计了我们的游戏,黄瓜甲虫,并计划使用我们的设计来推动我们的开发工作。我们的游戏设计包括功能、玩家角色、非玩家角色、游戏资源、动画等等。我们使用屏幕草图来帮助记录我们的游戏设计。此外,我们还计划了游戏难度的平衡,以确保游戏根据用户选择具有适当的难度。
在下一章中,你将学习如何创建游戏的地形。你将接触到地形塑造工具和地形绘制。你还将学习如何将植被和水添加到我们的地形中。到下一章结束时,你将为黄瓜甲虫游戏创建游戏世界。
第四章:创建我们的地形
在上一章中,我们设计了我们的游戏,以便我们有开发工作的计划。我们设计了游戏的所有功能,如何使用玩家角色,非玩家角色将扮演什么角色,我们将使用哪些资产,以及我们在游戏中将使用哪些动画。我们还模拟了主游戏屏幕以及我们的抬头显示。除此之外,我们还对我们游戏的难度平衡做出了关键决策。随着设计工作的完成,我们准备开始创建我们的游戏。
在本章中,我们将创建我们游戏的地形,并根据我们在第三章“设计游戏”中的设计草图对地形进行定制操作。我们将使用塑造工具并添加水和植被特征来完成我们的游戏环境。
具体来说,在本章中我们将涵盖以下主题:
-
创建地形
-
地形塑造
-
绘制地形
-
添加水
-
添加植被
创建地形
Unity 地形本质上是你游戏环境中的地面;它是你的景观。Unity 有几个我们可以用来创建、塑造和自定义地形的工具。我们将在本章后面使用这些工具。
对于我们的游戏,我们将使用从高度图创建并随后在 Photoshop 中编辑的地形。如果您对如何完成这项工作感兴趣,请阅读导入地形部分。否则,您可以跳过这个简短的部分,并继续阅读关于导入地形的部分。
使用高度图
您可以通过简单的网络搜索找到许多免费的高度图。一旦您找到了您想要的,请执行以下步骤:
此子部分不是必需的,仅提供信息目的。游戏的地形文件将随附说明,说明如何将其纳入我们的游戏。
- 打开 PhotoShop 并选择文件 | 新建。使用以下设置:
| 组件 | 设置 |
|---|---|
| 宽度 | 512 像素 |
| 高度 | 512 像素 |
| 分辨率 | 72 像素 |
| 颜色模式 | 灰度 8 位 |
-
将您下载的高度图拖放到新的 Photoshop 图像中。
-
进行任何必要的放置调整并提交变换。
-
选择图像 | 自动色调。进行任何你认为必要的亮度和对比度调整。
-
将文件保存为 Photoshop 原始文件。
导入地形
在我们导入地形之前,我们需要设置一个新的 Unity 3D 项目。为此,请按照以下步骤操作:
-
启动 Unity。
-
点击新建图标。
-
输入项目名称并选择 3D。
-
点击创建项目按钮。
-
使用顶部菜单,选择游戏对象 | 3D 对象 | 地形。这会创建一个默认的地形,称为
terrain。 -
在选择地形后,在检查器面板的地形下方点击设置齿轮图标:

-
将地形高度更改为
200。 -
仍然在检查器面板中,向下滚动直到您看到导入原始...按钮。点击该按钮,导航到上一节中创建的 Photoshop 原始文件。或者,您也可以从出版商的配套网站上下载
terrain.raw文件:

- 选择Mac作为字节顺序在导入高度图对话框窗口中,然后点击导入按钮。即使您在运行 Windows 的计算机上,使用此选项也很重要:

一旦您点击导入按钮,请观察 Unity 界面的右下角。导入过程可能需要几分钟。观察该部分界面将指示您的进度。请看以下示例。请耐心等待:

一旦导入过程完成,您的地形将反映导入的高度图。正如您在这里看到的,它将有一些尖锐的山峰和边缘。我们将在下一节中处理这些问题:

塑造地形
在本节中,我们将采取几个动作来塑造我们的地形。我们将从平滑开始,然后创建我们的五个出生点。
平滑地形
正如我们在上一节中确定的,我们的地形目前有一些凹凸不平的边缘和尖锐的山峰。我们将使用检查器面板地形部分中可用的平滑工具。正如您在这里看到的,它是从左数第三个图标:

当选择平滑工具时,您将看到不同的刷子类型和大小。一旦做出选择,只需使用鼠标的左键来平滑地形。
以下截图显示了应用了显著平滑后的地形结果:

如果您正在 Unity 中按照这些步骤进行,您的结果可能会有所不同,这是正常的。精确的地形构建不是跟随本节和其他章节剩余步骤所必需的。
创建我们的出生点
我们的游戏设计需要五个出生点:一个在中心,每个角落一个。我们的游戏环境不是一个完美的正方形,因此我们将在地形的中心创建一个出生点,并在地形中创建四个额外的出生点。
我们目前还没有编写出生点的脚本;这将在第十一章“编写胜利与失败”中进行。现在,我们将简单地使用地形塑造工具创建区域。具体来说,我们将使用绘制高度工具。正如您在这里看到的,它是检查器面板地形部分中的第二个图标:

选择画笔大小为25,我们只需点击并绘制我们想要生成点的五个区域。我们将在游戏开始时使用中心的那个作为默认的起始点,然后在游戏中随机选择五个中的一个进行生成。
在制作生成点时,我们希望它们是升高的,但不要太高。以下截图显示了散布在整个地形上的五个生成点:

绘制地形
我们现在准备给我们的地形添加一些颜色。我们将首先用草纹理覆盖整个地形。为了完成这个任务,确保在层次结构面板中选择地形。然后,在检查器面板中,选择绘制纹理工具。如您所见,这是检查器面板地形部分中的第四个图标:

如您所见,我们还没有定义任何纹理。实际上,在我们的项目中还没有合适的纹理,所以让我们通过几个简单的步骤来解决这个问题:
-
在项目面板中,点击
Assets文件夹。 -
在右侧部分,右键单击并选择创建 | 文件夹。将文件夹命名为
Textures。 -
从出版商的配套网站上下载
grass_starter_texture.jpg文件。 -
将
grass_starter_texture.jpg文件从您的文件系统拖到步骤 1 中创建的Textures文件夹中。
现在您已经在 Unity 项目中有了草纹理,让我们将其应用到我们的地形上。以下是步骤:
-
在层次结构面板中选择地形。
-
在检查器面板中,点击编辑纹理按钮,然后添加纹理。这将显示添加地形纹理对话框窗口,如图所示:

-
点击左侧的选择按钮以显示一组纹理。
-
双击
grass_starter_texture。 -
将 x 和 y 的大小从
15改为2。 -
点击添加按钮。
您会看到,您的地形现在被草覆盖了。当您放大时,您可以看到草的质量有所提高。
在下一节中,我们将向我们的地形添加水。
添加水
我们下一步是添加围绕我们岛屿的水。我们并不打算在水中游泳或放置物体,所以我们可以用一个简单的方法来处理。我们将在第六章,为我们的游戏创建和导入 3D 对象中完善我们的水。
执行以下步骤以创建一个材料和水面,然后将材料应用到平面上:
-
在项目面板中,点击
Assets文件夹。 -
在右侧部分,右键单击并选择创建 | 文件夹。将文件夹命名为
Materials。 -
双击您刚刚创建的
Materials文件夹。 -
在“材料”文件夹中右键单击并选择创建 | 材料。将材料命名为
temp_water。 -
在检查器面板中,选择
temp_water材料,点击左侧眼药水图标旁边的白色色块:

- 在颜色对话框窗口中,选择蓝色作为您的水面颜色,然后关闭对话框窗口。您现在应该在项目面板和检查器面板中看到您的颜色选择:

现在我们已经准备好了材质,让我们创建一个 GameObject 来模拟我们的水。按照以下步骤操作:
-
从顶部菜单中选择GameObject | 3D Object | Plane。
-
使用变换工具,增加平面的尺寸以匹配整个游戏世界的尺寸。
-
提高或降低平面,直到它看起来类似于这个:

- 在选择平面的情况下,点击检查器面板中网格渲染器部分右侧的
Default-Material旁边的小圆圈。以下截图显示了该部分:

-
您现在应该看到选择材质对话框窗口。选择您创建的
temp_water纹理,然后关闭对话框窗口。 -
在层次结构面板中,右键单击“Pane”对象并重命名为
WaterPane。这将帮助我们保持整洁,并使我们能够识别我们的游戏对象。
您的游戏环境现在应该看起来类似于这个:

保存您的作品
现在是保存您工作的好时机。为了保持整洁,让我们在Assets下创建一个名为Scenes的文件夹。以下是完成此任务的步骤:
-
在项目面板中,点击
Assets文件夹。 -
在右侧部分,右键单击并选择创建 | 文件夹。将文件夹命名为
Scenes。
现在您已经准备好保存您的作品。从顶部菜单中选择文件 | 保存项目。您可以使用文件 | 保存场景或文件 | 另存为...选项来保存您的场景。另外,当您未保存场景而退出时,系统会提示您输入名称。使用这些选项中的任何一个保存场景,将场景命名为Main,并确保它保存在Assets | Scenes文件夹中。
添加植被
到目前为止,我们的游戏环境只由覆盖着草的土地组成,周围是水。如您从第三章,设计游戏中记得的那样,我们计划整合以下与地形相关的游戏对象:
-
樱桃树
-
黄瓜田
-
黄瓜
我们将在第六章,创建和导入游戏中的 3D 对象中添加这些游戏对象。现在,我们可以添加一些常规树木,开始填充我们的游戏环境。
我们不会简单地导入别人制作的树木,而是将逐步创建我们自己的树木。按照以下步骤从零开始创建 Unity 中的第一个 3D 树:
- 从顶部菜单中选择 GameObject | 3D Object | Tree。这将创建一个新的树并将其放置在您的游戏世界中的变换 0, 0, 0 位置。
双击层次面板中的对象将在场景视图中将其聚焦于中心。它还将提供对象的放大视图。
- 使用变换工具,增加 Y 轴,使树位于你的水面之上。它将只包含一个分支:

在选择了树之后,我们将注意力转向检查器面板中的树窗口。以下图表显示了我们将使用以继续创建我们的树的各个按钮:

- 在树窗口中选择分支组图标。接下来,点击添加分支组按钮。你会看到主分支上已经添加了一个分支,它作为树的主干。正如你所见,在检查器面板中你可以调整长度设置:

有三个额外的工具可以帮助你制作独特的树。以下按钮所示,它们是移动分支、旋转分支和自由手绘:

- 继续在你的树干上创建分支,直到它看起来是你想要的样子。记住,你可以单独移动、调整大小和旋转分支。你还可以在分支上添加分支。当你添加一个新的分支时,它会被添加到当前选中的分支上。
当你第一次创建一个新的分支时,最好在移动它之前改变其大小。一旦你重新定位了分支,你可能就会失去进行某些调整的能力。
当你完成添加和配置分支后,你的树看起来将非常独特。以下截图提供了一个示例:

如你所见,这里有一根树干和几个分支。我们可以在树窗口中看到分支的层次结构:

你的下一步是给你的树添加叶子。
-
点击你想要应用叶子的分支,然后点击添加叶子组按钮。
-
在检查器面板的分布部分,将频率更改为选择你想要在该分支上的叶子数量。此图像显示了频率滑块界面:

- 继续向分支添加叶子,直到你得到你想要的外观。当你完成时,你的树看起来就像有纸叶子一样:

如你所见,这里有一根树干和几个分支以及叶子组。我们可以在树窗口中看到这一点:

下一步将是将材质应用到树枝和树叶上。由于我们游戏项目中还没有这些材质,我们将保持树的原样。学习如何创建独特的树可以有助于创建独特的游戏环境。Unity Technologies 和其他内容创作者提供了几个高质量的树模型。我们将在第六章,为我们的游戏创建和导入 3D 对象中探索一些这些树,并将它们实际整合到我们的游戏中。
-
在层次结构面板中,将
Tree重命名为Tree_Special。 -
使用变换工具,将树移动到干燥的土地上。你放置的位置不重要,只要它在干燥的土地上,而不是在出生点。
-
在选择
Tree_Special后,将比例从1, 1, 1更改为3, 3, 3。这样会使我们的树在我们的游戏世界中更加突出。 -
不要忘记保存你的 Unity 项目。
摘要
在本章中,我们创建了游戏的地形,并根据第三章,设计游戏中的设计草图对地形进行了定制。我们使用了造型工具,绘制了地形,添加了水,甚至从头开始创建了自己的树。此外,我们还创建了一个材质并导入了一个纹理文件。
在第五章,灯光、相机和阴影中,我们将探索 Unity 中可用的不同类型的光照和相机。我们将使用灯光和相机在我们的Cucumber Beetle游戏中实现适当的光影效果。
第五章:灯光、摄像机和阴影
在上一章中,我们创建了我们的游戏地形,并根据我们在第三章设计游戏中的设计草图对地形进行了自定义。我们使用了造型工具,绘制了地形,并添加了水。我们甚至从头开始创建了自己的树。此外,我们还创建了一个材质并导入了一个纹理文件。
在本章中,我们将探讨 Unity 中的摄像机和灯光。我们将从查看摄像机开始,包括透视、视锥体和 Skybox。接下来,我们将学习一些使用多个摄像机的用途,包括迷你地图。我们还将涵盖不同类型的灯光,探索反射探针,并以查看阴影结束。
具体来说,我们将涵盖以下概念:
-
与摄像机协同工作
-
使用多个摄像机
-
与灯光协同工作
-
实现反射探针
-
理解阴影
如果你想要使用本章中介绍的相同 Unity 项目,你可以从出版商的配套网站上下载Starting-Chapter-05.zip文件。下载文件后,解压缩它,然后在 Unity 中打开项目。它包含了前几章的完成工作。
与摄像机协同工作
摄像机渲染场景,以便用户可以查看。考虑一下这个陈述中隐藏的复杂性。我们的游戏是 3D 的,但玩我们游戏的人是在 2D 显示设备上查看的,如电视、计算机显示器或移动设备。幸运的是,Unity 使实现摄像机变得容易。
摄像机是 GameObject,可以使用场景视图和检查器面板中的变换工具进行编辑。每个场景至少必须有一个摄像机。实际上,当创建新场景时,Unity 会创建一个名为 Main Camera 的摄像机。正如你将在本章后面看到的那样,一个场景可以有多个摄像机。
在场景视图中,摄像机用白色摄像机轮廓表示,如下面的截图所示:

当我们在层次结构面板中点击主摄像机时,我们在场景视图中提供了一个摄像机预览。这让我们可以看到摄像机在游戏模式下的视图。我们将在第七章实现我们的玩家角色中更改这一点。我们还可以通过检查器面板访问几个参数。检查器面板中的摄像机组件如下所示:

让我们根据我们的Cucumber Beetle游戏来查看每个参数:
-
Clear Flags 参数允许你在 Skybox、纯色、深度只清除和不清除之间切换。这里的选项告诉 Unity 要清除屏幕的哪些部分。我们将保持此设置为 Skybox。你将在本章后面的内容中了解更多关于 Skybox 的信息。
-
背景参数用于设置游戏世界的默认背景填充(颜色)。此设置仅在所有游戏对象渲染完毕且没有天空盒的情况下可见。我们的Cucumber Beetle游戏将包含天空盒,因此此参数可以保留默认颜色。
-
剪裁遮罩参数允许您选择和取消选择相机要渲染的层。默认选择选项包括无、所有、默认、透明 FX、忽略射线投射、水和其他 UI。对于我们的游戏,我们将选择所有。如果您不确定游戏对象关联的层,请选择它,并在检查器面板顶部的层参数中查看。在那里您将看到分配的层。您可以轻松更改层以及创建自己的独特层。这为您提供了有限的渲染控制。
-
投影参数允许您选择相机所需的投影类型,是透视还是正交。我们将在本章后面讨论这两种投影。当选择透视投影时,我们将获得访问视场参数的权限。这是指相机视场角度的宽度。值范围是 1-179°。您可以使用滑块更改值,并在相机预览窗口中查看结果。当选择正交投影时,将可用一个额外的尺寸参数。这指的是视口大小。对于我们的游戏,我们将选择透视投影,并将视场设置为
60。 -
剪裁平面参数包括近剪裁和远剪裁。这些设置确定了相对于相机,渲染将发生的最近和最远点。目前,我们将保留近剪裁参数的默认值
0.3和远剪裁参数的默认值1000。 -
视口矩形参数有四个组件——X、Y、W 和 H——它们确定相机将在屏幕上的哪个位置绘制。正如您所期望的,X 和 Y 组件指的是水平和垂直位置,而 W 和 H 组件指的是宽度和高度。您可以尝试这些值,并在相机预览中查看变化。对于我们的游戏,我们将保留默认设置。
-
深度参数在实现多个相机时使用。我们可以在此设置一个值以确定相机相对于其他相机的优先级。较大的值表示更高的优先级。对于我们的游戏,默认设置是足够的。
-
渲染路径参数定义了我们的相机将使用哪些渲染方法。选项包括使用图形设置、前向渲染、延迟渲染、旧版顶点着色和旧版延迟(光预渲染)。我们将为我们的游戏使用使用图形设置选项,这也使用了默认设置。
-
目标纹理参数在我们的游戏中不会使用。当设置渲染纹理时,相机无法将渲染输出到屏幕。
-
消隐裁剪参数是一个强大的设置。如果启用,Unity 将不会渲染被遮挡或未被相机看到的对象。一个例子是建筑物内的对象。如果相机目前只能看到建筑物的外部墙壁,那么那些墙壁内的所有对象都不可见。因此,不渲染这些对象是有意义的。我们只想渲染绝对必要的对象,以确保我们的游戏有流畅的游戏体验和没有延迟。我们将保持这个设置为启用,用于我们的游戏。
-
允许 HDR 参数是一个复选框,用于切换相机的高动态范围(HDR)渲染。我们将保持游戏的默认设置为启用。
-
允许 MSAA 参数是一个切换,用于确定我们的相机是否将使用多采样抗锯齿(MSAA)渲染目标。MSAA 是一种计算机图形优化技术,我们希望为我们的游戏启用它。
理解相机投影
Unity 中使用了两种相机投影:透视和正交。使用透视投影,相机根据场景中的相机角度渲染场景。使用这种投影,离相机越远,对象显示得越小。这模仿了我们现实世界中看到事物的样子。由于制作逼真游戏或近似现实世界的游戏的愿望,透视投影在现代游戏中是最常用的。它也是我们将在我们的Cucumber Beetle游戏中使用的投影。
另一种投影是正交投影。正交透视相机以均匀的方式渲染场景,没有任何透视。这意味着远离相机的对象不会比靠近相机的对象显示得更小。这种类型的相机通常用于俯视游戏,并且在 2D 和 Unity 的 UI 系统中是默认使用的相机投影。
我们将为我们的Cucumber Beetle游戏使用透视投影。
定位你的视锥体
当在层次视图中选择相机时,其视锥体在场景视图中可见。视锥体是一种几何形状,看起来像被切掉顶部的金字塔,如图所示:

近平面或顶部平面与其底部平行。底部也被称为远平面。视锥体的形状代表了你游戏的可视区域。只有该区域内的对象会被渲染。使用场景视图中的相机对象,我们可以改变我们相机的视锥体形状。
创建天空盒
当我们创建游戏世界时,我们通常会创建地面、建筑物、角色、树木和其他游戏对象。那么天空呢?默认情况下,你的 Unity 游戏项目中将有一个纹理化的蓝色天空。那个天空对于测试来说是足够的,但不会增加沉浸式游戏体验。我们想要更多的现实感,比如云彩,这可以通过创建天空盒来实现。
Skybox 是一个六面立方体,玩家可以看到它,而其他所有对象都看不到。因此,当玩家看向您的对象之外时,他们看到的是您的 Skybox。正如我们所说的,Skyboxes 是六面立方体,这意味着您将需要六个单独的图像,这些图像基本上可以相互夹紧以形成立方体。
以下截图显示了 Unity 项目开始时的默认 Skybox以及您将在本节中创建的自定义 Skybox:

为了跟随,您将需要位于出版商配套网站上的Chapter5-Skybox.zip文件中的六个图像。
执行以下步骤以创建 Skybox:
-
在项目面板中,在
Assets文件夹中创建一个Skybox子文件夹。我们将使用此文件夹来存储我们的 Skybox 纹理和材质。 -
将提供的六个 Skybox 图像或您自己的图像拖放到新的
Skybox文件夹中。 -
确保在
Project面板中选择了Skybox文件夹。 -
从顶部菜单中选择 Assets | Create | Material。在项目面板中,将材质命名为
Skybox。 -
在选择 Skybox 材质后,将注意力转向检查器面板。
-
选择着色器下拉菜单,然后选择 SkyBox | 6 Sided。
-
使用每个图像的“选择”按钮,导航到步骤 2 中添加的图像。务必将适当的纹理匹配到相应的立方面。例如,
SkyBox_Front纹理与 Skybox 材料的Front[+Z]立方面相匹配。 -
为了将我们的新 Skybox 分配到场景中,从顶部菜单中选择 Window | Lighting | Settings。这将打开照明设置对话框窗口。
-
在照明设置对话框窗口中,单击 Skybox 材质输入字段右侧的小圆圈。然后,关闭选择窗口和照明窗口。参见图表:

现在,您将在场景视图中看到您的 Skybox。当您在层次结构面板中单击相机时,您也将看到从相机视角看到的 Skybox。
请务必保存您的场景和项目。
使用多个相机
我们的 Unity 游戏必须至少有一个相机,但我们并不局限于只使用一个。正如您将在第七章中看到的,实现我们的玩家角色,我们将把我们的主相机或主要相机连接到我们的玩家角色上。这将意味着相机将跟随角色在游戏环境中移动。这将成为我们角色的眼睛。我们将通过角色的视角玩游戏。
第二个摄像头的常见用途是在游戏显示顶部的窗口中创建一个迷你地图。这些迷你地图可以设置为可切换的或永久/固定的显示组件。实现方式可能包括战争迷雾显示、显示敌人的雷达,或用于定位的全局俯视图。你受到的限制仅限于你的想象力。在第九章,添加抬头显示中,我们将创建一个迷你地图,作为一个雷达,显示甲虫相对于黄瓜人的当前位置。
多个摄像头的另一个用途是给玩家提供在第三人称和第一人称视角之间切换的能力。你会记得从第一章,下载和安装 Unity中,第一人称视角将玩家的手臂放入视野,而在第三人称视角中,玩家的整个身体都是可见的。我们可以使用两个摄像头在适当的位置来支持从任一摄像头观看。在游戏中,你可能会将其设置为切换——比如使用C键盘键——从一台摄像头切换到另一台。根据游戏中的情况,玩家可能会享受这种能力。
一些单人游戏具有多个可玩角色。给玩家切换这些角色的能力,使他们能够更好地控制游戏策略。为了实现这一点,我们需要为每个可玩角色安装摄像头,并给玩家切换角色的能力。我们会通过脚本来实现。这是多角色的高级实现方式。
多个摄像头的另一个用途是在游戏中添加特殊视角。这些特殊视角可能包括通过门上的猫眼观察,通过高楼顶部的双筒望远镜观察,甚至通过潜望镜观察。我们可以将摄像头附着在物体上,并改变它们的观看参数,以在我们的游戏中创建独特的摄像头使用方式。我们受到的限制仅限于我们自己的游戏设计和想象力。
我们还可以将摄像头用作摄像头。没错!我们可以使用摄像头游戏对象来模拟实际游戏中的摄像头。一个例子是在越狱游戏中实现安全摄像头。
与照明工作
在前面的章节中,我们探讨了 Unity 游戏中使用摄像头的作用。就像在现实世界中一样,摄像头需要灯光来显示物体。在 Unity 游戏中,我们使用多个灯光来照亮游戏环境。
在 Unity 中,我们既有动态照明技术,也有光照烘焙选项,以获得更好的性能。我们可以在场景中添加多个光源,并选择性地启用或禁用对象投射或接收阴影的能力。这种具体性为我们提供了巨大的机会来创建逼真的游戏场景。
Unity 能够如此真实地渲染光线和阴影的秘密可能在于 Unity 对光线和阴影的实际行为进行了建模。实时全局光照使我们能够在每个场景中实例化多个光源,每个光源都有能力直接或间接影响场景中光线源范围内的物体。
间接光照指的是光线从物体上弹跳并反射到其他物体上。
我们还可以在我们的游戏场景中添加和操作环境光。这通常是通过 Skyboxes、三色渐变或甚至单色来实现的。在 Unity 中,每个新的场景都有默认的环境光照,我们可以通过编辑光照窗口中的值来控制它。在那个窗口中,你可以访问以下设置:
-
环境
-
实时光照
-
混合光照
-
光照贴图设置
-
其他设置
-
调试设置
目前我们的游戏不需要对这些设置进行更改。我们已经将环境光照设置到了 Skybox 中。在第十二章“为我们的游戏添加音频和视觉效果”中,我们将探讨雾气,这是在光照窗口的其他设置部分中可用的。
当我们在 Unity 中创建场景时,我们有三种光照选项。我们可以使用实时动态光照,使用烘焙光照方法,或者使用两者的混合。与实时动态光照相比,烘焙光照在性能上表现更佳,所以如果性能是考虑因素,尝试使用烘焙光照会更好。
除了环境光照外,还有四种类型的光:方向性、点光源、聚光灯和区域光。我们将在以下章节中探讨每种类型。
方向性光照
当我们在 Unity 中创建一个新的场景时,会自动为我们创建一个方向性光源。这强调了方向性光源的重要性。这种类型的光源在特定方向上提供照明。使用变换工具,我们可以完全控制这些光源的方向。
方向性光照的一个例子是在场景中生成阳光。尽管方向性光源的光线与阳光相似,但并没有实际类似太阳的光源物体。如图所示,当选择方向性光源时,黄色光线指示其照明的方向:

使用方向性光照的一个优点是光照强度不依赖于物体与方向性光源对象的距离。这种类型的光照定义了光线的方向,距离对光照没有影响。
选择方向光后,您可以在检查器面板中访问几个参数。除了变换部分外,还有一个灯光部分,您可以在其中更改几个设置,包括灯光颜色和强度。其余参数值得探索,并且不需要更改以完成我们的游戏。
要添加额外的方向光,您可以从顶部菜单中选择 GameObject | Light | Directional Light。
对于Cucumber Beetle游戏,我们将保持默认的方向光。您可以使用变换工具修改位置、旋转和光方向。
点光源照明
点光源的名字来源于它们是从一个特定点发出的光源。如图所示,这些灯光对象向所有方向发射光线:

这些灯光通常用于模拟火球或灯泡。它们也可以用来模拟一些魔法或特殊灯光效果。正如您可以从以下屏幕截图中所见,点光源有几个属性会影响它们对游戏环境的影响:

范围是指光源中心到光线外侧弧的距离。我们还可以更改颜色和强度以产生我们想要的结果。
要创建点光源,我们从顶部菜单中选择 GameObject | Light | Directional Light。
聚光灯照明
聚光灯是 Unity 中另一种类型的灯光。它们旨在为特定区域提供照明。常见的例子包括手电筒和车辆车头灯。正如您可以从以下屏幕截图中所见,光线从光源以向外锥形形状发出:

在检查器面板中,我们可以更改范围、聚光角度、颜色和强度。在这个上下文中,范围指的是光源与锥形最远点之间的距离。聚光角度是锥形外边缘的角度。聚光角度范围是 1-179°。数值越大,锥形面就越大。
要创建聚光灯,我们从顶部菜单中选择 GameObject | Light | Spot Light。
区域照明
要使用区域灯光,我们使用变换工具或检查器面板定义一个矩形。区域灯光从其矩形的侧面发射光线。以下屏幕截图显示了在 Unity 编辑器中区域灯光对象的外观:

区域灯光与其他类型的灯光不同,因为它们只能烘焙。这意味着在游戏过程中不会进行实时渲染。这样做的原因是在游戏开始之前完成所有关于区域灯光的处理。如果在游戏中实时完成这些处理,很可能会产生足够的延迟。
正如您可以从以下屏幕截图中所见,类型设置为区域(仅烘焙)且无法更改:

当烘焙光照可以接受且你希望使用软件阴影时,可以使用面光源代替点光源。
要创建面光源,我们从顶部菜单中选择 GameObject | Light | Area Light。
实现反射探针
反射探针捕捉其周围环境的 360°球形视图。在这方面,它有点像相机。捕获的图像被附近具有反射材料的物体使用。
要创建反射探针,我们从顶部菜单中选择 GameObject | Light | Reflection Probe。
如以下截图所示,你可以看到反射探针是一个球体,并捕捉了其周围环境的视图。当物体放置在反射探针附近时,反射将出现在物体上:

如下所示的检查器面板中的反射探针审查,揭示了我们可以更改的几个设置,以影响探针的工作方式和它对我们游戏环境的影响:

以下属性列表突出了你可能在创建的 Unity 游戏中最有可能更改的属性:
-
类型:你可以选择烘焙、自定义或实时。记住,如果我们尽可能烘焙光照,我们可以提高游戏性能。
-
重要性:当区域中有多个渲染探针时,你可以设置每个探针的重要性。值越高,重要性越大。
-
强度:最低值是零。你可以尝试改变此设置的结果。
-
分辨率:你可以选择 16、32、64、128、256、512、1024 或 2048 作为捕获图像反射的分辨率。
理解阴影
如本章前面所述,我们的游戏场景可以有多个光源,我们可以启用或禁用对象投射或接收阴影的能力。在现实世界中我们有阴影,对于我们的 Unity 游戏来说,考虑哪些物体投射阴影以及哪些物体接收阴影是很重要的。
以下截图显示了检查器面板中对象的网格渲染器组件。让我们回顾一下此组件的关键设置:

-
光探针:可以设置为混合探针、使用代理体积或关闭。对于简单的 Unity 游戏,你很可能会使用默认的混合探针。
-
反射探针:此设置可以关闭或设置为混合探针、混合探针和天空盒或简单。
-
投影阴影:此设置可以设置为开启、关闭、双面或仅阴影。默认是开启,所以你应该为所有不需要投射阴影的对象禁用此设置。
-
接收阴影:此设置是一个切换,告诉 Unity 是否希望该对象接收阴影。正如你所期望的,这需要额外的处理来在游戏期间显示。因此,如果你不需要对象接收阴影,取消选择此选项以获得更好的性能。
摘要
在本章中,我们探讨了摄像机和照明。我们从观察包括透视、视锥体和 Skyboxes 在内的摄像机开始。接下来,我们学习了 Unity 游戏中多个摄像机的可能用途。我们还涵盖了不同类型的照明,探讨了反射探针,并以观察阴影结束。
在下一章中,我们将使用 Unity 的内置工具集创建 3D 对象。我们还将导入几个对象到我们的游戏中,以完成我们的游戏环境,包括我们在第四章中制作的树,创建我们的地形。
第六章:为我们的游戏创建和导入 3D 对象
在上一章中,我们探讨了摄像机和照明。我们首先查看摄像机,以及透视、视锥体和天空盒的概念。接下来,我们学习了 Unity 游戏中多个摄像机的可能用途。我们还介绍了不同类型的照明,探讨了反射探针,并以阴影的查看作为结束。
我们已经准备好开始制作我们的游戏环境,使其更加健壮。我们将通过向场景中添加树木和其他对象来实现这一点。在本章中,我们将使用 Unity 的原生建模工具创建 3D 对象。我们还将从两个来源导入和使用资产。我们的第一个来源将是 Unity 资产商店,我们将从中下载免费使用的游戏资产。我们还将导入为我们的Cucumber Beetle游戏专门准备的 3D 资产。随着我们获取资产,我们将它们整合到游戏项目中,并观察我们的游戏开始成形。
本章我们将涵盖以下概念:
-
理解资产和 GameObject
-
在 Unity 中创建 3D 对象
-
使用 Unity 资产商店
-
在我们的游戏中整合自定义资产
-
使用导入的资产
如果您想使用本章中介绍的相同 Unity 项目,您可以从出版商的配套网站上下载Starting-Chapter-06.zip文件。下载文件后,解压缩它,然后在 Unity 中打开项目。它包含前几章的完成工作。
理解资产和 GameObject
资产被定义为有用的或有价值的东西。在 Unity 中,资产是您将与 GameObject 一起在游戏中使用的东西。我们将在本节后面讨论 GameObject。根据其来源,资产分为三类:Unity、用户创建和第三方。Unity 游戏引擎附带免费资产并提供一个付费(非免费)资产的库。用户创建的资产是您自己创建的。我们将在本章后面创建自己的资产。最后一种资产类型是第三方,这意味着它是由您或 Unity 之外的人创建的。
当我们选择“资产”下拉菜单,如图所示,我们有几个可供选择。在本章中,您将熟悉创建、导入新资产和导入包选项:

无论资产来源是 Unity、用户创建还是第三方,资产都可以有多种类型。如图所示,这些资产类型包括脚本、场景、预制体、材质、动画等等:

资产包
资产包是将资产分组在一起的集合。我们可以创建包与他人共享我们的资产,或者为在另一款游戏或 Unity 应用程序中使用而保存它们。我们甚至可以创建在 Unity 资产商店中出售的资产包。
要创建一个asset包,您只需使用项目面板选择包中所有想要的资源。然后您有两个选项。第一个选项是右键单击并选择导出包。第二个选项是从顶部菜单选择资产 | 导出包。两种选项都会产生相同的结果。
除了可以从 Unity 导出包外,我们还可以将asset包导入到我们的 Unity 游戏中。要导入一个asset包,我们只需选择资产 | 导入包菜单选项。这会显示以下截图中的选项:

当导入asset包时,我们可以从列出的标准资产包中选择一个:2D、相机、角色、跨平台输入、效果、环境、粒子系统、原型设计、实用工具和车辆。我们还可以从计算机文件系统中导入自定义包。
当选择一个包时,Unity 将解压缩该包,然后向您显示包内容。我们将在本章后面执行此操作。
理解 GameObject
GameObjects 是我们游戏中使用的东西,例如 2D 对象、3D 对象、音频、相机、灯光、用户界面和视觉效果。GameObject 具有属性,这些属性根据其类型和组件而异。组件可以包括脚本、动画等。随着我们继续制作游戏,您将了解很多关于 GameObject 及其如何与之协同工作的内容。
GameObject 由组件组成。正如您可以在以下检查器面板截图中所见,我们游戏的主相机有四个组件:变换、相机、GUI 层、闪光层和音频监听器:

在检查器面板底部是添加组件按钮。该按钮通过以下类别为我们提供了访问 Unity 组件的权限:
-
网格
-
效果
-
物理
-
二维物理
-
导航
-
音频
-
视频
-
渲染
-
布局
-
可播放对象
-
AR
-
杂项
-
分析学
-
脚本
-
事件
-
网络
-
用户界面
-
新脚本
在 Unity 中创建 3D 对象
如前所述,GameObject 可以包括具有属性和组件的 3D 对象。在本节中,我们将创建一个 3D 对象来表示血滴,这样我们就可以在黄瓜人战斗黄瓜甲虫时使用它。在我们创建对象之前,让我们创建一个材质,以便我们的血滴可以呈现逼真的红色。按照以下步骤创建材质:
-
在项目面板中,点击材质,然后在文件夹中右键单击并选择创建 | 材质
-
将新材质命名为红色
-
在选择新材质后,点击检查器面板主图部分中的颜色框
-
在弹出的颜色选择器窗口中,选择红色并关闭选择框
您的新材质,在检查器面板中查看时,应类似于以下截图:

接下来,让我们创建球体:
-
从顶部菜单中选择 GameObject | 3D Object | Sphere。
-
使用变换工具定位新球体,以便在场景视图中可以看到它。
-
使用检查器面板的变换部分,将 X、Y 和 Z 缩放参数的缩放增加到 5,这将有助于使球体更大,更容易操作。
-
接下来,我们将红色材质分配给球体。在层次结构面板中选择球体,并在项目面板中选择
Materials文件夹,将红色材质从Materials文件夹拖动到检查器面板 Mesh Renderer 组件的 Materials | Element 0 参数。
现在,你将看到场景视图中的球体变成了红色。
如果我们打算使用这个球体来模拟血液滴落,我们可能需要几十个这样的球体。我们将在下一节中探讨我们的选项。
使用预制件
在上一节中,你创建了一个球体来模拟血液滴落。你还创建了一个红色材质并将其应用到球体上。在游戏中,我们可能想要模拟一场大战,并让多个血液滴落同时可见。在 Unity 中,我们可以根据需要制作我们主球体的任意多个副本。例如,假设我们有 100 个球体,它们都是从我们的主球体复制的。当我们想要修改它们,比如改变大小或材质时,这将是一项费力的任务。
另一个选择是使用预制件。在 Unity 中,预制件是一种资产类型,它作为 GameObject 的壳,并包含属性和组件。我们创建的球体是一个 GameObject,正如你在下面的截图中所看到的,我们的球体有多个组件和属性:

因此,让我们创建一个预制件来存放我们的球体。按照以下步骤操作:
-
在项目面板中,选择
Prefabs文件夹。 -
右键单击
Prefabs文件夹,然后选择 Create | Prefab。或者,你也可以从顶部菜单中选择 Assets | Create | Prefab。 -
将预制件命名为
bloodDroplet。你将注意到,在检查器面板中对于新预制件没有可见的组件或属性。 -
接下来,将层次结构面板中的
Sphere游戏对象拖动到项目面板中的新bloodDroplet预制件上。现在,当你查看检查器面板中的预制件时,你可以看到我们的球体所拥有的组件和属性。 -
我们不再需要原始的球体,所以在层次结构面板中将其删除。
现在我们有了bloodDroplet预制件,我们可以根据需要将其拖动到场景中多次。我们也可以使用脚本将它们添加到场景中。
要体验 Unity 中预制件的强大功能,请尝试以下操作:
-
在项目面板中选择
bloodDroplet预制件。 -
将几个
bloodDroplet预制件的副本拖动到你的场景中。 -
在检查器面板中更改颜色。你将注意到,根据你对预制件所做的更改,场景中的所有
bloodDroplets都发生了变化。 -
将预制件的颜色恢复到红色材质。
-
从你的场景中删除任何
bloodDroplets。这最容易在层次结构面板中完成。
从你的场景中删除bloodDroplets并不会从你的游戏中删除所有的bloodDroplets。因为我们有bloodDroplet预制件,我们可以在任何时候将bloodDroplets添加到我们的场景中。
使用额外的 3D 对象
在上一节中,你创建了一个球体。Unity 还允许我们原生创建其他 3D 对象:立方体、胶囊、圆柱体、平面、四边形、布娃娃、地形、树木、风区以及 3D 文本。基本形状——立方体、球体、胶囊、圆柱体、平面和四边形——可能不是你游戏所需的,我们当然不需要它们在我们的Cucumber Beetle游戏中。这些 3D 对象非常适合在 Unity 项目中测试组件和脚本。我们可以比创建它们更容易地删除它们,因此它们是很好的、可消耗的测试资产。我们可以在我们的游戏中使用它们来实验,在我们花费任何时间在最终图形之前。
使用 Unity 资产商店
Unity 运营一个名为 Unity Asset Store 的商店。有许多资产可供 Unity 开发者使用。该商店可以通过assetstore.unity3d.com直接在网络上访问。你还可以在 Unity 中打开一个窗口来显示资产商店。这是通过选择 Window | Asset Store 来完成的。
无论你如何连接到 Unity 资产商店,你都会在商店右上角看到一个分层类别列表。正如以下截图所示,有十一个资产类别:

点击每个类别左侧的三角形图标可以显示子类别。一些类别有多个子类别,这有助于你快速找到你想要的东西。你还可以使用资产商店的搜索功能。
当你点击一个类别时,其内容将可查看。点击特定的资产将显示该资产的几个特性。这些特性包括以下内容:
-
标题
-
发布者
-
评分
-
价格
-
添加到购物车按钮,或者,在免费资产的情况下,下载按钮
-
所需 Unity 版本
-
发布日期
-
描述
-
包含内容
-
文件大小
-
资产版本号
-
图片
-
视频(不一定总是可用)
-
发布者网站链接
-
用户评价
在从 Unity 资产商店获取资产方面,有一些事情需要考虑。首先,你想要确保你有使用该资产的必要权利,正如你计划的那样。例如,如果你没有商业使用权利,你不会想在你的商业游戏中使用那个特定的资产。
在从资产商店选择资产之前,还需要考虑的一个问题是文件的大小。幸运的是,当你在预览资产时,这部分信息是元数据的一部分。
在下一节中,我们将访问 Unity 资产商店,选择一个资产,并将其添加到我们的游戏项目中。
亲身体验 Unity 资产商店
在本节中,我们将介绍获取 Unity Asset Store 中资源的必要步骤。请遵循以下步骤:
-
使用顶部菜单,选择 Window | Asset Store。
-
在搜索框中输入
Unity Particle Pack并按下键盘的回车键。 -
在结果中,通过 Unity Technologies 找到 Unity Particle Pack 项目。它将是一个免费资源包。通过点击标题选择该包。
-
在 Asset Store 中显示 Unity Particle Pack 后,点击下载按钮。
-
您将被提示接受许可协议。如果您同意条款,请点击接受按钮。这将开始下载过程。
-
下载完成后,将出现导入按钮。点击该按钮。
-
您现在将看到导入 Unity Package 对话框窗口。默认情况下,包中的所有资源都将被选中。点击界面底部的导入按钮。导入进度将在弹出窗口中显示,并在过程完成后自动关闭。
-
导入过程完成后,通过右键点击标签并选择 Close tab 来关闭 Asset Store。
如您从下面的截图中所见,在您的项目面板的“资产”下将会有一个新的EffectExamples文件夹:

我们现在有一套很好的特效可以用于我们的游戏。我们将在第十二章,“为我们的游戏添加音频和视觉效果”中整合一些。
在我们的游戏中整合自定义资源
到目前为止,在本章中,我们已经创建了自己的游戏资源并从 Unity Asset Store 下载了一个资源包。在本节中,我们将从出版者的配套网站上下载资源用于我们的游戏。
按照以下步骤获取为我们的Cucumber Beetle游戏专门创建的资源包:
-
导航到出版者的网站并下载以下文件:
-
CherriesAndTree.unitypackage -
CucumberAndPatches.unitypackage
-
-
从顶部菜单选择 Assets | Import Package | Custom Package,并导航到
CherriesAndTree.unitypackage文件。点击打开按钮。 -
如以下截图所示,导入 Unity Package 对话框窗口将包含我们樱桃树和樱桃所需的所有文件。点击导入按钮:

接下来,我们将导入黄瓜和两个 Cucumber Patches。这些包含在CucumberAndPatches.unitypackage资源包中。按照前面的步骤将此资源包导入到您的游戏中。
如您在下面的截图中所见,这个资源包中有几个与黄瓜和 Cucumber Patches 相关的文件。

在第七章,实现我们的玩家角色和第八章,实现我们的非玩家角色中,您将下载额外的资产包。
与导入的资产一起工作
在上一节中,您将以下列出的资产添加到您的游戏中:
-
樱桃
-
樱桃树
-
黄瓜
-
黄瓜地
我们将在第十章,脚本化我们的得分系统中结合樱桃和黄瓜资产。在本节中,我们将种植樱桃树和黄瓜地。
如您从第三章,设计游戏中回忆的那样,我们在游戏环境的四个角落和中心创建了出生点。我们还选择了四个种植樱桃树的区域。现在我们有了实际的地形,我们可以更具体地设计。查看以下图表以确定种植樱桃树和黄瓜地的位置:

在种植我们的樱桃树和黄瓜地时,我们将参考此图的前两个部分。
植树樱桃树
在第四章,创建我们的地形中,我们创建了一棵树来演示如何从头开始创建。由于我们在上一节中下载了樱桃树,我们不再需要我们的实验树。在层次面板中,删除我们之前创建的三个。接下来,删除项目面板中的任何树木资产和与树木相关的纹理文件夹。这将帮助我们保持组织并最小化我们的 Unity 游戏项目的大小。
在 Unity 中,基本树木可以绘制在地形上。要完成此操作,您首先需要调整场景视图,以便您有一个从上到下的地形视图。然后,在层次面板中,选择您的地形。接下来,您在检查器面板的地形组件中使用放置树木按钮来绘制地形上的树木。
由于我们的樱桃树是特殊的,包含多个网格,我们无法使用地形绘制工具。相反,我们将制作多个樱桃树预制件的副本,并使用之前提供的图作为参考,将它们放置在我们游戏世界中想要的位置。
在种植我们的樱桃树之前,我们需要采取一些准备步骤。按照以下步骤准备预制件:
-
在项目面板中,右键单击
Prefabs文件夹,然后选择创建 | 预制件。将预制件命名为CherryTreeCollider。 -
在项目面板中,将上一节中导入的
CherryTree.prefab拖动到场景视图中。位置不重要。 -
在层次面板中,选择您刚刚添加到场景视图中的樱桃树。
-
在检查器面板中,单击盒子碰撞体组件中的“是触发器”复选框。
-
仍然在检查器面板中,点击添加组件按钮,然后选择物理|方块碰撞器。我们将使用这个碰撞器帮助我们确定当黄瓜人从樱桃树中收集樱桃时。
-
接下来,我们将编辑碰撞器以确保它包含整个树。在检查器面板的方块碰撞器区域,点击编辑碰撞器按钮。
如以下截图所示,碰撞器位于Cherry Tree预制体的底部:

- 使用场景视图中的方块碰撞器的方块,放大碰撞器,使其包含大部分樱桃树。它不需要包含树干。以下截图显示了示例配置:

-
在项目面板中,确保已选择
Prefabs文件夹。 -
将樱桃树从层次结构面板拖动到
Prefabs文件夹中的CherryTreeCollider预制体。这创建了一个具有方块碰撞器的Cherry Tree预制体。 -
在层次结构面板中,删除樱桃树,除非它位于你想要的位置。
-
在
Prefabs文件夹中选择CherryTreeCollider预制体。 -
在检查器面板中,点击标签旁边的下拉菜单。选择添加标签按钮。
-
如以下截图中的箭头所示,点击空列表底部的+图标:

-
在弹出的窗口中,输入
CherryTree作为标签名称,然后点击保存按钮。 -
在
Prefabs文件夹中选择CherryTreeCollider预制体。 -
在检查器面板中,点击标签旁边的下拉菜单。选择
CherryTree标签。
现在你可以开始种植你的樱桃树了。只需将预制体拖动到你想放置樱桃树的地方。你可以创建任意多或少的樱桃树。本书提供的示例包含 25 棵樱桃树。
当你的樱桃树放置到你想要的位置后,你的层次结构面板可能变得杂乱无章。一个快速整理的方法是在层次结构面板内右键点击并选择创建空对象。然后,将 GameObject 重命名为Cherry Trees。最后,在层次结构面板中,选择所有樱桃树并将它们放入该 GameObject 中。现在这个 GameObject 作为一个文件夹用于查看。你可以根据需要折叠和展开文件夹。
如果你使用俯视图在场景视图中放置樱桃树,一些树木可能需要根据你的地形进行升高或降低。你可以在层次结构视图中双击樱桃树以自动缩放场景视图。
完成樱桃树的工作后,保存你的场景和项目。
种植黄瓜地
我们之前为我们的黄瓜补丁确定了六个区域。我们可以像我们对樱桃树所做的那样,在游戏环境中放置黄瓜补丁时采用相同的方法。如果我们采用自动种植的方法来种植黄瓜补丁,我们可能会得到比我们想要的更多的黄瓜补丁。因此,我们将采取手动方法。
我们在早期导入的Cucumber Patch文件夹中有预制体。让我们做一些事情来准备这些预制体以供我们使用。请按照以下步骤操作:
-
在项目面板中,在
Prefabs文件夹中右键单击并选择创建 | 预制体。将预制体命名为CucumberPatch1。 -
重复步骤 1,并将第二个预制体命名为
CucumberPatch2。 -
在项目面板中,将上一节中导入的
Patch1prefab拖动到场景视图中。位置无关紧要。 -
在层级面板中,选择您刚刚添加到场景视图中的黄瓜补丁。
-
在项目面板中,确保已选择
Prefabs文件夹。 -
将层级面板中的黄瓜补丁拖动到
Prefabs文件夹中的CucumberPatch1预制体。 -
从层级面板中删除黄瓜补丁。
-
在项目面板中,将上一节中导入的
Patch2prefab拖动到场景视图中。位置无关紧要。 -
在层级面板中,选择您刚刚添加到场景视图中的黄瓜补丁。
-
在项目面板中,确保已选择
Prefabs文件夹。 -
将黄瓜补丁从层级视图中拖动到
Prefabs文件夹中的CucumberPatch2预制体。 -
从层级面板中删除黄瓜补丁。
我们本可以将导入的预制体直接移动到Prefabs文件夹。我们的方法是将它们复制,以便在需要时可以使用原始的。
接下来,我们将为游戏世界中的黄瓜补丁准备六个区域。我们的目标是确定每个都有平坦地面的矩形区域。
使用以下参考图像和您用于创建地形的 Unity 技能,我们需要将六个区域压平并记录它们的矩形边界:

为了准备我们的黄瓜补丁区域,请按照以下步骤操作:
-
使用高度绘制工具将黄瓜补丁区域压平。
-
从顶部菜单选择 GameObject | 3D Object | 平面。
-
将平面放置在地面上,具体是在压平的区域。
-
在检查器视图中,将材料 | 元素 0 参数设置为 SpatialMappingOcclusion。这将使平面变得透明。
-
一旦平面就位,记下变换信息。
-
对剩余的黄瓜补丁重复步骤 1 至 5。
-
将平面重命名为
CucumberPatchArea1、CucumberPatchArea2、CucumberPatchArea3、CucumberPatchArea4、CucumberPatchArea5和CucumberPatchArea6。
完成后,您应该有六个定义好的黄瓜补丁区域。为了参考,书中示例中使用的六个区域如下表所示:
| 平面 | 变换 |
|---|---|
CucumberPatchArea1 |
| | X | Y | Z |
| 位置 | 7373 | 40.03 | 1689 |
| 旋转 | 0 | 10.082 | 0 |
|
CucumberPatchArea2 |
|---|
| | X | Y | Z |
| 位置 | 1211 | 40.03 | 1142 |
| 旋转 | 0 | 53.984 | 0 |
|
CucumberPatchArea3 |
|---|
| | X | Y | Z |
| 位置 | 1160 | 40.03 | 831 |
| 旋转 | 0 | 87.876 | 0 |
|
CucumberPatchArea4 |
|---|
| | X | Y | Z |
| 位置 | 892 | 40.03 | 849 |
| 旋转 | 0 | 120.877 | 0 |
|
CucumberPatchArea5 |
|---|
| | X | Y | Z |
| 位置 | 1200 | 40.03 | 568 |
| 旋转 | 0 | 143.801 | 0 |
|
CucumberPatchArea6 |
|---|
| | X | Y | Z |
| 位置 | 1184 | 40.03 | 330 |
| 旋转 | 0 | 103.911 | 0 |
|
我们的最后一步是将六个黄瓜补丁在层次面板中分组,以保持组织有序。就像我们对樱桃树所做的那样,我们将在层次面板内部右键单击并选择创建空对象。然后将 GameObject 重命名为Cucumber Patch Areas。最后,在层次面板中,选择所有六个黄瓜补丁窗格并将它们放置在那个 GameObject 中。
在第八章,实现我们的非玩家角色,我们将使用窗格来添加黄瓜补丁、黄瓜和甲虫。
摘要
在本章中,我们花费了大量时间使我们的游戏环境更加健壮。我们导入了并使用了几个资产。我们在场景中添加了树木和其他对象。我们使用 Unity 的原生建模工具创建了 3D 对象,并从 Unity 资产商店以及出版商的配套网站上导入资产。具体来说,我们添加了游戏中的樱桃树,并为黄瓜补丁准备了区域。
在第七章,实现我们的玩家角色,我们将整合游戏中的玩家角色,黄瓜人。我们将导入角色,审查控制,检查动画,并做出必要的配置更改,以便在游戏中完全使用我们的角色。到本章结束时,你将能够开始测试游戏。
第七章:实现我们的玩家角色
在 第六章,为我们的游戏创建和导入 3D 对象,我们处理了我们的游戏环境,并将几个资产添加到我们的游戏项目中。我们添加并种植了我们的樱桃树,并为我们的黄瓜地准备了六个区域。我们还学习了如何从 Unity 资产商店以及第三方来源导入资产。
在本章中,我们将整合我们游戏的角色,即黄瓜人。我们将导入角色,审查控制方式,检查动画,并对角色进行必要的配置更改,以便在游戏中完全使用我们的角色。到本章结束时,您将能够开始以游戏模式测试游戏。
本章我们将探讨以下主题:
-
使用 Unity 的标准资产包
-
导入游戏角色
-
配置玩家控制器
-
微调我们的角色
-
为我们的玩家角色制作动画
-
为我们的黄瓜人地形进行改造
使用 Unity 的标准资产包
游戏角色、玩家角色、用户控制的玩家——这些都是用来指代我们游戏的主要角色,即黄瓜人,人类玩家将控制这个角色。在 Unity 中,我们称这个角色为玩家角色。为了有一个玩家角色,我们需要一个控制器。在我们的案例中,我们将使用第三人称控制器。第三人称控制器是一个用于从第三人称摄像机视角控制角色的框架。
Unity 标准资产包包括一个 ThirdPersonController 资产包。该包,如下面的截图所示,包含 Animation、Materials、Models、Scripts、Textures 和 Prefabs:

本节演示了如何使用 Unity 创建的角色。它将不会整合到 黄瓜甲虫 游戏中。通过以下步骤,您将熟悉使用 Unity 提供的 ThirdPersonCharacter 的过程:
-
启动 Unity。
-
从顶部菜单选择 Assets | 导入包 | 角色。
-
您将看到一个导入 Unity 包的对话框窗口。点击导入按钮。
-
在项目面板中,您将看到
Standard Assets。导航到Standard Assets| 角色 |ThirdPersonCharacter|Prefabs并将ThirdPersonController预制件拖动到层次结构面板。 -
在层次结构面板中双击
ThirdPersonController以在场景视图中自动聚焦到控制器。如下面的截图所示,角色被胶囊控制器包围:

-
在层次结构面板中,将主摄像机拖动,使其成为
ThirdPersonController的下级。这是必要的,以便在游戏过程中摄像机跟随玩家角色。 -
在场景视图中,使用变换工具将摄像机放置在角色上方和后方。选择摄像机后,场景视图中的摄像机预览窗口应类似于以下截图:

现在你可以将游戏设置为游戏模式,并使用键盘和鼠标导航游戏世界。正如你所看到的,默认角色可以在世界中移动,摄像机将跟随。
使用提供的ThirdPersonController非常适合测试目的,但你不会想使用这些标准资产部署游戏。尽管 Unity Technologies 允许商业使用他们的标准资产,但使用它们会侵蚀你游戏的特点,因此建议它们仅用于测试。
导入游戏角色
我们将为我们的黄瓜甲虫游戏使用自定义的第三人称角色控制器。首先,我们应该确保你的游戏项目中没有冲突的资产。让我们从启动 Unity 并打开基于你在第六章,为我们的游戏创建和导入 3D 对象中完成的工作的游戏项目开始。或者,你也可以从出版商的配套网站上加载Starting-Chapter-07 Unity 项目。一旦你的项目在 Unity 中加载,请从层次结构面板中删除以下列出的任何项目:
-
摄像机(你将保留在第五章,灯光、摄像机和阴影)中首先探索的主摄像机)
-
ThirdPersonController
删除不必要的资产后,你的层次结构面板应只包含以下层次结构面板截图中的资产:

现在你可以导入我们游戏玩家角色,黄瓜人的资产包了。按照以下步骤导入包:
-
从出版商的配套网站上下载
CucumberMan_Controller.unitypackage文件 -
在 Unity 中,打开你的游戏项目后,从顶部菜单选择 Assets | 导入包 | 自定义包
-
导航到步骤 1 中下载的资产包位置,并点击打开按钮
-
当出现导入资产包对话框时,点击导入按钮
如你所注意到的,黄瓜人资产包包含与黄瓜人相关的多个资产,包括控制器、脚本、预制体和其他资产。在下一节中,我们将把黄瓜人添加到我们的游戏中。
配置玩家控制器
到目前为止,我们已经花费了很多时间来塑造我们的世界,并用樱桃树和出生点填充它,为黄瓜田做准备。我们一直在为我们的玩家角色,黄瓜人,创建游戏世界。现在是时候将我们的黄瓜人添加到游戏中了。
下面是将黄瓜人添加到我们的游戏并配置控制器的步骤:
-
在项目面板中,选择预制体。
-
将
CucumberMan预制体拖动到层级面板。使用以下截图作为参考,确保您正在拖动以下截图中突出显示的预制体。请注意,下级Cucumbor_Man已被故意拼写错误,以确保使用CucumberMan预制体:

-
在层级面板中,选择主摄像机。
-
在选择主摄像机的情况下,在检查器面板中点击添加组件按钮。
-
选择脚本 | 摄像机跟随,将摄像机跟随脚本添加到主摄像机。
-
在主摄像机的摄像机跟随组件中,在检查器面板中,将移动平滑度设置为
5。 -
在主摄像机的摄像机跟随组件中,将旋转平滑度设置为
5。 -
在层级面板中,点击
CucumberMan左侧的灰色三角形。这将显示下级组件。 -
在层级面板中重新选择主摄像机。
-
在层级面板中点击
CucumberMan下的 GameObject,并将其拖动到主摄像机摄像机跟随组件的跟随目标字段。 -
确保已勾选“可以跟随”复选框。
您主摄像机的摄像机跟随组件应与以下图像完全相同。如果不同,请重新检查步骤 6 至 11:

接下来,我们将编辑CucumberMan的玩家电机(脚本)组件。按照以下重要步骤正确配置玩家电机(脚本)组件:
-
在层级面板中,选择
CucumberMan。 -
如有必要,展开检查器面板中
CucumberMan的玩家电机(脚本)组件。 -
将主摄像机从层级面板拖动到检查器面板中
CucumberMan的玩家电机(脚本)组件的 Cam 字段。
接下来,让我们将黄瓜人放置到游戏的一个逻辑起始位置。你将黄瓜人放置在哪里由你决定。建议将其放置在其中一个重生点。一旦你知道角色在每场游戏中应该从哪里开始,请按照以下步骤操作:
-
在层级面板中,选择
CucumberMan。 -
使用变换工具,将角色移动到其中一个出生点。
-
放大并确保角色的脚在或略高于地面。我们还需要完成一个步骤,使黄瓜人成为我们游戏的可玩角色。
-
在选择
CucumberMan的情况下,在检查器面板中选择标签下拉菜单并选择玩家。这将使我们更容易引用碰撞。你将在第十章中了解更多关于脚本我们的得分系统。
现在,你可以将游戏设置为游戏模式并测试玩家角色。你可以使用以下表格中列出的键盘按键来控制黄瓜人:
| 键盘按键 | 操作 |
|---|---|
| W | 向上行走 |
| A | 向左行走 |
| S | 向下行走 |
| D | 向右行走 |
| E | 投掷 |
| 左键 Shift 键 | 跑步 |
| 空格键 | 跳跃 |
在下一节中,我们将微调 Cucumber Man。
微调我们的角色
现在 Cucumber Man 已经进入我们的游戏,我们可以开始尝试这个角色,以确保它看起来和表现方式符合我们的期望。在本节中,我们将查看对 Cucumber Man 的以下改进:
-
电机控制
-
缩放
-
胶囊碰撞体
-
输入控制
微调电机控制
您可以将游戏置于游戏模式,并使用上一节中详细说明的键盘输入来实验 Cucumber Man 的移动。您可能已经注意到的一件事是,玩家角色似乎移动得不太快。当然,这与我们的地形大小和角色大小有关。让我们通过查看相关脚本来看看如何增加角色的速度。
以下代码片段来自与 Cucumber Man 一起导入的PlayerMotor.cs脚本。这个片段是脚本的第一部分,而不是整个脚本。如您所见,有JumpPower、MoveSpeed和RunSpeed变量。这些变量在代码中创建,其值可以在 Unity 编辑器中控制:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;
[RequireComponent(typeof(Rigidbody))]
public class PlayerMotor : MonoBehaviour {
float horizontal, vertical;
Rigidbody m_Rigidbody;
public float JumpPower;
public float MoveSpeed, RunSpeed;
private float currentJumpPower = 0;
private float currentMoveSpeed = 0;
// Use this for initialization
void Start ()
{
m_Rigidbody = GetComponent<Rigidbody>();
currentMoveSpeed = MoveSpeed;
// m_Cam = Camera.main.transform;
m_Animator = GetComponent<Animator>();
}
. . .
以下截图来自 Cucumber Man 的检查器面板。在这里,您可以看到我们有能力更新跳跃力量、移动速度、奔跑速度、静止转向速度和移动转向速度的值:

使用 Cucumber Man 的玩家电机(脚本)组件,您可以尝试不同的值,使玩家角色的移动方式符合您的期望。您始终可以参考之前的截图来重置您的值。
您可以在游戏模式下实验玩家电机(脚本)参数。在游戏模式下做出的任何更改在离开游戏模式时都不会保留。这是一种在不影响任何已保存设置的情况下进行实验的好方法。
这里是 Cucumber Man 电机控制的一组典型参数:

我们还可以选择编辑移动速度乘数变量,以影响所有电机速度值。您可以在检查器面板中手动设置,也可以通过编程方式设置。要手动设置乘数,只需在检查器面板中编辑CucumberMan的玩家电机(脚本)组件中的值。要编程更改此值,您可以编辑PlayerMotor.cs脚本。以下代码片段来自该脚本,并代表该脚本的后续部分:
. . .
Animator m_Animator;
[SerializeField] float m_MoveSpeedMultiplier = 1f;
public void OnAnimatorMove()
{
// we implement this function to override the default root motion.
// this allows us to modify the positional speed before it's applied.
if (m_IsGrounded && Time.deltaTime > 0)
{
Vector3 v = (m_Animator.deltaPosition * m_MoveSpeedMultiplier) / Time.deltaTime;
// we preserve the existing y part of the current velocity.
v.y = m_Rigidbody.velocity.y;
m_Rigidbody.velocity = v;
}
}
. . .
如您在之前的代码片段中所见,m_MoveSpeedMultiplier设置为1。您可以直接在脚本中更改该值,作为使用 Unity 编辑器的替代方法。
微调缩放
我们的黄瓜人有一个变换,包括 X、Y 和 Z 的缩放值。默认情况下,这些值都设置为 1。我们可以通过增加或减少缩放值来轻松改变黄瓜人的大小。使用这种方法,你可以创建黄瓜宝宝或黄瓜巨人的黄瓜人副本。
以下截图显示了三个黄瓜人的副本。从左到右,每个黄瓜人都比前一个大:

下面是前面截图中的每个黄瓜人的变换设置:
| 变换比例设置 | |
|---|---|
| 黄瓜人版本 | X |
| 黄瓜宝宝 | 0.5 |
| 黄瓜人 | 1 |
| 黄瓜巨人 | 2 |
对于我们的游戏,我们只会有一个黄瓜人,我们希望他比甲虫大,比樱桃树小。默认比例的结果是黄瓜人的高度理想。这可以通过他与樱桃树的相对大小来明显看出,如下面的截图所示:

在第八章,“实现我们的非玩家角色”中,我们将检查我们的黄瓜甲虫的比例,并确保它们与黄瓜人的大小成适当比例。
精细调整胶囊碰撞体
胶囊碰撞体的形状是药丸——即一个两端圆润的圆柱体。我们可以通过在层级面板或场景视图中选择我们的角色,然后在检查器面板中查看胶囊碰撞体组件来查看胶囊碰撞体。如下面的截图所示,我们可以通过点击编辑碰撞体按钮来编辑胶囊碰撞体:

下面是胶囊碰撞体组件每个参数的简要描述:
-
是否触发:当我们启用此选项时,物理引擎将忽略胶囊碰撞体
-
材质:我们可以指定胶囊碰撞体如何与其他碰撞游戏对象交互
-
中心:有 X、Y 和 Z 字段来识别胶囊相对于自身的中心
-
半径:碰撞体宽度的半径
-
高度:碰撞体的高度
-
方向:胶囊的纵向方向
为了提高精度,胶囊碰撞体应该接受高度调整,以便黄瓜人能够适应其中,如下面的截图所示:

对你的黄瓜人的胶囊碰撞体进行任何必要的调整,以确保其类似于前面的截图。一旦完成调整,请务必保存你的工作。
本章中详细描述的关于“黄瓜人”游戏的全部更改都将包含在第八章,“实现我们的非玩家角色”开头的Starting-Chapter-08.zip文件中。
更改和优化输入控制
当我们使用 Unity 开发游戏时,在游戏过程中收集用户输入我们有几种选项。以下是一个输入选项列表:
-
键盘
-
鼠标
-
游戏手柄
-
触摸屏
-
移动设备运动传感器
-
麦克风
-
摄像机
如我们在本章前面所述,我们将使用键盘作为 Cucumber Man 游戏的唯一输入设备。让我们看看如果你想要实验或只是想要改变用户与游戏交互的方式,你可以在哪里更改游戏输入控制。我们将使用 Unity 的输入管理器来完成这项工作。
你的游戏项目输入管理器可以通过以下截图所示的方式访问,通过选择编辑 | 项目设置 | 输入下拉菜单选项:

这会导致输入管理器在检查器面板中显示。在那里,你可以找到游戏的相关虚拟轴。在下面的屏幕截图中,水平虚拟轴和垂直虚拟轴并排显示。你可以看到键盘的 W、A、S 和 D 键是如何设置为控制水平和垂直移动的。例如,W 键在垂直虚拟轴中被识别,并分配给 Alt 正按钮,这意味着它是向前移动的替代按钮。而主要的负和正按钮被列为使用左右箭头键进行水平移动,上下箭头键用于垂直导航,我们将实现 W、A、S 和 D 键作为我们在游戏世界中移动黄瓜人角色的默认方法:

输入管理器使我们能够确定所有的输入控制,并且是你设置鼠标、键盘和游戏手柄输入的地方。输入管理器中还有一个额外的设置,用于跳跃功能。如图所示,跳跃功能使用键盘的空格键作为正输入设备。你会注意到没有分配负按钮,因为假设角色只能向上跳,不能向下跳:

为我们的玩家角色动画
你的黄瓜人角色控制器包包括支持角色六种动画所需的资产。在本节中,我们将查看黄瓜人的动画,如下所示:
-
空闲
-
走路
-
跑步
-
跳跃
-
投掷
-
死亡
检查玩家控制器脚本
在 Unity 中,有几个区域可以审查 Cucumber Man 的动画。让我们首先查看CucumberMan控制器对象的Player Controller (Script)组件。当你选择层次结构面板中的 CucumberMan 控制器时,你应该在检查器面板中看到Player Controller (Script)组件。你可能需要点击组件左侧的展开按钮,以便显示组件的详细信息。
如以下截图所示,我们每个角色的动画都与一个状态相关联:

现在,让我们审查PlayerController.cs脚本。你可以通过在项目面板的“收藏”下选择“所有脚本”按钮来访问此文件。然后,在项目面板中滚动到PlayerController (Script)。以下代码片段显示了脚本的前 14 行:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour {
public string RunState, WalkState, IdleState, JumpState, DieState, ThrowState;
bool isWalking, isRunning, isJumping, isIdle, isDie, forward,left,right,back;
Animator mAnim;
// Use this for initialization
void Start () {
mAnim = GetComponent<Animator>();
isIdle = true;
}
前三行简单地标识了我们脚本中想要使用的命名空间。这些是System.Collections、System.Collections.Generic和UnityEngine。接下来,我们有PlayerController类的类声明。接下来的三行代码是我们的变量定义。正如你所见,我们为六个动画中的每一个都有一个公共字符串:RunState、WalkState、IdleState、JumpState、DieState和ThrowState。还有几个boolean(bool)变量,包括isWalking、isRunning、isJumping、isIdle和isDie。我们还标识mAnim为我们自己的 Animator。我们将在脚本中的后续部分看到这些是如何被使用的。
Start()方法将我们的初始状态isIdle设置为 true,并通过mAnim = GetComponent<Animator>();语句获取我们的 Animator 引用。
我们PlayerController.cs脚本中最大的方法是Update()方法。Update()方法每帧调用一次。以下代码段提供了此方法,其中省略号(...)表示为了简洁而删除的代码区域。你可以在 Unity 中查看完整的脚本。第一个代码段将适当的设置设置为 true 或 false。例如,当用户按下W键且玩家当前未跑步时,isWalking变量被设置为 true,并将两个参数(WalkState和true)传递给mAnim.SetBool。对A、S和D键也采取类似行动:
. . .
void Update () {
//Down states
if( Input.GetKeyDown(KeyCode.W))
{
if( !isRunning )
{
isWalking = true;
isIdle = false;
forward = true;
mAnim.SetBool(WalkState, true);
mAnim.SetBool(IdleState, false);
}
}
if( Input.GetKeyDown(KeyCode.A))
{
. . .
}
if( Input.GetKeyDown(KeyCode.S))
{
. . .
}
if( Input.GetKeyDown(KeyCode.D))
{
. . .
}
. . .
以下Update()方法的下一个代码片段,如以下片段所示,处理当按下左Shift键时的跑步动画。在这种情况下,动画从WalkState更改为RunState:
. . .
if( Input.GetKeyDown(KeyCode.LeftShift))
{
if( isWalking )
{
isRunning = true;
mAnim.SetBool(RunState, true);
mAnim.SetBool(WalkState, false);
}
}
. . .
以下代码段,如以下片段所示,展示了检测空格键输入导致Jump()动画运行,以及当检测到E键时Throw()动画运行的情况:
. . .
if( Input.GetKeyDown(KeyCode.Space))
{
Jump();
}
if( Input.GetKeyDown(KeyCode.E))
{
Throw();
}
. . .
审查 Animator 组件
在CucumberMan控制器对象中,还有一个最终组件需要检查:动画组件。如下面的截图所示,动画组件有两个关键属性:控制器和头像:

控制器指向CucumberMan.controller文件。该文件的.controller文件扩展名在组件界面中不会显示,但它是对该文件的引用。您可以在项目面板中导航到该文件。它位于资产 | Cucumber Man 下。当您双击该文件时,它将在动画窗口中打开,如下面的截图所示:

在这里,您可以查看每个状态,每个动画一个状态,以及它们之间的可能转换。每个状态都带有CM_前缀,以指示CucumberMan。这些状态如下列出:
-
CM_Idle
-
CM_Walk
-
CM_Run
-
CM_Jump
-
CM_Throw
-
CM_Die
如果动画窗口的布局混乱或不清晰,您可以重新排列动画窗口中的对象,以便更容易理解。您可以通过单击一个状态,将其拖动到您想要的位置,然后释放鼠标按钮来完成此操作。以下图形显示了 Cucumber Man 的状态和转换的一种可能的组织方法。如果您愿意,您的做法可以不同:

您可以保留这些转换,就像它们提供给您的那样,或者,如果您想的话,您可以使用动画窗口进行更改。
预览动画
您可以选择预览单个动画,而无需将游戏置于游戏模式。在项目面板中,选择资产 | Cucumber Man,您将看到列出的单个动画。例如,点击CucumberMan_Throw动画。这是您在本章早期导入的包中的一部分CucumberMan_Throw.fbx文件。
选择动画后,检查器面板为您提供了访问模型、绑定和动画标签的权限。虽然您在这里不需要做任何修改,但花时间探索各种功能是值得的。当您选择动画标签时,您可以在检查器面板底部预览动画,如下面的截图所示:

您可以点击动画预览窗口左上角的播放按钮来播放动画。动画将播放并循环,直到您停止播放。
您可以通过玩游戏来预览每个动画。这适用于所有动画,除了死亡动画;我们将不得不编写一个脚本来触发该动画。我们将在第十一章,脚本胜利与失败中这样做。
为我们的 Cucumber Man 地形进行改造
在进行游戏测试时,建议让黄瓜人行走、奔跑和跳跃到你希望玩家在游戏中能够执行这些动作的地方。这是一个常常被忽视或未给予足够关注的步骤。回想一下你玩过的游戏或过去玩过的游戏。你的角色是否曾经卡住或无法到达某个区域?很可能其中一些是由于测试不足造成的。
建议你着手进行这项工作。地形可能太陡峭,以至于角色无法攀爬。使用你在第四章,创建我们的地形中学到的技能来修改你的地形,以便玩家角色能够以你想要的方式导航你的游戏世界。
在这个重要步骤上花费的时间将有助于确保非玩家角色,我们的黄瓜甲虫,能够到达你希望它们去的地方。你将在下一章第八章,实现我们的非玩家角色中收到提示来检查这一点。
现在是保存你的场景和项目的绝佳时机。记住,尽早保存,经常保存!
摘要
在本章中,我们整合了我们的游戏玩家角色,黄瓜人。我们导入了角色,审查了控制方式,检查了动画,并进行了必要的配置更改,以便在游戏中完全使用我们的角色。
在第八章,实现我们的非玩家角色中,我们将实现我们的非玩家角色,黄瓜甲虫。我们将导入并审查与甲虫相关的资产,并开始编写游戏特定于甲虫的部分。这包括随机化黄瓜植物和黄瓜的位置。
第八章:实现我们的非玩家角色
在上一章中,我们专注于我们的游戏玩家角色——黄瓜人。我们导入了角色,审查了控制方式,检查了动画,并对游戏中的角色进行了必要的配置更改,以便完全使用我们的角色。我们审查了玩家角色的动画和角色的动画控制器。我们还确定了与玩家角色相关的脚本,并审查了其中几个。此外,我们还对游戏的地形进行了修改,以便在游戏过程中更好地适应玩家角色。
在本章中,我们将专注于非玩家角色。我们的黄瓜甲虫将作为我们游戏中的非玩家角色,并将成为黄瓜人的敌人。我们将通过直接放置的方式将黄瓜甲虫纳入我们的游戏。我们将回顾甲虫的 11 个动画,并对非玩家角色的动画控制器进行修改。此外,我们将编写脚本以控制非玩家角色。我们还将向游戏世界中添加黄瓜地、黄瓜和樱桃。
在本章中,我们将涵盖以下主题:
-
理解非玩家角色
-
将非玩家角色导入我们的游戏
-
为我们的非玩家角色动画
-
将非玩家角色纳入我们的游戏
-
为我们的黄瓜甲虫地形进行地形改造
-
将黄瓜地皮添加到我们的地形中
-
将黄瓜添加到我们的地形中
-
编写我们的非玩家角色脚本
理解非玩家角色
非玩家角色,通常简称为 NPC,只是不受人类玩家控制的游戏角色。这些角色通过脚本进行控制,其行为通常对游戏中的条件做出响应。
我们游戏中的非玩家角色是黄瓜甲虫。如图所示,这些甲虫有六条腿可以行走;在特殊情况下,它们也可以用后腿行走:

黄瓜甲虫是真实的昆虫,对黄瓜构成威胁。它们实际上不能用后腿行走,但在我们的游戏中可以。
在下一节中,你将导入为这款游戏专门准备的黄瓜甲虫资产包。该资产包中只有一个甲虫。我们将通过脚本制作多个甲虫副本。
在你继续到下一节之前,你应该打开你的 Unity 游戏项目。或者,你也可以从出版商的配套网站上下载可用的Starting-Chapter-08 Unity 项目。
将非玩家角色导入我们的游戏
现在,你已经准备好导入我们游戏非玩家角色——黄瓜甲虫的资产包了。按照以下步骤导入包:
-
从出版商的配套网站上下载
Cucumber_Beetle.unitypackage文件 -
在 Unity 中,打开你的游戏项目后,从顶部菜单选择 Assets | Import Package | Custom Package。
-
导航到步骤 1 中下载的资产包的位置,并点击打开按钮
-
当出现导入资产包对话框窗口时,点击导入按钮
正如您将注意到的,Cucumber_Beetle资产包包含与黄瓜甲虫相关的多个资产,包括控制器、脚本、预制体、动画和其他资产:

现在将Cucumber_Beetle资产包导入到我们的游戏项目中后,我们应该保存我们的项目。使用文件 | 保存项目菜单选项。
接下来,让我们回顾一下导入的内容。
在项目面板中,在资产 | 预制体下,您将看到一个新的Beetle.Prefab。同样,在项目面板中,在资产下,您将看到一个Beetle文件夹。了解文件夹中每个组件的作用非常重要。请参考以下截图,了解您在本章中将使用的与黄瓜甲虫相关的资产概览:

上一个截图中的其他资产包括readme.txt文件、黄瓜甲虫的纹理和材质以及源文件。我们将在下一节中回顾黄瓜甲虫的动画。
动画我们的非玩家角色
我们已经为游戏中的黄瓜甲虫准备了几个动画。以下是动画名称及其在项目中的出现顺序,以及我们将如何将动画融入游戏的简要描述。动画按名称字母顺序列出:
| 动画名称 | 使用详情 |
|---|---|
Attack_Ground |
甲虫从地面攻击黄瓜人的脚 |
Attack_Standing |
甲虫从站立位置攻击黄瓜人 |
Die_Ground |
甲虫从地面起始位置死亡 |
Die_Standing |
甲虫从站立位置的后腿起始位置死亡 |
Eat_Ground |
甲虫在地面上吃黄瓜 |
Idle_Ground |
甲虫没有进食、行走、战斗或站立 |
Idle_Standing |
甲虫站立,但没有行走、奔跑或攻击 |
Run_Standing |
甲虫用其后腿奔跑 |
Stand |
甲虫从地面位置站立(它站立起来) |
Walk_Ground |
甲虫用其六条腿行走 |
Walk_Standing |
甲虫用其后腿行走 |
您可以通过在项目面板中点击动画文件(如Eat_Ground.fbx)来预览这些动画。然后,在检查器面板中,点击播放按钮来观看动画。
我们有 11 个黄瓜甲虫动画,我们将在本章后面使用脚本确定何时播放动画。
在下一节中,我们将把黄瓜甲虫添加到我们的游戏中。
将非玩家角色融入我们的游戏
首先,让我们简单地将Beetle.Prefab从项目面板的Assets/Prefab文件夹拖到场景视图中。将甲虫放在黄瓜人前面,以便在将游戏置于游戏模式时即可看到甲虫。
建议的放置方式如图所示:

当您将游戏置于游戏模式时,您会注意到甲虫会循环播放其动画。如果您在项目面板的Assets|Beetle文件夹中双击Beetle.controller,您将看到,如图所示,我们目前有多个动画被设置为依次和重复播放:

这个初始设置旨在为您提供一种快速预览各种动画的方法。在下一节中,我们将修改动画控制器。
使用动画控制器
我们将使用动画控制器来组织 NPC 的动画。动画控制器还将用于管理动画之间的转换。
在我们开始修改动画控制器之前,我们需要确定我们的甲虫有哪些状态,然后确定每个状态相对于其他状态可以有哪些转换。
动画状态可以称为角色的动画状态。例如,行走是一个状态,跑步和跳跃也是如此。
这里是甲虫可以拥有的状态,每个状态都关联一个动画:
-
地面闲置
-
地面行走
-
地面进食
-
地面攻击
-
地面死亡
-
站立
-
站立闲置
-
站立行走
-
站立奔跑
-
站立攻击
-
站立死亡
在前面的状态列表中,我们可以分配以下转换:
-
从地面闲置到:
-
地面行走
-
在地面上奔跑
-
地面进食
-
地面攻击
-
站立
-
-
从站立到:
-
站立闲置
-
站立行走
-
站立奔跑
-
站立攻击
-
查看从地面闲置到站立的转换演示了您需要为游戏做出的状态到状态转换决策。
让我们将注意力转回到动画控制器窗口。您会注意到该窗口的左侧面板中有两个标签:层和参数。层标签显示一个基础层。虽然我们可以创建额外的层,但我们的游戏不需要这样做。参数标签为空,这是正常的。我们将使用动画控制器窗口的布局区域进行更改。这就是带有网格背景的区域。
让我们从以下更改开始。对于所有 11 个新状态按钮,执行以下操作:
-
左键点击状态按钮
-
在检查器面板中查看以确定哪个动画与状态按钮关联
-
在检查器面板中重命名状态名称以反映动画。
-
点击返回按钮
-
再次检查状态按钮以确保您的更改已生效
当你完成了前五个步骤的所有 11 个状态后,你的动画控制器窗口应该与以下截图相匹配:

如果你将游戏设置为游戏模式,你会看到没有任何变化。我们只是更改了状态名称,使其对我们更有意义。因此,我们还需要对动画控制器做一些更多的工作。
目前,“地面上的攻击”状态是默认状态。这并不是我们想要的。将“Idle on Ground”状态设置为默认状态更有意义。要做出这个更改,右键单击Idle on Ground状态并选择“设置为层默认状态”:

接下来,我们需要对状态转换进行一系列更改。状态很多,转换也会很多。为了使事情更简单,我们将首先删除所有默认转换。为此,左键单击带有箭头的每条白色线条,并按下键盘上的Delete键。不要删除从Entry到Idle on Ground的橙色线条。
删除所有转换后,您可以拖动状态,以便有更多的工作空间。您可能需要暂时以类似以下截图所示的方式重新组织它们:

我们下一个任务是创建所有的状态转换。为每个要添加的状态转换遵循以下步骤:
-
右键单击起始状态。
-
选择“创建转换”。
-
点击目标状态。
一旦您完成了所有转换,您可以将状态重新组织,以整理动画控制器布局区域。以下截图提供了一个建议的最终组织:

如您在最终安排中看到的,我们有 11 个状态和二十多个转换。您还会注意到Die on Ground和Die Standing状态没有任何转换。为了在我们游戏中使用这些动画,它们必须放入动画控制器中。
让我们进行一个快速实验:
-
在层级面板中选择“甲虫”角色。
-
在检查器面板中,点击“添加组件”按钮。
-
选择“物理”|“盒式碰撞体”。
-
点击“编辑碰撞体”按钮。
-
调整盒式碰撞体的尺寸和位置,使其包围整个甲虫身体。
-
再次点击“编辑碰撞体”按钮以退出编辑模式。
你的盒式碰撞体应该看起来与以下截图中的类似:

接下来,我们将创建一个脚本,当黄瓜人角色与甲虫碰撞时,会触发“Die on Ground”动画。这将模拟黄瓜人踩到甲虫的场景。按照以下步骤操作:
-
在层级面板中选择“甲虫”角色。
-
在检查器面板中,点击“添加组件”按钮。
-
选择“新建脚本”。
-
将脚本命名为
BeetleNPC。 -
点击创建和添加按钮。
-
在项目视图中,选择收藏夹 | 所有脚本 | BeetleNPC。
-
双击
BeetleNPC脚本文件。 -
编辑脚本,使其与以下代码块匹配:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BeetleNPC : MonoBehaviour {
Animator animator;
// Use this for initialization
void Start () {
animator = GetComponent<Animator>();
}
// Collision Detection Test
void OnCollisionEnter(Collision col)
{
if (col.gameObject.CompareTag("Player"))
{
animator.Play("Die on Ground");
}
}
}
此代码检测黄瓜人和甲虫之间的碰撞。如果检测到碰撞,则播放Die on Ground动画。如以下截图所示,黄瓜人击败了黄瓜甲虫:

这个简短的测试展示了两个重要的东西,这将帮助我们进一步开发这个游戏:
-
在本节前面,你在动画控制器窗口中重命名了所有状态。你给状态起的名字是你在代码中要引用的名字。
-
由于我们使用的动画没有其他状态之间的过渡,黄瓜甲虫将保持在动画的最终位置,除非我们编写脚本进行其他操作。所以,如果我们有 100 只甲虫并且击败了它们,所有 100 只都会在游戏世界中保持背部朝上。
这是对我们的黄瓜甲虫进行的一个简单而成功的脚本测试。我们需要编写更多的脚本来管理游戏中的甲虫。首先,我们将对游戏世界进行一些修改。
为我们的黄瓜甲虫进行地形改造
我们的游戏世界目前非常大,非常适合我们构想的游戏。为了有效地演示如何编写黄瓜甲虫的脚本,我们将指定游戏中的一个沙盒区域,并将黄瓜甲虫限制在该区域内。我们还需要将一些樱花树移到那里。
我们将对游戏地形进行以下顺序修改:
-
指定一个沙盒区域
-
植种额外的樱花树
-
创建孵化点
指定一个沙盒区域
我们不一定想对我们的游戏地形进行重大修改。我们之前花了很多时间来规划、创建和微调地形。相反,我们将选择一个大型平坦区域作为沙盒。如果你没有大型平坦区域,你可以选择你的一个平坦区域并将其稍微扩大。
以下图像显示了相对于我们当前游戏世界的大小。图像底部中央的白色方框代表一个理想的大小:

现在我们知道了沙盒的位置,让我们为沙盒创建的新项目准备层次结构面板。只需在层次结构面板的空白区域右键单击并选择创建空对象。选择那个新的GameObject后,在检查器面板中将其重命名为Sandbox。
植种额外的樱花树
由于我们的沙盒是我们更大游戏环境的一个较小表示,我们需要一个或多个樱花树。正如你所回忆的,黄瓜人将从樱花树上收集樱桃,并使用它们对黄瓜甲虫进行远程攻击。
在第六章,为我们的游戏创建和导入 3D 对象中,我们导入了我们的樱桃树对象,并对其进行了修改以适应我们的游戏。现在,我们只需简单地将额外的樱桃树添加到我们的沙盒区域。您可以直接从项目 | 资产 | 预制体中将 CherryTreeCollider 拖动到场景视图中的沙盒区域。
接下来,您将想要使用场景视图放大沙盒区域,以确保您的树木不在地面之上或需要额外的调整。
一旦您的额外樱桃树就位,每个新的樱桃树都将可在层次结构面板中访问。单击每个新创建的樱桃树,并将其拖动到您之前创建的Sandbox对象。我们只使用Sandbox对象进行组织。
创建出生点
在沙盒区域内,选择三个区域作为出生点。您创建它们的位置由您自己决定。将它们放置在彼此等距的位置是一个很好的方法。为了使这些出生点明显,让我们创建一个出生点,然后复制它并将其放置在沙盒的各个位置。
创建出生点的步骤如下:
-
在层次结构面板中,右键单击沙盒,选择 3D 对象 | 圆柱体。
-
在检查器面板中,将圆柱体重命名为
SpawnPad1。 -
单击变换 | 缩放,并按以下方式更改
SpawnPad1的缩放比例:-
x =
3 -
y =
0.05 -
z =
3
-
-
通过将平台放置在黄瓜人下方来测试平台的缩放比例。它应该看起来与以下截图中的相似。进行调整,使您的平台与这里看到的一致:

-
选择
SpawnPad1后,在检查器视图中取消选中 Mesh Renderer | 接收阴影复选框。这将防止阴影投射到我们的出生点。这一步并不是非常重要,但有助于使我们的出生点看起来不那么有机,而且由于我们将在该平台上生成黄瓜人,它增加了一个很好的视觉效果。 -
使用变换工具,调整平台的位置,使其位于黄瓜人的脚底下方,正好或略高于草地水平。
-
选择
SpawnPad1后,单击检查器面板中材质 | 元素 0 | 缺失(材质)输入框右侧的小圆圈:

-
从选择材质对话框窗口中选择您喜欢的材质。
-
最后,在检查器面板中取消选中胶囊碰撞组件。我们不需要这个组件。
您现在有了第一个出生点。再复制两个,确保它们命名为SpawnPad1、SpawnPad2和SpawnPad3。在层次结构面板中,确保它们都在您的Sandbox对象中。
我们最后一个任务是将在场景视图中Sandbox区域中我们想要的位置放置三个出生点实例。
现在是保存您的场景和项目的好时机。
将黄瓜补丁添加到我们的地形中
在第六章,为我们的游戏创建和导入 3D 对象中,我们使用了平面来指定六个黄瓜田地区域。在本节中,我们将创建第七个黄瓜田地区域,并在沙盒区域中使用它。我们还将种植新的黄瓜田地区域内的黄瓜田地。
在沙盒中创建黄瓜田地区域
本节将指导你完成在沙盒中创建黄瓜田地区域所需的步骤。在层次结构面板中,你应该有一个包含各种尺寸黄瓜田地区域的Cucumber Patch Areas游戏对象,每个都是一个plane类型的 3D 对象。如果你没有这些对象,它们在下面的屏幕截图中显示,你可以通过重新访问第六章,为我们的游戏创建和导入 3D 对象,或者从出版商的配套网站上下载Starting-Chapter-08Unity 项目:

在我们的游戏中创建沙盒区域的步骤如下:
-
在层次结构面板中,展开
Cucumber Patch Areas游戏对象以显示六个平面。记住,我们创建了Cucumber Patch Areas游戏对象,以便作为我们方便的视觉组织文件夹。 -
双击每个平面(
CucumberPatchArea1、CucumberPatchArea2、CucumberPatchArea3、CucumberPatchArea4、CucumberPatchArea5和CucumberPatchArea6)。当你双击层次结构面板中的对象时,场景视图将聚焦于该对象。 -
识别哪个黄瓜田地区域已经在你的沙盒中。
-
在层次结构面板中,右键单击已识别的黄瓜田地区域并选择复制。
-
将复制的黄瓜田地区域重命名为
CucumberPatchAreaSandbox。 -
在层次结构面板中,将
CucumberPatchAreaSandbox拖动到Sandbox游戏对象下以使其成为其子对象。 -
选择
CucumberPatchAreaSandbox平面后,修改其形状以适应沙盒区域。使用变换工具来完成这一操作。 -
如果需要,修改沙盒的地面部分,以确保
CucumberPatchAreaSandbox范围内的地面是平坦的,并且可以接收黄瓜田地。
你可能需要使用提升/降低地面和平滑高度地面工具来确保带有CucumberPatchAreaSandbox的区域是平坦的。以下屏幕截图显示了橙色轮廓的CucumberPatchAreaSandbox区域、两棵樱桃树和用红色标记的三处出生点。较小的、更难看到的是黄瓜人和一只黄瓜甲虫。你的沙盒不需要看起来完全像这样,但应该具有以下屏幕截图所示相同的组件:

种植黄瓜田地
我们将使用我们的黄瓜块,CucumberPatch1和CucumberPatch2预制件,在沙盒中创建黄瓜块区域。这些区域将作为我们黄瓜的掩护。在本节中,我们将创建一个空的游戏对象来封装一系列黄瓜块。让我们开始吧:
-
在层次结构面板中右键单击沙盒并选择创建空对象。
-
将新创建的游戏对象重命名为
cucumberPatches。 -
将几个
CucumberPatch1预制件拖放到你的沙盒场景中。 -
将几个
CucumberPatch2预制件拖放到你的沙盒场景中。 -
在层次结构面板中,将所有新创建的黄瓜块移动到沙盒下的
cucumberPatches游戏对象。这样做只是为了保持层次结构面板的整洁。 -
在层次结构面板中展开
cucumberPatches游戏对象。 -
选择你添加的所有黄瓜块,并使用Ctrl + C 和 Ctrl + V 分别复制和粘贴它们。复制的结果将在层次结构面板中高亮显示,并在场景视图中可访问。
-
在场景视图中,使用变换工具重新定位复制的黄瓜块组。
-
重复步骤 7 和 8,直到你对你的沙盒满意为止。
以下截图展示了你的沙盒可能看起来像什么,其中包含新创建的黄瓜块。你的沙盒可能会有所不同,这是正常的:

我们现在的沙盒中有足够的黄瓜块。在下一节中,我们将专注于黄瓜。
将黄瓜添加到我们的地形中
在我们的游戏中,黄瓜将被用来给甲虫提供寻找和食用的东西。我们将在沙盒区域放置几个黄瓜,并在第十章“脚本我们的得分系统”中,在游戏过程中跟踪它们的数量。在本节中,我们将为我们的使用准备黄瓜,并在游戏中填充它们。
你之前已经下载了必要的黄瓜资源。如以下截图所示,Cucumber在Assets/Cucumber文件夹中是可访问的。你会注意到当我们选择Cucumber时,项目面板底部将Cucumber文件识别为.fbx文件:

.fbx文件是从 3D 建模软件导出的,这样我们就可以在游戏中使用它。当选择该资源时,检查器视图会显示导入设置。我们想要访问变换、网格渲染器和其他组件,因此我们将将其转换为预制件并做一些更改。以下是初始步骤:
-
将
Cucumber.fbx文件从 Assets | Cucumber 拖动到层次结构面板。 -
在层次结构面板中选择
Cucumber,将其拖动到项目面板中的Assets/Prefabs文件夹。 -
从层次结构面板中删除
Cucumber。 -
在项目面板中,选择 Assets | Prefabs | Cucumber。
当我们从层级面板将刚刚移动的Cucumber文件拖到Prefabs文件夹时,项目面板底部显示的以下截图将Cucumber识别为预制件:

现在,当我们查看检查器面板时,我们可以访问我们需要的组件,以进一步准备黄瓜在游戏中的使用。让我们对预制件做一些修改:
-
在检查器面板中,选择标签 | 添加标签。
-
在标签和图层界面中,点击视图右侧的加号图标。如下所示,加号图标下方是 CherryTree 标签,右侧:

-
输入名称黄瓜。
-
点击保存按钮。
-
在项目面板中选择资产 | 预制件 | 黄瓜预制件。
-
在检查器面板中,选择标签 | 黄瓜。
现在我们已经为cucumber预制件添加了标签,所有副本都将具有相同的标签。这个标签将帮助我们进行以下游戏操作:
-
轻松计算游戏中剩余的黄瓜数量
-
识别甲虫找到黄瓜的时间
我们需要对我们的cucumber预制件进行一项修改——添加一个碰撞器:
-
在项目面板中选择资产 | 预制件 | 黄瓜预制件。
-
在检查器面板中,点击添加组件按钮
-
选择物理 | 矩形碰撞器
接下来,我们可以将黄瓜添加到沙盒区域。首先,让我们采取与黄瓜田地组织相同的方法:
-
在层级面板中右键单击沙盒并选择创建空对象。
-
将新的
GameObject重命名为cucumbers。 -
将几个
Cucumber预制件拖到场景中,在你的沙盒里。你也可以将单个预制件拖到场景视图中,并使用快捷键Ctrl + D来复制它,这将复制所有当前选定的对象。 -
使用变换工具旋转、调整大小和重新定位黄瓜。
-
确保你的沙盒区域至少有 10 个黄瓜。
-
在层级面板中,将所有新创建的黄瓜移动到沙盒下的
cucumbers游戏对象中。这只是为了保持层级面板的整洁。
一旦你在游戏世界的沙盒区域中分散了黄瓜,你就可以开始编写黄瓜甲虫的脚本了。我们将在下一节中处理这个问题。
现在是保存你的场景和项目的好时机。
编写非玩家角色的脚本
在本节中,我们将编写必要的脚本以管理游戏中的黄瓜甲虫。具体来说,我们将编写以下脚本来完成以下任务:
-
甲虫巡逻
-
甲虫找到并吃黄瓜
-
甲虫攻击地面上的玩家
-
甲虫站立准备攻击
整理组织
由于我们将编写多个脚本,我们应该保持组织。我们可以在项目面板中点击收藏 | 所有脚本以获取项目中所有脚本的列表,但它们有很多,其中大部分我们不会为我们的游戏进行编辑。因此,让我们在项目面板中创建一个文件夹来组织我们的自定义脚本。以下是步骤:
-
在项目面板中,右键点击
Assets文件夹 -
选择创建 | 文件夹
-
将新文件夹命名为
Custom Scripts
现在我们已经有了自定义脚本的文件夹,让我们移动一些脚本:
-
在项目面板中,点击收藏 | 所有脚本。
-
滚动直到找到本章早期创建的
BeetleNPC脚本。 -
将
BeetleNPC脚本拖到我们的Custom Scripts文件夹。这将把BeetleNPC脚本移动到指定的文件夹。您仍然会在收藏 | 所有脚本中看到它,因为此功能显示所有脚本,无论它们的位置如何。 -
将
CameraFollower脚本移动到Custom Scripts文件夹。 -
将
PlayerController脚本移动到Custom Scripts文件夹。 -
将
PlayerMotor脚本移动到Custom Scripts文件夹。
当您完成移动脚本后,您的Cucumber Scripts文件夹应与以下截图相同:

在我们的游戏中,甲虫将具有以下行为:
-
搜索黄瓜(巡逻)
-
当找到黄瓜时吃掉它们
-
在地面上防御来自黄瓜人的攻击
-
站立以防御来自黄瓜人的攻击
下面的章节将向您展示如何编写这些行为。
甲虫巡逻
我们游戏中的甲虫将在沙盒区域内寻找黄瓜。在本节中,我们将编写一个脚本来管理它们的巡逻。让我们做一些准备工作。
我们将从为我们的Beetle预制体创建一个角色控制器开始。以下是步骤:
-
在项目面板中,选择资产 | 预制体,并点击
Beetle预制体 -
在检查器面板中,点击添加组件按钮
-
选择物理 | 角色控制器
确保您将角色控制器添加到Beetle预制体,而不是场景中的甲虫。
我们不需要对默认的角色控制器进行任何更改,但我们确实需要一个。
接下来,让我们简化操作,为甲虫创建一些临时的包含墙壁。您可以简单地添加 3D 立方体游戏对象,并使用变换工具将它们定位,以便它们与沙盒或沙盒的一部分相邻。您可以将墙壁放入名为Walls的空游戏对象中,并在层次结构面板中组织它们,使其位于Sandbox游戏对象内部。以下截图是一个示例:

好的,现在我们准备开始编写甲虫巡逻的脚本。要开始,在项目面板中右键点击 Assets | Custom Scripts 文件夹。选择 Create | C# Script,然后给脚本命名为 BeetlePatrol。这将在我们的 Custom Scripts 文件夹中为我们创建一个 C# 脚本。我们将从小块开始,从上到下逐步分析这个脚本。
这段代码的第一个部分简单地导入了 System.Collections、System.Collections.Generic 和 UnityEngine。该部分还包括我们的 BeetlePatrol 类头:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BeetlePatrol : MonoBehaviour {
在下一节中,我们提供了变量。第一个变量 isDie 是一个全局变量,我们将使用它来确定黄瓜甲虫是否应该停止巡逻。其余的变量是局部的。它们的使用在查看后续代码部分后将是显而易见的:
// Variables
public static bool isDie = false;
public float speed = 5;
public float directionChangeInterval = 1;
public float maxHeadingChange = 30;
Animator beetleAnim;
CharacterController controller;
float heading;
Vector3 targetRotation;
下一个代码部分是我们的 Start() 方法,它只在游戏开始时运行。这段代码设置了甲虫巡逻的初始旋转:
void Start () {
controller = GetComponent<CharacterController>();
beetleAnim = GetComponent<Animator> ();
// Set random initial rotation
heading = Random.Range(0, 360);
transform.eulerAngles = new Vector3(0, heading, 0);
StartCoroutine(NewHeading());
}
在以下代码中显示的我们的 Update() 方法是下一个部分。这段代码将在每个游戏帧中执行一次。在这里,您可以看到如果条件 isDie 是 false(或不是 true),则代码将被执行:
void Update () {
if (!isDie) {
transform.eulerAngles = Vector3.Slerp (transform.eulerAngles, targetRotation,
Time.deltaTime * directionChangeInterval);
var forward = transform.TransformDirection (Vector3.forward);
controller.SimpleMove (forward * speed);
}
}
这最后一段代码提供了两个方法。NewHeading() 和 NewHeadingRoutine() 方法计算甲虫移动的新方向:
IEnumerator NewHeading () {
while (true) {
NewHeadingRoutine();
yield return new WaitForSeconds(directionChangeInterval);
}
}
void NewHeadingRoutine () {
var floor = transform.eulerAngles.y - maxHeadingChange;
var ceil = transform.eulerAngles.y + maxHeadingChange;
heading = Random.Range(floor, ceil);
targetRotation = new Vector3(0, heading, 0);
}
} // this is the end of the Beetle Patrol class
保存您的脚本。接下来,我们需要将其与 Beetle 预制件关联。选择预制件后,在检查器面板中点击添加组件按钮。然后,选择 Scripts | Beetle Patrol。
您可以将多个甲虫拖入场景并测试游戏。您应该看到它们在您建造的墙壁内四处游荡。
甲虫找到并吃掉黄瓜
在本章的早期,我们创建了一个 BeetleNPC 脚本文件并将其附加到我们的 Beetle 预制件上。该脚本检测与黄瓜人的碰撞。在本节中,我们将修改该脚本,使其能够检测与黄瓜的碰撞。
让我们首先确保黄瓜被正确设置。检查在检查器面板中黄瓜的 Box Collider 组件是否被选中(勾选)。接下来,在场景的沙盒区域内复制几个黄瓜。您可以将它们放在甲虫附近,以便更容易进行测试。以下截图显示了最佳测试配置:

BeetleNPC 脚本需要一个新的变量和两个方法,其中一个将用作协程。让我们从新的变量开始。正如您在下面的代码片段中可以看到的,我们现在有一个第二个变量,cucumberToDestroy。我们将使用它来引用被甲虫吃掉的那个黄瓜:
Animator animator;
public GameObject cucumberToDestroy;
接下来,我们将添加一个类似于我们在本章之前创建的 OnCollissionEnter 的 OnTriggerEvent() 方法。如你所见,我们正在测试草莓虫是否与黄瓜相撞。当检测到这种情况时,将执行四行代码。第一行将 cucumberToDestroy 变量指向草莓虫相撞的具体黄瓜。下一行将 isEating 值设置为 true。我们将更新 BeetlePatrol 脚本来适应这一变化。第三条语句播放进食动画。最后一条语句调用 DestroyCucumber 函数,我们将在下一节中查看该函数:
void OnTriggerEnter(Collider theObject) {
if (theObject.gameObject.CompareTag ("Cucumber")) {
cucumberToDestroy = theObject.gameObject;
BeetlePatrol.isEating = true;
animator.Play ("Eating on Ground");
StartCoroutine ("DestroyCucumber");
}
}
对 BeetleNPC 脚本的最后修改是 DestroyCucumber() 函数。我们使用此函数来延迟黄瓜的销毁。这模拟了草莓虫吃黄瓜所需的时间。你可以根据需要更改 WaitForSecondsRealTime 参数。该参数代表现实世界中的秒数。一旦延迟结束,对象将被销毁,isEating 变量将被设置为 false:
IEnumerator DestroyCucumber() {
yield return new WaitForSecondsRealtime (4);
Destroy (cucumberToDestroy.gameObject);
BeetlePatrol.isEating = false;
}
我们需要对我们的 BeetlePatrol 脚本进行两项修改。首先,如以下代码所示,我们将添加新的 isEating 变量:
public static bool isDie, isEating = false;
我们对 BeetlePatrol 脚本的最后修改是更新条件语句,如下所示代码所示。现在,如果草莓虫正在死亡或进食,我们将停止巡逻:
void Update () {
if (!isDie && !isEating) {
transform.eulerAngles = Vector3.Slerp (transform.eulerAngles, targetRotation,
Time.deltaTime * directionChangeInterval);
var forward = transform.TransformDirection (Vector3.forward);
controller.SimpleMove (forward * speed);
}
}
草莓虫攻击地面的玩家
目前,当我们的黄瓜人与黄瓜虫相撞时,会播放死亡动画,但没有实现其他行为。在本节中,我们将修改必要的脚本,以便每次黄瓜人与黄瓜虫相撞时发生以下行为:
-
草莓虫面对黄瓜人
-
草莓虫在指定时间内攻击黄瓜人
-
草莓虫的死亡动画播放
-
草莓虫从游戏中移除
我们将在 OnCollisionEnter() 方法中使用以下三行代码来强制草莓虫在碰撞时面对黄瓜人。如以下代码所示,我们创建一个变量以便轻松引用黄瓜人,然后为黄瓜人的当前变换创建第二个变量。第三行代码告诉当前黄瓜虫面对黄瓜人:
var cm = GameObject.Find ("CucumberMan");
var tf = cm.transform;
this.gameObject.transform.LookAt (tf);
现在,我们只需编辑 OnCollisionEnter 方法,包括两个语句。第一个语句播放 Attacking on Ground 动画。第二个语句调用将销毁当前黄瓜虫的函数。以下是这两行代码:
animator.Play ("Attacking on Ground");
StartCoroutine ("DestroySelf");
对 BeetleNPC 脚本的最后修改是 DestroySelf() 函数。我们使用此函数来模拟当前黄瓜虫的战斗和生命结束。函数内部有三个语句。第一个语句模拟攻击时间。第二个语句播放 Die on Ground 动画。最后一行销毁游戏对象,即当前黄瓜虫:
IEnumerator DestroySelf() {
yield return new WaitForSecondsRealtime (4);
animator.Play ("Die on Ground");
Destroy (this.gameObject, 4);
}
我们需要对我们的BeetlePatrol脚本进行两项修改。首先,如以下代码所示,我们将添加新的isAttacking变量:
public static bool isDie, isEating, isAttacking =</span> false;
我们对BeetlePatrol脚本的最后一次修改是更新条件语句,如下所示。现在,如果甲虫正在死亡、进食或攻击,我们将停止巡逻:
void Update () {
if (!isDie && !isEating && !isAttacking)) {
transform.eulerAngles = Vector3.Slerp (transform.eulerAngles,
targetRotation, Time.deltaTime * directionChangeInterval);
var forward = transform.TransformDirection (Vector3.forward);
controller.SimpleMove (forward * speed);
}
}
我们将在第十章脚本化我们的得分系统中对脚本和行为进行进一步的修改。
甲虫站立以攻击
你会记得黄瓜人能够向黄瓜甲虫投掷樱桃。这是一种远程攻击,如果黄瓜甲虫开始在地面行走或奔跑以攻击黄瓜人,那么甲虫很可能会在到达黄瓜人之前死亡。
因此,如果甲虫被樱桃击中,我们希望发生以下情况:
-
甲虫面向黄瓜人
-
甲虫站立
-
甲虫在站立的同时向黄瓜人奔跑
-
甲虫在站立时攻击黄瓜人
如果你需要回顾动画的外观,可以查看动画。
我们将对BeetleNPC脚本进行一些重大修改。更新的脚本在以下代码中完整呈现,分为按顺序的几个部分,并附有说明。
本节展示了导入和类级别变量。你会注意到最后三个变量(cherryHit、smoothTime和smoothVelocity)是新的。我们将使用cherryHit来跟踪导致甲虫死亡的序列。其余两个变量将用于控制甲虫到达黄瓜人的速度和流畅度:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BeetleNPC : MonoBehaviour {
Animator animator;
public GameObject cucumberToDestroy;
public bool cherryHit = false;
public float smoothTime = 3.0f;
public Vector3 smoothVelocity = Vector3.zero;
没有对Start()方法进行修改:
void Start () {
animator = GetComponent<Animator>();
}
我们现在开始使用Update()方法。这是必要的,以便展示甲虫可以朝向黄瓜人移动的每一帧。你还可以看到我们在条件语句中使用了cherryHit变量:
void Update () {
if (cherryHit) {
var cm = GameObject.Find ("CucumberMan");
var tf = cm.transform;
this.gameObject.transform.LookAt (tf);
animator.Play ("Standing Run");
transform.position = Vector3.SmoothDamp (transform.position, tf.position,
ref smoothVelocity, smoothTime);
}
}
我们脚本的下一部分是OnCollisionEnter()方法。我们将之前在这个方法中的语句移动,以便它们被一个if语句封装。如果cheeryHit为false,则将执行原始代码,否则将执行else语句之后的两个代码行。我们看到那里我们触发了两个动画:
void OnCollisionEnter(Collision col) {
if (col.gameObject.CompareTag ("Player")) {
if (!cherryHit) {
BeetlePatrol.isAttacking = true;
var cm = GameObject.Find ("CucumberMan");
var tf = cm.transform;
this.gameObject.transform.LookAt (tf);
animator.Play ("Attacking on Ground");
StartCoroutine ("DestroySelfOnGround");
} else {
animator.Play ("Standing Attack");
StartCoroutine ("DestroySelfStanding");
}
}
}
下一部分代码是之前创建的用于处理与黄瓜碰撞的OnTriggerEnter()方法。从以下代码中可以看出,我们添加了一个else if语句来检查我们是否与一个标签为Cherry的gameObject发生了碰撞。当这个条件为true时,我们将isAttacking布尔变量设置为true,这样由BeetlePatrol脚本驱动的向前运动就会停止。我们还设置了cherryHit的值为true,并播放显示甲虫站立的动画:
void OnTriggerEnter(Collider theObject) {
if (theObject.gameObject.CompareTag ("Cucumber")) {
cucumberToDestroy = theObject.gameObject;
BeetlePatrol.isEating = true;
animator.Play ("Eating on Ground");
StartCoroutine ("DestroyCucumber");
} else if (theObject.gameObject.CompareTag ("Cherry")) {
BeetlePatrol.isAttacking = true;
cherryHit = true;
animator.Play ("Stand");
}
}
我们BeetleNPC脚本的最后部分包含三个与Destroy相关的函数。你已经熟悉DestroyCucumber()。我们将DestroySelf()函数重命名为DestroySelfOnGround(),并添加了新的DestroySelfStanding():
IEnumerator DestroyCucumber() {
yield return new WaitForSecondsRealtime (4);
Destroy (cucumberToDestroy.gameObject);
BeetlePatrol.isEating = false;
}
IEnumerator DestroySelfOnGround() {
yield return new WaitForSecondsRealtime (4);
animator.Play ("Die on Ground");
Destroy (this.gameObject, 4);
}
IEnumerator DestroySelfStanding() {
yield return new WaitForSecondsRealtime (4);
animator.Play ("Die Standing");
Destroy (this.gameObject, 4);
cherryHit = false;
}
} // End of BeetleNPC.cs
为了测试这个功能,我们需要在我们的场景中放置一些樱桃。首先,从发布者的网站上下载Cherries.unitypackage资产包。这个包包含一个已经设置好以在我们的游戏中工作的Cherry.prefab文件。它有一个Cherry标签和一个带有Is Trigger选中的Box Collider。
在第十章,脚本化我们的得分系统中,我们将为黄瓜人添加投掷樱桃的能力。现在,让我们在我们的沙盒中放置一些樱桃以进行测试。以下截图所示的一种方法是将樱桃围绕在甲虫周围。这将使我们的测试更容易、更快:

现在是保存你的场景和项目的好时机。
摘要
在本章中,我们专注于我们游戏中的非玩家角色——黄瓜甲虫。我们回顾了甲虫的 11 个动画,并对非玩家角色的动画控制器进行了修改。此外,我们还编写了控制非玩家角色的脚本。我们的脚本产生了几个甲虫行为:巡逻、消耗黄瓜、地面战斗,以及被樱桃击中时站立、奔跑和用后腿攻击。我们还向我们的游戏世界添加了黄瓜地、黄瓜和樱桃。
在第九章,添加抬头显示中,我们将设计、开发和整合一个抬头显示(HUD)到我们的游戏中。我们将创建文本和图形,提供得分、健康和额外信息的视觉指示,以帮助玩家在游戏过程中保持对局势的了解。
第九章:添加抬头显示
在上一章中,我们配置了非玩家角色,即黄瓜甲虫,并回顾了其 11 个动画。我们对非玩家角色的动画控制器进行了重大调整。我们还计划和编写了脚本以控制非玩家角色的游戏内行为,包括巡逻、吃黄瓜、地面战斗和后腿战斗。此外,我们还向游戏世界中添加了黄瓜地、黄瓜和樱桃。
在本章中,我们将设计、开发和集成我们的游戏中的抬头显示(HUD)。我们将使用画布来创建文本和图形,提供关于得分、生命值和额外信息的视觉指示,以帮助玩家在游戏过程中保持对局势的了解。我们还将使用 Unity 相机实现一个迷你地图。
在本章中,我们将涵盖以下内容:
-
设计我们的 HUD
-
使用画布
-
创建迷你地图
-
动态内容的脚本编写
设计我们的抬头显示
如您从第三章,“设计游戏”中记得的,我们将有六个屏幕组件:生命值、生命、得分、樱桃、黄瓜和黄瓜甲虫。现在,这个设计将修改以包括屏幕右下角的迷你地图:

现在我们将有七个组件组成我们的 HUD,让我们按区域组织来回顾每个组件:
-
左上角:
-
玩家生命值:这将由一个指示生命值的文本标签和一个与黄瓜人生命值相关的生命值表组成。
-
生命值:我们将有一个视觉指示,显示黄瓜人的剩余生命值。玩家开始时有三个生命值。
-
-
右上角:
-
得分:我们将添加一个文本标签来指示得分,然后在这里更新玩家的实际得分。
-
樱桃:黄瓜人拥有的樱桃数量将在这里更新并显示。
-
-
右下角:
- 迷你地图:这将是一个迷你地图,显示游戏地图的概述,并指示黄瓜甲虫的位置。
-
左下角:
-
黄瓜:我们将包括一个文本标签,以及游戏中剩余的黄瓜数量。
-
黄瓜甲虫:HUD 的这个最后区域将包含一个文本标签和游戏中剩余的黄瓜甲虫数量。
-
现在我们已经为我们的 HUD 有了明确的设计,我们准备创建它。在您进入下一节之前,您应该打开您的 Unity 游戏项目。或者,您可以从出版商的配套网站上下载可用的Starting-Chapter-09Unity 项目。
使用画布
在 Unity 中,我们可以使用画布作为 UI 组件的容器。画布是 GameObject,UI 组件是我们希望在游戏过程中显示在屏幕上的视觉组件。在本节中,我们将创建一个画布,然后添加并配置必要的 UI 组件,以匹配上一节中我们设计的 HUD。
添加画布
添加 UI 画布的步骤如下:
- 在层次面板的空白区域右键单击并选择 UI | Canvas。您可以在检查器面板中查看画布的详细信息。
你会在层次面板中注意到,当你创建画布时,Unity 创建了一个 EventSystem。此系统用于管理用户输入。
- 将新画布 GameObject 的名称从
Canvas更改为HUD_Canvas。为我们的 GameObject 使用描述性的名称被认为是良好的开发实践。不需要对任何画布属性进行其他更改。
添加健康条 UI 组件
接下来,我们将创建 UI 组件以显示黄瓜人的健康:
-
在层次面板中右键单击 HUD_Canvas GameObject 并选择 UI | Text。这将创建一个属于画布的文本对象。
-
将文本组件重命名为
Health_Label。 -
在层次面板中右键单击 HUD_Canvas GameObject 并选择 UI | Slider。这将创建一个属于画布的滑动对象。
-
将滑动组件重命名为
Health_Slider。
到目前为止,我们已经创建了一个包含文本和滑动组件的画布。接下来,我们将对画布上的 UI 组件进行定位,以匹配上一节中我们的 HUD 设计中的位置:
-
在层次面板中双击 HUD_Canvas。这将使场景视图中的画布聚焦,这将为您提供一个关于画布外观的概述。
-
在层次面板中双击 Health_Label。现在,您可以看到我们创建的文本和滑动组件。
-
缩小视图,直到您可以在场景视图中看到整个画布。
-
在场景视图中切换 2D 视图。这将使在画布上定位我们的 UI 组件变得容易得多:

-
确保在层次面板中选择 Health_Label。
-
在检查器面板中,将 Text (Script) 组件的 Text 值更改为
Health。请参阅以下截图以获取详细信息:

-
将 Health_Label 拖动到画布的左上角。
-
在 Text (Script) 组件中,进行以下更改,或选择您喜欢的其他设置。您可以在游戏模式中实验游戏,看看这将如何在游戏中看起来:
| 属性 | 值 |
|---|---|
| 字体 | Arial |
| 字体样式 | 加粗 |
| 字体大小 | 16 |
| 颜色 | 深蓝色 |
-
要么在检查器面板中,要么在场景视图中,调整包围 Health_Label UI 文本组件的矩形大小,以确保文本上方、下方或右侧没有过多的空白空间。
-
将 Health_Slider 移动到 Health_Label 文本立即右侧。
-
在检查器面板中,对 Slider (Script) 组件进行以下更改:
-
取消选择 Interactable 复选框
-
将最大值更改为
100 -
选择 Whole Numbers 复选框
-
-
在层次面板中,展开 Health_Slider 对象以显示以下下属部分:

-
在层次结构面板中,选择 HUD_Canvas | Health_Slider | 背景。
-
在检查器面板中,选择 Image (Script) | 颜色并将颜色更改为红色。这将成为我们的生命值计的背景。
-
在层次结构面板中,选择 HUD_Canvas | Health_Slider | Fill Area | Fill。
-
在检查器面板中,选择 Image (Script) | 颜色并将颜色更改为绿色。这将成为我们的生命值计的填充区域。
我们生命值计的最后两个步骤将是增加高度和移除滑块把手:
-
在层次结构面板中,选择 HUD_Canvas | Health_Slider。
-
在检查器面板中,选择矩形变换 | 高度并将高度更改为
30。 -
在层次结构面板中,选择 HUD_Canvas | Health_Slider | Handle Slide Area | Handle。
-
取消选择 Image (Script) 组件。这将从 UI 中移除滑块的把手。
-
在场景视图中,调整 Health_Label 和 Health_Slider,使它们在画布的左上角对齐。
您完成的工作应类似于以下来自游戏视图的截图:

在继续下一节之前,进行任何必要的调整。
创建剩余生命 UI 组件
在本节中,我们将在健康标签和计下方添加三个代表剩余生命的黄瓜人头像图像:
-
从出版商的网站上下载
cmLives.png文件 -
将该图像拖动到项目面板中的
Assets|Cucumber Man文件夹 -
选择导入的图像,并在检查器面板中,将纹理类型更改为 Sprite (2D and UI)
-
在层次结构面板中,右键单击
HUD_Canvas并选择 UI | Image -
在检查器面板中,点击 Image (Script) | Source Image 下的设置右侧的小圆圈
-
选择在步骤 2 中添加的
cmLives.png图像 -
在检查器面板中,将此图像重命名为
Life1 -
点击矩形变换 | 缩放并将 Life1 的 X、Y 和 Z 缩放比例更改为
0.4 -
在场景视图中,重新定位 Life1 图像,使其直接位于健康标签之前
-
在层次结构面板中,右键单击并复制 Life1 两次,并将副本重命名为
Life2和Life3 -
在场景视图中,重新定位三个图像,使它们按顺序排列并视觉上对齐
您完成的工作应类似于以下来自游戏视图的截图:

在继续下一节之前,进行任何必要的调整。
添加得分 UI 组件
我们游戏界面和画布的右上角将包含得分信息。本节演示如何创建必要的 UI 组件:
-
在层次结构面板中,右键单击 HUD_Canvas GameObject 并选择 UI | 文本。我们将使用此文本对象作为得分标签。
-
将文本组件重命名为
Score_Label。 -
在检查器面板中,将文本(脚本)组件的文本值更改为
Score。 -
在文本(脚本)组件中,进行以下更改或根据需要选择其他设置。您可以在游戏模式中实验游戏,看看这将如何在游戏中显示:
-
字体样式:粗体
-
字体大小:16
-
颜色:深蓝
-
-
要么在检查器面板中,要么在场景视图中,调整包围你的
Score_LabelUI 文本组件的矩形大小,以确保文本上方、下方或右侧没有过多的空白空间。 -
在层级面板中右键点击 HUD_Canvas GameObject 并选择 UI | 文本。我们将使用此文本对象来显示当前分数。
-
将文本组件重命名为
Score_Value。 -
在检查器面板中,将文本(脚本
)组件的文本值更改为000000000。这将帮助我们确定布局。 -
要么在检查器面板中,要么在场景视图中,调整包围你的
Score_ValueUI 文本组件的矩形大小,以确保文本上方、下方或右侧没有过多的空白空间。 -
对
Score_Value文本组件重复步骤 4。
现在您已经将两个与分数相关的 UI 文本组件添加到画布中,您就可以在屏幕上重新定位它们了。
-
在场景视图中,调整 Score_Label 和 Score_Value 文本对象,使它们的矩形变换大小足以包围文本值
-
最后,在场景视图中重新定位两个文本对象,使它们在画布的右上角对齐。
您完成的工作应类似于以下来自游戏视图的截图。

在进行下一节之前,进行任何必要的调整。
添加樱桃 UI 组件
在本节中,我们将添加樱桃的图像和用于显示 Cucumber Man 拥有的樱桃数量的文本对象。
我们将首先添加樱桃图像:
-
从出版商的网站上下载
Cherries.png文件 -
将该图像拖动到项目面板中的
Assets|Cherry文件夹 -
选择导入的图像,并在检查器面板中,将纹理类型更改为 Sprite(2D 和 UI)
-
在层级面板中,右键点击 HUD_Canvas 并选择 UI | 图像
-
在检查器面板中,点击 Image(脚本)| 源图像设置右侧的小圆圈
-
选择在步骤 2 中添加的
Cherries.png图像 -
在检查器面板中,将其重命名为
Cherries -
点击 矩形变换 | 缩放 并将樱桃的缩放比例更改为 x、y 和 z 的
0.4 -
在场景视图中,将 Cherries 图像直接放置在 Score_Value 文本对象的开头下方
接下来,我们将添加一个文本对象来显示樱桃数量:
-
在层级面板中右键点击 HUD_Canvas GameObject 并选择 UI | 文本。
-
将文本组件重命名为
Cherries_Count。 -
在检查器面板中,将文本(脚本)组件的文本值更改为
0000。这将帮助我们确定布局。 -
在文本(脚本)组件中,将字体样式更改为粗体,字体大小 更改为 16,颜色更改为红色,或者选择你喜欢的其他设置。
-
要么在检查器面板中,要么在场景视图中,调整包围你的
Cherries_CountUI 文本组件的矩形大小,以确保文本上方、下方或右侧没有过多的空白空间。 -
在场景视图中,将
Cherry_Count文本对象重新定位到Cherries图像对象的右侧。
你的最终作品应该看起来类似于以下游戏视图的截图:

在继续下一节之前,进行任何必要的调整。
添加黄瓜和黄瓜甲虫 UI 组件
在本节中,我们将向画布的左下角添加四个 UI 文本组件。这些文本组件将用于以下目的:
-
黄瓜标签
-
黄瓜计数值
-
黄瓜甲虫标签
-
黄瓜甲虫计数值
到目前为止,你应该已经熟悉了向画布添加 UI 文本组件,因此本节将简短。请随时查阅前面的部分以回顾涉及到的步骤:
-
为黄瓜标签创建一个文本对象:
-
在层次结构面板中右键单击 HUD_Canvas GameObject 并选择 UI | Text
-
将文本组件重命名为
Cucumbers_Label -
将文本(脚本)组件的文本值更改为 Cucumbers
-
在文本(脚本)组件中,将字体样式更改为粗体,字体大小 更改为 16,颜色更改为蓝色
-
要么在检查器面板中,要么在场景视图中,调整包围你的 Cucumbers_Label UI 文本组件的矩形大小,以确保文本上方、下方或右侧没有过多的空白空间
-
在场景视图中,将 Cucumbers_Label 文本对象重新定位,以反映本章早期提到的 HUD 设计
-
-
为黄瓜甲虫标签创建一个文本对象:
-
在层次结构面板中右键单击 HUD_Canvas | Cucumbers_Label GameObject 并选择复制
-
将复制的文本组件重命名为
Beetles_Label -
将文本(脚本)组件的文本值更改为
Cucumber Beetles
-
-
-
要么在 检查器 面板中,要么在 场景 视图中,调整包围你的 Beetles_Label UI 文本组件的矩形大小,以确保它足够宽,可以显示整个文本
-
在 场景 视图中,重新定位 Beetles_Label 文本对象,以反映本章早期提到的 HUD 设计
-
-
为黄瓜计数创建一个文本对象:
-
在层次结构面板中右键单击 HUD_Canvas | Cucumbers_Label GameObject 并选择复制
-
将复制的文本组件重命名为
Cucumber_Count -
将文本(脚本)组件的文本值更改为
0000 -
要么在检查器面板中,要么在场景视图中,调整包围你的 Cucumber_Count UI 文本组件的矩形大小,以确保文本上方、下方或右侧没有过多的空白空间
-
在文本(脚本)组件中,将颜色更改为红色
-
在场景视图中,重新定位 Cucumber_Count 文本对象以反映本章早期提到的 HUD 设计
-
-
创建一个用于 Cucumber Beetle 计数的文本对象:
-
在层次结构面板中右键单击 HUD_Canvas | Cucumber_Count GameObject 并选择复制
-
将复制的文本组件重命名为
Beetle_Count -
在场景视图中,重新定位 Beetle_Count 文本对象以反映本章早期提到的 HUD 设计
-
你的最终作品应该类似于以下从游戏视图中捕获的截图:

在继续下一节之前,进行任何必要的调整。
创建迷你图
在 第五章,灯光、相机和阴影 中,你学习了如何使用相机创建迷你图。在本节中,我们将为我们的游戏实现一个迷你图。这个迷你图将提供我们游戏沙盒区域的俯视图,中心位于你在 第八章,实现我们的非玩家角色 中创建的沙盒区域。玩家可以使用这个迷你图作为雷达来帮助在沙盒中找到 Cucumber Beetles。
首先,我们需要关闭场景 2D 切换。这将使场景视图回到 3D 模式。
下面是创建我们游戏中迷你图的步骤:
-
在层次结构面板的空白区域右键单击并选择相机
-
将
camera重命名为Camera_Minimap -
在层次结构面板中双击 Camera_Minimap 相机以在场景视图中聚焦于该对象
-
将变换 | 旋转 | X 值更改为
90,使其朝向地面 -
在场景视图中,使用变换工具定位相机,使其覆盖你的沙盒
-
你可以使用相机预览来帮助确定游戏过程中的可见内容
以下截图显示了一个覆盖整个沙盒区域并带有白色矩形墙壁的相机预览:

现在,Camera_MiniMap 相机已被添加到我们的场景中,我们需要确保它不会像我们的主相机那样被对待。以下是步骤:
-
在项目面板中,在 Assets 下,右键单击
Textures文件夹并选择创建 | 渲染纹理 -
将新的渲染纹理重命名为
Render_Texture_Minimap -
在层次结构面板中选择 Camera_Minimap 相机
-
在检查器面板中,单击 Camera | 目标纹理属性右侧的小圆圈
-
选择你创建的
Render_Texture_Minimap纹理
现在迷你图相机已正确配置。我们的下一步需要我们修改我们的 HUD_Canvas。我们将通过以下步骤来完成:
-
在层次结构面板中,将 Camera_Minimap 对象拖动到 HUD_Canvas 下,使其成为其子对象
-
在层次结构面板中右键单击 HUD_Canvas 并选择 UI | 原始图像
-
将新的原始图像 UI 组件重命名为
RawImage_Minimap -
在检查器面板中,单击 Raw Image (Script) | 纹理属性右侧的小圆圈
-
选择您创建的
Render_Texture_Minimap纹理 -
打开场景视图 2D 切换
-
在层次结构面板中双击
HUD_Canvas,然后放大以便可以看到画布的特写 -
将 RawImage_Minimap 重新定位,使其位于画布的右下角
-
将 Rect Transform | Scale 的 X、Y 和 Z 值更改为
2
您现在可以进入游戏模式测试您的游戏。您应该在屏幕的右下角看到迷你地图,如下面的截图所示:

当您努力完善游戏中的外观时,您可以使用这个迷你地图作为一个很好的起点。一些改进选项包括使迷你地图与游戏世界有更多的对比度,以及显示 Cucumber Beetles 的红点而不是只显示整个地形。
动态内容的脚本编写
现在您的 HUD 已经完成,是时候考虑如何更新 HUD 上的信息了。以下是我们 HUD 的以下元素需要脚本编写,以便在游戏过程中动态更新:
-
Cucumber Man 的生命值
-
Cucumber Man 剩余的生命
-
分数
-
Cucumber Man 的库存中樱桃的数量
-
黄瓜数量
-
Cucumber Beetle 的数量
在本节中,我们将为更新 HUD 信息打下基础。
编写黄瓜计数脚本
让我们从黄瓜计数开始:
-
在层次结构面板中,选择
HUD_Canvas | Cucumber_Count -
在检查器面板中,点击添加组件按钮
-
选择“新建脚本”并命名为
CucumberManager -
编辑脚本
在脚本打开的情况下,进行必要的修改以匹配提供的脚本,如下所示:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class CucumberManager : MonoBehaviour {
public int currentCucumberCount;
Text Cucumber_Count;
public GameObject[] cucumbers;
void Awake () {
Cucumber_Count = GetComponent<Text> ();
currentCucumberCount = 0;
}
void Update () {
cucumbers = GameObject.FindGameObjectsWithTag ("Cucumber");
Cucumber_Count.text = cucumbers.Length.ToString();
}
}
如您所见,我们正在使用UnityEngine.UI命名空间。在CucumberManager类中,我们声明了三个变量,在Awake()方法中初始化计数为零,然后在Update()方法中每帧计算黄瓜的数量,从而在屏幕上提供剩余黄瓜数量的更新。我们能够轻松编程的部分原因是我们将Cucumber标签分配给了所有的黄瓜。
编写甲虫计数脚本
如您所想象,甲虫计数的脚本将与我们用于计数黄瓜的方法非常相似。这次,我们将添加一个BeetleManager脚本到我们的Beetle_Count UI 组件中。以下是所需的脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class BeetleManager : MonoBehaviour {
public int currentBeetleCount;
Text Beetle_Count;
public GameObject[] beetles;
void Awake () {
Beetle_Count = GetComponent<Text> ();
currentBeetleCount = 0;
}
void Update () {
beetles = GameObject.FindGameObjectsWithTag ("Beetle");
Beetle_Count.text = beetles.Length.ToString();
}
}
此代码与我们的CucumberManager脚本非常相似。唯一的区别是 GameObject 标签和我们的变量名。
如果此脚本对您不起作用,请确保您的甲虫被分配了Beetle标签。
我们 HUD 的剩余脚本将在后续章节中完成。在第十章,脚本化我们的得分系统中,我们将为 Cucumber Man 的库存中的得分和樱桃数量编写脚本。在第十一章,脚本化胜利与失败中,我们将编写必要的脚本以更新健康滑块和 Cucumber Man 剩余的生命值。
请确保保存你的 Unity 场景和项目!
摘要
在本章中,我们设计、开发和整合了游戏中的 HUD。我们使用画布 GameObject 创建文本和图形,提供得分、生命值和其他信息,帮助玩家在游戏过程中保持对局势的了解。此外,我们还编写了脚本以更新 HUD 的关键组件。我们还使用 Unity 相机实现了迷你地图。
在第十章,脚本化我们的得分系统中,我们将设计、编写和实现我们游戏的得分系统。这包括为游戏 HUD 的关键屏幕组件提供逐帧更新。
第十章:编写我们的积分系统脚本
在上一章中,我们设计、开发和整合了游戏中的 抬头显示 (HUD)。我们使用画布 GameObject 创建文本和图形,提供关于积分、健康和额外信息的视觉指示,以帮助玩家在游戏过程中保持态势感知。此外,我们还编写了脚本以更新 HUD 的关键组件。我们还在游戏场景中使用第二个摄像头实现了迷你地图。
在本章中,我们将设计、编写脚本并实现游戏积分系统。这包括提供游戏 HUD 关键屏幕组件的逐帧更新。
在本章中,我们将编写以下脚本的脚本:
-
从树上收集樱桃
-
添加樱桃投掷功能
-
根据樱桃收集和战斗击中次数添加积分
从树上收集樱桃
在本节中,我们将对游戏对象和脚本功能进行必要的调整,以启用从樱桃树收集樱桃。更具体地说,我们将创建以下游戏玩法:
-
检测黄瓜人与樱桃树的碰撞
-
模拟樱桃收集
-
更新库存和 HUD 中的樱桃计数
在我们开始之前,你应该打开你的 Unity 游戏项目。或者,你也可以从出版商的配套网站上下载 Starting-Chapter-10 Unity 项目。
检测黄瓜人与樱桃树的碰撞
在本节中,我们将启用并测试黄瓜人与樱桃树之间的碰撞。我们的第一步是确保沙盒区域中的樱桃树都具有 CherryTree 标签。我们之前创建了标签名称,所以我们只需要将其应用到沙盒中的树上。以下是步骤:
-
在层次结构面板中,选择沙盒区域内的一个樱桃树
-
在检查器面板中,点击标签标签左侧的下拉框
-
选择樱桃树标签
-
对于沙盒区域中的每个樱桃树 GameObject,重复步骤 1 到 3
-
可选地,对于层次结构视图中的所有樱桃树 GameObject,重复步骤 1 到 3,而不仅仅是你的沙盒区域中的那些
接下来,我们将创建一个 CucumberManManager 脚本来处理与樱桃树的碰撞。以下是创建该脚本的步骤:
-
在层次结构面板中,选择 CumcuberMan 玩家角色
-
在检查器面板中,滚动到最底部并点击添加组件按钮
-
选择新建脚本
-
将脚本命名为
CucumberManManager -
点击创建和添加按钮
-
在项目面板中,点击收藏 | 所有脚本
-
将
CucumberManManager脚本拖到Assets|Custom Scripts文件夹 -
双击
CucumberManManager脚本来在编辑器中打开它 -
编辑脚本以匹配此处提供的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CucumberManManager : MonoBehaviour {
void OnTriggerEnter(Collider theObject) {
if (theObject.gameObject.CompareTag ("CherryTree")) {
// Do something
}
}
}
前面的脚本是一个 C# 脚本,包含一个 OnTriggerEnter() 方法。正如你所见,我们检查了与黄瓜人碰撞的对象的标签,以查看该游戏对象是否具有 CherryTree 标签。
接下来,我们需要在检测到碰撞时执行某些操作。我们将在下一节中处理这个问题。
模拟樱桃的收集
在本节中,我们将继续在CucumberManManager脚本上工作,以模拟樱桃的收集。
如果您尚未打开脚本,现在在编辑器中打开它。我们将按顺序审查更新的代码的五个部分。
如下所示,我们的第一部分导入了三个必要的命名空间。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CucumberManManager : MonoBehaviour {
public static int currentCherryCount;
public int tempCurrentCherryCount;
public bool collectingCherries;
代码的第四行是类声明语句。本节最后三行代码是该类的变量。以下是每个变量的简要描述:
-
currentCherryCount: 保持黄瓜人库存中的当前樱桃数量 -
tempCurrentCherryCount: 用于限制樱桃收集每秒最多一个 -
collectingCherries: 用于确定库存计数器是否应该激活
如下所示,我们的第二部分包含Awake()方法。此方法用于在游戏开始时初始化我们的变量:
void Awake () {
currentCherryCount = 0;
tempCurrentCherryCount = 0;
collectingCherries = false;
}
如下所示,我们的第三部分包含Update()方法。此方法每帧执行一次。我们在外层 if 语句中嵌套了一个 if/else 语句。外层语句检查collectingCherries布尔值是否为真。如果是,则评估内层 if/else 语句块。
内层 if/else 块检查tempCurrentCherryCount变量的值是否大于或等于60。如果是,则将currentCherryCount值增加一;否则,将tempCurrentCherryCount值增加一。Update()方法每帧调用一次,帧率可能不同。因此,我们实际上每 60 帧向黄瓜人的库存中添加一个樱桃:
void Update () {
if (collectingCherries) {
if (tempCurrentCherryCount >= 60) {
currentCherryCount = currentCherryCount + 1;
tempCurrentCherryCount = 0;
} else {
tempCurrentCherryCount = tempCurrentCherryCount + 1;
}
}
}
如下所示代码块中的第四部分包含我们在上一节中开始的OnTriggerEnter()方法。我们编辑了此方法,包括一个 if 语句,检查黄瓜人是否进入与樱桃树的碰撞。如果是,则将collectingCherries布尔变量设置为true,并将一个樱桃添加到库存中:
void OnTriggerEnter(Collider theObject) {
if (theObject.gameObject.CompareTag ("CherryTree")) {
collectingCherries = true;
currentCherryCount = currentCherryCount + 1;
}
}
如下所示,我们的第五部分包含OnTriggerExit()方法。当黄瓜人停止与樱桃树碰撞时,将触发此事件。当发生这种情况时,我们将collectingCherries布尔值设置为false:
void OnTriggerExit(Collider theObject) {
if (theObject.gameObject.CompareTag ("CherryTree")) {
collectingCherries = false;
}
}
} // end of CucumberManManager.cs
更新库存和 HUD 上的樱桃计数
我们现在已经建立了一个系统,可以根据黄瓜人与樱桃树的碰撞向黄瓜人的库存中添加樱桃。接下来,我们需要更新适当的 UI 文本组件以显示当前库存量。我们将在这个部分处理这个重要任务。
下面是创建该脚本的步骤:
-
在层次结构面板中,选择
HUD_Canvas|Cherries_CountUI 文本组件 -
在检查器面板中,滚动到最底部并点击“添加组件”按钮
-
选择“新建脚本”
-
将脚本命名为
CherryManager -
点击“创建并添加”按钮
-
在项目面板中,单击收藏夹 | 所有脚本
-
将
CherryManager脚本拖到Assets|Custom Scripts文件夹 -
双击
CherryManager脚本以在编辑器中打开它 -
编辑脚本,使其与这里提供的代码匹配
代码块后面的代码提供了对此代码的解释:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class CherryManager : MonoBehaviour {
Text Cherries_Count;
void Awake () {
Cherries_Count = GetComponent<Text> ();
}
void Update () {
Cherries_Count.text = CucumberManManager.currentCherryCount.ToString ();
}
}
代码的前四行导入适当的命名空间。
下一行代码是类声明:public class CherryManager : MonoBehaviour {。
接下来是单个类变量,一个名为 Cherries_Count 的 text 对象。
类中的第一个方法是 Awake() 方法。我们使用此方法创建对 Cherries_Count UI 文本组件的引用。
我们 CherryManager 类的最后一部分是 Update() 方法。该方法有一个用于将 currentCherryCount 从 int 转换为字符串并更新 HUD 的单个语句。
您可以玩测试游戏以验证功能。只需将黄瓜人导航到樱桃树旁,并观察樱桃库存增加。对我们来说,有一个收集樱桃的方法很重要,因为玩家可以按键盘上的 E 键扔出樱桃。我们将在下一节中编写该功能。
现在是保存您的场景和项目的绝佳时机。
添加樱桃投掷功能
在 第七章,实现我们的玩家角色中,我们展示了使用 E 键盘键的黄瓜人投掷动画。在本节中,我们将进行必要的更改,以便在按下 E 键盘键时在黄瓜人右手内部实例化一个樱桃,并允许它被发射。我们将检查黄瓜人是否至少有一个樱桃在库存中,这样我们就可以知道是否应该实例化樱桃。好的,让我们开始吧。
创建樱桃的位置
以下步骤演示了如何在黄瓜人右手创建樱桃的位置。
-
在层次结构面板中,展开
CucumberMan对象,直到看到Character1_RightHand。 -
右键单击
Character1_RightHand并选择创建空对象。 -
将新游戏对象重命名为
cherrySpot。这将是我们渲染樱桃的位置。
您的 CucumberMan 对象的层次结构应如下所示:

- 使用变换工具,将
cherrySpot游戏对象移动到黄瓜人右手内部。您的放置应类似于以下显示:

我们需要将 Rigidbody 添加到 Cherry 预制件中,以便在运行时实例化和投掷它。
-
在项目面板中,选择资产 | 樱桃 |
Cherry.Prefab -
在检查器面板中,单击添加组件按钮
-
选择物理 | 刚体
-
取消选中使用重力选项
-
展开刚体组件的约束部分
-
选择所有冻结位置和冻结旋转框
接下来,我们将把樱桃预制件添加到我们的场景中。
-
将樱桃预制件从项目面板拖动到层次结构面板,使其成为 cherrySpot 游戏对象的子对象。
-
在场景视图中,放大并调整 cherrySpot 的位置,使樱桃看起来像黄瓜人拿着它。建议的位置如下:

编写 CherryControl 脚本
接下来,我们将为樱桃预制件添加一个脚本,以支持根据黄瓜人手的位置创建和释放樱桃。
-
在项目面板中,选择资产 | 樱桃 |
Cherry.Prefab -
在检查器面板中,点击添加组件按钮
-
选择新建脚本并命名为 CherryControl
-
点击创建并添加按钮
-
在项目面板中,将新脚本拖动到
Assets|Custom Scripts文件夹 -
编辑脚本以匹配以下代码块
脚本的第一部分由命名空间导入语句和类声明组成:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CherryControl : MonoBehaviour {
下一个部分包含类变量。我们有一个Rigidbody,两个浮点数和一个GameObject:
public Rigidbody cherry;
public float throwDistance = 2000000000f;
public float time2Die = 4.0f;
GameObject cucumberHand;
这里显示的Update()方法检查是否按下了E键,以及黄瓜人是否至少有一个樱桃:
void Update () {
int count = CucumberManManager.currentCherryCount;
if (Input.GetKeyDown (KeyCode.E)) {
if (count >= 1) {
ThrowACherry ();
}
}
}
代码的最后部分是ThrowACherry()方法。如您所见,我们在黄瓜人手中克隆了樱桃,打开重力,释放约束,并使用AddForce()方法将其向前推。接下来,我们使用Destroy()方法在四秒后销毁克隆的樱桃。最后一条语句减少黄瓜人的樱桃库存:
public void ThrowACherry () {
Rigidbody cherryClone = (Rigidbody)Instantiate
(cherry, transform.position, transform.rotation);
cherryClone.useGravity = true;
cherryClone.constraints = RigidbodyConstraints.None;
cherryClone.AddForce(transform.forward * throwDistance);
Destroy (cherryClone.gameObject, time2Die);
CucumberManManager.currentCherryCount = CucumberManManager.currentCherryCount - 1;
}
}
根据樱桃收集和战斗击中添加积分
在本节中,我们将编辑适当的脚本,以便黄瓜人可以根据以下标准获得与樱桃收集和战斗击中相关的积分:
| 游戏内事件 | 积分 |
|---|---|
| 黄瓜人捡起樱桃 | + 5 |
| 黄瓜人用樱桃击中甲虫 | + 10 |
创建积分管理器脚本
在本节中,我们将创建和编辑一个脚本以管理我们的积分并在游戏 HUD 上显示它们。以下是步骤:
-
在层次结构面板中,选择 HUD_Canvas | Score_Value
-
在检查器面板中,点击添加组件按钮
-
选择新建脚本并命名为
PointsManager -
在项目面板中,点击收藏 | 所有脚本
-
将 PointsManager 脚本拖动到
Assets|Custom Scripts文件夹 -
双击 PointsManager 脚本以在编辑器中打开它
-
编辑脚本以匹配以下代码
代码的第一部分包含命名空间导入语句和PointsManager类声明:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PointsManager : MonoBehaviour {
脚本的下一部分声明了两个类变量;一个用于当前分数,另一个是显示游戏过程中分数的Text UI 组件的引用:
public static int currentScore;
Text score;
我们类中的第一个方法是Awake()方法。我们使用此方法来初始化我们的变量:
void Awake () {
score = GetComponent<Text> ();
currentScore = 0;
}
我们类中的最后一部分是Update()方法。在这里,我们有一个将当前分数转换为字符串并更新 HUD 的单一语句:
void Update () {
score.text = currentScore.ToString ();
}
}
为每次摘取的樱桃增加分数
在本节中,我们将编辑适当的脚本,为每次摘取的樱桃增加 5 分。以下是步骤:
-
在项目面板中,选择自定义脚本 | CucumberManManager
-
编辑脚本
-
添加以下类变量:public
PointsManager _ptsManager; -
在
Update()方法中的嵌套 if 语句内添加以下代码行:
_ptsManager = GameObject.Find ("Score_Value").GetComponent<PointsManager> ();
PointsManager.currentScore = PointsManager.currentScore + 5;
前两行代码创建了对PointsManager脚本的引用,并在第二行将currentScore增加了 5 分。
更新后的CucumberManManager脚本应如下所示:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CucumberManManager : MonoBehaviour {
public static int currentCherryCount;
public int tempCurrentCherryCount;
public bool collectingCherries;
public PointsManager _ptsManager;
void Awake () {
currentCherryCount = 0;
tempCurrentCherryCount = 0;
collectingCherries = false;
}
void Update () {
if (collectingCherries) {
if (tempCurrentCherryCount >= 60) {
currentCherryCount = currentCherryCount + 1;
tempCurrentCherryCount = 0;
_ptsManager = GameObject.Find
("Score_Value").GetComponent<PointsManager> ();
PointsManager.currentScore =
PointsManager.currentScore + 5;
} else {
tempCurrentCherryCount = tempCurrentCherryCount + 1;
}
}
}
void OnTriggerEnter(Collider theObject) {
if (theObject.gameObject.CompareTag ("CherryTree")) {
collectingCherries = true;
currentCherryCount = currentCherryCount + 1;
}
}
void OnTriggerExit(Collider theObject) {
if (theObject.gameObject.CompareTag ("CherryTree")) {
collectingCherries = false;
}
}
}
为用樱桃击中甲虫增加分数
我们最后一个与分数相关任务是更新 BeetleNPC 脚本,以便当樱桃击中黄瓜甲虫时添加适当的分数。以下是步骤:
-
在项目面板中,选择自定义脚本 | BeetleNPC
-
编辑脚本
-
添加以下类变量:
public PointsManager _ptsManager; -
在
OnTriggerEnter()方法中的嵌套 else if 语句内添加以下代码行:
_ptsManager = GameObject.Find ("Score_Value").GetComponent<PointsManager> ();
PointsManager.currentScore = PointsManager.currentScore + 10;
前两行代码创建了对PointsManager脚本的引用,并在第二行将currentScore增加了 10 分。
更新后的BeetleNPC脚本的OnTriggerEnter()方法应如下所示:
void OnTriggerEnter(Collider theObject) {
if (theObject.gameObject.CompareTag ("Cucumber")) {
cucumberToDestroy = theObject.gameObject;
BeetlePatrol.isEating = true;
animator.Play ("Eating on Ground");
StartCoroutine ("DestroyCucumber");
} else if (theObject.gameObject.CompareTag ("Cherry")) {
_ptsManager = GameObject.Find
("Score_Value").GetComponent<PointsManager> ();
PointsManager.currentScore = PointsManager.currentScore + 10;
BeetlePatrol.isAttacking = true;
cherryHit = true;
animator.Play ("Stand");
}
}
摘要
在本章中,我们设计、编写和实现了游戏分数系统。这包括为游戏 HUD 的关键屏幕组件提供逐帧更新。我们的脚本使收集樱桃树上的樱桃成为可能,为我们的 Cucumber Man 添加了樱桃投掷功能。我们还根据樱桃的收集和战斗击中添加了分数。
在第十一章,脚本编写胜利与失败中,我们将设计和编写游戏胜利和失败条件的脚本。这包括管理 Cucumber Man 的生命值、剩余生命和玩家角色的重生。
第十一章:编写胜利和失败脚本
在第十章,编写我们的得分系统中,我们设计、编写和实现了游戏的得分系统。我们对适当的 GameObject 进行了更改,并编写了几个脚本来管理得分系统。我们确保每帧游戏中的得分都更新到我们的抬头显示(HUD)上。此外,我们还增加了 Cucumber Man 从樱桃树上收集樱桃并将其用作对抗 Cucumber Beetles 武器的功能。
在本章中,我们将设计和编写游戏的胜利和失败条件。我们将更新在其他章节中创建的脚本,以管理 Cucumber Man 的健康状况,提供逐帧的屏幕更新,并在健康耗尽时确保玩家生命值丢失。我们将通过脚本管理剩余的生命值。我们还将设计和编写玩家角色的重生脚本。
具体来说,本章将涵盖以下内容:
-
设计失败和胜利条件
-
更新玩家的健康
-
实现胜利逻辑
-
实现游戏结束逻辑
-
更新剩余生命值的 HUD
-
编写玩家角色重生的脚本
设计胜利和失败条件
目前,我们的游戏没有明确的结束,也没有为玩家的行为实现任何奖励。在本节中,我们将设计游戏的胜利和失败条件,以便有一个明确的目标或获胜的方式。
在第三章,设计游戏中,我们确定了三种游戏结束条件:Cucumber Man 有两种结束条件以失败告终,而 Cucumber Beetles 只有一种结果会导致失败。
这里是我们游戏的胜利条件:
-
如果以下条件满足,Cucumber Beetles 获胜:
-
游戏中没有剩余的黄瓜
-
Cucumber Man 的生命值耗尽
-
-
如果以下条件满足,Cucumber Man 获胜:
- 游戏中没有剩余的 Cucumber Beetles
这些胜利和失败条件相当简单,只有一个赢家。如果 Cucumber Beetles 获胜,Cucumber Man 就会失败。
为了实现这些条件,我们需要通过脚本跟踪以下内容:
-
黄瓜的数量
-
Cucumber Beetles 的数量
-
Cucumber Man 剩余的生命值
在第十章,编写我们的得分系统中,我们编写了必要的脚本来跟踪黄瓜和 Cucumber Beetles。我们还确保 HUD 持续更新这些计数。对于玩家来说,了解他们在游戏中的表现感非常重要。除了得分之外,玩家还希望不断查看游戏中有多少黄瓜和 Cucumber Beetles。玩家还想知道 Cucumber Man 剩余的生命值。
在下一节中,我们将更新我们的游戏,以便更新生命值并开始使用玩家的健康条。这些将为玩家在游戏过程中提供所需的 HUD 视觉组件。
更新玩家的健康
在本节中,我们将完全实现玩家的健康系统。我们将让 Cucumber Man 从三个生命开始,每个生命都有满值的 100 健康点。我们将更新脚本,以便当 Cucumber Man 被黄瓜甲虫攻击时,它会失去健康。我们的方法是从 Cucumber Man 的健康中扣除每秒钟与黄瓜甲虫碰撞的分数。我们还将编写 HUD 健康条的脚本,并在下一节中开始这个任务。
在我们开始之前,你应该打开你的 Unity 游戏项目。或者,你也可以从出版商的配套网站上下载可用的 Starting-Chapter-11 Unity 项目。
编写健康条脚本
在本节中,我们将创建一个新的脚本并将其附加到我们的 HUD 健康条上。我们将编辑脚本并使用它来管理 Cucumber Man 的健康和 HUD 健康条的可见状态。
让我们先来回顾一下我们的健康条:
-
在层次结构面板中,选择
HUD_Canvas|Health_Slider。 -
在检查器面板中,查看滑块(脚本)组件。正如你在以下截图中所见,界面底部有一个带有滑块的值组件:

-
点击游戏视图标签,这样你就可以在不进入游戏模式的情况下看到 HUD。
-
在检查器面板中,拖动值滑块并观察健康条的功能。
你可以看到健康条已经可以正常工作;我们只需要给滑块添加一个脚本,以便它跟踪玩家的健康并在游戏过程中更新 HUD。以下是这些步骤:
-
在检查器面板中,点击添加组件按钮
-
选择新建脚本并命名为
HealthManager -
在项目面板中,点击收藏夹 | 所有脚本
-
将
HealthManager脚本拖到Assets|Custom Scripts文件夹 -
双击
HealthManager脚本来在编辑器中打开它 -
编辑脚本以匹配以下代码,代码的第一部分包含
import语句和HealthManager类声明:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HealthManager : MonoBehaviour {
下一段代码声明了我们的类变量。我们将使用 currentHealth 来保存 Cucumber Man 的最新健康值。我们将使用 healthBar 作为对滑块的引用:
public static int currentHealth;
public Slider healthBar;
我们的 HealthManager 类有三个方法;第一个是 Awake() 方法。在这个方法中的第一条语句获取了对 Slider 组件的引用。第二条语句将 currentHealth 设置为 100。这是 Cucumber Man 的最大健康起始点:
void Awake () {
healthBar = GetComponent<Slider> ();
currentHealth = 100;
}
我们的第二个方法是 ReduceHealth() 方法。这个方法将被其他脚本用来请求健康减少。正如你所见,第一条语句只是将 currentHealth 值减一。第二条语句更新屏幕上的滑块:
void ReduceHealth () {
currentHealth = currentHealth - 1;
healthBar.value = currentHealth;
}
这个类最后的 Update() 方法中有一句用于每帧更新滑块的语句。这导致滑块准确地显示玩家的健康值:
void Update () {
healthBar.value = currentHealth;
}
}
在下一节中,我们将修改适当的脚本,以便当黄瓜人被黄瓜甲虫咬时调用 ReduceHealth() 方法。
减少健康值
我们的 BeetleNPC 脚本已经检测到与黄瓜人的碰撞,因此我们可以简单地更新该脚本,在检测到碰撞时从黄瓜人的健康值中减去一点。我们将通过调用 HealthManager 脚本的 ReduceHealth() 方法来完成此操作。以下是步骤:
-
在项目面板中,选择 Assets | Custom Scripts 并双击
BeetleNPC脚本。 -
在脚本的类变量部分添加以下语句。这创建了一个我们可以用来引用
HealthManager类的变量:
public HealthManager _healthManager;
- 在第一个
if语句之后,在OnCollisionEnter()方法中添加以下两个语句:
_healthManager = GameObject.Find
("Health_Slider").GetComponent<HealthManager>();
_healthManager.ReduceHealth();
通过这两个语句,我们获得了对 Health_Slider 的 HealthManager 脚本的引用,然后调用 ReduceHealth() 方法。
现在,您可以测试您的游戏,并观察生命条随黄瓜甲虫开始攻击黄瓜人而变化。
实现胜利
在本节中,我们将实现黄瓜人的胜利条件。黄瓜人的唯一胜利条件是黄瓜甲虫的数量为零。我们的 BeetleManager 脚本已经提供了计数黄瓜甲虫的功能。如您所回忆的,这就是我们在我们的 HUD 上更新我们的黄瓜甲虫计数的方式。我们将对该脚本进行一些修改,并在本节中创建一个新的脚本。
让我们从创建一个屏幕文本组件开始,当黄瓜甲虫的数量达到零时显示“你赢了!”以下是步骤:
-
在层次结构面板的空白区域右键单击。
-
选择创建空对象。
-
在检查器面板中,将新的 GameObject 重命名为
EndofGame。我们将使用它作为胜利和失败文本标签的容器。 -
在层次结构面板中,将
EndofGameGameObject 拖动以使其从属我们的HUD_Canvas。 -
选择
EndofGameGameObject,在检查器面板中,选择 Transform 下拉菜单并单击重置。这将重置对象的变换。 -
在层次结构面板中,右键单击并选择 UI | Text。
-
将新的文本对象从
EndofGameGameObject 下属。 -
将新的文本对象重命名为
Victory。
下面的四个步骤用于在检查器面板中配置胜利文本对象:
-
将文本属性更改为
You Won! -
将字体样式设置为粗体
-
将字体大小增加到 24。
-
选择一个明亮的文本颜色
通过单击游戏选项卡或将游戏置于游戏模式,您可以看到新的胜利文本显示在屏幕中央。我们只想在玩家赢得游戏时显示该文本。让我们解决这个问题:
-
确保选择胜利文本组件
-
在检查器面板中,点击添加组件按钮
-
选择新建脚本,并将脚本命名为
VictoryManager -
在项目面板中,点击收藏夹 | 所有脚本
-
将
VictoryManager脚本拖到Assets|Custom Scripts文件夹 -
双击
VictoryManager脚本来在编辑器中打开它 -
编辑脚本,使其与以下代码匹配,代码的第一部分包含命名空间
import语句和VictoryManager类声明:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class VictoryManager : MonoBehaviour {
我们脚本的下一部分包含两个类变量声明和 Awake() 方法。在 Awake() 方法中,我们获取 Victory UI 对象的文本组件引用。我们还设置初始文本为空,这样就不会显示任何内容:
Text Victory;
int beetleCount;
void Awake () {
Victory = GetComponent<Text> ();
Victory.text = "";
}
我们脚本的最后一部分是 Update() 方法。在这里,我们将计数值设置为当前黄瓜甲虫的数量,然后测试计数值是否为零。如果 (count == 0) 条件为真,我们将在屏幕上显示胜利文本:
void Update () {
beetleCount = BeetleManager.currentBeetleCount;
if (beetleCount == 0) {
Victory.text = ("You won!");
}
}
}
我们接下来的任务是更新 BeetleManager 脚本。我们将对该脚本进行三项更改:
- 将
static修饰符添加到currentBeetleCount类变量。新的一行代码应该是:
public static int currentBeetleCount;
-
在
Awake()方法中,将currentBeetleCount = 0;改为currentBeetleCount = 1;。这将有助于确保游戏在开始时不会认为没有黄瓜甲虫。 -
在
Update()方法中添加以下语句作为最后的语句:currentBeetleCount = beetles.Length;。这将更新每个帧的currentBeetleCount变量。
现在你可以测试游戏了。杀死所有的黄瓜甲虫来测试你做的代码更改。如果某些东西没有正确工作或你收到错误,请参考以下更新的 BeetleManager 脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class BeetleManager : MonoBehaviour {
public static int currentBeetleCount;
Text Beetle_Count;
public GameObject[] beetles;
void Awake () {
Beetle_Count = GetComponent<Text> ();
currentBeetleCount = 1;
}
void Update () {
beetles = GameObject.FindGameObjectsWithTag ("Beetle");
Beetle_Count.text = beetles.Length.ToString();
currentBeetleCount = beetles.Length;
}
}
现在胜利条件已经实现,我们准备实现我们的失败条件。我们将在下一节中这样做。
实现失败
有两种游戏条件会导致玩家输掉游戏。第一个条件是游戏中没有剩余的黄瓜。第二个条件是如果所有三个生命都消失了。让我们分别看看这些失败条件。
基于剩余黄瓜数量实现脚本失败
我们的 CucumberManager 脚本已经跟踪了游戏中的黄瓜数量,所以我们只需要给该脚本的 currentCucumberCount 类变量添加静态修饰符,然后更新我们的 VictoryManager 脚本。以下是步骤。
- 编辑
CucumberManager脚本,使currentCucumberCount变量声明如下:
public static int currentCucumberCount;
-
在
Awake()方法中,将currentCucumberCount = 0;改为currentCucumberCount = 1;。这将有助于确保游戏在开始时不会认为没有黄瓜。 -
在
Update()方法的末尾添加以下语句,currentCucumberCount = cucumbers.Length;。这将保持计数器在每个帧中更新。
这些就是 CucumberManager 脚本所需的唯一更改。接下来,我们将编辑 VictoryManager 脚本:
-
通过添加
int cucumberCount;类变量来编辑VictoryManager脚本。 -
将以下代码行添加到
Update()方法的底部。这些行将不断检查是否没有剩余的黄瓜,并在计数等于零时显示“你输了!”文本:
cucumberCount = CucumberManager.currentCucumberCount;
if (cucumberCount == 0) {
Victory.text = ("You Lost!");
}
您现在可以测试这个失败条件。
您可以通过在游戏模式下从层次结构面板中删除黄瓜来加快测试速度。当您退出游戏模式时,所有已删除的对象将被恢复。
编写无生命剩余的失败脚本
我们将使用 CucumberManManager 脚本来跟踪剩余的 Cucumber Man 生命值。编辑该脚本并做出以下更改:
-
添加
public static int livesRemaining;类变量。我们将使用这个变量来跟踪剩余的生命值。 -
添加
public Animator anim;类变量。我们将使用它来播放 Cucumber Man 的死亡动画。 -
将
livesRemaining = 3;语句添加到Awake()方法的底部。 -
将以下
if语句块添加到Update()方法的底部:
if (livesRemaining == 0) {
anim = GetComponent<Animator> ();
anim.Play ("CM_Die");
}
如您在 Update() 方法的更改中看到的,我们只是检查 livesRemaining 的值,当没有生命剩余时,播放相应的死亡动画。
您可以通过以下步骤测试这个失败条件:
-
从
public static int livesRemaining语句中移除static修饰符 -
将游戏置于游戏模式
-
在层次结构面板中,点击
CucumberManGameObject -
在检查器面板中,滚动到 Cucumber Man Manager (Script) 组件
-
将剩余生命值改为
0(零)。以下截图提供了详细信息:

- 当您的测试完成时,将步骤 1 中的语句的
static修饰符放回
更新显示剩余生命值的 HUD
在上一节中,我们修改了 CucumberManManager 脚本来跟踪玩家剩余的生命值,当没有生命剩余时,播放相应的动画。在本节中,我们将继续修改 CucumberManManager 脚本来更新 HUD 中的剩余生命值。
我们只需要修改 CucumberManManager 脚本的 Update() 方法。下面提供了修改后的 Update() 方法及其更改说明:
void Update () {
if (collectingCherries) {
if (tempCurrentCherryCount ></span>= 60) {
currentCherryCount = currentCherryCount + 1;
tempCurrentCherryCount = 0;
_ptsManager = GameObject.Find
("Score_Value").GetComponent<PointsManager>();
PointsManager.currentScore =
PointsManager.currentScore + 5;
} else {
tempCurrentCherryCount = tempCurrentCherryCount + 1;
}
}
if (livesRemaining == 2) {
Destroy (GameObject.Find ("Life3"));
}
if (livesRemaining == 1) {
Destroy (GameObject.Find ("Life2"));
}
if (livesRemaining == 0) {
Destroy (GameObject.Find ("Life1"));
anim = GetComponent<Animator> ();
anim.Play ("CM_Die");
}
}
我们添加了条件语句来检查剩余的生命值。当剩下两个生命时,我们销毁第三个生命图像。当只剩下一个生命时,我们销毁第二个生命图像,当没有生命剩余时,我们销毁第一个生命图像。我们使用 Destroy() 方法来完成这个任务。
编写玩家角色复活脚本
在上一节中,我们修改了 CucumberManManager 脚本来跟踪剩余的生命数量,并在适当的时候销毁 UI 图像元素。在本节中,我们将修改该脚本,以便在生命丢失时完成以下操作:
-
播放死亡动画
-
在重生垫上重生玩家
让我们先修改 CucumberManManager 脚本:
- 添加以下类变量:
public Transform SpawnPad1;
public Transform SpawnPad2;
public Transform SpawnPad3;
-
在 Hierarchy 面板中,选择
CucumberManGameObject,然后在 Inspector 面板中滚动,直到找到 Cucumber Man Manager (Script) 组件。 -
将
SpawnPad1、SpawnPad2和SpawnPad3从 Hierarchy 视图中拖动到 Inspector 面板中指定的位置。以下为详细信息:

- 修改
Update()方法的底部部分,如下所示:
if (livesRemaining == 2) {
Destroy (GameObject.Find ("Life3"));
anim = GetComponent<Animator> ();
anim.Play ("CM_Die");
StartCoroutine ("ReSpawnCucumberMan");
}
if (livesRemaining == 1) {
Destroy (GameObject.Find ("Life2"));
anim = GetComponent<Animator> ();
anim.Play ("CM_Die");
StartCoroutine ("ReSpawnCucumberMan");
}
if (livesRemaining == 0) {
Destroy (GameObject.Find ("Life1"));
anim = GetComponent<Animator> ();
anim.Play ("CM_Die");
}
检查前面的代码可以发现,根据 livesRemaining 变量的值,有三种条件正在被检查。在每种情况下,适当的 UI 图像组件都会从 HUD 中移除,并且播放 CM_Die 动画。对于前两种情况(livesRemaining 等于两或一),我们有一个 StartCoroutine("RespawnCucumberMan"); 方法调用。我们将在下一节中编写该方法:
- 编写
ReSpawnCucumberMan()方法。在CucumberManManager类中的OnTriggerEnter()方法之后输入以下代码:
IEnumerator ReSpawnCucumberMan() {
int randomNumber = Random.Range (1, 4);
if (randomNumber == 1) {
yield return new WaitForSecondsRealtime (4);
this.transform.position = SpawnPad1.transform.position;
} else if (randomNumber == 2) {
yield return new WaitForSecondsRealtime (4);
this.transform.position = SpawnPad2.transform.position;
} else {
yield return new WaitForSecondsRealtime (4);
this.transform.position = SpawnPad3.transform.position;
}
anim.Play ("CM_Idle");
}
我们的 ReSpawnCucumberMan() 方法首先获取一个随机的 1、2 或 3。然后我们检查生成了哪个随机数,并相应地进行分支。我们有一个四秒的延迟,以便死亡动画完成。然后我们将 Cucumber Man 重生到与随机生成的数字相对应的重生垫上。最后,我们播放空闲动画。
摘要
在本章中,我们设计和编写了游戏的胜利和失败条件。我们更新了游戏脚本以管理 Cucumber Man 的健康,包括更新 HUD 的健康条。我们通过脚本实现了胜利和游戏结束的逻辑。我们还实现了剩余生命和屏幕上的生命指示器。最后,我们编写了玩家角色的重生脚本。
在下一章中,我们将计划和实现游戏中的音效和视觉效果,以增强整体游戏体验。具体来说,我们将为战斗系统中的关键事件添加音效,并使用 Unity 的粒子系统添加几个特殊效果,以增加游戏的可视吸引力。
第十二章:为我们的游戏添加音频和视觉效果
在第十一章“脚本化胜利与失败”中,我们设计和脚本化了游戏的胜利和失败条件。我们更新了游戏脚本以管理黄瓜人的健康,包括更新 HUD 的健康条。我们通过脚本实现了胜利和游戏结束的逻辑。我们还实现了剩余生命数,并启用了动态的屏幕指示器。最后,我们脚本化了玩家角色的随机重生。
在本章中,我们将计划和实现音频和视觉效果,以增强整体游戏体验。具体来说,我们将为我们的战斗系统中的关键事件添加音频,并使用 Unity 的粒子系统添加几个特殊效果,以增强游戏的外观。
具体来说,在本章中,我们将涵盖以下内容:
-
Unity 音频系统的概述
-
规划我们游戏的音频
-
实现我们游戏的音频
-
Unity 的灯光和阴影简介
-
Unity 特殊效果的概述
-
为我们的游戏添加视觉效果
探索 Unity 的音频系统
Unity 拥有令人印象深刻的音频功能。它支持 3D 空间音效,提供环绕声效果。这为我们的音频源提供了一个点和维度。Unity 还提供了广泛的混音和母带制作功能。
在本节中,我们将探讨 Unity 中音频的基础知识,并查看音频混音器。
Unity 音频基础
Unity 支持多种音频效果,包括 Duck Volume Audio Effect,它允许我们操纵音频信号的音量。
术语duck指的是使音频信号变弱。
我们可以在 Unity 中使用各种音频格式。您可以看到这里列出的四种格式:
-
音频交换文件格式(AIFF)
-
MP3
-
Ogg Vorbis(OGG)
-
WAV
如果您的音频资产使用的是这四种文件格式之外的格式,您可能可以使用免费的音频转换工具来更改文件格式。
当我们将音频文件导入 Unity 游戏时,该文件成为音频剪辑。您还可以通过使用计算机的麦克风在 Unity 内部创建音频剪辑。您甚至可以在游戏过程中通过脚本创建音频剪辑。
在 Unity 中,音频源附加到对象上。这些音频源发出或播放声音,而音频监听器组件则接收音频源发出的声音。你可以将音频监听器视为游戏世界中的玩家耳朵,听到他们附近的声音。通常,一个音频监听器组件会附加到主相机上,以便它拾取的声音与游戏视图中的显示相匹配。监听器也会附加到对象上。让我们通过一个例子来看看 GameObject、音频源和监听器之间的关系。
假设我们正在创建一个动物园模拟,并正在处理牛的区域。有一个牛的饲养区。牛通常很安静,但可能会在吃东西时很吵。牛将是我们的 GameObject,它将发出“咀嚼”音频剪辑。音频剪辑的 RAW 文件(即chewing.aiff)将被识别为附加到牛 GameObject 上的音频源组件。现在,假设有一个农民角色。我们可以将监听器附加到农民身上,它也是一个对象。这个监听器是一个脚本,如果它听到牛的声音,就会执行某些操作。也许,如果监听器捕捉到“咀嚼”声音,他们就会知道需要更多的干草。
下面的图解展示了各个组件之间的关系:

你将在本章的后面学习如何使用这些组件。
Unity 的音频混音器
Unity 的音频混音器为你提供了混合和主控音频源和效果的能力。要访问音频混音器,你从下拉菜单中选择“窗口”,然后选择“音频混音器”。
当你第一次打开音频混音器窗口时,你会看到你的项目中没有任何混音器。正如你在下面的截图中所见,窗口的右侧有一个加号图标:

点击该图标可以让你创建一个混音器。
当你在项目中有一个混音器时,音频混音器窗口在左侧显示了四个部分。如图所示,这些是混音器、快照、组和视图。你项目中的所有混音器都将列在这里。快照是一组你可以用于混音器的参数。音频混音器组可以用来在声音到达听众之前修改音频源声音。最后一部分是视图。视图只是混音器组的保存状态:

在音频混音器窗口的右侧,你看到我们项目中的一个混音器。当你点击添加按钮时,你会看到一个弹出窗口,如图所示,它显示了几个选项:

我们的游戏中不会使用这些选项,但如果你想在游戏中实现高级音频效果,它们值得探索。
规划我们游戏的音频
规划游戏的音频是游戏设计的重要部分。仅仅因为我们可以在音频方面做些什么,并不意味着我们应该这样做。过多的音频可能和不足的音频一样令人反感。
为黄瓜人游戏选择的音频旨在充分展示如何导入、配置和编写脚本以实现各种音频效果。为此,以下音频将在我们的黄瓜人游戏中实现:
-
需要声音的动画:
-
黄瓜人
-
跳跃
-
投掷
-
死亡
-
重生
-
-
黄瓜甲虫
-
吃
-
站立跑步
-
死亡
-
-
-
需要声音的事件:
-
玩家失败
-
玩家胜利
-
我们将在下一节进行实现。
实现我们游戏的音频
在本节中,我们将实现上一节中列出的音频要求。我们将导入、配置和编写脚本以完成实现。在开始之前,你应该打开你的 Unity 游戏项目。或者,你可以从出版商的配套网站上下载 Starting-Chapter-12 Unity 项目。
我们将分三个步骤实现我们游戏中的音频,每个步骤将在后续章节中处理:
-
导入音频资产
-
实现黄瓜甲虫音频
-
实现黄瓜人音频
导入音频资产
我们的第一项任务是导入音频资产到我们的游戏项目中。以下是步骤:
-
打开游戏项目。
-
在项目面板中,右键点击 Assets 并选择创建 | 文件夹。
-
将新文件夹命名为
Audio。 -
右键点击
Audio文件夹,选择导入包 | 自定义包。 -
导航到本书出版商网站上提供的
cucumber_man_audio.unitypackage文件。你应该会在你的 Unity 界面中看到以下截图弹出:

-
在导入 Unity 包对话框窗口中,确保所有音频文件都被选中,然后点击导入按钮。
-
在项目面板中,选择第一个音频文件。然后,在检查器面板中,使用界面播放音频剪辑。
-
对每个九个音频剪辑重复步骤 7。这将使你熟悉每个声音并确保它们可以在你的电脑上播放。
现在我们已经将音频资产添加到我们的游戏项目中,让我们回顾一下每个资产将如何被使用。以下表格将每个音频文件映射到相应的游戏内动画或事件:
| 游戏内动画/事件 | 相关游戏对象 | 音频资产 |
|---|---|---|
| 跳跃动画 | 黄瓜人 | jump.wav |
| 投掷动画 | 黄瓜人 | throw.wav |
| 死亡动画 | 黄瓜人 | cm_die.wav |
| 重生事件 | 黄瓜人 | respawn.wav |
| 吃动画 | 黄瓜甲虫 | eating.wav |
| 站立奔跑动画 | 黄瓜甲虫 | attack.wav |
| 地上死亡动画 | 黄瓜甲虫 | beetle_die.wav |
| 站立死亡动画 | 黄瓜甲虫 | beetle_die.wav |
| 玩家失败事件 | 黄瓜人 | game_over.wav |
| 玩家胜利事件 | 黄瓜人 | victory.wav |
实现黄瓜甲虫音频
在本节中,我们将配置黄瓜甲虫预制体,使其在黄瓜甲虫吃东西、站立时奔跑和死亡时支持音频。以下是步骤:
-
在项目面板的
Assets|Prefabs文件夹中选择甲虫预制体。如果你有多个预制体,请确保使用你在游戏中使用的那个。 -
在检查器面板中,滚动到最底部并点击添加组件按钮。
-
选择音频 | 音频源。
-
取消勾选“唤醒时播放”框。
通常,我们会将AudioClip分配给我们的音频源组件。由于我们的黄瓜甲虫将拥有多个音频剪辑,所以我们在这里不会分配一个。
我们下一步是编辑BeetleNPC脚本。打开该脚本文件并做出以下修改:
- 添加以下成员变量:
public AudioSource audioSource;
public AudioClip eating;
public AudioClip attack;
public AudioClip die;
- 将以下语句添加到
Start()方法中:
audioSource = GetComponent<AudioSource> ();
- 按照以下所示编辑
OnTriggerEnter()方法。您将看到两个audioSource.PlayOneShot()语句,每个语句对应一个eating和attack音频片段:
void OnTriggerEnter(Collider theObject) {
if (theObject.gameObject.CompareTag ("Cucumber")) {
cucumberToDestroy = theObject.gameObject;
BeetlePatrol.isEating = true;
animator.Play ("Eating on Ground");
audioSource.PlayOneShot (eating);
StartCoroutine ("DestroyCucumber");
} else if (theObject.gameObject.CompareTag ("Cherry")) {
_ptsManager = GameObject.Find
("Score_Value").GetComponent<PointsManager>();
PointsManager.currentScore = PointsManager.currentScore + 10;
BeetlePatrol.isAttacking = true;
cherryHit = true;
animator.Play ("Stand");
audioSource.PlayOneShot (attack);
}
}
- 按照以下所示编辑
DestroySelfOnGround()方法。在这里,您可以看到我们添加了audioSource.PlayOneShot(die)语句:
IEnumerator DestroySelfOnGround() {
yield return new WaitForSecondsRealtime (4);
animator.Play ("Die on Ground");
audioSource.PlayOneShot (die);
Destroy (this.gameObject, 4);
}
- 按照以下代码块所示编辑
DestroySelfStanding()方法。在这里,您可以看到我们添加了audioSource.PlayOneShot(die)语句:
IEnumerator DestroySelfStanding() {
yield return new WaitForSecondsRealtime (4);
animator.Play ("Die Standing");
audioSource.PlayOneShot (die);
Destroy (this.gameObject, 4);
cherryHit = false;
}
现在脚本任务完成后,我们需要将指定的音频片段分配给创建的变量:
-
在检查器面板中,滚动直到您看到 Beetle NPC (Script)组件。
-
将
eating音频片段从项目面板的Assets|Audio文件夹拖到 Beetle NPC (Script)组件的适当位置。 -
对
attack和beetle_die音频片段重复步骤 11。您的 Beetle NPC (Script)组件应如下所示:

剩下的就是您通过玩游戏来测试这个新功能。
实现 Cucumber Man 音频
在本节中,我们将配置 Cucumber Man 预制件,以便它在 Cucumber Beetles 进食、站立时奔跑以及死亡时支持音频。以下是步骤:
-
在层次结构面板中选择 Cucumber Man。
-
在检查器面板中,滚动到最底部并点击添加组件按钮。
-
选择音频 | 音频源。
-
取消勾选“在启动时播放”框。
通常,我们会将AudioClip分配给我们的音频源组件。由于我们的 Cucumber Man 将拥有多个音频片段,所以我们在这里不会分配一个。
我们下一步是编辑BeetleNPC脚本。打开该脚本文件并做出以下修改:
- 添加以下成员变量:
public AudioSource audioSource;
public AudioClip dying;
public AudioClip respawning;
public AudioClip gameOver;
- 创建一个如下所示的
Start()方法:
void Start () {
audioSource = GetComponent<AudioSource> ();
}
- 编辑
Update()方法,如图所示,使其包含三个audioSource.PlayOneShot()语句:
if (livesRemaining == 2) {
Destroy (GameObject.Find ("Life3"));
anim = GetComponent<Animator> ();
anim.Play ("CM_Die");
audioSource.PlayOneShot (dying);
StartCoroutine ("ReSpawnCucumberMan");
}
if (livesRemaining == 1) {
Destroy (GameObject.Find ("Life2"));
anim = GetComponent<Animator> ();
anim.Play ("CM_Die");
audioSource.PlayOneShot (dying);
StartCoroutine ("ReSpawnCucumberMan");
}
if (livesRemaining == 0) {
Destroy (GameObject.Find ("Life1"));
anim = GetComponent<Animator> ();
anim.Play ("CM_Die");
audioSource.PlayOneShot (gameOver);
}
- 按照以下所示编辑
ReSpawnCucumberMan()方法。您可以看到我们添加了audioSource.PlayOneShot()语句:
IEnumerator ReSpawnCucumberMan() {
int randomNumber = Random.Range (1, 4);
if (randomNumber == 1) {
yield return new WaitForSecondsRealtime (4);
this.transform.position = SpawnPad1.transform.position;
} else if (randomNumber == 2) {
yield return new WaitForSecondsRealtime (4);
this.transform.position = SpawnPad2.transform.position;
} else {
yield return new WaitForSecondsRealtime (4);
this.transform.position = SpawnPad3.transform.position;
}
audioSource.PlayOneShot (respawning);
anim.Play ("CM_Idle");
}
现在我们对CucumberManManager脚本文件的脚本更改完成后,我们需要将指定的音频片段分配给创建的变量。以下是步骤:
-
在检查器面板中,滚动直到您看到 Cucumber Man Manager (Script)组件。
-
将
cm_die音频片段从项目面板的Assets|Audio文件夹拖到 Cucumber Man Manager (Script)组件的适当位置。 -
对
respawn和game_over音频片段重复步骤 10。您的 Cucumber Man Manager (Script)组件应如下所示:

到目前为止,我们已经处理了死亡、重生和游戏结束的音频片段。接下来,我们将处理跳跃和投掷的音频片段:
-
打开
PlayerController脚本进行编辑。 -
添加以下成员变量:
public AudioSource audioSource;
public AudioClip jumping;
public AudioClip throwing;
- 将以下语句添加到
Start()方法中:
audioSource = GetComponent<AudioSource> ();
- 将以下语句添加到
Jump()方法的开始处:
audioSource.PlayOneShot (jumping);
- 将以下语句添加到
Throw()方法的开始处:
audioSource.PlayOneShot (throwing);
现在我们对PlayerController脚本文件的修改已完成,我们需要将指定的音频剪辑分配到我们创建的变量中。以下是步骤:
-
在检查器面板中,滚动直到你看到玩家控制器(脚本)组件。
-
将
jump音频剪辑从项目面板的Assets|Audio文件夹拖动到玩家控制器(脚本)组件的适当位置。 -
将
throw音频剪辑从项目面板的Assets|Audio文件夹拖动到玩家控制器(脚本)组件的适当位置。你的玩家控制器(脚本)组件应如下所示:

我们要实现的最后一个音频剪辑是胜利剪辑。我们将首先编辑VictoryManager脚本。打开该脚本文件并做出以下修改:
- 添加以下成员变量:
public AudioSource audioSource;
public AudioClip victory;
- 创建一个
Start()方法,如下所示:
void Start () {
audioSource = GetComponent<AudioSource> ();
}
- 编辑
Update()方法,如下所示。你会注意到我们只为胜利条件添加了音频剪辑播放,因为我们已经处理了失败条件:
void Update () {
beetleCount = BeetleManager.currentBeetleCount;
if (beetleCount == 0) {
Victory.text = ("You won!");
audioSource.PlayOneShot (victory);
}
cucumberCount = CucumberManager.currentCucumberCount;
if (cucumberCount == 0) {
Victory.text = ("You Lost!");
}
}
现在我们对VictoryManager脚本文件的修改已完成,我们需要将胜利音频剪辑分配到我们创建的变量中。以下是步骤:
-
在层次结构面板中,选择
HUD_Canvas|EndOfGame| 胜利。 -
在检查器面板中,滚动直到你看到胜利管理器(脚本)组件。
-
将
victory音频剪辑从项目面板的Assets|Audio文件夹拖动到胜利管理器(脚本)组件的适当位置。你的胜利管理器(脚本)组件应如下截图所示:

现在你已经准备好通过玩游戏来测试这个新功能。
这是一个保存你的场景和项目的绝佳时机。
Unity 的灯光和阴影简介
游戏中的光照非常重要,因为它允许 GameObject 被看到。如果我们游戏中没有灯光,游戏屏幕将会是完全黑色的。在 Unity 中,很容易将光照视为理所当然,因为我们创建新场景时,默认有一个主摄像机和一个方向光。
游戏世界中的阴影是另一个可以视为理所当然的组件,因为 Unity 关于阴影的默认设置通常对游戏来说已经足够。
在本节中,我们将探讨光源和阴影。
添加光源
在 Unity 中,灯光是 GameObject,并且有几种不同类型。以下是最常见的光源类型:
-
方向光
-
点光源
-
聚光灯
-
区域光源
让我们逐一查看这些灯光。
方向光
轴向光就像太阳。正如您可以从以下“检查器”面板中看到,除了变换的位置、旋转和缩放之外,还有几个可以调整的轴向光设置:

以下表格提供了关于方向光关键设置的详细信息。
| 设置 | 细节 |
|---|---|
| 颜色 | 您可以为灯光选择一种颜色。对于逼真的户外场景,您可能会选择浅黄色。 |
| 模式 | 您可以选择实时、烘焙或混合照明模式。当选择实时时,在游戏过程中每帧都会计算直接光线。这提供了逼真的游戏体验,并且是默认模式。 |
| 亮度 | 调整此选项以控制亮度。 |
| 阴影类型 | 这里有三个选项:软阴影、硬阴影和无阴影。软阴影产生柔和的边缘,避免了使用硬阴影产生的尖锐边缘阴影。正如您所预期的那样,软阴影比硬阴影更占用处理器资源。 |
| 实时阴影 | 此区域提供了对阴影的额外控制。 |
点光源
点光源就像没有灯罩的灯泡。实际上,它们是模拟小型、局部光源(例如台灯、壁灯或吊灯)的理想光源。正如您可以从以下“检查器”面板的屏幕截图中所见,您可以修改范围、颜色和亮度。您还可以选择实时、烘焙或混合渲染模式:

聚光灯
聚光灯就像手电筒,提供锥形照明。这种类型的灯光非常适合模拟手电筒、汽车前灯、飞机灯光、探照灯和聚光灯。正如您可以从以下“检查器”面板的屏幕截图中所见,有一个聚光角度属性。聚光灯也具有与其他类型灯光共有的属性。

面光源
面光源(仅烘焙)是在将图像烘焙到纹理时使用的。这种类型的灯光非常适合模拟从建筑物中发出的光线,例如从窗户中发出的光线。面光源也适用于 LED 体育场照明。以下屏幕截图显示的宽度和高度属性在方向光、点光源和聚光灯中不存在:

阴影
Unity 为我们提供了对游戏场景中阴影的强大控制。我们可以控制哪些对象投射阴影,哪些对象可以接收阴影,以及阴影的各种属性。Unity 中的阴影紧密地复制了现实世界中阴影的存在方式。正如本章前面所讨论的,有软阴影和硬阴影。
软阴影可以带来更高的真实感,但代价是额外的计算和处理。硬阴影在游戏中提供的真实感较低,但通常是可以接受的,且对处理器的需求较低。在游戏中,硬阴影的边缘通常较为锐利、块状。
发现 Unity 的特殊效果
Unity 在三个类别中具有强大的特殊效果能力:粒子系统、轨迹渲染器和线渲染器。这些选项可通过 GameObject | Effects 顶菜单访问。
通过检查器面板中的添加组件 | 效果选项,可以将效果组件添加到 GameObject 中。如以下截图所示,粒子系统、轨迹渲染器和线渲染器选项可通过此方法访问。还有其他选项,如镜头光晕和光环:

在本节中,我们将讨论粒子系统和轨迹渲染器效果。
粒子系统
粒子系统使用场景中大量的小 2D 图像或网格来模拟液体、烟雾、火焰、精灵尘埃、烟花和云等效果。基本概念是,一个简单的 2D 图像或网格可以大量使用,以创建强大且密集的视觉效果。这些 2D 图像或网格是粒子,它们共同构成了粒子系统。
粒子系统中的每个粒子都从特定点渲染,并具有有限的生命周期。这个生命周期有多长取决于你的实现,但通常只有几秒钟。
如以下检查器面板截图所示,有几个参数允许我们自定义粒子系统的行为。我们将在本章后面添加粒子系统到我们的游戏中:

当我们将粒子系统添加到场景中时,Unity 在场景视图的右下角提供了一个界面,即粒子效果面板。此界面允许我们播放、暂停和停止粒子模拟,同时观察模拟的变化,并添加额外的参数设置:

Unity 2018,在本书出版时处于测试版,支持粒子系统网格渲染的 GPU 实例化、支持轨道速度,并启用粒子发射器形状,允许纹理读取以进行遮罩和着色。
轨迹渲染器
轨迹渲染器是一种视觉效果,可以在 GameObject 移动时在其后面创建轨迹。经典的例子包括喷气式飞机的尾焰、汽车的排气和塔斯马尼亚魔鬼的视觉滑行(尘云)。以下检查器面板截图说明了可以调整以自定义轨迹渲染器的参数:

这里描述了主要的轨迹渲染器设置:
| 设置 | 描述 |
|---|---|
| 投影阴影 | 这里有四个选项:开启、关闭、双面和仅阴影。默认设置为开启。 |
| 接收阴影 | 这是一个开启/关闭切换。 |
| 材质 | 您可以选择粒子着色器作为材质并调整其大小。 |
| 时间 | 此设置定义了尾迹的长度。 |
| 自动销毁 | 您可以选择此选项,使尾迹游戏对象在闲置了设置在时间设置中的秒数后销毁。 |
为我们的游戏添加视觉效果
我们的游戏已经有一个方向光,如本章前面所讨论的,它就像太阳一样——从我们地形上方照射光线。黄瓜人、黄瓜甲虫、樱桃、樱桃树和黄瓜地的阴影可以在草地上看到。我们不需要对它们进行任何更改。
本节我们将进行的视觉效果更改包括:
-
为我们的樱桃树添加点光源
-
使用粒子系统添加特殊效果
为我们的樱桃树添加点光源
目前,黄瓜人可以在我们的沙盒区域内的樱桃树下行走。这些树非常密集,而且由于唯一的光源来自我们的方向光,所以对于黄瓜人来说太暗了,看不清他从树上摘的樱桃。这只是为了添加点光源而进行的模拟。因此,我们将使用以下步骤在我们的场景沙盒区域的樱桃树上添加点光源:
-
在场景视图中,导航到一个樱桃树并放大,以便您可以清楚地看到树的底部
-
使用顶部菜单,选择 GameObject | 光 | 点光源
-
将点光源重新定位,使其位于树干中心
-
在检查器面板中,选择一个如浅红色等颜色
-
在检查器面板中,将范围更改为
11 -
在检查器面板中,将强度增加到
30 -
将点光源重新定位,使其看起来类似于以下图像:

对您沙盒区域中的每一棵樱桃树重复步骤 1 至 7。在游戏模式下测试您的游戏以查看结果。
使用粒子系统添加特殊效果
在本节中,我们将向我们的孵化垫添加粒子系统,以便它们附加特殊效果。以下是步骤:
-
在场景视图中,导航到一个孵化垫并放大,以便您可以清楚地看到垫子。
-
使用顶部菜单,选择 GameObject | 效果 | 粒子系统。
-
将粒子系统重新定位,使其位于孵化垫的中心和底部。
-
在层次结构面板中,将粒子系统拖动到孵化垫下,使其成为其子对象。
-
在检查器面板中,点击变换组件中的设置齿轮并选择重置。这会将粒子系统的位置重置为孵化垫的变换。
-
在检查器面板中,选择一个与孵化垫的红色和地形以及樱桃树的绿色形成对比的颜色,例如蓝色。
-
在检查器面板中,将最大粒子数更改为
10,000。 -
在检查器面板中,将形状改为边缘形状。
-
在检查器面板中,将形状的半径增加到 1.5。
当完成时,你的粒子系统应该看起来类似于以下截图:

对你的沙盒区域中的每个出生点重复步骤 1 至 9。
摘要
在本章中,我们计划和实现了音频和视觉效果,以增强整体游戏体验。具体来说,我们在战斗系统的关键事件中添加了音频,并添加了照明和粒子特效。我们从 Unity 音频系统的概述开始,然后计划和实现了我们游戏的音频。然后转向 Unity 中灯光和阴影的介绍,并涵盖了选定的特效。最后,我们在樱桃树上添加了一个点光源,并在出生点添加了一个特效。
在第十三章,“优化我们的游戏以供部署”,你将学习如何诊断性能问题以及如何优化脚本和图形渲染。你还将学习如何将你的游戏部署到多个平台。
第十三章:优化我们的游戏以进行部署
在上一章中,我们计划和实现了音频和视觉效果到我们的游戏中,以增强整体游戏体验。具体来说,我们在战斗系统的关键事件中添加了音频,并添加了照明和粒子特效。我们从 Unity 音频系统的概述开始,然后计划和实现了我们的游戏音频。然后转向 Unity 中灯光和阴影的介绍,并涵盖了 Unity 中的选择特效。最后,我们在樱桃树上添加了一个点光源,并在出生点添加了一个特效。
本章有两个重点领域:优化和部署。在优化部分,你将学习如何诊断你的 Unity 游戏性能问题,以及如何优化脚本和图形渲染。在部署部分,你将了解 Unity 的构建过程,如何创建独立玩家,以及如何将你的游戏部署到多个平台。
具体来说,在本章中,我们将涵盖以下主题:
-
使用 Profiler 窗口
-
优化脚本
-
优化图形渲染
-
额外的优化
-
创建构建
使用 Profiler 窗口
Unity 有一个本机工具帮助我们检查游戏性能。这是一个 Profiler 工具,可以通过 Window | Profiler 顶级菜单选项访问。正如你在以下插图中所见,Profiler 窗口中有 13 个组件可用:

Profiler 组件
单个 Profiler 名称清楚地表明正在分析的性能度量。为了完全探索 Profiler 窗口,让我们按照以下步骤启动它:
-
打开你的 Unity 游戏项目。或者,你也可以从出版商的配套网站上下载可用的 Starting-Chapter-13 Unity 项目。
-
使用顶级菜单,选择 Window | Profiler。根据你的系统,这可以打开 Profiler 在新窗口或作为标签页。
-
在 Profiler 窗口或标签页中,使用添加 Profiler 按钮添加任何默认未加载的 Profiler。已加载的 Profiler 将以灰色显示,未加载的 Profiler 将以黑色显示。
-
将你的游戏置于游戏模式。
-
滚动 Profiler,并使用 Profiler 框右上角的x关闭提供少量或无数据的 Profiler。例如,网络操作和网络消息 Profiler 不适用于 Cucumber Man 游戏。你可以在 Profiler 窗口中保留以下 Profiler:
-
CPU 使用情况
-
GPU 使用情况
-
渲染
-
内存
-
音频
-
物理引擎
-
用户界面
-
全局照明
-
-
让你的游戏运行至少一分钟或更长时间,然后退出游戏。无论你在游戏运行时玩游戏还是只是坐下来观看瓜蚜虫寻找和吃黄瓜,你都可以在游戏停止后获得 Profiler 数据。
我们可以检查每个分析器,以帮助确定我们的游戏性能,并识别任何性能问题。让我们通过在分析器窗口中单击它来查看 GPU 使用分析器。当我们选择一个分析器时,详细的信息将显示在分析器窗口的底部部分。
通过审查提供详细信息的详细信息,例如此处显示的 GPU 使用信息,我们可以确定组件级别的性能。在以下示例中,我们可以看到我们的Camera.Renderer对 GPU 的绘制需求最大。我们还可以通过单击每个组件左侧的灰色三角形来进一步深入到更高的保真度:

充分利用分析器
分析器窗口有几个控件,分为界面的左侧、中间和右侧部分。正如您可以从以下屏幕截图中看到的那样,添加分析器下拉按钮消耗了分析器窗口工具栏的最左侧部分。我们可以使用该按钮向窗口添加额外的分析器:

分析器工具栏
界面的中间部分包含四个功能,如下详细说明:
-
记录:此按钮默认启用,用于记录或停止记录活动游戏的配置文件信息。当您在游戏的特定部分进行故障排除时,此按钮可以轻松地开始和停止记录。
-
深度分析:当启用此功能时,将分析所有脚本使用情况,包括函数调用。
-
分析器编辑器:此功能允许您切换分析器的分析。
-
编辑器:您可以使用此功能为分析器指定 IP 地址,并将日志数据发送。默认情况下,它将发送到编辑器。
分析器窗口工具栏的最右侧部分包括清除、加载和保存分析器信息的控件。如果您选择了一个帧,帧信息将显示出来。您还可以使用本节中的导航按钮遍历帧。
如果您在使用分析工具时注意到游戏性能明显受到影响,请不要感到惊讶。
优化脚本
Unity 游戏每秒运行多个帧,并且我们的大多数脚本在每个帧都会执行。即使是简单的游戏,如黄瓜人,也可能在每个帧运行多个脚本,导致计算机的 CPU 非常忙碌。目标是确保我们的脚本不会造成任何不必要的 CPU 负载。
我们的游戏有 145 个脚本,但其中大多数是标准资产包的一部分,并且没有分配给任何游戏对象。我们想要审查的脚本是我们放在Assets | Custom Scripts文件夹中的脚本。审查该文件夹显示,其中只有 14 个脚本:
-
BeetleManager
-
BeetleNPC
-
BeetlePatrol
-
CameraFollower
-
CherryControl
-
CherryManager
-
CucumberManager
-
CucumberManManager
-
HealthManager
-
PlayerController
-
PlayerMotor
-
PointsManager
-
ThrowCherry
-
VictoryManager
我们的目标是减少 CPU 需要执行的指令数量,尤其是在我们每帧处理多个执行并且每秒多个帧的情况下。在审查你的 Unity 脚本时,以下是一些需要检查的事项:
-
确保所有函数调用都是必要的。
-
根据需要,将函数调用移出
Update()方法。 -
根据需要,将语句移出循环以限制它们执行的次数。
-
只在需要时使用 NPC。我们的游戏相对简单,我们没有成群的黄瓜甲虫。你可能尝试一个实验,在你的游戏中放置 10,000 个黄瓜甲虫。由于每个甲虫都有与 AI 相关的代码,CPU 将会非常忙碌。
优化代码示例
以下脚本未经过优化。请审查脚本,看看可以采取哪些措施来优化它。然后,审查脚本之后提供的信息:
public class CucumberManager : MonoBehavior {
public static int currentCucumberCount;
Text Cucumber_Count;
public GameObject[] cucumbers;
void Update() {
Cucumber_Count = GetComponent<Text>();
currentCucumberCount = 1;
cucumbers = GameObject.FindGameObjectsWithTag("Cucumber");
Cucumber_Count.text = cucumbers.Length.ToString();
currentCucumberCount = cucumbers.Length;
}
}
希望你能够发现脚本中低效、未优化的部分。在上面的例子中,除了变量声明之外的所有语句都发生在Update()方法中。考虑以下脚本的优化版本:
public class CucumberManager : MonoBehavior {
public static int currentCucumberCount;
Text Cucumber_Count;
public GameObject[] cucumbers;
void Awake() {
Cucumber_Count = GetComponent<Text>();
currentCucumberCount = 1;
}
void Update() {
cucumbers = GameObject.FindGameObjectsWithTag("Cucumber");
Cucumber_Count.text = cucumbers.Length.ToString();
currentCucumberCount = cucumbers.Length;
}
}
在此脚本的优化版本中,GetComponent()方法调用和currentCucumberCount变量初始化被移动到Awake()方法。这些语句只需要运行一次。将它们放在Update()方法中会给 CPU 带来不必要的压力。
对于游戏的整体性能来说,拥有经过优化的脚本非常重要。在项目结束时检查脚本优化是一个好主意。理想情况下,你将在编写脚本时确保它们已经优化,而不是在之后进行审查。
优化图形渲染
当试图提高游戏性能时,应该探索以下三个图形渲染概念:遮挡消除、照明和网格渲染器。这些概念将在以下各节中介绍。
漏洞消除
在我们的 Unity 游戏中,摄像机是一个关键的游戏对象。它们允许玩家看到游戏环境。Unity 在游戏过程中勤奋地渲染摄像机视锥体内的对象。图形渲染可能代表一个巨大的性能问题。因此,我们特别关注摄像机的遮挡消除参数非常重要。当启用时,Unity 将不会渲染被遮挡的对象,或者摄像机看不到的对象。一个例子是建筑物内的对象。如果摄像机目前只能看到建筑物的外部墙壁,那么那些墙壁内的所有对象都看不到。因此,不渲染这些对象是有意义的。我们只想渲染绝对必要的对象,以确保我们的游戏具有流畅的游戏体验和没有延迟。
照明
当我们在 Unity 中创建场景时,我们有三种光照选项。我们可以使用实时动态光、烘焙光照方法或实时与烘焙的混合。与实时动态光照相比,烘焙光照可以使游戏性能更优,所以如果性能是关注点,尝试在可能的情况下使用烘焙光照。
区域光源与其他类型的光源不同,它们只能烘焙。这意味着在游戏过程中不会进行实时渲染。这样做的原因是在游戏开始之前完成所有关于区域光源的处理。如果在游戏中实时完成这些处理,很可能会产生足够的延迟。
网格渲染器
游戏对象的网格渲染器组件可以在检查器面板中查看。有多个可调整的设置,可以用来提高性能。
投影阴影设置可以设置为开启、关闭、双面或仅阴影。默认设置为开启,所以你应该为所有不需要投射阴影的对象禁用此设置。
接收阴影是一个切换选项,用于告诉 Unity 是否希望该对象接收阴影。正如你所预期的那样,这需要额外的处理来在游戏过程中显示。因此,如果你不需要对象接收阴影,取消选择此选项以获得更好的性能。
额外的优化
你可以在两个额外的区域优化你的游戏:级别细节和使用静态碰撞体。这两个概念将在接下来的章节中讨论。
级别细节
级别细节指的是在任意游戏对象上渲染的细节程度。多边形数量越多,游戏对象的细节级别就越高。为了减少渲染时间,你应该考虑哪些细节元素需要包含在 3D 模型中,哪些可以简单地包含在纹理中。
此外,还有级别细节(LOD)模型。这是在游戏中使用同一对象的多个模型,每个模型具有不同的细节级别。想象一下,玩家站在海岸上,望着地平线。距离 12 英里的船不需要与距离仅几码时的同样细节级别。
静态碰撞体
静态碰撞体是具有碰撞器但没有刚体的游戏对象。正如其名所示,这些游戏对象不会移动。因为物理引擎知道这些对象不会移动,可以预先计算以使游戏更高效。
因此,为了提高效率,你应该尽可能使用静态碰撞体。
创建构建
当您在自己的项目上工作时,为您的游戏创建构建并不复杂。您本质上确保您的资产(例如,3D 模型、纹理、动画和脚本)在您的计算机上,并使用 Unity 创建构建。当您在大型和分布式团队上工作时,这个过程要复杂得多。本节涵盖了单个开发者,其中所有资产都在一台计算机上。
理解 Unity 构建过程
Unity 的构建过程看起来相当简单。我们只需从顶部菜单中选择文件 | 构建 & 运行,就能得到很好的结果。Unity 实际上做了很多工作来创建构建。以下是 Unity 游戏引擎创建游戏构建的基本步骤:
-
生成一个空白构建副本的游戏
-
按顺序遍历场景列表,在将其集成到构建之前对其进行优化
-
计算并存储有关每个场景需要哪些资产的数据
构建过程中还有一些额外的方面我们应该注意。例如,如果我们将 EditorOnly 标签分配给一个游戏对象,它将不会集成到构建中。
一旦您完成了您的游戏,您可以从文件下拉菜单中选择构建 & 运行或构建设置选项:

构建设置
我们通过文件 | 构建 & 运行顶部菜单选项访问构建设置对话框窗口。当该界面打开时,显示如下,我们可以就我们的构建做出几个决定:

构建设置界面的顶部列出了将包含在构建中的场景。您可以使用添加打开场景按钮快速将这些场景添加到构建中。您还可以选择和取消选择场景,以确保您只包含构建所需的场景。
在构建设置界面的下一部分,您将选择一个平台。接下来的几个部分将涵盖每个平台选项。
PC、Mac 和 Linux 独立平台
对于这个平台组,您需要选择 Mac OS X、Windows 或 Linux。正如您可以从下面的截图中所见,每个操作系统选择都有自己的选项集:

对于 Mac OS X,您将决定这是一个开发构建还是不是。如果是,您将拥有自动连接分析器、脚本调试和仅脚本构建的附加选项。
对于 Windows,您将选择它是 32 位还是 64 位构建,并选择是否要复制 PDB 文件。您还将决定这是否是一个开发构建。如果是,您将拥有自动连接分析器、脚本调试和仅脚本构建的附加选项。
对于 Linux,您将选择它是 32 位、64 位还是通用构建。您还将决定这是否是一个开发版本。如果是,您将拥有额外的选项,包括自动连接分析器、脚本调试和仅脚本构建。最后,您将选择您的构建是否支持无头模式。
无头模式指的是不包含视觉元素的基于服务器的游戏。
iOS
当为 iOS 设备(iPad、iPad Pro、iPad Mini、iPhone、iPod Touch)开发时,您需要在您的计算机上安装 Xcode。开发 macOS 设备也需要 Xcode。
您可以在此处获取 Xcode 的最新版本:developer.apple.com/develop/。
使用构建设置界面,您可以识别您的 Xcode 版本并决定是否以发布或调试模式运行它。您可以决定是否希望您的 Xcode 项目直接从 Unity 编辑器的安装位置引用 Unity iOS 运行时库。这通过选择“链接 Unity 库”复选框来完成。您不应该在最终构建中使用此选项:

您还将决定这是否是一个开发版本。如果是,您将拥有额外的选项,包括自动连接分析器、脚本调试和仅脚本构建。
tvOS
如您在此处所见,tvOS 的选项与 iOS 相同:

Android
当为 Android 设备开发时,您可以从多个纹理压缩选项中进行选择。您还可以将 16 位、32 位或 32 位半分辨率识别为 ETC2 回退。您可以使用内部构建系统或 Gradle,Gradle 是 Android Studio 和其他地方使用的构建工具:

您还将决定这是否是一个开发版本。如果是,您将拥有额外的选项,包括自动连接分析器、脚本调试和仅脚本构建。
您可以在此处了解更多关于为 Android 设备开发的信息:developer.android.com/。
HTML 5/WebGL
当开发 HTML 5/Web GL 时,您将决定这是否是一个开发版本。如果是,您将拥有额外的选项,包括自动连接分析器和仅脚本构建:

当开发将在 Facebook 上发布的 Unity 游戏时,您将需要 Facebook SDK 和一个 App ID。对于目标平台,您可以选择 Gameroom(Windows)或 WebGL:

您还将决定这是否是一个开发版本。如果是,您将拥有额外的选项,包括自动连接分析器和仅脚本构建。
有关如何注册和配置 Facebook 应用的说明,请参阅以下 URL:developers.facebook.com/docs/apps/register。
Xbox One
Xbox One 玩家的访问通过 Microsoft ID@Xbox 计划处理。
访问以下链接了解如何为 Xbox One 开发游戏:www.xbox.com/en-US/developers。
PlayStation 4 和 PlayStation Vita
PlayStation 4 和 PlayStation Vita 玩家的访问通过 Dev NET 处理,需要 Unity Plus 或 Unity Pro 许可证。
玩家设置
使用 Edit | Project Settings | Player 菜单选项,你可以在检查器面板中访问 Unity 玩家设置。在这个上下文中,术语玩家并不指玩游戏的人,而是指运行游戏的软件。
如以下截图所示,有公司名称和游戏标题的数据字段。图标和光标也在此上传:

在 PlayerSettings 界面的一般设置区域下方有六个按钮。如图所示,每个平台都有一个按钮,Unity 可以为它生成玩家。这不包括 Xbox 或 PlayStation 玩家:

对于每个平台,都有一组玩家配置设置部分。这包括以下内容:
-
分辨率和展示
-
图标
-
启动图像
-
调试和崩溃报告
-
其他设置
并非每个设置部分都适用于所有平台类型。此外,其他设置内容根据你正在为哪个平台开发而有所不同。
如果你正在为多个平台开发,你需要审查每种平台类型的设置。
摘要
在本章中,我们专注于优化和部署。我们学习了如何诊断 Unity 游戏中的性能问题,以及如何优化脚本和图形渲染。我们还探讨了如何部署我们的 Unity 游戏,包括了解 Unity 构建过程、如何创建独立玩家以及如何将游戏部署到多个平台。
在第十四章“虚拟现实”中,我们将探讨 Unity 在虚拟现实方面的能力。我们将从虚拟现实的快速介绍开始,包括硬件要求,然后看看如何使用 Unity 游戏引擎创建虚拟现实游戏。我们还将查看 Unity 资产商店中可用的起始内容。
第十四章:虚拟现实
在上一章中,我们探讨了几个诊断 Unity 游戏的过程和技术,以帮助提高性能。我们通过优化脚本、垃圾回收和图形渲染来实现这一点。我们还探讨了如何将我们的 Unity 游戏部署到多个分发平台。
在本章中,我们将探讨 Unity 在虚拟现实方面的能力。我们将从虚拟现实的快速介绍开始,包括硬件要求,然后查看如何使用 Unity 游戏引擎创建虚拟现实游戏。这是一章额外内容,不需要您拥有昂贵的虚拟现实硬件。本章的目的是为您介绍虚拟现实及其与 Unity 游戏引擎的可能性。
具体来说,本章将涵盖以下内容:
-
欢迎来到虚拟现实
-
在 Unity 中启用虚拟现实
-
入门内容
欢迎来到虚拟现实
虚拟现实不是一个新术语,但最近它受到了极大的关注。3D 图形和立体镜的出现促进了今天虚拟现实的状态。以下图表说明了立体镜的基本概念以及它们如何创建虚拟 3D 图像:

如您所见,相同的图像从不同的角度呈现了两次。这产生了虚拟 3D 图像。视频也存在同样的概念。
从概念上讲,虚拟现实相当简单。它是一个合成且可信的虚拟环境或世界的创造。有两个基本组成部分:硬件和软件。硬件组件通常涉及一个头戴式设备,每个眼睛都有一个单独的镜头,每个镜头后面都有一个或多个显示屏。通常,我们是在头戴式设备中插入我们的手机,而不是显示屏。专业镜头用于将手机表面的光线聚焦到您的视网膜上。
要看到专业镜头的价值,请将您的手机举到离眼睛大约 2 英寸的距离。您的眼睛无法聚焦在屏幕上——它太近了。专业镜头使我们能够看到如此近的屏幕。
一些高级头戴式设备包括运动和位置跟踪硬件,以下是一些示例:
-
惯性测量单元(IMUs)用于高速运动跟踪
-
各种位置跟踪技术,例如使用摄像头、激光和磁场
软件组件包括用户与之交互的游戏或模拟软件。在虚拟现实游戏或模拟中创建该内容并将其实例化是虚拟现实的重头戏。
开发工具
可用的虚拟现实硬件和软件解决方案越来越多。在本书出版时,Unity 原生支持 Oculus、Gear VR、OpenVR 和 PlayStation VR。以下是对每个的简要描述。
Oculus
Oculus 是一个包含 Facebook 旗下的 Oculus VR 和虚拟现实头戴式设备 Oculus Rift 的统称。
你可以在以下官方网站上了解更多关于 Oculus 的信息:
-
硬件网站:
www.oculus.com/ -
开发者网站:
developer.oculus.com/
GearVR
GearVR 是一个将手机插入其中的头戴式设备。它需要以下之一:
-
三星 Galaxy S6
-
三星 Galaxy S6 Edge
-
三星 Galaxy S6 Edge+
-
三星 Galaxy S7
-
三星 Galaxy S7 Edge
-
三星 Galaxy S8
-
三星 Galaxy S8+
你可以在官方网站上了解更多关于 GearVR 的信息,www.samsung.com/global/galaxy/gear-vr/.
OpenVR
OpenVR 是一个包含运行时和应用程序编程接口(API)的软件开发工具包(SDK)。OpenVR 支持对多种 VR 硬件的通用访问。这种类型的访问消除了为特定虚拟现实硬件开发的需求。
你可以在 GitHub 网站上了解更多关于 OpenVR 的信息,github.com/ValveSoftware/openvr.
PlayStation VR
PlayStation 虚拟现实,正式称为 Morpheus 项目,是索尼互动娱乐为 PlayStation 硬件平台提供的 VR 解决方案。
你可以在以下官方网站上了解更多关于 PlayStation VR 的信息:
在 Unity 中启用虚拟现实
在 Unity 中启用虚拟现实包括两个基本步骤。过程从确保您已安装所需的 SDK 或运行时环境开始,下一步是配置 Unity 项目。我们将在本节中查看这两个步骤,然后是 Unity 技术推荐的硬件和软件。
必需的 SDKs
例如,如果你正在为 Oculus Rift 设备开发虚拟现实游戏,你将在 Oculus 开发者网站上获得以下可用的软件包 (developer.oculus.com/downloads/):
-
Oculus Utilities for Unity:这是开发虚拟现实游戏的核心包。由于 Unity 原生支持 Oculus Rift,因此此包是可选的。除了包含 Oculus
OVRPlugin之外,此包还包括预制件、脚本、示例场景和其他资产,以帮助您开始。 -
OculusAvatar SDK:如果您在虚拟现实游戏中实现手部存在感或使用触摸控制器,此 SDK 非常有用。您还可以使用此包将 Oculus Home 中创建的化身集成进来。
-
Oculus 平台 SDK:平台 SDK 用于将以下组件集成到您的虚拟现实游戏中:成就、云存储、应用内购买、匹配和语音聊天。此 SDK 包含一个示例应用程序,可以帮助您比从头开始开发这些功能更快地入门。
-
Unity 5 项目 Oculus 示例框架:此示例项目让您通过查看工作场景来学习。它还演示了虚拟现实功能,如准星、驾驶、第一人称移动和手部存在感。
-
立方体贴图查看器:此工具提供了一个虚拟现实立方体贴图查看应用程序。您可以使用它来预览提交到应用商店作为提交过程一部分的立方体贴图快照。
-
OS X 版 Oculus 远程监控工具:此客户端工具连接到在远程设备上运行的移动虚拟现实应用程序,用于捕获数据、显示数据和存储数据。据称,这种数据捕获、显示和存储的目的是为了性能评估和测试。
-
Windows 版 Oculus 远程监控工具:此工具类似于 OS X 版 Oculus 远程监控工具。
此外,还有几个音频包可供您使用。这些包包括:
-
Oculus Ambisonics Starter Pack
-
Oculus Audio Loudness Meter
-
Oculus Audio Pack 1
-
Windows 版 Oculus 音频分析器
-
Oculus Lipsync Unity
-
Oculus OVRVoiceMod for Unity 5
-
Oculus Spatializer DAW Mac
-
Oculus Spatializer DAW Windows
-
Oculus Spatializer FMOD
-
Oculus Spatializer Native
-
Oculus Spatializer Unity
-
Oculus Spatializer Wwise
配置您的 Unity 项目
一旦安装了所有必要的 SDK,您就可以创建一个新的 Unity 项目。您会像创建任何其他 Unity 项目一样这样做。在新项目打开后,您将选择“编辑”|“项目设置”|“玩家”。这将暴露出检查器面板中的 PlayerSettings。如图所示,有一个“其他设置”选项:

点击“其他设置”按钮会显示几个可切换的选项。要启用虚拟现实,您需要选中“虚拟现实支持”复选框。如图所示,开启该选项会显示虚拟现实 SDK:

Unity 技术推荐
在虚拟现实游戏中确保良好的游戏体验,拥有强大的硬件和最新的软件是关键组成部分。有一些关键考虑因素可以极大地影响用户体验。这些包括:
-
确保图形驱动程序是最新的
-
确保帧率与头戴式显示器的刷新率相匹配
-
更新了核心处理器
-
更新了显卡
-
确保您有足够的易失性内存
播放设备上的操作系统也很重要。以 Oculus 为例,以下是被支持的:
-
Windows 7、8、8.1 和 10
-
安卓操作系统 Lollipop 5.1 及更高版本
您可能已经注意到 Mac OS 没有被列出。在撰写本文时,Oculus 不再支持 OS X。这意味着您将需要使用 Windows PC 上的 Unity 来开发原生支持的虚拟现实游戏。
启动内容
在游戏行业,硬件制造商希望您购买他们的产品,软件工具制造商希望您使用他们的软件。并且,在硬件和软件方面,他们的创作者希望您开发游戏,以便人们可以玩。更具体地说,VR 头戴设备制造商希望尽可能多地销售头戴设备,因此他们通过提供免费工具和资产来鼓励开发者。这并不是一个邪恶的计划,而是一种硬件制造商和游戏开发者之间的共生关系。
由于商店的动态性质,Unity 资产商店的任何查看都是时间快照。在撰写本文时,有几个来自不同硬件制造商的免费 VR 相关资产。本节将回顾这些资产。
Oculus VR
Oculus VR 在 Unity 资产商店提供了一些免费资产,以支持您为 Oculus Rift 和 Samsung Gear VR 的开发。
Oculus 样式框架
第一个包是 Oculus 样式框架。它包含三个样本场景以及如何实现基本 VR 相关功能的指南。以下部分脚本列表让您了解您可以使用这个免费资产包发现的功能类型:
-
边界
-
摄像机架
-
调试头控制器
-
显示
-
可抓取
-
抓取器
-
触觉
-
触觉夹
-
输入
-
完成监听器
-
遮罩
-
平台菜单
-
重置方向
-
触摸板
-
追踪器
此资产包还包括动画、材质、网格、预制体、场景、着色器和纹理。此外,还有帮助文本文件。
使用这个资产包是启动您的虚拟现实探索的绝佳方式。
Oculus 立体阴影重投影样本
2017 年 8 月,Oculus 发布了 Oculus 立体阴影重投影样本资产包到 Unity 资产商店。此包提供了一种创新技术来优化 VR 渲染。该技术采用两步法。在第一步中,从一只眼睛渲染的像素被重新投影到另一只眼睛。然后,在第二步渲染中,填补空缺。
Oculus 立体阴影重投影样本资产包包含一个教学文本文件以及所有必要的内容,如材质、预制体、着色器、脚本、纹理以及场景。
Oculus VR 报告称,这种重投影方法每帧可节省超过 20%的 GPU 成本。您的结果可能会有所不同,但在这个规模上优化纯粹的可能性使得检查这个资产包成为必须。
Oculus 集成
Oculus 集成资产包为 Oculus Rift、Gear VR 和 Touch 提供了脚本示例。这些示例包括:
-
输入和触觉
-
分层渲染
-
平台:
-
成就
-
云存储
-
权限检查
-
身份
-
应用内购买
-
键盘
-
排行榜
-
匹配
-
对等网络
-
房间
-
IP 语音(VoIP)
-
-
渲染音频:
-
Reverb 的房间模型设置
-
早期反射的房间模型设置
-
空间化微调
-
-
渲染化身:
-
定制外观
-
社交存在
-
触摸和手建模
-
-
社交渲染
-
房间规模
-
跟踪
这是 Oculus 提供的庞大样本列表,使我们的虚拟现实游戏开发更容易,并有助于确保我们的成功。
Vive 软件
Vive 软件在 Unity Asset Store 中提供了一些免费资产,以支持您为 HTC Vive 的开发。
Vive 立体渲染工具包
Vive 立体渲染工具包包括一套可重复使用的游戏资产,可用于创建立体渲染效果。此工具包与 Unity 的本地 VR 渲染兼容。
立体渲染效果包括以下内容:
-
基本示例
-
回调示例
-
镜像示例
-
传送门门示例
-
渲染器创建示例
与立体渲染效果相关的网格、材料、纹理、着色器和脚本包含在资产包中。
Vive 输入实用工具
Vive 输入实用工具是一个插件,它为您提供了访问 Vive 设备状态(包括 Vive Tracker)的权限。该插件具有以下功能:
-
碰撞事件
-
指针 3D
-
姿态追踪器
-
Vive 输入实用工具
-
2D 拖放
-
3D 拖放
-
传送
-
该插件还包含材料、预制体、脚本、动画、模型、精灵和着色器,以支持资产包中的示例。此包还包括几个教程。
Vive 媒体解码器
Vive 媒体解码器插件可用于在 Windows 上支持流媒体。该插件支持 Windows 7 和 DirectX 11。此插件的两个主要功能是:
-
高性能解码
-
视频流
NVIDIA
NVIDIA 是我们游戏电脑中使用的 GPU 的主要制造商。他们在 Unity Asset Store 中有几个免费资产,以帮助展示他们 GPU 的强大功能。其中两个免费资产专门针对 VR,在本节中进行了介绍。
NVIDIA VRWorks
此资产包包含基于文本的指南和用于创建以下内容的脚本和着色器:
-
镜头匹配着色
-
多分辨率着色
-
单次传递立体
-
VR SLI
使用此包,您必须具备以下条件:
-
Windows PC
-
DX11
-
NVIDIA GeForce 9 或更高版本
-
VS 2015 重新分发
-
Unity 2017.1.0b6 或更高版本
NVIDIA VR 样本
此包有助于展示现代 GPU 的功能,并集成了 VRWorks 包。包含在此资产包中的示例包括:
-
抗锯齿
-
花瓣和闪光
-
模糊
-
摄像机运动
-
色彩校正
-
对比度
-
停止着色
-
景深
-
边缘检测
-
鱼眼
-
全局雾效
-
灰度
-
图像效果
-
动态模糊
-
噪音和颗粒
-
噪音和划痕
-
后期效果
-
四边形
-
屏幕叠加
-
屏幕空间环境遮挡和遮挡
-
褐色调
-
日光束
-
虚化
-
色调映射
-
三角形
-
旋转
-
暗角和色差
-
漩涡
支持示例的着色器、纹理、模型、场景、动画、控制器、网格、音频、字体、材质、预制体和脚本都包含在资产包中。
Unity Technologies
Unity Technologies,这家公司制作了 Unity 并拥有资产商店,有一个强大的 VR 示例资产包。它包含一个菜单和四个迷你游戏,可以帮助你开始为 Oculus DK2 和 GearVR 创建虚拟现实游戏。
下面是资产包中包含的基本内容:
-
漂浮世界空间 GUI
-
静态世界空间 GUI
-
飞行器
-
迷宫
-
射击游戏
正如你所预期,所有相关的着色器、音频、字体、材质、纹理、动画、控制器、混合器、网格、模型、预制体、场景和脚本都包含在内。
摘要
在本章中,我们探讨了 Unity 在虚拟现实方面的能力。我们从一个关于虚拟现实的快速介绍开始,包括硬件要求,然后探讨了如何使用 Unity 游戏引擎创建虚拟现实游戏。我们还查看了一些来自 Unity 资产商店的可用起始内容。
阅读完这本书后,你将能够使用 Unity 创建自己的动态游戏,并且对制作虚拟现实游戏所需的内容有一个大致的了解。你正处于一个良好的起点:虚拟现实仍然是一个非常年轻的技术。例如,为了获得高质量的虚拟现实体验,人眼大约需要每只眼睛 16,000 x 16,000 像素,但当前最好的 VR 显示器提供的分辨率最高为 1080 x 780。因此,我们在这一技术上还有很长的路要走,其未来前景广阔。















浙公网安备 33010602011771号