Unity3D脚本语言UnityScript初探

译者注:javascript

Unity3D中支持三种语言:JavaScript、C#、Boo,不少人不知道如何选择,经过这篇译文,咱们能够搞清楚这三者语言的前因后果,对选择主语言有必定的借鉴意义。html

首先,Unity是基于Mono也就是.Net的运行环境的,因此它确定支持C#;而后,Unity团队自行开发了一种Boo的语言;后面可能考虑到用户的接受程度的问题,又开发了相似JS的一种语言,但那绝对不是JS,勉强能够称之为UnityScript。这三种语言的代码最后都会被编译执行,并且能够互相访问。java

花了一上午,才译完,文章有点长,估计有耐心看完的都很少,呵呵。编程

1、Unity中的"JavaScript"与你所了解的JavaScript对比

1. 使用 #pragma strict

#pragma strict

 

进行这样的声明,是一种很好的习惯,而且对于进行iOS开发来讲也是必须的。 #pragma strict 意味着强制进行更严格的类型检测、尽早生成更多有用的错误信息、养成更好的编程习惯。api

2. 使用枚举 enum

enum WeaponType { 
  pistol, 
  rifle, 
  launcher
}
var type : WeaponType = WeaponType.pistol;
 

这种方式更加简洁,而且是比使用字符串产生更少潜在错误的方法。数组

3. 实际上是大不相同的

尽管Unity中的JavaScript尝试尽可能作得至少某种程度上要像ECMAScript标准,但它与一样基于ECMAScript的JavaScript在其余实现方面有不少不一样。也许它与微软的JScript更加类似,尤为是它们都是.NET平台上的语言。固然,Unity的JavaScript版本是本身独立开发实现的,而且二者之间也有不少不一样之处。浏览器

4. 运行速度快

Unity JavaScript 是编译型的,因此性能很高,但浏览器中的JavaScript是动态解释型的。ide

在Unity中,JavaScript、C#与Boo在运行速度上显然没有差别。它们各有优缺点,但速度上是一致的。函数

说明:若是你关注过浏览器之争的话,你应该知道现代浏览器中JavaScript已经不是简单的解释执行,而是以JIT方式编译执行。固然,确定是不支持严格类型定义的。若是ECMAScript标准改为容许显式声明变量类型(像Adobe公司所提倡的,译注:其实我的以为UnityScript更像是ActionScript3),JavaScript的性能还能以数量级的提高。尽管如此,现实是真正的JavaScript就算是拿Safari浏览器的Squirrelfish Extreme引擎进行测试,比Unity中的UnityScript仍要慢上两个数量级。工具

5. 必须用var关键字声明变量

JavaScript中,若是你定义变量时不用var关键字,该变量将会做为全局变量处理。

function DoSomeStuff() {
   x = 3;
}
 
DoSomeStuff();
 
alert(x); // returns 3 ... in JavaScript (not in Unity's UnityScript)
 

为了不JS老用户在Unity碰到这种模棱两可的状况,就要求在定义变量时加上var关键字,那样就能够自动将变量的做用域限定在当前范围。

function DoSomeStuff() {
   var x = 3;
}
 
DoSomeStuff();
 
print(x); // raises an error because x is not global in any sense.
 

6. UnityScript是基于类式继承,而不是原型式继承

UnityScript中,没有.prototype那样混乱的写法。要定义类,你只要这样简单的定义:

// Foo.js
var foo = "hello, world";
 
function doEet () {
  // does nothing, intended to be overridden
}
 

编译器最后在编译以前会自动补全一些代码,构造一个完整的类定义结构。最终形式应该相似以下:

// Foo.js
import UnityEngine;
class Foo extends MonoBehaviour {
  public var foo = "hello, world";
 
  public function doEet () {
    // does nothing, intended to be overridden
  }
}

 

请注意,文件名就是对应的类名。

子类写法:

// PrintingFoo.js
class PrintingFoo extends Foo {
   function doEet() {
    print( foo );
  }
}

虚函数可用于重载函数

在UnityScript中,你能够建立虚函数。

class Foo
{
     virtual function DoSomething () 
     {
         Debug.Log("from base class");
     }
}
 
//SubFoo.js
class SubFoo extends Foo
{
     virtual function DoSomething() 
     {
          Debug.Log("from sub class");
     }
}
 
//Elsewhere
var foo : Foo = new SubFoo();
foo.DoSomething();//prints from sub class

 

若是你要调用父类的方法,用关键字super。示例以下:

class SubFoo extends Foo
{
     virtual function DoSomething()
     {
          super.DoSomething();
          Debug.Log("from sub class");
     }
}
 
//Elsewhere
var foo : Foo = new SubFoo();
foo.DoSomething();//prints "from base class" and "from sub class"

 

考虑用编写Mixins与Helpers的方式替代子类继承

能够很容易编写相互访问与调用的类,但还有一种方式可能比用对指定对象进行子类继承具备更好的维护性。

如:

/* Foo.js */
var bar : Bar;
 
function Start(){
  bar = gameObject.GetComponent(Bar);
}
 
function doEet(){
  // do my own thing
  if( bar ){
    bar.doEet();
  }
}
 
/* Bar.js */
  function doEet(){
    // do something special
  }
 

7. 声明字符串类型是使用String (表明Mono中的String类) 而不是string

var x : String;

你在JavaScript所知道及喜欢的字符串函数都在,不一样的是调用时首字母大写。


好比如何分割字符串,写法以下:

var qualifiedName = "System.Integer myInt";
 
var name = qualifiedName.Split(" "[0]);
 

分割后,name[1] 就包含"myInt"。


要查看可用的字符串函数清单,请访问Mono文档(http://go-mono.com/docs/monodoc.ashx?link=T%3aSystem.String%2f*)

8. 变量在使用前必须进行声明

a = "fred"; // works in JavaScript (a is treated as a global), error in Unity

var a = "fred"; // a is now a string variable containing 'fred'
var b: String; // b is now a string variable, with no assigned value
b = "wilma";
var c; // c is now a dynamically typed variable with no assigned value
c = "barney";
c = 17;

 

a) 你能够(一般也应该这么作)显式声明变量的做用域,如private、public等。不声明的话,默认表明public。

b) 在你声明一个变量时,若是直接赋值给它,Unity就会隐式的给它定义一个数据类型,因此:

var a = "fred"; // a is now of type String
a = 5; // ERROR! -- a is a String
var b : String = "fred"; // redundant
 

但:

var a; // a is dynamically typed;
a = "fred"; // works
a = 5; // works

9. 方法名与类名一般都是首字母大写

方法名与类名通常是首字母大写的,除非当它们不是遵循这一原则的时候。这句话很矛盾。本质上,UnityScript是处在.NET的命名约定的环境 - 方法名采用CamelCase这种骆驼峰式及camelCase这种首字母大写的写法、属性采用骆驼峰式且首字母小写,但它也试着像JavaScript的写法 - 像C同样,严重分化成小写命名及camelCase骆驼峰式。

如 JavaScript 中, typeof("fred") == 'string', 但在Unity中你的写法是 var a: String;

10. 多了不少数据类型、两种数组、无对象语法糖

JavaScript本质上有三种类型:数值、字符串与对象(函数与数组都是对象)。UnityScript则具备更多的数据类型,包括:

1)对象:不能与array、Array进行互相转换;

var a = new Object(); // works
a.fred = "wilma"; // runtime exception!
 

2)原生数组:没法进行动态调整;

var a = [1, 2, 3];
a.Push(4); // ERROR -- won't work!
 

若是要定义指定类型的数组,语法以下:

public var friendsOfCarlotta : Transform[];

 

3)UnityScript Array:能够动态调整

var a = new Array();
a.Push(4); // This works
 

你能够把UnityScript Array转换成原生array,效率更高但灵活性下降,具体是使用方法ToBuiltIn(ArrayType),如:

var a = new Array();
a.Push(1);
a.Push(3.1415926535);
a.Push(17);
var b = a.ToBuiltin(float);
 

4)整型(包括int、uint3二、等等):

Unity支持各类整型的大数,你不须要担忧。

5)Unity的大量内置类(如Vector3):

你在使用Unity的过程当中,你会愈来愈熟悉这些类,就像Web开发时那些DOM类同样。Unity中的类相比DOM要少。

使得用Unity很是有趣的一件事是,它的类采用了很是自由的mixin策略。一般你能够很是快速简单的查到你所须要的类。最通用的一个例子是Transform类,对于你正在处理的一个对象,全部被附加到该对象的相关联的类,你均可以简单快速的获取。(译注:这段话没有翻译好)

好比,典型的动做就是你会访问名叫"transform"的变量,它表明与该对象关联的Transform类的实例。若是你须要相关的位置坐标,就访问transform.position(一个 Vector3对象);若是你须要它的GameObject,就访问transform.gameObject;若是你须要它的渲染器,就访问transform.renderer。等等。通常若是你一个对象的主要属性,你就能够快速获取全部其余的属性。

a) Unity的String类缺乏JavaScript中字符串的好的特性;

b) Unity的内部数组远不如JavaScript中数组和对象的灵活。固然,能够用各类集合类如List、Queue、Dictionary等来配合实现。内部数组速度是最快的。

11. 每一个.js文件默认表明一个类

通常来讲,贴入以下代码:

var x : int;
function y(){}
 

并保存到Foo.js文件中,等同于在该文件中输入以下代码:

class Foo extends MonoBehaviour {
  var x : int;
  function y(){}
}
 

可是,你能够在同一个文件中声明多个类,尤为是当你须要使用一些辅助工具类时很是有用,通常这种辅助类没有继承自MonoBehaviour。

class ButtonState {
  var currentState : int;
  var offset : Vector2;
}
 

若是你在一个文件中声明了MonoBehaviour的子类,但类名与文件名不匹配的话,即便是大小写不一致,你也会碰到麻烦。

必定要理解,当你在js文件中编写行为脚本是,你实际上在编写一个类:

a) 文件名就是类名,若是文件名是foo.js,你就能够在其余地方以var x = new foo()的格式进行调用;

b) 有一些特定的方法是实现系统预先定义的一些事件处理器,如Start、FixedUpdate等。任何事件中,声明的一个函数就是这个文件所表明的类的一个方法。

c) 文件中在函数定义以外编写的代码都在该类的范围以内执行,声明的变量也是该类的成员变量。

d) 类中的静态函数、变量本质上是类的方法与属性。

这种方式远比真正的JavaScript中实现的类来的更优雅,但某种程度上来讲是限定你以更好的方式进行编码。

例如,建立一个行为脚本,命名为foo,那文件名就应该是foo.js。假设文件的内容以下:

public name : String; // when you drag the behavior onto a gameobject, these values will be visible and editable
public age : int; // other scripts which have a reference to this object (e.g. if they're attached to the same object) can see public functions
private favoriteColor : Color; // private members are NOT visible to other scripts, even if they have a reference to this object
public bestFriend : foo; // you can assign a value to bestFriend by dragging a gameObject with an attached copy of the foo behavior to this property. This will give you access to bestFriend's public methods and members
 
function Update(){
  // this function will be called every frame by Unity, so it's actually an event handler
  var t = transform; // transform is a property inherited from the gameObject the behavior is attached to
}
 
function Bar(){
  // this is just a function, if you don't call it yourself, it will never do anything
}
 

调用父类方法

super()表明父类的构造函数,supper则至关于父类中的本来应该以this访问的成员函数,如super.foo()表明代用父类的foo()方法。

12. 分号不可缺乏

JavaScript中分号是可写可不写的,但在Unity中必需要写。

var foo = 3 // OK in JavaScript but an error in Unity
foo += 17
 

13. Math 对应 Mathf; Math.abs 对应 Mathf.Abs

JavaScript的Math库在Unity中对应为Mathf库,而且方法名是首字母大写的,如Math.abs()在Unity中就应该是Mathf.Abs()。

2、Mono运行环境 (.NET)

UnityScript运行环境使用Mono - .NET的开源克隆。实际上,UnityScript是用Boo实现的,Boo是运行在Mono虚拟机上的一种语言,而且编译成本机代码。JavasScript不少典型的运行环境如String和Math库由Mono提供。你也就知道了为何UnityScript中方法名要大写了,由于要与Mono中相同。

要使用Mono库,就须要导入它们,如:

import System;
import System.IO;
 

不然,你带指定完整的命名空间来调用函数,如System.IO.File.Open(),而不是File.Open()。

Mono中的数据类型

当Mono函数须要字符char做为输入参数时,你能够简单的使用索引来获取,好比你像将小写的a做为字符char a传递,你能够这样写:

"a"[0]

如,当使用String.Replace()函数时,写法以下:

var s : String = "Whatever_it_may_be";
s = s.Replace("_"[0], " "[0]); // replace all the underscores with spaces

 

当处理Array对象时,能够把它转换成更快的内置array类型数据:

fastArray : SomeType[] = monoArray.ToBuiltin(SomeType);

UnityScript可使用泛型,因此当用到动态大小的数组时,最好用List来替代Array。基本上就没有什么理由要用到Array了,List更快而且功能更多。若是你须要混合类型的数组,你能够用Object List。UnityScript中泛型的语法与C#中接近,除了要加一个额外的“.”符号在“<>”以前。如C#中的"var myList = new List<int>();"在UnityScript中对应的写法为:"var myList = new List.<int>();"。

使用第三方.NET库

第三方.NET库如XML-RPC能够以新建资源Asset的方式引入。

3、调试

脚本错误 会在Unity窗口状态栏中以红色x图标显示。点击该图标,会打开console视图,显示错误列表,而且能够快速跳转到脚本中出错的那一行。

Unity也会生成有用的警告,这些警告以黄色的!图标显示,好比会告诉你声明的一个变量没有用到过。努力让编写的代码不会生成警告信息是一个很是好的习惯。

print()函数将会生成消息,并输出到状态栏及控制台中,但仅限MonoBehavioour类范围内。更好的办法是使用Debug.Log("insert message here");,该方法处处均可使用。还可用Debug.LogWarning 和 Debug.LogError生成警告和错误消息。

Debug.Break(); 能够将游戏暂停在一个精确的点。当一种特定的情条件发生时,若是你想检查对象的状态的时候,这个特性很是有用。

开发环境运行项目时,编辑界面也是彻底实时更新的,你能够查看对象实例的内部状态。

 

本文由Tonio Loewald (a.k.a. podperson)编写。翻译:http://x3d.cnblogs.com/p/3838619.html

相关文章
相关标签/搜索