Unity3D脚本语言UnityScript初探

简介: 译者注: Unity3D中支持三种语言:JavaScript、C#、Boo,很多人不知道如何选择,通过这篇译文,我们可以搞清楚这三者语言的来龙去脉,对选择主语言有一定的借鉴意义。 首先,Unity是基于Mono也就是.Net的运行环境的,所以它肯定支持C#;然后,Unity团队自行开发了一种Boo的语言;后面可能考虑到用户的接受程度的问题,又开发了类似JS的一种语言,但那绝对不是JS,勉强可以称之为UnityScript。

译者注:

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

目录
相关文章
|
C# 图形学
Unity 3D开发-C#脚本语言的一些基础用法
Unity 中C#语言的一些基础用法 本文提供全流程,中文翻译。Chinar坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 —— 高分辨率用户请根据需求调整网页缩放比例) ...
1886 0
|
3月前
|
图形学 C#
超实用!深度解析Unity引擎,手把手教你从零开始构建精美的2D平面冒险游戏,涵盖资源导入、角色控制与动画、碰撞检测等核心技巧,打造沉浸式游戏体验完全指南
【8月更文挑战第31天】本文是 Unity 2D 游戏开发的全面指南,手把手教你从零开始构建精美的平面冒险游戏。首先,通过 Unity Hub 创建 2D 项目并导入游戏资源。接着,编写 `PlayerController` 脚本来实现角色移动,并添加动画以增强视觉效果。最后,通过 Collider 2D 组件实现碰撞检测等游戏机制。每一步均展示 Unity 在 2D 游戏开发中的强大功能。
160 6
|
2月前
|
测试技术 C# 图形学
掌握Unity调试与测试的终极指南:从内置调试工具到自动化测试框架,全方位保障游戏品质不踩坑,打造流畅游戏体验的必备技能大揭秘!
【9月更文挑战第1天】在开发游戏时,Unity 引擎让创意变为现实。但软件开发中难免遇到 Bug,若不解决,将严重影响用户体验。调试与测试成为确保游戏质量的最后一道防线。本文介绍如何利用 Unity 的调试工具高效排查问题,并通过 Profiler 分析性能瓶颈。此外,Unity Test Framework 支持自动化测试,提高开发效率。结合单元测试与集成测试,确保游戏逻辑正确无误。对于在线游戏,还需进行压力测试以验证服务器稳定性。总之,调试与测试贯穿游戏开发全流程,确保最终作品既好玩又稳定。
93 4
|
3月前
|
图形学 缓存 算法
掌握这五大绝招,让您的Unity游戏瞬间加载完毕,从此告别漫长等待,大幅提升玩家首次体验的满意度与留存率!
【8月更文挑战第31天】游戏的加载时间是影响玩家初次体验的关键因素,特别是在移动设备上。本文介绍了几种常见的Unity游戏加载优化方法,包括资源的预加载与异步加载、使用AssetBundles管理动态资源、纹理和模型优化、合理利用缓存系统以及脚本优化。通过具体示例代码展示了如何实现异步加载场景,并提出了针对不同资源的优化策略。综合运用这些技术可以显著缩短加载时间,提升玩家满意度。
112 5
|
2月前
|
前端开发 图形学 开发者
【独家揭秘】那些让你的游戏瞬间鲜活起来的Unity UI动画技巧:从零开始打造动态按钮,提升玩家交互体验的绝招大公开!
【9月更文挑战第1天】在游戏开发领域,Unity 是最受欢迎的游戏引擎之一,其强大的跨平台发布能力和丰富的功能集让开发者能够迅速打造出高质量的游戏。优秀的 UI 设计对于游戏至关重要,尤其是在手游市场,出色的 UI 能给玩家留下深刻的第一印象。Unity 的 UGUI 系统提供了一整套解决方案,包括 Canvas、Image 和 Button 等组件,支持添加各种动画效果。
122 3
|
2月前
|
设计模式 存储 人工智能
深度解析Unity游戏开发:从零构建可扩展与可维护的游戏架构,让你的游戏项目在模块化设计、脚本对象运用及状态模式处理中焕发新生,实现高效迭代与团队协作的完美平衡之路
【9月更文挑战第1天】游戏开发中的架构设计是项目成功的关键。良好的架构能提升开发效率并确保项目的长期可维护性和可扩展性。在使用Unity引擎时,合理的架构尤为重要。本文探讨了如何在Unity中实现可扩展且易维护的游戏架构,包括模块化设计、使用脚本对象管理数据、应用设计模式(如状态模式)及采用MVC/MVVM架构模式。通过这些方法,可以显著提高开发效率和游戏质量。例如,模块化设计将游戏拆分为独立模块。
169 3
|
3月前
|
图形学 开发者 存储
超越基础教程:深度拆解Unity地形编辑器的每一个隐藏角落,让你的游戏世界既浩瀚无垠又细节满满——从新手到高手的全面技巧升级秘籍
【8月更文挑战第31天】Unity地形编辑器是游戏开发中的重要工具,可快速创建复杂多变的游戏环境。本文通过比较不同地形编辑技术,详细介绍如何利用其功能构建广阔且精细的游戏世界,并提供具体示例代码,展示从基础地形绘制到植被与纹理添加的全过程。通过学习这些技巧,开发者能显著提升游戏画面质量和玩家体验。
127 3
|
3月前
|
图形学 机器学习/深度学习 人工智能
颠覆传统游戏开发,解锁未来娱乐新纪元:深度解析如何运用Unity引擎结合机器学习技术,打造具备自我进化能力的智能游戏角色,彻底改变你的游戏体验——从基础设置到高级应用全面指南
【8月更文挑战第31天】本文探讨了如何在Unity中利用机器学习增强游戏智能。作为领先的游戏开发引擎,Unity通过ML-Agents Toolkit等工具支持AI代理的强化学习训练,使游戏角色能自主学习完成任务。文章提供了一个迷宫游戏示例及其C#脚本,展示了环境观察、动作响应及奖励机制的设计,并介绍了如何设置训练流程。此外,还提到了Unity与其他机器学习框架(如TensorFlow和PyTorch)的集成,以实现更复杂的游戏玩法。通过这些技术,游戏的智能化程度得以显著提升,为玩家带来更丰富的体验。
61 1
|
3月前
|
图形学 数据可视化 开发者
超实用Unity Shader Graph教程:从零开始打造令人惊叹的游戏视觉特效,让你的作品瞬间高大上,附带示例代码与详细步骤解析!
【8月更文挑战第31天】Unity Shader Graph 是 Unity 引擎中的强大工具,通过可视化编程帮助开发者轻松创建复杂且炫酷的视觉效果。本文将指导你使用 Shader Graph 实现三种效果:彩虹色渐变着色器、动态光效和水波纹效果。首先确保安装最新版 Unity 并启用 Shader Graph。创建新材质和着色器图谱后,利用节点库中的预定义节点,在编辑区连接节点定义着色器行为。
235 0
|
2月前
|
图形学 C++ C#
Unity插件开发全攻略:从零起步教你用C++扩展游戏功能,解锁Unity新玩法的详细步骤与实战技巧大公开
【8月更文挑战第31天】Unity 是一款功能强大的游戏开发引擎,支持多平台发布并拥有丰富的插件生态系统。本文介绍 Unity 插件开发基础,帮助读者从零开始编写自定义插件以扩展其功能。插件通常用 C++ 编写,通过 Mono C# 运行时调用,需在不同平台上编译。文中详细讲解了开发环境搭建、简单插件编写及在 Unity 中调用的方法,包括创建 C# 封装脚本和处理跨平台问题,助力开发者提升游戏开发效率。
167 0