讲稿unity脚本语法.doc

上传人:jian****018 文档编号:9187952 上传时间:2020-04-03 格式:DOC 页数:37 大小:89.50KB
返回 下载 相关 举报
讲稿unity脚本语法.doc_第1页
第1页 / 共37页
讲稿unity脚本语法.doc_第2页
第2页 / 共37页
讲稿unity脚本语法.doc_第3页
第3页 / 共37页
点击查看更多>>
资源描述
(五)游戏开发平台 (1)游戏开发平台的种类(2)各种游戏开发平台的适用范围(3)各种游戏开发平台介绍,如Torque,Unreal,Unity(4)游戏开发平台Unity的基本操作,游戏开发平台Unity的脚本(5)三维模型在游戏开发平台中的使用(6)游戏开发平台的各种元素:GUI,地形引擎,刚体,碰撞器,粒子系统等(7)人工智能(8)FPS游戏案例分析Unity脚本语法#pragma strict,#pragma downcast、#pragma implicit在一些Unity脚本的开始经常可以看到 #pragma strict,#pragma downcast、#pragma implicit指令,而有的Unity脚本则没有这些指令,那这些指令有什么作用呢?#pragma strict,此指令的含义是强制输入,也就是我们在声明变量时,需要精确的声明变量是什么类型,而不是让编译器来自己推测变量的类型,因此,也不能随便的使用一个随机的名称并让编译器来为你实例化此变量。例如,在使用此指令后,我们随意声明一个变量: private var bobby;/是不可以的,而需要这样: private var bobby : GameObject;/这个可以通过使用#pragma strict,可以强制我们养成良好的编程习惯,但这在Unity中也不是必须要这样做。 而#pragma downcast和#pragma implicit指令,则是可以与#pragma strict指令搭配使用,从而做到“严中有松”,真是有点意思。先看#pragma implicit指令,它的意思是在使用#pragma strict指令,搭配此指令又可以含蓄的声明变量,例如: #pragma strict foo = 5; / 不可以 #pragma strict var foo = 5; /可以 #pragma strict #pragma implicit foo = 5; / 通过使用#pragma implicit,此句是可以的。 而#pragma downcast语句则允许在使用#pragma strict指令时,使变量可以从super(父类型)到sub(子)类型的转换,例如: #pragma strict var go : GameObject; var clone : GameObject = Instantiate(go); /此语句是不可以的,因为Instantiate后返回/的物体类型是Object,而不是GameObject #pragma strict #pragma downcast var go : GameObject; var clone : GameObject = Instantiate(go); /此语句是可以的,使用了#pragma downcast 下面的语句也是可以的:#pragma strictvar go : GameObject;var clone : GameObject = Instantiate(go) as GameObject; /此语句也是可以的,因为使/用了类型转换as。UnityScript程序设计语言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 strictfunction Start () print(Hello Worldn);在Unity的控制台上输出”Hello World”。需要注意的是,在Unity中创建该脚本后,需要绑定到某个GameObject中成为一个Script的组建(compent)后才能运行。function Start() code here 就是Unity里面用function关键字来定义新函数的方式。这个没有太多好讲的。#pragma strict可以更加严格的进行静态类型检查,能够引导你更加好的写更加严谨和更好风格的代码,这个在后面还会再次提到,现在记得在开发的时候推荐都写上就行。类比较需要说明的是,一个UnityScript文件默认就是一个类,并且继承自MonoBehavior,类的名字就是文件名。以上的代码(假设保存在名为HelloWorld.js文件中)与下面的代码意义一模一样:#pragma strictclass HelloWorld extends MonoBehaviour function Start () print(Hello Worldn); 其中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 strictfunction 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 strictfunction Start () /* defines */ var lower : float = 0; var upper : float = 300; var step : float = 20; var fahr : float = lower; / define and assign var celsius : float; while (fahr int,int=long。通过上面的类型转换规则可以看到,这样的使用方式可以避免出现同类型有符号和无符号计算的情况,可以避免很多类型的转换和提升,对数值的溢出判断也更加直接一些。常数变量UnityScript中使用了final关键字用于表示常量,同时,我们可以以全大写的惯例来表示常量。有意思的是,我以前从来没在文档中看到过Unity3D开发组对final的描述。其中总的来说,UnityScript对数据类型的处理和C+很相似,有的地方甚至更加严格,并且完全不支持任何C/C+,C#里面那种类型加括号方式的强制类型转换,所以还算比较好理解。就是表达方法上为了接近javascript(实际上和JScript一样),总是以var开头,导致每个变量的定义都会多敲6次键盘,(为了好看,我一般还需要加一些空格,例子中都是)这个算是挺烦人的事情。表达式和操作符Javascript是面向表达式(Statement)的语言,这个就像我们熟悉的绝大部分语言一样,每个表达式用分号(;)结尾,并且这个分号不能忽略。(和javascript不一样)去掉了undefined和NahUnityScript去掉了javascript中的和undefined,NaN,只有null,用于表示空对象。见下例:#pragma strictfunction Start () var x; print(x);这个例子,在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”,”010”这种格式会出现运行错误。并且不带第二参数(javascript中用第二参数来表示进制)。parseFloat也换成了System.Single.Parse (System.String s)函数,也没有javascript中那样的灵活性。不支持任何格式。#pragma strictfunction Start () var b = byte.Parse(256); print(b);数值运算操作符支持+,-,*,/,%,+, 数值运算操作符,完全和C+及大多数语言里面的意义一模一样,不介绍了。赋值操作符除了=外,C+中有的两元赋值操作符(=等),都存在,不介绍了。位操作符&, |, , ,和C+里面一模一样,不介绍了。只要知道不支持javascript里面的无符号位移就好了。因此,编程实践方面,最好也是和C+一样,进行位移操作的最好是无符号整数。比较操作符,=,=,=,!=需要注意的是没有javascript的严格比较=,因为类型本来就是相对确定的,同时在=比较时,不会进行javascript中那么多的类型转换。比如下例:#pragma strictfunction Start () if ( 42 = 42 ) print(42 = 42); else 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应该怎么比较的时候,发现了JScript里面推荐的方法,那就是比较两个对象ToString以后的结果。我只能说,想出用这种办法来比较Array的人简直就是神。#pragma strictclass C var i : int; 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); else print(str1 != str2); 这种比较在数组元素是简单类型时,一定正确,但是存储对象时,要对对象也有正确的ToString实现,不然结果可能会不正确。这里还有个有意思的地方,在javascript的习惯里面,对象会有个ToString函数,而在C#的习惯里面,对象会有个toString函数,注意,两个函数的首字母大小写不一样,作为UnityScript,在mono这个开源.Net平台上自创的javascript语言,在使用.Net类的时候(比如上面的Array)两者都有!并且功能完全一样!这个实在是太丑陋了。这里总结一下,上面提到的所有数值类型都是原生类型,再加上String,它们的比较都是通过比较值的方式,同时,在函数传参的时候,也是传值(意味着有复制的发生),其他的,包括原生数组,其他的对象,都是比较引用地址,在传参数的时候也是传引用地址,也就是说,没有内容复制的发生,同时,对引用地址变量的任何更改都是更改了原对象,见下例,用一个原生数组作为示例:#pragma strictfunction Start () var array : int = 1, 1, 2; var array2 : int = new int3; array20 = 0; array21 = 1; array22 = 2; if (ArrayUtility.ArrayEquals(array, array2) print(array = array2); else print(array != array2); #pragma strictfunction changeStr( str : char ) : char str0 = b0; return str;function Start () var str : char = a0, b0, c0 ; changeStr(str); print( Array(str) );最后输出的是b,b,c,因为在changeStr里面对参数str的更改,实际更改了外部的str,这个C+及其他语言中见得多了,不再过多描述。逻辑操作符&, |, !像C+一样,也有逻辑截断效果,当前面的操作可以确认结果时,后面的操作不再进行。这里需要注意的是在UnityScript中,什么样的值会是true,什么样的值会是false:false: 空字符串,0, nulltrue: 任何对象,非空字符串,非0数字条件判断操作符?:三目操作符UnityScript也支持,和C+一样操作符优先级操作符计算顺序和C+也完全一样,也没有理由不一样,不介绍了。语句和控制结构这是最没有什么好讲的一节。需要理解的是在UnityScript中变量的scope和C+不一样。比如下面的代码:#pragma strictfunction Start () for (var i : int; i 10; +i) print(i); for(var m : int; m 10; +m) print(i); / 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 strictfunction Start () var charArray : char = new char10; for (var i : int; i 10; +i) charArrayi = 97 + i; for(var value in charArray) print(value); 直接遍历整个容器算是语法糖,但是从抽象上来说的确比通常的for层次更高,也更难用错。switch多条件控制UnityScript的switch本质上和C/C+中的一样,但是有些加强,那就是case不再是仅可使用数值,甚至可以是变量,这个的确很强大,很多复杂情况的if-else都可以被替代了。#pragma strictfunction Start () var num : float = 10.0; var numEqual : int = 10; switch (num) case (num 10.0): print(num 10.0): print(num 10.0); break; case numEqual: print(num = numEqual); break; default: break; 上例中会输出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 = sumFun;调用sumFun2和调用sum,sumFun是一样的效果。函数作为参数函数是第一类值,也就是说可以想变量一样使用,那么,参数也可以是函数,见下面的例子:#pragma strictfunction 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 strictfunction 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 strictfunction 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中那种参数个数不够也能调用。LamdaUnityScript支持匿名函数,即我们常说的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() ; / OK return fun2;上例中,被注释掉的第一个fun2的定义方式不被UnityScript支持,而第二种方式是支持的,虽然我们现在已经知道了,这两种方式其实没有啥区别,但是,UnityScript就是有这么诡异的情况闭包虽然在Python和Lua中都接触过闭包,大概明白什么回事,但是要从概念到细节的描述,我感觉自己还是不太明白,于是查了下网络,发现我最喜欢的两个博客,阮一峰和酷壳上正好有两篇文章,阮一峰的文章作为入门,概念讲解的清楚,酷壳上那篇,细节描述的到位准确,关于闭包的概念,我发现我只需要发两个链接就够了。类UnityScript中类的使用方式没有用javascript中较为别扭的prototype方式,(说prototype别扭会有人来喷我吗?-_-!)而是选择了JScript中MS加入的class-base方式,这种方式前面讲过一些,事实上和C+的比较像。下面看稍微复杂些,带继承的情况。#pragma strictclass Base public var x : int; protected var y : int; private var z : int; function Base() x = 1; y = 2; z = 3; 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 /pub
展开阅读全文
相关资源
相关搜索

当前位置:首页 > 办公文档 > 演讲稿件


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!