解析unity3d的script物件模型





Unity 是一个以 Mono 为基础的游戏开发环境,能同时支持三种脚本语言,包括 C#、Javascript 和Boo (类似 Python)。

由于Unity 的开发工具暂时只有 Mac 的版本,所以暂时未能测试。但是它有很详细的文档,看上来很易用,所以就从文字上学习它的Script 使用方式。

根据一些Tutorial 及参考手册,我用 Graphviz 画了一个 (我认为) 最核心的 UML 类别图:

从这个类别图我们可以理解它的结构,及如何把一些常用功能映射至这系统里,以下分节讨论。

GameObject 和 Component

Unity 的执行环境里,会有一个场境 (Scene)。这个场境包含一个 GameObject 物件的层阶(Hierarchy)。这个 GameObject 类别只是一个包容器,本身没有其他功能。使用者需要为 GameObject 加入各种Component 物件来定义它的行为,而不是透过继承 (inherit) GameObject 来加入 行为。

一个物件可拥有多个Component 物件,但有一些Component 类别只可以在一个 GameObject 中有一个实体(instance)。



MonoBehavior

我最感兴趣的,是使用者如何自行定义行为来做出不同的 Gameplay。在Unity 中,程式员编写的Script,其实也是Component 的一种,所有的Script 都会继承自 MonoBehavior 类别。以下是一个简单例子:

var speed = 5.0;

function Update () {

var x = Input.GetAxis("Horizontal") * Time.deltaTime * speed;

var z = Input.GetAxis("Vertical") * Time.deltaTime * speed;

transform.Translate(x, 0, z);

}

把这个Script 加进一个 GameObject 的话 (成为该 GameObject 的一个Component),Runtime 会在每帧呼叫 Update(),玩家就可以用上下左右键控制那个 GameObject 在水平方向移动。。

Transform

每个能在三维空间里的 GameObject 都会有 Transform Component (未有详细看是否有一些

GameObject 可以省郄 Transform,例如一个用来定义一个游戏任务的 GameObject)。Transform 包括位置、旋转及缩放。之前的例子已用了 transform component,不过它其实是 Object 类别的一个 shortcut(快捷办法),这 shortcut(快捷办法)其实等同:

GetComponent(Transform).Translate(x, 0, z)

Component 的连结

在Script Tutorial 里的例子是写一个 Follow 的行为,拥有这个Component 的 GameObject 会自动追踪 (面对着) 一个目标物件:

var target : Transform;

function Update () {

transform.LookAt(target);

}

这个Script 暴露了一个 target 变数 (应当作成员变数吧),使用者可以把其他物件的变数 assign 至这个变数。这 assignment(任务) 有两种方法实现,其一是利用Unity 的 GUI 工具把一个Component 实体的变数 (如Transform) drag-and-drop 至这个Component 实体的 target 变数,而另一个方法是写代码:
var newTarget = GameObject.Find("Cube").transform;

GetComponent(Follow).target = newTarget;

用代码就可以这样动态改变这些Component 之间的联结方式。或者另一个说法是,GUI 工具是可以设定起始的联结,而Script 可以在执行期改变这些联结。



渲染

一个可被渲染的 GameObject 需要有以几个 Components,以 Mesh 为例:

1. MeshFilter: 用来找出现时的 Mesh 物件

2. MeshRenderer: 用来渲染 Mesh 的 Component,会参考一个 material 物件

要注要 Mesh 和 Material 物件并非 Component,它们是继承自 Object 的。但是你仍然可以去动态改变它们。但由于它们不是Component ,所以可以被分享,例如多个 GameObject 的 MeshRenderer都参考到同一个 Material。一个Component 实体只属于一个 GameObject (所以在 UML 中我用黑色钻石表示 Composition)。而 Light 和 Camera 则是 Component,这意未着可以简单的设定联结。

分析

Unity 的Script 物件模型是以Component 为基础的。透过把Component 实体加入 GameObject 实体来组合不同功能的物件,而Component 实体之间可以建立联结。这种方式不需要透过继承 (inheritance),而是透过聚合 (aggregation)加入物件的功能和行为。使用聚

合的好处是不会产生复杂的继承层阶,亦可以动态改变聚合的结构 (例如在执行期加入或移除Component)。

有一些细节我暂时未清楚,例如多个Component 在一个 GameObject 中的执行次序如何设定;联结会否有 cylic(环)的问题等等。可能要拿到软件再试用才可以知道。

结语

Unity 的脚本系统给我的感觉是使用非常简单。透过很少的代码就能写一些行为,甚至把行为组合到物件中。但是,通常容易的东西都会有相对的缺点,例如在效能上或是 Scalability(扩展性)上。后者可能是一个很大的问题,当游戏规模扩大,Component 和联结就会变成一个很复杂的 graph(图表),由于连结是发生于执行期(而非静态),可能要作改动会变得困难。换句话说,就是改几十个类别容易,改它们的几千个实体就会很困难。
软件设计世界里当然没有银子弹,每个方案都适合不同的情况。我认为Unity 的一个设计目标是容易使用,就是像 Virtools 之流,可以给没有程式底子的人做游戏,相对来说做比较复杂的项目可能会遇到许多问题。但参考一下总可以给予对事物新的观点,或分析另一个科案的强劲之处。我在周末就会再研究其他游戏工业上最强的引擎的脚本,例如 CryEngine2/Sandbox2 和 Unreal。

posted @ 2012-04-23 09:12  渡蓝  阅读(587)  评论(0编辑  收藏  举报