译者注:

Unity3D中支持三种语言:JavaScript、C#、Boo,很多人不知道如何选择,通过这篇译文,我们可以搞清楚这三者语言的来龙去脉,对选择主语言有一定的借鉴意义。

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

花了一上午,才译完,文章有点长,估计有耐心看完的都不多,呵呵。

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

1. 使用 #pragma strict

#pragma strict

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

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是动态解释型的。

在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、uint32、等等):

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()。

二、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的方式引入。

三、调试

脚本错误 会在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

Unity3D脚本语言UnityScript初探的更多相关文章

  1. unity3d脚本语言中的引用类型

    在之前的文文里有说到,值类型和引用类型,那么这会就单独说下引用类型: Unity3D中的C#语言提供了专门的类型来为开发者提供使用C#开发游戏的便利条件: 在该引擎中,使用UnityEngine命名空 ...

  2. unity3d为什么会有三种脚本语言?

    相信这个问题多多少少会令许多初学者感到困惑,因为他们不知道应该选择哪种语言好,但是都会从以下几个方面进行考虑: 1.学习成本.哪门语言让我快速上手. 2.文档帮助.说白了就是出了问题,有没有人能解决. ...

  3. Unity3d中如何混用三种脚本语言?

    首先要明白,这三种说的混用是指文件级别的混用,就是说一个文件是由一种语言写的.而不是说你这一个文件可以混用这三种语言,注意这是不允许的. 第二要明白,在unity3d中为什么可以使用三种语言混合开发? ...

  4. 为什么叫Unity3d为脚本语言

    初接触Unity,看到大家说的都是工作主要是写脚本语言. 一直纳闷为什么说脚本语言呢,c#可不是脚本语言啊. -- -- 后来释然,说它是脚本语言是因为传统程序都是由代码构成的(像iOS.Androi ...

  5. 小试牛刀C#作为脚本语言执行解密

    背景 我们知道Unity3d是通过C#脚本语言的形式来实现游戏的逻辑代码编写,同样SCOTT服务器也设置了通过C#脚本来实现游戏逻辑,但是本文并不是想真正分析解密他们的运行机制,只是想通过自己的一个需 ...

  6. [转]unity3d 脚本参考-技术文档

    unity3d 脚本参考-技术文档 核心提示:一.脚本概览这是一个关于Unity内部脚本如何工作的简单概览.Unity内部的脚本,是通过附加自定义脚本对象到游戏物体组成的.在脚本对象内部不同志的函数被 ...

  7. Unity3D脚本中文系列教程(三)

    http://dong2008hong.blog.163.com/blog/static/4696882720140302323886/ Unity3D脚本中文系列教程(二) 示,属性不被序列化或显示 ...

  8. Unity3D脚本--真实1

    1. Unity3D动作脚本 Unity3D脚本用于Unity3D发动机订单公布. JavaScript全局变量:在Inspector中能够看到,且能够改动其值.其他脚本可调用此变量. C#公有(pu ...

  9. InstallShield 脚本语言学习笔记

    InstallShield脚本语言是类似C语言,利用InstallShield的向导或模板都可以生成基本的脚本程序框架,可以在此基础上按自己的意愿进行修改和添加.     一.基本语法规则      ...

随机推荐

  1. [转]一个小试验验证对象的指针计数置为nil的情况

    本文转载于新风作浪的博客专栏,博客地址:http://blog.csdn.net/duxinfeng2010/article/details/8757211 以下博客原文: 最近遇到这样一个问题,以前 ...

  2. 新一代服务器性能测试工具Gatling

    新一代服务器性能测试工具Gatlinghttp://automationqa.com/forum.php?mod=viewthread&tid=2898&fromuid=2

  3. Oracle 11g RAC客户端使用SCAN IP无法连接问题

    Oracle 版本:11.2.0.1.0 客户端:Windows Server 2003/PLSQL Developer Oracle服务器端的ip设置如下: ##公网ip 192.168.135.2 ...

  4. Java Web 工作技巧总结 16.8

    摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢! 四时不谢之兰,百节长青之竹,万古不败之石,千秋不变之人. 1. AOP – LOG项目中,一个请 ...

  5. WPF常用控件样式集锦

    1.不规则形状按钮(通过更改path实现) <Style x:Key="ButtonStyleForPath" TargetType="{x:Type Button ...

  6. 自定义控件的自定义的属性attrs.xml下的declare-styleable中format详解

    最近在摸索自定义控件,查找到一些自定义属性的一些资料,解决转载记载下来:看了此详解才方便理解! 我们在做项目的时候,由于android自带的属性不能满足需求,android提供了自定义属性的方法,其中 ...

  7. 如何在IIS7或IIS7.5中导入导出站点及应用程序池.

    为实现负载平衡,我们可能会使用多个WEB服务器,也就会需要给多个IIS配置同样的站点和应用程序池.那么我们需要一个一个的重新建吗?当然不用,我们只需要一些简单的命令就可以在IIS7(Windows S ...

  8. Jenkins+Maven+SVN快速搭建持续集成环境(转)

    Jenkins是一个可扩展的持续集成引擎,Jenkins非常易于安装和配置,简单易用,下面看看我们是如何几分钟就快速搭建一个持续集成环境吧. 假设我们目前已经有2个maven项目:entities(J ...

  9. ruby -- 进阶学习(八)自定义方法route配置

     在route中进行修改,添加下面代码 namespace :mycontroller do get 'mymethod' , :on=> :member end end 注: :on => ...

  10. NPM使用详解(下)

    NPM使用详解(下) *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !impo ...