作为一名软件架构师,我已经编写了很多系统,反向工程本机代码恶意软件,并且通常会搞清楚代码方面的事情。 但是当说起制作游戏,我还有点搞不清楚从哪里开始讲起。 我曾早期在 Windows 工作时,做了一些本机代码图形编程,但那不是什么有趣的经历。 后来,我开始专研 DirectX 开发,但我意识到,虽然它非常强大,但似乎我要做的事所需的代码非常多。
然后,有一天,我决定尝试使用 Unity,我也看到了它确实可以做一些令人惊讶的事情。 这篇文章在四部分系列当中作为首篇,内容囊括了 Unity 的基础及架构方面的知识。 我将演示如何创建 2D 和 3D 游戏,最后演示如何构建 Windows 平台。
Unity 是什么
Unity 是一个 2D/3D 引擎和框架,为您提供设计 2D、2.5D 和 3D 游戏或应用场景的系统。 我之所以说是游戏和应用程序,是因为我看到的不只有游戏,还有训练模拟器、第一响应者应用程序,以及使用 Unity 开发需要与 2D/3D 空间交互的其他以业务为核心的应用程序。 Unity 凭借的不仅有代码,还有可视化组件,让您能够与它们进行交互,并将它们导出到各个主要的移动平台,其中很多都是免费的。 (还有一个专业版非常不错,但它不是免费的。 您可以使用免费版本来实现大部分功能。)Unity 支持所有主要的 3D 应用程序和多种音频格式,它甚至了解 Photoshop 的 .PSD 格式,以便您能将 .psd 文件导入到一个 Unity 项目中。 Unity 允许您导入和组装资产,编写代码与您的对象进行交互,创建或导入动画以便在高级的动画系统中使用,等等。
如图 1 所示,Unity 已完成了用于确保跨平台支持的工作,理论上您可以通过单击来更改平台,虽说如此,但通常至少还需要做少量努力,比如为应用内购买与各商店集成。
图 1 Unity 支持的平台
也许 Unity 最强大的部分在于“Unity 资产商店”,它可以说是在游戏市场上最好的资产市场。 在这里,您可以满足对所有游戏组件的需求,如用于 3D 模型的艺术品、3D 模型、动画文件(参见商店内超过 10,000 个动画的 Mixamo 内容)、音频效果和全曲目、插件(包括那些可以提供多平台支持的多平台工具包)、可视化脚本系统(如 PlayMaker 和 Behave)、先进的着色器、纹理、粒子特效等等。 Unity 界面是完全脚本化的,允许很多第三方插件通过适当的集成来加入到 Unity GUI 中。 如果不是全部的话,至少也是大多数的专业游戏开发人员会使用资源商店中的大量软件包,如果您有不错的资源,也可以将它发布在那里。
人们对 Unity 的误解
我犹豫地解释,Unity 并不像人们一直质疑的那样。 然而,默认情况下 Unity 并不是一个用来设计您的 2D 资产和 3D 模型(地形除外)的系统。 您可以把一堆僵尸带入到场景中,对它们进行控制,但您不能在 Unity 的默认工具中创建僵尸。 从这个意义上来说,Unity 并不是类似于 Autodesk Maya 或 3DSMax、Blender 甚至 Adobe Photoshop 那样的资产创建型工具。 但是,至少有一个第三方建模插件 (ProBuilder) 可以让您在 Unity 的内部创建一些 3D 组件模型;有 2D 环境生成器插件(如用于创建 2D 平铺环境的 2D 地形编辑器),您还可以在 Unity 中设计地形——使用地形工具创建出有树木、草地、高山等令人叹为观止的景观。 所以,我再一次犹豫地指出 Unity 可实现事项存在的一些局限。
Microsoft 如何能融入进来? Microsoft 与 Unity 的紧密合作能够确保整个 Microsoft 堆栈可以得到平台的强大支持。 Unity 支持 Windows 的独立可执行性、Windows Phone,Windows 应用商店应用程序、Xbox 360 和 Xbox One。
开始使用
下载 Unity 的最新版本,手边准备好一个带有双按键和可单击滚轮的鼠标。 还有一个用于免费版模式或专业版模式下获得许可的单个下载。 您可访问 unity3d.com/unity/licenses 查看两种版本之间的区别。 编辑器是主要的 Unity 界面,它运行在 Windows(包括 Surface Pro)、Linux 和 OS X 上。
我将在接下来的文章中使用 Unity 进入真正的游戏开发,但是,首先,我将探索 Unity 界面、项目结构和体系结构。
体系结构和编译
Unity 是一个基于 C++ 的本机游戏引擎。 使用 C#、JavaScript (UnityScript) 或较少使用的 Boo 编写代码。 您的代码,而不是 Unity 引擎代码,运行在 Mono 上或 Microsoft.NET Framework 上,这是实时 (JIT) 编译的(iOS 除外,因为它不允许使用 JIT 代码,并且它是由 Mono 使用预先 [AOT] 编译将其编译到本地代码)。
Unity 让您能够在 IDE 中测试游戏,而无需执行任何种类的导出或生成。 当您在 Unity 中运行代码时,您使用的是 Mono 的 3.5 版本,它的 API 兼容性基本上与 .NET Framework 3.5/CLR 2.0 的 API 相当。
在项目视图中双击代码文件,可以打开默认的跨平台编辑器 MonoDevelop,这样就可以在 Unity 中编辑您的代码了。 如果您愿意,您可以配置 Visual Studio 作为您的编辑器。
使用 MonoDevelop 进行调试,或使用 Visual Studio、UnityVS 的第三方插件。 在没有 UnityVS 的情况下,您不能将 Visual Studio 用作调试器,因为当您调试游戏时,您不是在调试 Unity.exe,而是通过使用一个作为发出命令并执行相关操作的软件调试器在调试 Unity 内部的虚拟环境。
若要进行调试,就需要从 Unity 启动 MonoDevelop。 MonoDevelop 有一个插件,您在 MonoDevelop 中调试 | 附加到进程之后,这个插件可以打开返回到 Unity 调试器的连接,并向其发出命令。 UnityVS 使您能够将 Visual Studio 调试器连接回 Unity。
当您第一次打开 Unity 时,您会看到如图 2 中所示的项目对话框。
图 2 Unity 项目向导
在项目对话框中,您为您的项目 (1) 指定了名称和位置。 您可以将任何程序包导入到您的项目 (2) 中,但此处您不必核对所有事项;列表仅为方便使用而提供。 您也可以稍后导入一个程序包。 程序包是一个包含预先打包的资源(模型、代码、场景、插件,可在 Unity 中打包的一切)的 .unitypackage 文件,且您可以重复使用或轻松地分发它们。 但如果您不知道它是什么,就不要在此处进行核对;您的项目规模逐渐变大,有时会显著增长。 最终,您可以选择 2D 或 3D (3)。 这个下拉列表是 Unity 中的新增项,它之前并没有强大的 2D 游戏工具,直到最近才刚刚出现。 当设置为 3D 时,默认首选 3D 项目,一直以来它都作为 Unity 的经典行为,所以它并不需要任何特别提及。 当选择 2D 时,Unity 改变了一些看似小,而实则很重要的东西,我将在本系列的后续 2D 文章中对此进行介绍。
此列表的填充内容来自您系统上的特定位置的 .unitypackage 文件;Unity 提供了有关安装的一些内容。 您从 Unity 资产商店下载的所有内容也作为 .unitypackage 文件而出现,本地缓存在您的系统的 C:Users<you>AppDataRoamingUnityAsset Store 中。 因此,当它存在于您的系统上时,它就会显示在这个列表中。 您可以简单地双击一个 .unitypackage 文件,该文件就会导入到您的项目中。
通过继续使用 Unity 界面,我会从单击图 2 中的对话框中的“创建”开始,以便创建一个新的项目。 默认的 Unity 窗口布局如图 3 所示。
图 3 默认的 Unity 窗口
在此,您可以看到:
- 项目:您项目中的所有文件。 您可以通过从资源管理器拖放到 Unity 中,将文件添加到您的项目中。
- 场景:当前打开的场景。
- 层次结构:场景中的所有游戏对象。 注意,使用术语 GameObjects 和 GameObjects 下拉菜单。
- 检查器:场景中选定对象的组件(属性)。
- 工具栏:最左边是平移、移动、旋转、缩放,中央是播放、暂停、前进帧。 单击“播放”可立即播放游戏,而不必执行单独的生成。 “暂停”可暂停游戏,“前进帧”运行一次前进一帧,为您提供非常紧凑的调试控制。
- 控制台:此窗口可以隐藏,但它显示的输出来自您的编译、错误、警告等。 它还显示来自代码的调试信息;例如,Debug.Log 会在此处显示它的输出。
值得一提的是,“游戏”选项卡紧挨着“场景”选项卡。 当您单击“播放”时,此选项卡激活,您的游戏开始在这个窗口中运行。 这就是所谓的播放模式,它为您提供一个用来测试游戏的场地,甚至可以让您切换回“场景”选项卡进行实时修改游戏。 但此处要非常小心。 当播放按钮高亮显示时,您处在播放模式下,当您离开它时,您在播放模式下所做的任何更改都将丢失。 我,以及我曾交谈过的几乎所有的 Unity 开发人员,都曾因为这种方式而丢失了工作,所以我在播放模式下,通过编辑 | 首选项 | 颜色 | 播放模式色彩更改了我的编辑器颜色,使它更加明显。
关于场景
您的游戏中运行的一切都存在于一个场景中。 当您针对某个平台打包您的游戏时,由此产生的游戏是一个或多个场景的集合,包括您添加的所有依赖于平台的代码。 在一个项目中您可以有足够多您需要的场景。 一个场景可以看作是游戏中的一个基准,但您在一个场景文件中可以有多个基准,只要将玩家/照相机移动到场景中不同的地点即可。 当您下载第三方程序包,或从资产商店中下载示例游戏时,通常您必须在项目中查找可打开的场景文件。 场景文件是一个单个文件,它包含各种关于项目中用于当前场景的资源的元数据及其属性。 重要的是,和任何其他工具一样,要在开发过程中经常按下 Ctrl+S 组合键保存场景。
虽然 Unity 有时在打开一个项目时,会创建一个新的空场景,这时您必须在项目资源管理器中查找场景,但通常情况下,Unity 会打开您最后操作的那个场景。 这会让新用户感到很困惑,但是如果您碰巧打开自己的最后一个项目,并且想知道您所完成的工作都去了哪里,那么记住这一点很重要! 请放轻松,您将在项目中保存的场景文件中找到工作。 通过单击图 4 中显示的图标并对场景进行筛选,您可以搜索您项目中的所有场景。
图 4 筛选项目中的场景
在一个场景中,如果没有照相机,您就看不到任何东西,如果没有附加到 GameObject 的音频侦听器组件,您就什么都听不到。 但是请注意,在任何新的场景中,Unity 总是创建一个照相机,上面已经配备有音频侦听器组件。
项目结构和导入资产
Unity 项目和 Visual Studio 项目不同。 您不打开项目文件或解决方案文件,因为它不存在。 您将 Unity 指向一个文件夹结构,它将该文件夹打开为一个项目。 项目中包含“Assets”、“Library”、“ProjectSettings”和“Temp”文件夹,但显示在界面中的只有“Assets”文件夹,如图 4 所示。
“Assets”文件夹包含您所有的资产(艺术作品、代码、音频);您放入项目中的每一个文件都放在这里。 这始终是 Unity 编辑器中的顶层文件夹。 但只能在 Unity 界面中进行更改,决不能通过文件系统。
“Library”文件夹是导入资产的本地缓存;它为资产保留所有元数据。 “ProjectSettings”文件夹存储您通过“编辑 | 项目设置”而配置的设置。 “Temp”文件夹用于生成过程中来自 Mono 和 Unity 的临时文件。
我想强调的是只通过 Unity 界面(而不是直接通过文件系统)进行更改的重要性。 这甚至包括简单的复制和粘贴。 Unity 通过编辑器跟踪您的对象的元数据,所以使用编辑器来进行更改(几个附加案例之外)。 您可以从您的文件系统拖放到 Unity;这种效果很好。
至关重要的 GameObject
您的场景中所有的一切实际上都是一个 GameObject。 思考 .NET Framework 中的 System.Object。 几乎所有类型都是从它派生出来的。 同样的概念也适用于 GameObject。 它是 Unity 场景中的所有对象的基类。 图 5 中显示的所有(以及更多)对象都是从 GameObject 中派生出来的。
图 5 Unity 中的 GameObjects
GameObject 非常简单,因为它从属于“检查器”窗口。 您可以在图 6 中看到一个空的 GameObject 已被添加到场景中;请注意它在检查器中的属性。 GameObjects 在默认情况下没有可视属性,当您突出对象时会显示小部件 Unity 的情况除外。 从这一点上来说,它只是一个空对象。
图 6 简单的 GameObject
GameObject 有一个“名称”、一个“标签”(类似于您通过 XAML 中的 FrameworkElement.Tag 分配的文本标签或在 Windows 窗体中的标签)、一个“层”和“变换”(有可能是最重要的属性)。
“Transform”(变换)属性只不过是 GameObject 的位置、旋转以及缩放的信息。 Unity 使用左手坐标系,因此您可以将您的计算机屏幕的坐标视为 X(水平)、Y(垂直)和 Z(深度,也就是进出屏幕的方向)。
在游戏开发中,使用矢量非常普遍,我将在以后的文章对此详细介绍。 就目前而言,知道 Transform.Position 和 Transform.Scale 都是 Vector3 对象就可以了。 Vector3 是一个简单的三维矢量;换句话说,它只不过就是三个点:X、Y 和 Z。 通过使用这三个简单的值,您可以设置一个对象位置,甚至沿着某个矢量的方向移动一个对象。
组件
您可以通过添加组件,向 GameObjects 添加功能。 您所添加的是一个组件,它们都会显示在“检查器”窗口中。 有 MeshRender 和 SpriteRender 组件;音频和照相机功能组件;物理学相关的组件(对撞机和刚体)、粒子系统、路径查找系统、第三方自定义组件等。 您可以使用脚本组件将代码分配给对象。 通过添加功能,组件能够为您的 GameObjects 带来勃勃生机,这类似于软件开发中的装饰器模式,甚至比这更好些。
我会将一些代码分配给新的 GameObject,在这种情况下,您可以通过“GameObject | 创建其他 | 多维数据集”方式创建一个简单的多维数据集。 我将该多维数据集重新命名为“Enemy”,然后又创建了一个,这下就有两个多维数据集了。 在图 7 中您会看到我将一个多维数据集移开另一个多维数据集 15 个单位距离,这只需要您在突出显示某个对象时,使用工具栏上的移动工具或 W 键就能够做到。
图 7 包含两个多维数据集的当前项目
该代码是一个简单类,它能够查找玩家和并让它的所有者朝向它移动。 通常,以下两种方法都可以执行移动操作:要么通过改变 Transform.Position 属性在每一帧都将对象移动到一个新位置,或者可以对该对象应用一个滞阻力,让 Unity 执行其余的工作。
每一帧都做些处理与“移动到这一点”相比,在思维方式上略有不同。在这个示例中,我打算每一帧都对该对象移动一点点,所以我对将其移动到什么地方有着精确的控制。 如果您不愿每一帧都做调整,那么有些库可以执行单个函数调用移动操作,比如免费开放的 iTween 库。
我做的第一件事就是在“项目”窗口中用鼠标右键单击,创建名为 EnemyAI 的一个新 C# 脚本。 若要将此脚本分配给对象,只需将脚本文件从项目视图拖动到场景视图或层次结构中的对象,同时代码也会分配给该对象。 Unity 将执行其余的工作。 就是这么简单。
图 8 显示已分配有脚本的 Enemy 多维数据库。
图 8 已分配有脚本的 Enemy
查看图 9 中的代码,并注意公共变量。 在编辑器中,您可以看到我的公共变量出现的同时,有一个选项可以在运行时覆盖默认值。 这实在太棒了。 您可以在 GUI 中对基元类型更改默认设置,也可以显示许多不同对象类型的公共变量(而不是属性)。 如果我拖放此代码到另一个 GameObject,则该代码组件的完全独立的实例被实例化。 这是一个基本的示例,它可以变得更有效,比方说,添加一个 RigidBody 组件到这个对象,但这里我会将它保持简单的样子。
图 9 EnemyAI 脚本
public class EnemyAI : MonoBehavior { // 这些值将出现在编辑器中,而完整的属性则不会出现。 public float Speed = 50; private Transform _playerTransform; private Transform _ myTransform; // 在分配到的 GameObject 启动时调用。 void Start() { // 查找某个分配有文本标签“玩家”的 gameobject。 // 这是启动代码,不应该每一帧都查询该玩家 。 // 对象。 存储对它的引用。 var player = GameObject.FindGameObjectWithTag("Player"); if (!player) { Debug.LogError( "Could not find the main player. Ensure it has the player tag set."); } else { // 获得对其变换的引用,以备后用(将托管 // 代码保存到本机代码调用中)。 _playerTransform = player.transform; } // 获得对我们的变换的引用,以备后用。 _myTransform = this.transform; } // 调用每一帧。 每一秒钟的帧速率都在变化。 void Update() { // 我正在设置我应该以每秒多快的速度移向 // “玩家”。在 Unity 中,单位是米。 // Time.deltaTime 给出距离最后一帧的时间量。 // 如果您正在以 60 FPS(每秒帧数)的速率运行,那么就是 1/60 = 0.0167, // 所以当 Speed=2,帧速率是 60 FPS(帧速率 // 每秒都在变化)时,我得到的移动量是 2*0.0167 = .033 个单位 //每帧。这是 2 个单位。 var moveAmount = Speed * Time.deltaTime; // 更新位置,根据 moveAmount 移向玩家的位置。 _myTransform.position = Vector3.MoveTowards(_myTransform.position, _playerTransform.position, moveAmount); } }
在代码中,我可以得到对编辑器中显示的任何组件的引用。 我也可以为 GameObject 分配脚本,其中每一个都有它自己的启动和更新方法(以及许多其他方法)。 假设包含此代码的脚本组件需要引用 EnemyAI 类(组件),我可以简单地查询该组件:
public class EnemyHealth : MonoBehavior private EnemyAI _enemyAI; // 用来初始化。 void Start () { // 获取对这个游戏对象上的 EnemyAI 脚本组件的引用。 var enemyAI = this.GetComponent<EnemyAI>(); } // 每帧调用一次更新。 void Update () { _enemyAI.MoveTowardsPlayer(); }
当您在 MonoDevelop 或您选择的代码编辑器中编辑好代码并切换回 Unity 之后,通常会看到一个短暂的延迟。这是因为 Unity 作为后台正在编译您的代码。您可以通过“编辑 | 首选项 | 外部工具 | 外部脚本编辑器”来修改您的代码编辑器(而不是调试器)。任何编译问题都将出现在您的 Unity 编辑器屏幕的最下方状态栏,所以请留意它们。>如果您尝试运行代码中存在错误的游戏,Unity 将不会让您继续。
编写代码
在先前的代码示例中,有两种方法,“启动”和“更新”,而且类 EnemyHealth 是从 MonoBehavior 基类继承而来的,通过使用基类,您可以简单地将 EnemyHealth 类分配给一个 GameObject 对象。在基类中有很多可用的功能,并且通常还有很多方法和属性。主要的方法就是,如果它们存在于您的类中,Unity 将进行调用。也有少数方法可以调用(请参阅 bit.ly/1jeA3UM)。虽然类似于 ASP.NET Web 窗体页面生命周期,有很多方法,但通常只能使用几个。>以下是在类中最常见的可执行的编码方法,这些方法涉及到 MonoBehavior 派生类事件的序列:
Awake 方法:当对象第一次进行初始化时,每个对象调用一次此方法。其它组件可能还没有被初始化,所以这种方法通常用来初始化当前的 GameObject。您应该始终使用这个方法来初始化 MonoBehavior 派生类,而不是某个构造函数。此处,不要试图在您的场景中查询其他对象,因为它们可能还没有初始化。
开始:在对象生命周期的第一帧中,并且在使用任何“更新”方法之前,调用此方法。这看起来可能非常类似于 Awake 方法,但使用 Start 方法,您知道其他对象已通过 Awake 初始化并且存在于您的场景中,因此,您可以在代码中轻松查询其他对象,如下:
// 返回在任何游戏对象上找到的第一个 EnemyAI 脚本组件实例。 // 这种类型是 EnemyAI(一个组件),而不是一个 GameObject。 var enemyAI = GameObject.FindObjectOfType<EnemyAI>(); // 实际上,我将获得对其顶层 GameObject 的引用。 var enemyGameObject = enemyAI.gameObject; // 想知道 Enemy 的位置吗? var position = enemyGameObject.transform.position;
更新:每一帧都会调用此方法。您是想问更新的频率?它是可变的。这完全取决于计算。出于呈现不同事物的原因,您的系统总是不断变化着负载,所以这个帧速率每秒都会发生变化。进入播放模式查看当前的帧速率时,您可以按下“游戏”选项卡中的“统计”按钮,如图 10 所示。
图 10 获取统计信息
FixedUpdate:一秒钟内会以固定次数调用此方法,和帧速率无关。因为一秒钟内调用“更新”方法的次数是变化的,并且不与物理引擎同步,所以,当您想为某个对象提供一个力或其他一些物理相关的函数时,最好的方法通常是使用 FixedUpdate。默认情况下,FixedUpdate 每 0.02 秒被调用一次,这意味着 Unity 也在每 0.02 秒执行一次物理计算(这个时间间隔称为“固定时间步长”,并且可供开发人员进行调整),这同样与帧速率无关。
Unity 生成的代码项目
一旦您的项目中放入了代码,Unity 就会在您的根文件夹中创建一个或多个项目文件(这在 Unity 界面中是不可见的)。这些都不是 Unity 引擎的二进制文件,而是 Visual Studio 或 MonoDevelop 的项目,您可以在其中编辑和编译代码。Unity 可以创建很多个看似独立的项目,如图 11 所示,虽然每一个都有着重要的用途。
图 11 Unity 创建的项目
如果您的 Unity 项目很简单,您看不到所有这些文件。它们只有在您将代码放入各种特定的文件夹之后,才会被创建。图 11 中显示的项目是只按照三种类型剖视的剖视图:
- Assembly-CSharp.csproj
- Assembly-CSharp-Editor.csproj
- Assembly-CSharp-firstpass.csproj
对于这些项目中的每一个而言,创建的重复项目都会附加 -VS,例如 Assembly-CSharp-vs.csproj。如果 Visual Studio 是您的代码编辑器,就会使用这些项目,并且会将它们从 Unity 添加到您的导出项目中,用于在 Visual Studio 解决方案中调试特定平台。
其他项目服务于同一目的,但会使用 UnityScript 来取代 CSharp。这些只是项目的 JavaScript (UnityScript) 版本, 仅当您在 Unity 游戏中使用 JavaScript 并且仅当您将脚本放在触发要创建这些项目的文件夹中时,才会存在这些版本。
既然您已经看到了所创建的项目,我将探讨触发这些项目的文件夹,并告诉您它们的用途。 每个文件夹路径都假设它在项目视图中的 /Assets 根文件夹下。Assets 始终是根文件夹,其中包含您的所有资产文件。例如,Standard Assets 实际上是 /Assets/Standard Assets。您的脚本的生成过程通过四个阶段来生成程序集。第 1 阶段编译的对象看不到第 2 阶段编译的对象,因为它们还没有被编译。当您将 UnityScript 和 C# 都放入同一个项目中时,了解这一点是非常重要的。如果您想引用来自 UnityScript 的 C# 类,您需要确保在早期阶段中对其进行编译。
第 1 阶段由“Standard Assets”、“Pro Standard Assets”和“Plug-ins”文件夹中的运行时脚本组成,它们全部位于 /Assets 下。这一阶段创建 Assembly-CSharp-firstpass.csproj 项目。
第 2 阶段的脚本位于 Standard Assets/Editor、Pro Standard Assets/Editor 和 Plug-ins/Editor 文件夹中。最后一个文件夹是为与提供设计时功能的 Unity 编辑器 API 进行交互的脚本二准备(考虑 Visual Studio 插件及其如何如何增强 GUI,而且仅有它在 Unity 编辑器中运行)。这一阶段创建 Assembly-CSharp-Editor-firstpass.csproj 项目。
第 3 阶段包括不在“Editor”文件夹内的所有其他脚本。这一阶段创建 Assembly-CSharp-Editor.csproj 项目。
第 4 阶段包括所有剩余的脚本(所有其他称为“Editor”的文件夹中的脚本,如 /Assets/Editor 或 /Assets/Foo/Editor)。这一阶段创建 Assembly-CSharp.csproj 项目。
但是也有一些这里没有涉及到的其他几个不常用的文件夹,如“Resources”。还有就是关于编译器所使用内容方面的未决问题。是 .NET 吗?是 Mono 吗?是用于 Windows 运行时 (WinRT) 的 .NET 吗?是用于 Windows Phone 运行时的 .NET 吗?图 12 列出了用于编译的默认值。知道这一点非常重要,特别是对基于 WinRT 的应用程序,因为每个平台可用的 API 会有所不同。
图 12 编译变体
平台 | 游戏程序集的生成者 | 最终编译的执行者 |
Windows Phone 8 | Mono | Visual Studio/.NET |
Windows 应用商店 | .NET | Visual Studio/.NET (WinRT) |
Windows Standalone (.exe) | Mono | Unity - 生成 .exe + 库 |
Windows Phone 8.1 | .NET | Visual Studio/.NET (WinRT) |
当您执行 Windows 生成时,Unity 负责进行调用,以从您的 C#/UnityScript/Boo 代码 (DLL) 生成游戏库,并包括其本机的运行时库。对于 Windows 应用商店和 Windows Phone 8,它会导出 Visual Studio 解决方案,独立 Windows standalone 除外,因为在其中 Unity 会生成 .exe 和所需的 .dll 文件。当我涉及到平台生成话题时,我将在本系列的最后一篇文章中讨论各种生成类型。较低级别的图形绘制由 Windows 平台上的 DirectX 执行。
在 Unity 中设计游戏是一个相当简单的过程:
- 引进您的资产(艺术品,音频等)。 使用资产商店。 编写自己的资产。 聘请艺术家。 请注意,Unity 确实为 Maya、Cheetah3d、Blender 和 3dsMax 提供本机支持,在某些情况下要求将软件安装为可以使用那些本机 3D 格式,同时还可以使用 obj 和 .fbx 这些常见的文件格式。
- 使用 C#、JavaScript/UnityScript 或 Boo 编写代码,以控制您的对象、场景以及实现游戏逻辑。
- 在 Unity 中进行测试。 导出到平台。
- 在平台上进行测试。 部署。
请稍等,我将讲解更多内容!
本文是对 Unity 中的体系结构和过程的概述。我介绍了界面、分配代码的基础知识、GameObjects、组件、Mono 和 .NET 等。这使我们能够更好的为下一篇文章做好准备,在下一篇文章中我将深入探讨 2D 游戏的游戏组件组装。请关注 Microsoft 虚拟学院,我会在夏末进行一个为期两天的 Unity 学习活动。请访问unity3d.com/pages/windows/events 留意本地区的学习活动。
Adam Tuliper 是生活在阳光明媚的加利福尼亚州南部的一位 Microsoft 资深技术传播者。他是一位独立的游戏开发人员,Orange County Unity Meetup(奥兰治县 Unity 聚会)的共同管理者,以及pluralsight.com 的作者。他和他的妻子即将拥有自己的第三个孩子,所以在他尚有闲暇的时间里,您可以通过访问 adamt@microsoft.com 或 Twitter twitter.com/AdamTuliper. 来联系到他。
衷心感谢以下技术专家对本文的审阅:Matt Newman (Subscience Studios)、Jaime Rodriguez (Microsoft) 和 Tautvydas Žilys (Unity)
作为一名软件架构师,我已经编写了很多系统,反向工程本机代码恶意软件,并且通常会搞清楚代码方面的事情。 但是当说起制作游戏,我还有点搞不清楚从哪里开始讲起。 我曾早期在 Windows 工作时,做了一些本机代码图形编程,但那不是什么有趣的经历。 后来,我开始专研 DirectX 开发,但我意识到,虽然它非常强大,但似乎我要做的事所需的代码非常多。
然后,有一天,我决定尝试使用 Unity,我也看到了它确实可以做一些令人惊讶的事情。 这篇文章在四部分系列当中作为首篇,内容囊括了 Unity 的基础及架构方面的知识。 我将演示如何创建 2D 和 3D 游戏,最后演示如何构建 Windows 平台。
Unity 是什么
Unity 是一个 2D/3D 引擎和框架,为您提供设计 2D、2.5D 和 3D 游戏或应用场景的系统。 我之所以说是游戏和应用程序,是因为我看到的不只有游戏,还有训练模拟器、第一响应者应用程序,以及使用 Unity 开发需要与 2D/3D 空间交互的其他以业务为核心的应用程序。 Unity 凭借的不仅有代码,还有可视化组件,让您能够与它们进行交互,并将它们导出到各个主要的移动平台,其中很多都是免费的。 (还有一个专业版非常不错,但它不是免费的。 您可以使用免费版本来实现大部分功能。)Unity 支持所有主要的 3D 应用程序和多种音频格式,它甚至了解 Photoshop 的 .PSD 格式,以便您能将 .psd 文件导入到一个 Unity 项目中。 Unity 允许您导入和组装资产,编写代码与您的对象进行交互,创建或导入动画以便在高级的动画系统中使用,等等。
如图 1 所示,Unity 已完成了用于确保跨平台支持的工作,理论上您可以通过单击来更改平台,虽说如此,但通常至少还需要做少量努力,比如为应用内购买与各商店集成。
图 1 Unity 支持的平台
也许 Unity 最强大的部分在于“Unity 资产商店”,它可以说是在游戏市场上最好的资产市场。 在这里,您可以满足对所有游戏组件的需求,如用于 3D 模型的艺术品、3D 模型、动画文件(参见商店内超过 10,000 个动画的 Mixamo 内容)、音频效果和全曲目、插件(包括那些可以提供多平台支持的多平台工具包)、可视化脚本系统(如 PlayMaker 和 Behave)、先进的着色器、纹理、粒子特效等等。 Unity 界面是完全脚本化的,允许很多第三方插件通过适当的集成来加入到 Unity GUI 中。 如果不是全部的话,至少也是大多数的专业游戏开发人员会使用资源商店中的大量软件包,如果您有不错的资源,也可以将它发布在那里。
人们对 Unity 的误解
我犹豫地解释,Unity 并不像人们一直质疑的那样。 然而,默认情况下 Unity 并不是一个用来设计您的 2D 资产和 3D 模型(地形除外)的系统。 您可以把一堆僵尸带入到场景中,对它们进行控制,但您不能在 Unity 的默认工具中创建僵尸。 从这个意义上来说,Unity 并不是类似于 Autodesk Maya 或 3DSMax、Blender 甚至 Adobe Photoshop 那样的资产创建型工具。 但是,至少有一个第三方建模插件 (ProBuilder) 可以让您在 Unity 的内部创建一些 3D 组件模型;有 2D 环境生成器插件(如用于创建 2D 平铺环境的 2D 地形编辑器),您还可以在 Unity 中设计地形——使用地形工具创建出有树木、草地、高山等令人叹为观止的景观。 所以,我再一次犹豫地指出 Unity 可实现事项存在的一些局限。
Microsoft 如何能融入进来? Microsoft 与 Unity 的紧密合作能够确保整个 Microsoft 堆栈可以得到平台的强大支持。 Unity 支持 Windows 的独立可执行性、Windows Phone,Windows 应用商店应用程序、Xbox 360 和 Xbox One。
开始使用
下载 Unity 的最新版本,手边准备好一个带有双按键和可单击滚轮的鼠标。 还有一个用于免费版模式或专业版模式下获得许可的单个下载。 您可访问 unity3d.com/unity/licenses 查看两种版本之间的区别。 编辑器是主要的 Unity 界面,它运行在 Windows(包括 Surface Pro)、Linux 和 OS X 上。
我将在接下来的文章中使用 Unity 进入真正的游戏开发,但是,首先,我将探索 Unity 界面、项目结构和体系结构。
体系结构和编译
Unity 是一个基于 C++ 的本机游戏引擎。 使用 C#、JavaScript (UnityScript) 或较少使用的 Boo 编写代码。 您的代码,而不是 Unity 引擎代码,运行在 Mono 上或 Microsoft.NET Framework 上,这是实时 (JIT) 编译的(iOS 除外,因为它不允许使用 JIT 代码,并且它是由 Mono 使用预先 [AOT] 编译将其编译到本地代码)。
Unity 让您能够在 IDE 中测试游戏,而无需执行任何种类的导出或生成。 当您在 Unity 中运行代码时,您使用的是 Mono 的 3.5 版本,它的 API 兼容性基本上与 .NET Framework 3.5/CLR 2.0 的 API 相当。
在项目视图中双击代码文件,可以打开默认的跨平台编辑器 MonoDevelop,这样就可以在 Unity 中编辑您的代码了。 如果您愿意,您可以配置 Visual Studio 作为您的编辑器。
使用 MonoDevelop 进行调试,或使用 Visual Studio、UnityVS 的第三方插件。 在没有 UnityVS 的情况下,您不能将 Visual Studio 用作调试器,因为当您调试游戏时,您不是在调试 Unity.exe,而是通过使用一个作为发出命令并执行相关操作的软件调试器在调试 Unity 内部的虚拟环境。
若要进行调试,就需要从 Unity 启动 MonoDevelop。 MonoDevelop 有一个插件,您在 MonoDevelop 中调试 | 附加到进程之后,这个插件可以打开返回到 Unity 调试器的连接,并向其发出命令。 UnityVS 使您能够将 Visual Studio 调试器连接回 Unity。
当您第一次打开 Unity 时,您会看到如图 2 中所示的项目对话框。
图 2 Unity 项目向导
在项目对话框中,您为您的项目 (1) 指定了名称和位置。 您可以将任何程序包导入到您的项目 (2) 中,但此处您不必核对所有事项;列表仅为方便使用而提供。 您也可以稍后导入一个程序包。 程序包是一个包含预先打包的资源(模型、代码、场景、插件,可在 Unity 中打包的一切)的 .unitypackage 文件,且您可以重复使用或轻松地分发它们。 但如果您不知道它是什么,就不要在此处进行核对;您的项目规模逐渐变大,有时会显著增长。 最终,您可以选择 2D 或 3D (3)。 这个下拉列表是 Unity 中的新增项,它之前并没有强大的 2D 游戏工具,直到最近才刚刚出现。 当设置为 3D 时,默认首选 3D 项目,一直以来它都作为 Unity 的经典行为,所以它并不需要任何特别提及。 当选择 2D 时,Unity 改变了一些看似小,而实则很重要的东西,我将在本系列的后续 2D 文章中对此进行介绍。
此列表的填充内容来自您系统上的特定位置的 .unitypackage 文件;Unity 提供了有关安装的一些内容。 您从 Unity 资产商店下载的所有内容也作为 .unitypackage 文件而出现,本地缓存在您的系统的 C:Users<you>AppDataRoamingUnityAsset Store 中。 因此,当它存在于您的系统上时,它就会显示在这个列表中。 您可以简单地双击一个 .unitypackage 文件,该文件就会导入到您的项目中。
通过继续使用 Unity 界面,我会从单击图 2 中的对话框中的“创建”开始,以便创建一个新的项目。 默认的 Unity 窗口布局如图 3 所示。
图 3 默认的 Unity 窗口
在此,您可以看到:
- 项目:您项目中的所有文件。 您可以通过从资源管理器拖放到 Unity 中,将文件添加到您的项目中。
- 场景:当前打开的场景。
- 层次结构:场景中的所有游戏对象。 注意,使用术语 GameObjects 和 GameObjects 下拉菜单。
- 检查器:场景中选定对象的组件(属性)。
- 工具栏:最左边是平移、移动、旋转、缩放,中央是播放、暂停、前进帧。 单击“播放”可立即播放游戏,而不必执行单独的生成。 “暂停”可暂停游戏,“前进帧”运行一次前进一帧,为您提供非常紧凑的调试控制。
- 控制台:此窗口可以隐藏,但它显示的输出来自您的编译、错误、警告等。 它还显示来自代码的调试信息;例如,Debug.Log 会在此处显示它的输出。
值得一提的是,“游戏”选项卡紧挨着“场景”选项卡。 当您单击“播放”时,此选项卡激活,您的游戏开始在这个窗口中运行。 这就是所谓的播放模式,它为您提供一个用来测试游戏的场地,甚至可以让您切换回“场景”选项卡进行实时修改游戏。 但此处要非常小心。 当播放按钮高亮显示时,您处在播放模式下,当您离开它时,您在播放模式下所做的任何更改都将丢失。 我,以及我曾交谈过的几乎所有的 Unity 开发人员,都曾因为这种方式而丢失了工作,所以我在播放模式下,通过编辑 | 首选项 | 颜色 | 播放模式色彩更改了我的编辑器颜色,使它更加明显。
关于场景
您的游戏中运行的一切都存在于一个场景中。 当您针对某个平台打包您的游戏时,由此产生的游戏是一个或多个场景的集合,包括您添加的所有依赖于平台的代码。 在一个项目中您可以有足够多您需要的场景。 一个场景可以看作是游戏中的一个基准,但您在一个场景文件中可以有多个基准,只要将玩家/照相机移动到场景中不同的地点即可。 当您下载第三方程序包,或从资产商店中下载示例游戏时,通常您必须在项目中查找可打开的场景文件。 场景文件是一个单个文件,它包含各种关于项目中用于当前场景的资源的元数据及其属性。 重要的是,和任何其他工具一样,要在开发过程中经常按下 Ctrl+S 组合键保存场景。
虽然 Unity 有时在打开一个项目时,会创建一个新的空场景,这时您必须在项目资源管理器中查找场景,但通常情况下,Unity 会打开您最后操作的那个场景。 这会让新用户感到很困惑,但是如果您碰巧打开自己的最后一个项目,并且想知道您所完成的工作都去了哪里,那么记住这一点很重要! 请放轻松,您将在项目中保存的场景文件中找到工作。 通过单击图 4 中显示的图标并对场景进行筛选,您可以搜索您项目中的所有场景。
图 4 筛选项目中的场景
在一个场景中,如果没有照相机,您就看不到任何东西,如果没有附加到 GameObject 的音频侦听器组件,您就什么都听不到。 但是请注意,在任何新的场景中,Unity 总是创建一个照相机,上面已经配备有音频侦听器组件。
项目结构和导入资产
Unity 项目和 Visual Studio 项目不同。 您不打开项目文件或解决方案文件,因为它不存在。 您将 Unity 指向一个文件夹结构,它将该文件夹打开为一个项目。 项目中包含“Assets”、“Library”、“ProjectSettings”和“Temp”文件夹,但显示在界面中的只有“Assets”文件夹,如图 4 所示。
再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow