【入门】Unity3D游戏开发快速上手指南

发表于2015-04-29
评论1 1.26w浏览

大家对Unity3D游戏引擎应该并不陌生,因为Unity3D在轻量级游戏开发和跨平台上面有他独特的优势,所以在当前可谓是炙手可热。本文简单介绍了Unity3D的一些基础。并且有部分内容根据天天飞车项目经验做了简单分析。适合没有接触过Unity3D和手游开发,并想了解其大概的同学。

1       Unity3D简介

1.1     编辑器简介

编辑器整体视图如图1.1所示。里面包括了Unity常用的编辑窗口:

http://km.oa.com/files/post_photo/480/209480/1e682500617b43690e067ab14325685f.jpg

http://avocado.oa.com/fconv/files/201408/dbcb742e77f680d5d9a0c227d54c7b6c.files/image001.png

Project视图、Hierarchy视图、Scene视图、Game视图、Inspector视图、Console视图、Profiler视图。

1.1.1        Project视图

Project视图可以理解为工程目录,里面罗列了工程里面的所有资源文件。常见的资源包括:脚本、预设(Prefab)、模型、贴图、动画、Shader等。用户可以通过右上角的搜索框,搜索工程内的文件。

1.1.2        Hierarchy视图

Hierarchy视图显示了当前游戏场景中,所有的游戏对象。游戏对象是通过树形结构排布,展开后可以看到每个子节点对象。常用的游戏对象包括:摄像机、场景物件、玩家、光源等。

1.1.3        Game视图

Game视图是游戏视角,即游戏最终展示给玩家的内容。游戏视角包括两部分:1、场景中当前摄像机照射的场景;2、游戏UI界面。

1.1.4        Scene视图

Scene视图有点像3DMax的编辑环境,在这里可以看到当前场景中的所有游戏对象。双击Hierarchy中的游戏对象,可以在Scene中定位到对应的物件。在游戏运行期间,暂停游戏。开发人员可以在Scene中找到对应的游戏对象,查看当前帧的世界场景,方便查找BUG。

1.1.5        Inspector视图

Inspector视图是游戏对象的属性面板。选择一个物件后,可以在Inspector面板中查看或编辑游戏对象的属性。游戏运行期间,修改游戏对象属性,可以马上作用到游戏对象。这一特点对于美术的编辑、程序查BUG或者策划调整游戏参数有很大帮助。

         Unity的游戏对象是通过Component(组件)控制的。常见的Component有:Transform(模型坐标)、Collider(碰撞检测器)、Rigidbody(刚体属性)、Animation(动画)、AudioSource(声音源)、Script(游戏脚本)等。

1.1.6        Console视图

Console视图是控制台信息输出窗口。输出的信息包括:游戏脚本编译错误信息、游戏运行期间的日志输出、断言、崩溃信息。

1.1.7        Profiler视图

Profiler视图是游戏性能分析器。游戏运行期间,每一帧的数据都被记录,并显示在Profiler窗口。在时间轴上,最多可以保存几百帧的数据,点击一个特定的帧上,窗口下面会显示该帧的游戏运行细节,包括CPU使用率、GPU使用率、渲染、内存、音频、物理。

1.2     打包与发布

在Unity编辑器中,选择“File -> Build Settings”可以打开游戏打包发布面板。在此面板中,可以选择打包的游戏场景、发布平台以及游戏设置。Unity支持跨平台,所以在这里可以有多种平台选择。打包后的文件格式有:*.swf、*.exe、*.apk、Xcode工程等。

1.3     脚本组件

Unity游戏开发中,游戏脚本在游戏开发中是关键要素,游戏的逻辑控制都是由脚本来控制的。Unity支持3种游戏脚本语言:C#、JavaScript和Boo。这三种语言中,C#因为最接近C++更符合大部分程序员编程习惯,所以使用的比较广。而JavaScript就比较容易上手属于入门级别,Boo的使用人数则是最少的。

游戏脚本(类)继承自MonoBehaviour后,该脚本即可以作为一个游戏组件挂到游戏对象上。继承MonoBehaviour后,提供大部分系统控制的脚本函数。下面列了一些常用的函数的执行顺序:(详细请看:http://docs.unity3d.com/Manual/ExecutionOrder.html

不同游戏脚本之间的执行顺序,可以通过调整游戏脚本优先级来修改。(打开调整面板方式:选择某个脚本文件,在Inspector面板,点击右上角的Execution Order按钮。)

1.4     场景切换与数据传递

在打包发布面板,我们可以选择需要包含的游戏场景。在游戏过程中,我们需要切换场景,可以调用Application.LoadLevel()函数。

切换场景的时候,默认会把场景中的物件清空。如果需要把数据从不同场景中传递成问题了。常用的方法有两种:

1、  静态数据变量。静态数据变量可以用来桥接不同脚本之间的数据交换。当然也可以作为不同场景之间的数据传递。当然过多的静态变量,对于其管理也是个比较麻烦的问题。

2、  利用函数DontDestroynoxss()指定某些物件随着场景的切换而移除。定义一个长期存在的节点,其实挺方便的。可以把游戏的主框架搭建放在这个节点的脚本控件上,这个脚本就有点像GameMain函数,里面可以定义单件管理器,方便管理游戏数据和流程。

1.5     GUI

Unity提供一套自带的GUI系统,但是这套GUI系统实在是太难用了。几乎没有游戏会使用这个原生GUI系统的,也因此催生了很多UI插件。市面上成功用Unity开发的游戏,都是使用UI插件的。最常见的UI插件有:NGUI 和EZGUI。天天飞车采用的是NGUI,所以本文简单介绍一下NGUI。

1.5.1        原生GUI系统

原生GUI系统的运行效率低,而且UI需要由程序调整控制,生产效率极低。

下面,我们看一个Unity官网提供的一个用GUI系统的例子。如图1.2,我们需要绘制两个按钮,点击按钮对应输出一个提示语句。代码1是官方给出的代码程序。

可见如果需要调整按钮位置,修改按钮的外观,可能需要修改程序脚本。如果UI较少尚且可以这样做。但是如果游戏UI复杂度较高,这个UI维护成本就非常高了。

http://top.oa.com/pictures/201408/1407994302_100.jpg

        图1.2 GUI例图

1.        //代码 1:  

2.        // C#  

3.        using UnityEngine;  

4.        using System.Collections;  

5.        public class GUITest : MonoBehaviour  

6.        {  

7.            public Texture2D icon;  

8.            void OnGUI ()  

9.            {  

10.            GUI.Box (new Rect (10,10,100,50), new GUIContent("This is text", icon));  

11.        }  

12.    }  

 

1.5.2        NGUI

NGUI使用树形结构组织游戏UI。并且使用了Unity定义的元素拼接UI界面。可以在不运行游戏的情况下编辑和查看UI面板。让美术和程序工作分离:美术可以把UI拼接好,做成一个Prefab提供给程序控制逻辑。如图1.3是NGUI编辑的天天飞车单局UI。我们可以直接在Scen视图中查看和编辑UI面板。这种结构,既方便美术制作UI动画,也方便程序查找UI显示上的BUG。

http://avocado.oa.com/fconv/files/201408/dbcb742e77f680d5d9a0c227d54c7b6c.files/image007.jpg

                        图1.3 NGUI编辑UI界面

1.6     Prefabs介绍

Prefabs是一种Unity的资源类型,可以简单理解为一个预先设置好的游戏对象。我们可以把这个游戏对象创建到场景后,其结构和属性都跟我们预设一样的。

我们定义和使用Prefab的原因在于,把场景中动态创建或者需要重复使用的物件,设置成一个Prefab,我们就可以根据需要创建。创建出来的实例跟我们预设是一致的。包括模型结构以及节点的组件属性。

几乎所有的游戏对象都可以设置成一个Prefab,例如,一辆车、一个UI界面、一个武器、一颗子弹…..

Prefabs的制作很简单,我们只需要把游戏对象在Scen中编辑好后,从Hierarchy窗口把该游戏对象拖到Project窗口,改游戏对象就被保持成一个Prefab对象了。

Prefabs的实例化也很方便:

加载:GameObject goTest = Resources.Load(“TestPrefab”) as GameObject;

克隆:GameObject goTest2 =  Instantiate(goTest) as GameObject;

1.7     对象池

天天飞车大部分常见的游戏对象都采用对象池来管理。对象池的特点是:

       加载后不释放

       不使用,放到对象池

       下次用,直接从池子里取

       定义最大的分配数量,控制内存

对于同一个对象,使用完成后放到指定的空节点,并且Deactive。下次使用的时候,直接从改池子里面取。池子中的对象列表为空时,允许克隆新的对象,放到池子。对于某个对象,定义一个最大的分配数量,控制内存。

http://top.oa.com/pictures/201408/1407994493_92.png

     图1.4 对象池处理流程图

使用对象池最大的好处就是减少了资源加载时间。当然,它同时带来了内存消耗的增加。根据游戏对象的特性,合理的设定其对象池的最大分配数量也是一个关键。可以考虑增加一个算法,把那些长时间不用的内存池释放或者减少分配的对象数量。

2       反外挂策略

手机游戏进入运营阶段,对于开发人员来说,最棘手的就是反外挂问题。天天飞车单局中没有协议交互,在游戏运行期间统计游戏数据,游戏结束把数据提交给服务器进行校验。所以,反外挂的难度相对于PC游戏来说是更加大的。

常用的反外挂策略有:

       客户端的关键数据使用安全数据类型。

数据加密有很多种方法,常用的是采用数据影子进行加密。

       数据统计,结算上报统计数据。

单局中没有数据交换的情况下,只能把数据统计放到客户端。关键逻辑采用多数据校验记录,外挂需要同时修改多个变量才能骗过反外挂校验。

       开局数据下发,结算回带。

某些关键数据,在开局的时候,由服务器下发。并且保证游戏过程中和结算上报都使用同一数据。服务器最后对客户端回传的数据与下发的数据进行校验。

       降低外挂在玩家中的声誉。

这算是一种非纯技术反外挂。游戏更新后,预留一些坑,服务器不开对应反外挂策略。当大量玩家购买外挂使用的时候,再打开对应策略,给玩家一定量的处罚。一方面,给玩家造成使用外挂的恐惧。另外一方面,降低了外挂供应商在玩家中的口碑。

最后要注意一点:对于使用Unity3D游戏引擎的项目,继承自MonoBehaviour的变量容易被外挂利用。例如,MonoBehaviour的Enable变量就可以被修改。天天飞车前段时间出现无限隐身的外挂,就是利用了这一点。当玩家进入隐身状态后,把控制隐身的脚本Disable,导致客户端无法统计隐身相关的反外挂信息。

3       配置

一个好的游戏,配置无疑是一个关键的因素。便利的配置,可以大大加快游戏开发进度。也可以一定量的减轻程序员的工作。我们在这里讨论一下天天飞车项目使用Unity3D引擎开发的各种配置方式。

在天天飞车项目里,常见的配置方法有以下几种:

       文本文件

       Xml文件

       Excel文件

       Unity场景或者Prefab中配置

天飞的配置获取和更新方式主要有三种:

       版本发布时,打进安装包

       CDN服务器中拉取最新配置

       服务器通过协议下发给客户端

Unity编辑器里面可以在Inspector面板上填写物件属性。通过Inspector面板,我们可以给场景中的某个物件中的组件进行编辑,编辑后的属性可以直接应用在游戏中。用户可以把Prefab实例化到场景中。暴露Prefab属性给策划,可以让策划方便地快捷的修改参数配置,并且直接重启游戏就可以看到效果。

Prefabs中配置的优点是开发方便,反馈及时。但是,同样也要注意到其维护成本高、需要随着版本更新,以及反外挂困难等特点。下面介绍一个简单的例子:

天天飞车的赛道块配置。游戏研发初期,因为还没有接入服务器,而且要保证开发速度,所以在场景中配置赛道块和NPC车的刷出逻辑数据。但是,随着游戏进入运营阶段,新版本的开发,发现这样的配置方法维护成本高,灵活性低。而且服务器读取不了这部分数据,反外挂不好做。在战车模式版本中,就需要把部分赛道块配置抽离到Excel表格配置了。

4       Unity3d 资源加载与内存管理

Unity提供多种加载和释放方式。大部分刚接触Unity的开发人员可能对这个比较难理解。这里引用一篇其他人整理的文章大概介绍一下。

原文出处:http://game.ceeger.com/forum/read.php?tid=4394

资源加载:

l AssetBundle.CreateFromWWW.AssetBundle

创建一个AssetBundle内存镜像

l AssetBundle.LoadResources.Load

AssetBundle读取一个指定名称的Asset并生成Asset内存对象。如果多次Load同名对象,除第一次外都只会返回已经生成的Asset对象,也就是说多次Load一个Asset并不会生成多个副本。

l Instantiate

Clone一个object的完整结构,包括其所有Component和子物体(详见官方文档),Copy,并不复制所有引用类型。

资源释放:

l Destroy

主要用于销毁克隆对象,也可以用于场景内的静态物体,不会自动释放该对象的所有引用。

l AssetBundle.Unload(false)

释放AssetBundle文件内存镜像

l AssetBundle.Unload(true)

释放AssetBundle文件内存镜像同时销毁所有已经LoadAssets内存对象

l Reources.UnloadAsset(Object)

显式的释放已加载的Asset对象,只能卸载磁盘文件加载的Asset对象

l Resources.UnloadUnusedAssets

用于释放所有没有引用的Asset对象

l GC.Collect()

强制垃圾收集器立即释放内存UnityGC功能不算好,没把握的时候就强制调用一下

 http://avocado.oa.com/fconv/files/201408/dbcb742e77f680d5d9a0c227d54c7b6c.files/image010.jpg

 http://avocado.oa.com/fconv/files/201408/dbcb742e77f680d5d9a0c227d54c7b6c.files/image009.png

5       总结

文章是根据本人的一个PPT细化出来的。PPT是给QQ飞车客户端团队内部分享的时候做的。查看PPT请跳转至:http://km.oa.com/group/1667/articles/show/188951

本人接触手游开发Unity3D引擎的时间也不长,可能有部分内容理解不够深入,敬请谅解。最后,感谢您看完了这篇文章。

 

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引