UnityScript学籍验证码是什么么,怎么学

Unity 是一个以 Mono 为基础的游戏开发环境,能同时支持三种脚本语言,包括 C#、Javascript 和Boo (类似 Python)。由于Unity 的开发工具暂时只有 Mac 的版本,所以暂时未能测试。但是它有很详细的文档,看上来很易用,所以就从文字上学习它的Script 使用方式。根据一些Tutorial 及参考手册,我用 Graphviz 画了一个 (我认为) 最核心的 UML 类别图:从这个类别图我们可以理解它的结构,及如何把一些常用功能映射至这系统里,以下分节讨论。GameObject 和 ComponentUnity 的执行环境里,会有一个场境 (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 *var z = Input.GetAxis("Vertical") * Time.deltaTime *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 : Tfunction Update () {transform.LookAt(target);}这个Script 暴露了一个 target 变数 (应当作成员变数吧),使用者可以把其他物件的变数 assign 至这个变数。这 assignment(任务) 有两种方法实现,其一是利用Unity 的 GUI 工具把一个Component 实体的变数 (如Transform) drag-and-drop 至这个Component 实体的 target 变数,而另一个方法是写代码:var newTarget = GameObject.Find("Cube").GetComponent(Follow).target = newT用代码就可以这样动态改变这些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。
阅读(...) 评论() &unity3d java script 脚本学习笔记_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
unity3d java script 脚本学习笔记
&&unity3d java script
阅读已结束,下载文档到电脑
想免费下载本文?
定制HR最喜欢的简历
你可能喜欢2443人阅读
转载自/articles/179.html,写得实在太好
UnityScript(即javascript for Unity)的教程网上千千万,中文的也不少,但是讲Unity3D界面操作的多,讲UnityScript这个语言的少,同时对于UnityScript的描述部分,也是入门的教程多,对语言特性的描述少,能够成系统的我就根本没有找到过。连续的看了不少的Unity3D的文章,书籍,但是发现写代码的时候,对UnityScript的细节掌握仍然不甚了了,也就是对怎么写UnityScript效率更高,更加符合语言设计的目的,风格等事情并还没有清晰的认识。这个对于习惯写脚本的人来说,可能是常态,对于习惯C++我来说,简直难以忍受。
看到这样的名字,学过编程的人都知道我是模仿了经典的C语言教材,目的也是一样。,本文的目的不是再多写一个教程,而是希望对UnityScript这个语言进行一个较为深入细节,并且准确的描述。也就是说,相对于教程,本文会更加像一个语言说明书。同时,更不用说的就是,本文会甚少涉及Unity3D本身的界面操作,仅仅关注于UnityScript这个语言,不要希望通过本文学会Unity3D,但是,当你对Unity3D有了些基本的了解后,希望写一个大型游戏时,本文会对你该怎么写脚本,怎么写对脚本,怎么样写好脚本,并且避免掉进语言的陷阱中有一些帮助。
更进一步的说,因为UnityScript完全是Unity3D控制的语言,同时仅在Unity3D中可用,所以对于UnityScript来说,甚至于连哪些是属于语言本身的特性,哪些属于库的扩展,这些都分不清楚。这比Objective C还混乱……在Unity里面想要完全的区分开库和语言几乎不可能,但是本文还是会尽量做这方面的尝试,尽量将本文的主要关注点放在语言上,而不是库上。
一般的说法UnityScript是javascript for Unity,事实上,这个脚本语言更加接近JScript,这个MS发挥了其一贯不尊重标准本性做的javascript方言。甚至于,对JScript的风格更加喜欢,而抛弃了javascript本身的一些东西。这些事情,MS因为兼容性问题都没有敢做。当然,考虑到Unity的开发者使用了Mono这个开源的.Net,而不是诸如Java, Lua, Python等其他选择,说明Unity开发者有很强烈的MS向,这点也不让我感到意外。
Hello World
下面是经典Hello World程序的UnityScript代码
#pragma strict
function Start () {
print('Hello World\n');
在Unity的控制台上输出”Hello World”。需要注意的是,在Unity中创建该脚本后,需要绑定到某个GameObject中成为一个Script的组建(compent)后才能运行。
function Start() { code here… } 就是Unity里面用function关键字来定义新函数的方式。这个没有太多好讲的。
#pragma strict可以更加严格的进行静态类型检查,能够引导你更加好的写更加严谨和更好风格的代码,这个在后面还会再次提到,现在记得在开发的时候推荐都写上就行。
比较需要说明的是,一个UnityScript文件默认就是一个类,并且继承自MonoBehavior,类的名字就是文件名。以上的代码(假设保存在名为HelloWorld.js文件中)与下面的代码意义一模一样:
#pragma strict
class HelloWorld extends MonoBehaviour
function Start () {
print('Hello World\n');
其中MonoBehaviour是Unity内置的一个类,目前我们只需要知道其中Start函数和Update即可。Start函数能保证在第一次Update被调用前调用,Update函数就是游戏每帧调用的刷新函数。其中Update函数没有参数,可以通过读取Time.deltaTime变量来获得两次Update之间流逝的时间。
上面的代码同时展示了用class关键字定义新类,用extends关键字继承的方式。这个和C++,Java的语法很像,都是class-based的,这也是UnityScript不同于普通JavaScript,而更像JScript的地方。
这种类定义思维方式,使得从C++,Java过来的人更加能够上手。这可能是MS和Unity开发者都喜欢这种方式的原因。
在UnityScript中使用一个变量,遵循先定义后使用的原则。
如下代码首先定义了一个名为str的字符串,然后输出str的值。
#pragma strict
function Start () {
var hello : String = 'Hello';
var world : String = 'World';
print(hello + ' ' + world + '\n');
需要注意一下的是var hello : String这种语法,表示静态的指定hello这个变量名(上例中还有个变量world也是一样)的类型为String(UnityScript内建的字符串),这种使用方式效率最高,虽然在javascript中原来直接使用var hello = “Hello”;的方式也还可以使用,但是不建议使用。这个问题以后也还会提到。
下面看一个较为完整的例子,打印一个从华氏温度(Fahrenheit)到摄氏温度(Celsius)的转换表(来自于K&R page 19)
#pragma strict
function Start () {
/* defines */
var lower : float = 0;
var upper : float = 300;
var step : float = 20;
var fahr : float = // define and assign
var celsius :
while (fahr &= upper) { // while loop
celsius = (5.0f / 9.0f) * (fahr - 32.0f);
print(fahr + & & + celsius + &\n&);
fahr = fahr +
没有什么太多新内容,展示了新的UnityScript类型float,表示浮点数,还有while循环。还有,UnityScript支持C++所有的两种注释形式,意义和使用方式完全一样,这个就不多说了。
本节是属于为了完整性加的引导,下面开始上干货了~~~
UnityScript中包含的数值数据类型(指int,float等数值)和普通的javascript差异巨大,因为从效率和内存占用的双重考虑(游戏开发必须的),都比网页开发要严格的多,所以UnityScript事实是沿用了.Net的数值类型定义方式,并且这些基本类型(貌似)其实是使用了.Net的System库中的类型,所以实际类型是System.XXX,UnityScript提供的仅仅是一个缩写别名而已。
这种方式对数值的大小和范围进行了严格限定,并提供了丰富的数值数据类型(普通的javascript和很多脚本语言,为了方便理解,都将所有的数字归为统一的Number类型),具体的如下:
类型名(type name)
类型(type)
位数(width)
范围(range)
Unsigned integer
Signed integer
-128 ~ 127
Signed integer
-32,768 ~ 32,767
Unsigned integer
Signed integer
-2,147,483,648 ~ 2,147,483,647
Unsigned integer
Signed integer
-477508 ~ 477507
Unsigned integer
Single-precision floating point type
Double-precision floating point type
-1.32e308 ~ 1.32e308
A single Unicode character
Unicode Symbols
以上数值的定义完全是因为机器的表示实现导致的,所以虽然是.Net中的一套东西,但是和C++的数值体系几乎完全一致,只是把long作为单独的64位整数而已(JAVA早就这么做了)。
特别需要注意的是,UnityScript的char类型默认就是16位的,用于Unicode表示。
知道数值类型是这样的体系后,可以预想的是,其行为和C++里面完全一样,该有的溢出坑一个也不会少,运算时,各种类型转换复杂规则和各种转换带来的坑也少不了。好处当然就是速度及你能选择最合适大小的数值类型,在大规模数据保存的时候,可以省很多内容和磁盘。
需要顺面提到的一点是,所有的数值类型,其实自带一对内置的成员变量,MinValue和MaxValue,用于表示此类型可以表达的最小,最大值。
先看数值溢出的坑:
#pragma strict
function Start () {
var bytenumber : byte = 255;
var result : byte = bytenumber + 1;
print(&result=& + result);
上面的代码会输出0,从C++过来的人就不用惊讶了,也许仅了解Python,lua等脚本语言的人很难接受吧。具体的原因在于数值位表示的局限,当(上例中255的机器码)再加一的时候,已经没法再表达,变成了,即0.更多细节参考.类似的问题在符号类型的时候会更加让人惊讶,比如sbyte的&#再加1会等于-128。
再看类型转换的问题,一般而言,像C++这样的语言,很大程度上靠静态类型(编译期验错)来减少程序的bug,这在用户自定义的类上面,能起到很大的作用。但是对于数值运算来说,要是还是严格遵循类型定义,那么就会相当麻烦,想象一下一个两个整数想加,或者1个整数加1个浮点数时还必须用强制转换才能进行的情况,那样的表达式表达一个长的数学表达式简直就会是噩梦,(何况在UnityScript没有提供强制转换的方式)系统一般会提供一套各个数值类型之间运算时隐式转换(Implicit conversion)的规则,常见的规则是表达更大范围的数值类型进行转换,称为类型提升(type
promotion),int + long,转成long,以减少数值溢出的情况。
见下面的例子:
#pragma strict
function Start () {
var bytenumber : byte = 255;
var result = bytenumber + 1;
print(&result=& + result);
我仅仅把result的类型由Unity自动识别,程序就能正常输出256。也许有些人会奇怪,为什么不是0了?
print(“result type=” + result.GetType());
语句,我们能看到最后result的类型:
result type=System.Int32
result实际是int类型(在.net中的System.Int32),理由呢?就是类型自动提升,因为在bytenumber + 1的表达式中的整数常量1实际上就是int类型的,在byte类型的变量bytenumber与其相加时,bytenumber自动提升到int型,然后再进行运算,所以没有溢出。
到目前为止规则还好理解?看个不好理解的:
#pragma strict
function Start () {
var number : int = int.MaxV
var unsignedNumber : uint = 1;
var result = number + unsignedN
print(&result=& + result);
print(&result type=& + result.GetType());
在无符号和有符号运算时会发生什么?
结果输出:
result type=System.Int64
结果没有溢出,也没有把result改为uint(其实也不会溢出),而是直接换成了long(在.Net中用System.Int64表示)快晕了吗?这就是想要随心所欲的使用自己数值类型的代价。
我总结的转换倾向如下:
1.优先转为浮点类型
2.优先转为可表示数值范围大的类型
还有很多更值得琢磨的东西,浮点和整数一起运算的情况,仿照K&R,我这里给出一个详细规则:(自己研究的,目前不保证完全正确,假如有问题请提出来)
1.在比int更小(sbyte,byte,short,ushort)的整数类型之间进行运算时,都换成int类型进行运算。(此时肯定不会发生溢出)
2.int类型与比int小的整数类型间发生的运算,会将另外一个提升到int进行运算。(此时有可能发生溢出,从C语言继承过来的老规则,原因可能还是因为32位效率最高)
3.有符号和无符号的int类型发生计算提升到long。(此时肯定不会发生溢出)
4.在int和long,ulong之间发生的计算,提升int到long。(此时有可能发生溢出)
5.在long和ulong之间发生的计算,将ulong转为long。(此时有可能发生溢出)
6.在double参与的计算中, 将另一个数转为double。
7.在float参与的计算(另一个数不是double)中,将另一个数转为float。即时另一个数是比float宽度更大的long也是如此。
这里仅列出规则1的例子:
#pragma strict
function Start () {
var num1 : sbyte = 1;
var num2 : byte = 1;
var result = num1 + num2;
print(&result=& + result);
print(&result type=& + result.GetType());
result type = System.Int32
两点说明:
一、即使sbyte,byte相加时,short肯定能表示,但是还是会提升到int(类似越级),原因可能是因为int类型的运算在原来的32位机器上效率是最高的。
二、类型提升更会不会实际发生溢出没有关系,即使是1+1=2这样简单的运算,还是会进行类型提升,因为在运算前,编译器没法判断是否会发生溢出。
另外,除了这些基本的数值类型,还有以下类型:
类型名(type name)
类型(type)
位数(width)
范围(range)
Logical Boolean type
true or false
A sequence of characters
Base type of all other types
其中,boolean是布尔型,只能是是true和false,需要特别注意的是,布尔类型比C++的要严格,不能当作整形来计算。
然后,UnityScript虽然用的是.Net的数值类型,但是直接支持decimal这个精确浮点数类型,但是允许通过System.Decimal的形式使用。
Object类型是所有类型的基类,类似JAVA中的情况,这里不多说了,在类的部分会再次提到。
除此以外,UnityScript还提供了内置的数组及Array类型。这些以后会详细讲到。
字面常量(Literals)
在表达式中出现的整形常量,默认为int。
在表达式中出现的浮点型常量,默认为float。(这个需要特别注意)
但是可以像C++中那样,直接通过后缀指定常量的类型,但是可用的后缀比C++少很多,能用的后缀见下例:(还有其他的可以告诉我)
#pragma strict
function Start () {
// integer
var a = 10; // default is int
var b = 10l; // long
var c = 10e5; // float
print(&a's type=& + a.GetType());
print(&b's type=& + b.GetType());
print(&c's type=& + c.GetType());
var x = 10.0; // default is float
var y = 10.0f; // float
var z = 10.0d; // double
print(&x's type=& + x.GetType());
print(&y's type=& + y.GetType());
print(&z's type=& + z.GetType());
注释就是其实际类型,有些不一样的是浮点数默认为单精度浮点类型,不是C++中的double,好处是,将来可以少些很多f后缀……
赋值时的隐式类型转换
除了数值计算时会发生隐式转换外,赋值时,左右类型不一样的情况,也会发生隐式的类型转换,比如下例中:
#pragma strict
function Start () {
var x : char = 97;
var y : byte = 256;
print('x=' + x);
print('y=' + y);
97,256默认是int型,但是将其赋值给char,byte型时,进行了类型转换。在超出左边类型表达上限的情况时,会对右边的数值进行截断(truncation)操作。比如上例中,y会等于0.
与计算时的隐形转换有尽量提升类型,减少溢出的可能性相比,不管右边的数值是什么类型,是比等号左边的类型大,还是小,只要能进行有意义的转换,都会进行转换,即使是从大到小(此时发生截断)。
转换的规则和C++很类似
1.小数到整数直接去掉小数部分
2.从大的整数到小的整数发生截断,相当于直接忽略大整数中高位(小整数类型无法表示的部分)
讲到这里,顺面说条好的编程实践规则:
一般的计算仅使用有符号类型,仅在进行位运算时才使用无符号类型。
衍生出两条规则:
1.即使当1个变量从逻辑上来讲完全不可能是负,也使用有符号类型
2.即使当你发现有符号类型的上限快到了,希望扩充其上限,也不要使用将有符号类型改为无符号的方式,这种方式仅将上限扩充了一倍而已,要使用直接扩充到上1级类型的方式,即从short=&int,int=&long。
通过上面的类型转换规则可以看到,这样的使用方式可以避免出现同类型有符号和无符号计算的情况,可以避免很多类型的转换和提升,对数值的溢出判断也更加直接一些。
UnityScript中使用了final关键字用于表示常量,同时,我们可以以全大写的惯例来表示常量。有意思的是,我以前从来没在文档中看到过Unity3D开发组对final的描述。
其中总的来说,UnityScript对数据类型的处理和C++很相似,有的地方甚至更加严格,并且完全不支持任何C/C++,C#里面那种类型加括号方式的强制类型转换,所以还算比较好理解。就是表达方法上为了接近javascript(实际上和JScript一样),总是以var开头,导致每个变量的定义都会多敲6次键盘,(为了好看,我一般还需要加一些空格,例子中都是)这个算是挺烦人的事情。
表达式和操作符
Javascript是面向表达式(Statement)的语言,这个就像我们熟悉的绝大部分语言一样,每个表达式用分号(;)结尾,并且这个分号不能忽略。(和javascript不一样)
去掉了undefined和Nah
UnityScript去掉了javascript中的和undefined,NaN,只有null,用于表示空对象。见下例:
#pragma strict
function Start () {
这个例子,在C++中根本就无法编译通过,但是UnityScript可以,同时,不会像javascript一样输出undefined,而是输出null。
UnityScript也没有Nah类型,直接在代码中存在的除0表达式会出现编译错误,运行时出现除0操作,会出现运行错误,不会有Nah值出现的情况。
数值转换
UnityScript拥有parseInt()和parseFloat()这两种javascript的数值转换函数,Number()实际等于double类型,不再做此用途。
而且parseInt函数实际是System.Int32.Parse (System.String s)函数,不像javascript中那样’灵活’的容错,不识别任何格式,需要完全是整数才能识别,意味着类似”1234blue”,”0×10”这种格式会出现运行错误。并且不带第二参数(javascript中用第二参数来表示进制)。
parseFloat也换成了System.Single.Parse (System.String s)函数,也没有javascript中那样的灵活性。不支持任何格式。
#pragma strict
function Start () {
var b = byte.Parse('256');
数值运算操作符
支持+,-,*,/,%,++, –
数值运算操作符,完全和C++及大多数语言里面的意义一模一样,不介绍了。
赋值操作符
除了=外,C++中有的两元赋值操作符(*=等),都存在,不介绍了。
&, |, ~, ^,&&,&&和C++里面一模一样,不介绍了。只要知道不支持javascript里面的无符号位移&&&,&&&就好了。因此,编程实践方面,最好也是和C++一样,进行位移操作的最好是无符号整数。
比较操作符
&,&,&=,&=,==,!=
需要注意的是没有javascript的严格比较===,因为类型本来就是相对确定的,同时在==比较时,不会进行javascript中那么多的类型转换。
比如下例:
#pragma strict
function Start () {
if ( 42 == '42' ) {
print(&42 == '42'&);
print(&42 != '42'&);
输出42 != ‘42’
这样结果会让习惯javascript的人感到意外,同时也会让习惯C++的感到意外,42和’42’竟然能够比较….-_-!我前面说UnityScript的类型有时候比C++要严格,注意,也就是有时候。不过,好在UnityScript中数值和字符串发生比较时,不会将字符串转换为整数后再比较,而是判断类型不一样,导致结果为假。这比javascript已经和好多了。
在不同类型发生比较时,也会发生类型转换。和计算是发生的事情类似,但是因为类似的转换放到逻辑操作中较少会出现意外,这里不详细描述了。
原生类型及引用类型
一般的语言中都会分原生类型和引用类型,比如在JAVA中,你比较两个字符串类型的变量,要是直接使用==操作符,其实比较的是两个变量指向的是否是同一个字符串,而不是判断两个字符串是否相等,需要使用单独的Equal函数,这个问题在C++比较char[]时也存在(因为C++的操作符重载的存在,将std::string的==比较操作符重载了)
UnityScript使用的方式还是类似的,前面提到的那些数值类型,一如既往的是原生类型,只是,特别注意的是,String被认为是原生类型,也就是说,在两个String进行比较时,比较的就是String的内容,而不是引用地址。见下面的代码:
var str1 : String = 'abc';
var str2 : String = 'abc';
if (str1 == str2) {
print('str1 == str2');
上面代码会输出str1==str2
而在比较其它的对象类型的时候,比较就是引用地址了。比如下例中:
var str1 : Array = ['a', 'b', 'c'];
var str2 : Array = ['a', 'b', 'c'];
if (str1 == str2) {
print('str1 == str2');
str1是不等于str2的….比较有意思的是,Array类型还提供了一个Equals函数,其实比较的也是引用地址,这个简直就是天坑。
在查找那么到底两个Array应该怎么比较的时候,发现了,那就是比较两个对象ToString以后的结果。我只能说,想出用这种办法来比较Array的人简直就是神。
#pragma strict
function ToString() {
return i.ToString();
function Start () {
var obj : C = new C();
obj.i = 1;
var str1 : Array = [obj, 'b', 'c'];
var obj2 : C = new C();
obj2.i = 2;
var str2 : Array = [obj2, 'b', 'c'];
print(obj.ToString());
if (str1.ToString() == str2.ToString()) {
print('str1 == str2');
print('str1 != str2');
这种比较在数组元素是简单类型时,一定正确,但是存储对象时,要对对象也有正确的ToString实现,不然结果可能会不正确。
这里还有个有意思的地方,在javascript的习惯里面,对象会有个ToString函数,而在C#的习惯里面,对象会有个toString函数,注意,两个函数的首字母大小写不一样,作为UnityScript,在mono这个开源.Net平台上自创的javascript语言,在使用.Net类的时候(比如上面的Array)两者都有!并且功能完全一样!这个实在是太丑陋了。
这里总结一下,上面提到的所有数值类型都是原生类型,再加上String,它们的比较都是通过比较值的方式,同时,在函数传参的时候,也是传值(意味着有复制的发生),其他的,包括原生数组,其他的对象,都是比较引用地址,在传参数的时候也是传引用地址,也就是说,没有内容复制的发生,同时,对引用地址变量的任何更改都是更改了原对象,见下例,用一个原生数组作为示例:
#pragma strict
function Start () {
var array : int[] = [1, 1, 2];
var array2 : int[] = new int[3];
array2[0] = 0;
array2[1] = 1;
array2[2] = 2;
if (ArrayUtility.ArrayEquals(array, array2)) {
print('array == array2');
print('array != array2');
#pragma strict
function changeStr( str : char[] ) : char[] {
str[0] = 'b'[0];
function Start () {
var str : char[] = [ 'a'[0], 'b'[0], 'c'[0] ];
changeStr(str);
print( Array(str) );
最后输出的是b,b,c,因为在changeStr里面对参数str的更改,实际更改了外部的str,这个C++及其他语言中见得多了,不再过多描述。
逻辑操作符
像C++一样,也有逻辑截断效果,当前面的操作可以确认结果时,后面的操作不再进行。
这里需要注意的是在UnityScript中,什么样的值会是true,什么样的值会是false:
false: 空字符串,0, null
true: 任何对象,非空字符串,非0数字
条件判断操作符
?:三目操作符UnityScript也支持,和C++一样
操作符优先级
操作符计算顺序和C++也完全一样,也没有理由不一样,不介绍了。
语句和控制结构
这是最没有什么好讲的一节。需要理解的是在UnityScript中变量的scope和C++不一样。
比如下面的代码:
#pragma strict
function Start () {
for (var i : i & 10; ++i) {
for(var m : m & 10; ++m) {
// still could read i
你会发现在第二个循环里面还能够读取到第一个循环里面的变量i,这种方式对于习惯了C++的人来说是个大坑,也就是说UnityScript中的局部变量不像C++中习惯的那个样子,UnityScript没有块级作用域(这个奇怪的特性来自于标准的javascript,不明白UnityScript为什么不改了)
其他的诸如if,else等条件控制,while,do-while,for等循环的代码和C/C++一样,不介绍了。
首先介绍一下for-in,for-in就是通常意义的foreach循环,直接遍历整个容器。
#pragma strict
function Start () {
var charArray : char[] = new char[10];
for (var i : i & 10; ++i) {
charArray[i] = 97 +
for(var value in charArray) {
print(value);
直接遍历整个容器算是语法糖,但是从抽象上来说的确比通常的for层次更高,也更难用错。
switch多条件控制
UnityScript的switch本质上和C/C++中的一样,但是有些加强,那就是case不再是仅可使用数值,甚至可以是变量,这个的确很强大,很多复杂情况的if-else都可以被替代了。
#pragma strict
function Start () {
var num : float = 10.0;
var numEqual : int = 10;
switch (num) {
case (num & 10.0):
print('num & 10.0');
case (num & 10.0):
print('num & 10.0');
case numEqual:
print('num == numEqual');
上例中会输出num == numEqual,这么复杂的条件判断,在C/C++中是不允许的。
函数,Lambda和闭包
函数里面值得研究的东西就多了,也是UnityScript中最有意思的部分,没有之一。这也是UnityScript作为从javascript过来的最大特点之一,javascript可是号称函数式编程的语言。
作为最大的特点,函数本身就是一个Function类型的对象,不是什么特殊的种类,也称函数为第一类值。此时函数可以像普通变量一样的操作,甚至从外部看不知道这是函数,并且还能随时调用。
首先看函数的定义,前面讲过一下,下面这个函数定义带参数和返回值:
function sum(num1 : float, num2 : float) : float {
return num1 + num2;
并且,和下面的定义方式没有区别:
var sumFun : Function = function(num1 : float, num2 : float) : float {
return num1 + num2;
也看赋值和传递:
var sumFun2 : Function = sumF
调用sumFun2和调用sum,sumFun是一样的效果。
函数作为参数
函数是第一类值,也就是说可以想变量一样使用,那么,参数也可以是函数,见下面的例子:
#pragma strict
function Start () {
var sum = function(num1 : float, num2 : float) : float {
return num1 + num2;
var call : Function = function( fun : Function, num1 : float, num2 : float, num3 : float ) : float {
return fun(fun(num1, num2), num3);
print(call(sum, 1.0, 2.0, 3.0));
在C++11中,Function,Bind库是最让我高兴的扩展,这在UnityScript中是语言本身的特性。
函数作为返回值
当函数作为第一类值的时候,函数甚至可以作为返回值返回,也就是说,可以实现一个返回值是函数的函数。见下例:
#pragma strict
function Start () {
var multipleFun : Function = function() {
return function(num1 : float, num2 : float) : float {
return num1 * num2;
var call : Function = function( fun : Function, num1 : float, num2 : float, num3 : float ) : float {
return fun(fun(num1, num2), num3);
print(call(multipleFun(), 1.0, 2.0, 3.0));
multipleFun返回一个函数,再作为call函数的参数传进call函数。
另外,虽然这些特点和javascript的函数特点一样,但是在UnityScript中函数不再有默认的内部属性,很大原因是因为UnityScript不再依靠函数来完成对象的设计,所以更改了这部分的设计,让函数更加的纯粹,没有了arguments属性,稍微有些遗憾。
函数的重载
javascript没有函数的重载,这个很好理解,因为函数的参数都是var,没有重载的太大必要,但是UnityScript中可以直接指定参数的类型,那么重载就有一定必要了,事实上,UnityScript实现了函数的重载:
#pragma strict
function sum( num1 : int, num2 : int) : int {
return num1 + num2 + 1;
function sum( num1 : float, num2 : float) : float {
return num1 + num2 + 2;
function Start () {
print(sum(1, 1)); // call sum( num1 : int, num2 : int) : int
print(sum(1.0, 1.0)); // call sum( num1 : float, num2 : float) : float
print(sum(1.0, 1)); // call sum( num1 : float, num2 : float) : float/
如上代码,会输出3,4,4,正确的匹配了合适的函数,并且在出现二义性的时候,进行了类型提升,第三个sum的调用也调用了浮点数的那个。这种类型提升多恰当其实也说不上,在C++中这是会出现二义性编译错误的 ,但是总得来说还是较为符合预期,毕竟和计算时的提升方式类似。稍微提及一点,在UnityScript中,函数的参数个数要求是严格的(编译期检查),不像javascript中那种参数个数不够也能调用。
UnityScript支持匿名函数,即我们常说的Lamda函数,简单的说就是没有名字的函数,说来简单,其实作用挺强大的。
在本节前面的例子中
function sum(num1 : float, num2 : float) : float {
return num1 + num2;
var sumFun : Function = function(num1 : float, num2 : float) : float {
return num1 + num2;
两种定义函数的方式其实是有些区别的,前一种是在当前作用域中定义了一个名为sum的函数,这在程序编译时就确定了,在代码执行之前就已经加载到作用域了,无论你在这个作用域的哪个地方都能直接调用,而后一种定义方式,实际上是先定义了一个匿名的函数,然后将匿名的函数赋给变量sumFun,只在这一行代码在运行时被执行后才有效。
在前面例子里面的call函数,也可以用Lambda函数调用:
function Start () {
var call : Function = function( fun : Function, num1 : float, num2 : float, num3 : float ) : float {
return fun(fun(num1, num2), num3);
print(call(function(num1 : float, num2 : float) { return num1 + num2; }, 1.0, 2.0, 3.0));
对于只使用一次的函数来说,这样做更加便捷,并且还少费一个变量~~也许对于这个例子,这么调用没有太大必要,但是在大规模的使用函数作为参数的情况下,这样的简化就会带来很大的意义,当然,那就看你的编码思维是函数式的还是JAVA,C++那样面向对象的方式了,要是一直习惯使用C++,可能都不会有把函数作为参数的抽象思维,那样,这样的代码你写几W行代码都不会碰到。相对来说,因为UnityScript中去掉了很多与函数编程相关的便捷方法(类似map,filter等),使得用函数编程的方式成本偏高,所以按照C++的思维来写UnityScript,完全是靠谱的。
这里有个UnityScript很大的特点需要注意,那就是不支持函数内的函数定义,但是支持函数定义表达式。也就是说
function fun1() {
//function fun2() {
// not support
var fun2 : Function = function() {
return fun2;
上例中,被注释掉的第一个fun2的定义方式不被UnityScript支持,而第二种方式是支持的,虽然我们现在已经知道了,这两种方式其实没有啥区别,但是,UnityScript就是有这么诡异的情况……
虽然在Python和Lua中都接触过闭包,大概明白什么回事,但是要从概念到细节的描述,我感觉自己还是不太明白,于是查了下网络,发现我最喜欢的两个博客,阮一峰和酷壳上正好有两篇文章,作为入门,概念讲解的清楚,,细节描述的到位准确,关于闭包的概念,我发现我只需要发两个链接就够了。
UnityScript中类的使用方式没有用javascript中较为别扭的prototype方式,(说prototype别扭会有人来喷我吗?-_-!)而是选择了JScript中MS加入的class-base方式,这种方式前面讲过一些,事实上和C++的比较像。下面看稍微复杂些,带继承的情况。
#pragma strict
class Base
public var x :
protected var y :
private var z :
function Base() {
function PrintToOverride() {
Debug.Log('x=' + x + ',y=' + y + ',z=' + z);
final function PrintCantOverride() {
Debug.Log('x=' + x + ',y=' + y + ',z=' + z);
class Derived extends Base
//public var x :
//protected var y :
private var z :
function Derived() {
function PrintToOverride() {
super.PrintToOverride();
Debug.Log('x=' + this.x + ',y=' + this.y + ',z=' + this.z);
function Start () {
var base : Base = new Derived();
base.PrintToOverride();
base.PrintCantOverride();
能想到的情况上面的例子中都展示了,包括和C++一样的构造函数,丰富的访问控制(public,protected,private),和Java一样的默认全虚函数,final定义不可override的非虚函数,用super关键字调用父类的对象。其中PrintToOverride函数就是在Derived类中重载了Base类中的同名函数,并且调用了基类的同名函数。
有个小细节需要注意,z这个变量在Base中是private的变量,因为在Derived中完全不可见,在Derived类中还可以重复定义,但是public的x和protected的y是不能在Derived中重新定义的,这个比C++要严格,在C++中是可以通过this指针来进行区别的,当然,就算真的可以,比如z,也完全不推荐大家去定义重名变量。可以看看上例中输出的结果有没有超过你的预期,要是超过了的话,更加应该把不定义重名变量作为规则来遵守。
这部分UnityScript的设计基本来自于JScript的class-base object创建方式。类似面向对象的一般思维。在第一节就讲过一些基本的东西了,这里不做重复的叙述。
数组和字符串
Unity中有两种数组,一种是原生数组(Buildin array),类似C/C++中原生数组的使用方式。
function Start () {
var str : char[] = new char[10];
for (var index = 0; index & str. ++index) {
str[index] = 97 +
for (var printIndex = 0; printIndex & str. ++printIndex) {
print(str[printIndex]);
会依次输出a,b,c….
需要注意的是,即使是内置数值,其实也还带名为length的成员变量,表示数组的长度。(这是C/C++ coder多年的梦想)
因为原生数组是类型安全的,并且效率很高,内存占用很少,在需要不是频繁增删的容器时,优先考虑的应该是UnityScript的原生数组,比如游戏的配置读取后的结构。
这个是一个奇怪的特性,用逗号来分割维度,没有使用C/C++中[ ][ ][ ]的方式。
function Start () {
var multidim : double[,,] = new double[5,4,3]; // three dimension
multidim[0, 0, 0] = 1;
在UnityScript可以直接使用.Net中的所有容器,还有Array这个javascript的数组。
Array算是UnityScript中最乱的类了,一方面为了照顾javascript的习惯,函数需要以小写开头,一方面还要照顾自己的习惯,函数又要用大写开头,于是乎,UnityScript两者都保留了,因为我对javascript没有什么感情,推荐碰到类似的情况一律使用大写开头的函数。
因为Array来自于javascript,不是一个类型安全的容器,不推荐使用。推荐使用.Net的ArrayList等容器替代。当你确定需要一个类型不确定的容器时,才使用Array。Unity的Script Reference对Array有足够的描述,这里就不多介绍了。
在UnityScript中,用char(见上表)来表示字符,并且默认支持Unicode。
在C/C++中,常用char[]来表示字符串。并且字面常量无论是用双引号”,还是用单引号’,表示的都是字符串,而不像C++中用”表示字符串,用’表示字符。实在在需要字符的时候,这里有个hack,那就是使用’a’[0]的形式…….这么扭曲的方法,来自,见下面的代码:
var str : char[] = [ 'a'[0], ‘b’[0], ‘c’[0] ];
上面讲到做,char默认保存的就是Unicode的值,可以完美支持中文。见下例:
#pragma strict
function Start () {
var str : char[] = [ '中'[0], '国'[0] ];
var value0 : int = str[0];
var value1 : int = str[1];
print( Array(str) );
print( str[0] + & & + str[1] );
print( value0 + & & + value1 );
倒是monoDevelop对中文支持的不是很好,不能输入中文,上面的代码在其他地方编辑后,在Unity中运行正常。
5.The C Programming Language
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:85675次
积分:1176
积分:1176
排名:千里之外
原创:25篇
转载:22篇
评论:10条
(2)(5)(3)(3)(1)(4)(1)(2)(1)(1)(2)(2)(2)(5)(5)(4)(4)
(window.slotbydup = window.slotbydup || []).push({
id: '4740881',
container: s,
size: '200,200',
display: 'inlay-fix'}

我要回帖

更多关于 学信网打印页是什么 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信