作者:Milo
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。
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。