下载

1下载券

加入VIP
  • 专属下载特权
  • 现金文档折扣购买
  • VIP免费专区
  • 千万文档免费下载

上传资料

关闭

关闭

关闭

封号提示

内容

首页 Unity_脚本参考

Unity_脚本参考.pdf

Unity_脚本参考

wlangg
2014-01-20 0人阅读 举报 0 0 0 暂无简介

简介:本文档为《Unity_脚本参考pdf》,可适用于IT/计算机领域

Unity脚本参考第一章概述Unity中的脚本(Script)由附加到游戏对象(GameObject)的自定义脚本对象(CustomScriptObject)组成它们又被称为行为。脚本对象中各种函数被称为必然事件(CertainEvent)。常用的必然事件有如下三个:、Update:该函数在渲染帧之前被调用大部分的游戏行为代码都在这里执行除了物理代码。、FixedUpdate:该函数在每进行一次物理时间步调时被调用它用来执行基于物理的游戏行为。、Codeoutsideanyfunction:这类函数在对象加载时被调用它们用来执行脚本状态的初始化工作。你可以自定义事件处理器(EventHandler)它们都以“On”前缀进行命名例如:OnCollisionEnter。第二章常用操作大部分游戏对象的操作都是通过它们的Transform和Rigidbody实例来实现的我们可以在脚本中直接通过成员变量transform和rigidbody来访问这两个实例。例如:要实现游戏对象每帧以Y轴旋转度的效果可以进行如下编码:functionUpdate(){transformRotate(,,)}让游戏对象向你移动可进行如下编码:functionUpdate(){transformTranslate(,,)}第三章时间Time类包含了一个重要的类变量deltaTime它表示距上一次调用Update或FixedUpdate所用的时间。因此通过它可以让游戏对象按照一个常速进行旋转而不是依赖于它的帧频:functionUpdate(){tranformRotate(,*TimedeltaTime,)}同样地移动效果:functionUpdate(){transformTranslate(,,*TimedeltaTime)}如果想要一个值根据每帧的变化而变化(增加或减少)你应该使用TimedeltaTime来乘以这个值。这样才能使得变化的效果依赖于单位时间而不是帧频。这不仅使得游戏的运行独立于帧频也使得运动的效果符合现实。同理要让灯光的照射范围在每秒使半径增加个单位可进行如下编码:functionUpdate(){lightrange=*TimedeltaTime}但是在通过force来处理rigidbody时一般情况下不要乘以TimedeltaTime因为Unity引擎已经为你进行了处理。第四章访问组件组件被用来附加到游戏对象。当附加一个渲染器组件(Renderer)时将使得游戏对象被渲染到屏幕上当附加一个摄像机组件(Camera)时将使得游戏对象变为摄像机对象。所有的脚本都是组件因此它们可以被附加到游戏对象中。常用的组件都可以通过一个简单的成员变量来进行访问:组件用于访问相应组件的成员变量TransformtransformRigidbodyrigidbodyRendererrendererCameracamera(只能用于摄像机对象)Lightlight(只能用于灯光对象)AnimationanimationCollidercollider如果游戏对象没有你想要获取的组件类型上面的变量将被设置为。另外也可以通过GetComponent函数来获取被附加到游戏对象中的任意内置组件或脚本。例如:transfromTranslate(,,)等同于GetComponent(Transfrom)Translate(,,)两者之间不同在于前者(transform等)是一个变量而后者(Transform等)是一个类或脚本名。现在我们进行一个练习以便更加深入地掌握GetComponent函数。这个游戏对象附加了两个脚本我们在其中一个脚本中通过GetComponent函数来获取另一个脚本(叫做OtherScript)并访问它的DoSomething函数:functionUpdate(){在当前游戏对象中查找名为OtherScript的脚本并调用它的DoSomething函数。otherScript=GetComponent(OtherScript)otherScriptDoSomething()}第五章访问游戏对象大多数高级的游戏代码都不只是操作单个游戏对象。Unity脚本接口拥有多种方式来查找和访问其他游戏对象或其中的组件。假设有一个名为OtherScript的脚本被附加到游戏对象中该脚本包含了如下的代码片段:varfoo=functionDoSomething(param:String){print(param"withfoo:"foo)}、通过检查器进行访问你可以通过检查器来为任意对象类型的变量分配值:需要将Translate拖拽到target变量上vartarget:TransformfunctionUpdate(){targetTranslate(,,)}你也可以把指向其他对象的引用暴露在检查器中。例如:你可以拖拽一个附加了OtherScript脚本的游戏对象到检查器中的target变量中:可以在检查器中通过target变量来设置foo变量vartarget:OtherScriptfunctionUpdate(){设置foo变量targetfoo=调用DoSomething函数targetDoSomething("Hello")}、通过对象层次进行访问你可以通过一个游戏对象中的Transform组件来查找该对象的子对象和父对象:查找游戏对象中名为“Hand”的子对象我们将该脚本附加到当前游戏对象中transformFind("Hand")Translate(,,)一旦找到了层次中的transform对象你就可以通过使用GetComponent函数来获取其他脚本:查找名为“Hand”的子对象获取其中附加的OtherScript脚本并设置foo变量为。transformFind("Hand")GetComponent(OtherScript)foo=查找名为“Hand”的子对象获取其中附加的OtherScript脚本并调用DoSomething函数transformFind("Hand")GetComponent(OtherScript)DoSomething("Hello")查找名为“Hand”的子对象获取其中附加的rigidbody对象并申请一个force。transformFind("Hand")rigidbodyAddForce(,,)使用相同的原理可以通过如下的代码来循环访问所有的子对象:将所有子对象都向上移动个单位for(varchild:Transformintransform){childTranslate(,,)}、通过名称或Tag进行访问可以通过特定的Tag来调用GameObjectFindWithTag或GameObjectFindGameObjectsWithTag函数从而在所有的游戏对象中查找想要的游戏对象。也可以通过游戏对象的名称来调用GameObjectFind函数来进行查找。functionStart(){通过名称查找vargo=GameObjectFind("SomeGuy")gotransformTranslate(,,)通过tag查找varplayer=GameObjectFindWithTag("Player")playertransformTranslate(,,)}在查找到的游戏对象上调用GetComponent函数可以获取该游戏对象中所附加的内置组件或脚本:functionStart(){通过名称查找vargo=GameObjectFind("SomeGuy")goGetComponent(OtherScript)DoSomething()通过tag查找varplayer=GameObjectFindWithTag("Player")playerGetComponent(OtherScript)DoSomething()}一些特殊的对象都拥有一个访问自己的快捷方式例如:可以通过Cameramain变量来代表主摄像机。、传递参数一些事件消息都包含了详细的信息例如:触发器事件会把碰撞对象的碰撞器组件(Collider)传递给事件处理器函数。OnTriggerStay函数提供了指向一个碰撞器对象的引用通过它我们可以获取其中附加的rigidbody:functionOnTriggerStay(other:Collider){如果other碰撞器对象拥有rigidbody就申请一个force。if(otherrigidbody){otherrigidbodyAddForce(,,)}}使用相同的原理也可以获取碰撞器游戏对象中附加的内置组件或脚本:functionOnTriggerStay(other:Collider){如果other碰撞器对象拥有OtherScript脚本就调用它的DoSomething函数。大多数情况下碰撞器都不会附加脚本。因此我们需要先进行检查以避免引用异常。if(otherGetComponent(OtherScript)){otherGetComponent(OtherScript)DoSomething()}}、查找同类型脚本的所有对象通过类名或脚本名调用ObjectFindObjectOfType或ObjectFindObjectsOfType函数可以在场景中的所有游戏对象中获取一个或多个同类型的类对象或脚本对象:functionStart(){场景中查找附加了OtherScript脚本的任意一个游戏对象varother:OtherScript=FindObjectOfType(OtherScript)otherDoSomething()}第六章向量Unity使用Vector类来表示所有的D向量可以通过x、y和z成员变量来访问一个D向量对象的各个组件:varaPosition:VectoraPositionx=aPositiony=aPositionz=也可以使用Vector的构造函数来一次性初始化所有的组件:varaPosition=Vector(,,)Vector也定义了一些常量用来表示常用的D向量对象:等同于Vector(,,)vardirection=Vectorup可以通过如下代码片段来访问单个D向量对象:someVectorNormalize()可以通过如下代码片段来访问多个D向量对象:theDistance=VectorDistance(oneVector,otherVector)注:Distance函数为类函数所以在调用时必须在前面加上Vector。也可以在多个D向量对象之间使用常用的数学操作符:combined=vectorvector第七章成员变量和全局变量在函数外面定义的变量叫做成员变量它们能够通过Unity的检查器进行访问存储在成员变量中的值将自动地保存在项目中。varmemeberVariable=上面的变量将作为一个叫做“MemeberVariable”的数字属性出现在检查器中。如果设置变量的类型为组件类型(Transform、Rigidbody、Collider和脚本名等)可以通过把游戏对象拖拽到检查器中这种类型的变量上来设置它们。varenemy:TransformfunctionUpdate(){if(VecterDistance(enemypostion,transformposition)<){print("Isensetheenemyisnear!")}}你也可以创建私有成员变量它们主要用来存储状态信息而且在脚本外具有不可见性。私有成员变量不保存在磁盘上也不能在检查器中进行编辑。只有在检查器被设置为调试模式时才允许你通过修改私有成员变量来实时地更新调试器。privatevarlastCollider:ColliderfunctionOnCollisionEnter(collisionInfo:Collision){lastCollider=collisionInfoother}使用static关键字进行声明的变量叫做全局变量例如:一个名为“TheScriptName”的脚本中拥有一个someGlobal静态变量staticvarsomeGlobal=你可以在脚本中像使用普通变量一样来使用它print(someGlobal)someGlobal=为了能在其他脚本中访问全局变量需要加上“TheScriptName”前缀:print(TheScriptNamesomeGlobal)TheScriptNamesomeGlobal=第八章实例化实例化表示复制一个对象包括所有附加的脚本和整个层次结构。它并不理会指向克隆层次结构外的对象的引用而指向克隆层次结构内的对象的引用将会被映射到克隆的对象上。下面的脚本被附加到一个具有碰撞器的rigidbody上当发生碰撞时将摧毁本身并替代为大量的爆炸物对象:varexplosion:Transform当碰撞发生时将摧毁自身并产生大量的预置爆炸物对象。functionOnCollisionEnter(){Destroy(gameObject)vartheClonedExplosion:TransformtheClonedExplosion=Instantiate(explosion,transformposition,transformrotation)}实例化通常用于与预置对象(Prefabs)进行交互。第九章协同程序和让步在编写游戏代码时通常需要在脚本中结束一系列的事件。例如:privatevarstate=functionUpdate(){if(state==){dostepstate=return}if(state==){dostepstate=return}}这时我们使用让步(yield)语句是非常方便的yield语句是一种特殊的return语句。它确保函数在下次被调用时能够从yield语句开始继续执行。while(true){dostepwaitforoneframeyielddostepwaitforoneframeyield}你也可以传递一个特定的值给yield语句来推迟Update函数的执行直到某个事件发生:dosomethingwaitforsecondsyieldWaitForSeconds()dosomethingmore你可以堆积并连接一些协同程序(Coroutine)。例如下面的示例在执行Do函数时立即执行随后的代码:Do()print("Thisisprintedimmediately")functionDo(){print("Donow")yieldWaitForSeconds()print("Dosecondslater")}下面的示例在执行完Do函数后才执行随后的代码:chainthecoroutineyieldStartCoroutine("Do")print("Alsoafterseconds")print("ThisisaftertheDocoroutinehasfinishedexecution")functionDo(){print("Donow")yieldWaitForSeconds()print("Dosecondslater")}注:不能在Update或FixedUpdate函数中使用yield语句但可以在它们中使用StartCoroutine函数来调用一个函数。第十章使用C#编写脚本在Unity中可以使用JavaScript、C#和Boo来编写脚本。在使用C#编写脚本时需注意以下几个特点:、所有脚本都继承至MonoBehaviour所有的行为脚本都必须直接地或间接地继承至MonoBehaviour。在使用JavaScript时这种继承关系将自动生成但在使用C#编写脚本时必须显式地进行定义。通过模板创建的C#脚本时(Asset>Create>CSharpScript)自动生成的代码已经包含了这样的定义:C#publicclassNewBehaviourScript:MonoBehaviour{}、使用Awake或Start方法来进行初始化在使用JavaScript时所有在函数外的代码在使用C#时都必须把它们放置在Awake或Start方法中。Awake和Start方法的不同点在于:前者在场景进行加载时被调用后者在调用Update或FixedUpdate方法之前被调用。因此Awake方法在Start方法之前被调用。、类名必须与文件名相同在JavaScript中类名被隐式地设置为文件名。但在C#中必须手动地进行设置。、在C#中协同程序的使用语法与JavaScript不同。协同程序必须返回一个IEnumerator类型并且使用yieldreturn来代替JavaScript中的yield。usingSystemCollectionsusingUnityEnginepublicclassNewBehaviourScript:MonoBehaviour{C#coroutineIEnumeratorSomeCoroutine(){Waitforoneframeyieldreturn}WaitfortwosecondsyieldreturnnewWaitForSeconds()}、不能使用命名空间(Namespace)当前的Unity不支持把脚本放到一个命名空间中。这可能在将来的版本中所有改变。、只有成员变量才能被序列化并显示在检查器中私有和保护成员变量仅仅在专家模式(ExpertMode)下才显示在检查器中。属性不能被序列化并显示在检查器中。、避免使用构造器永远不要使用构造器来初始化值而应该在Awake或Start中进行到时Unity会自动触发相应的构造器即使在编辑模式(EditMode)修改成员变量的值也会自动触发。这通常在脚本编译完成后直接发生以便为它们设置默认值。这样设计的主要原因有两个:构造器的调用不可预期构造器的调用可能为了预置的或未活动的游戏对象。例如:单例模式(SingletonPattern)对象使用构造器进行初始化时可能会导致值为空的严重后果。因此应该在Awake方法中来初始化单例模式对象。请记住:任何继承至MonoBehaviour的类中都不能包含使用构造器的代码。第十一章重要类、在JavaScript或基于C#的类中可访问的全局函数:http:unitydcomsupportdocumentationScriptReferenceMonoBehaviourhtml、对游戏对象进行移动或旋转:http:unitydcomsupportdocumentationScriptReferenceTransformhtml、动画系统:http:unitydcomsupportdocumentationScriptReferenceAnimationhtml、Rigidbodies:http:unitydcomsupportdocumentationScriptReferenceRigidbodyhtml、FPSorThirdpersoncharactercontroller:http:unitydcomsupportdocumentationScriptReferenceCharacterControllerhtml第十二章性能最优化、使用静态类型化在JavaScript中使用静态类型化(StaticTyping)来代替动态类型化(DynamicTyping)对性能的优化非常重要。Unity使用一种叫做类型推导(TypeInference)的技术自动把JavaScript转化为静态类型代码(StaticallyTypedCode)而不需要你做其他任何工作。varfoo=像foo变量将会被自动推导为整数值。Unity可以完成许多编译时优化(CompileTimeOptimization)的功能并且不进行动态变量的耗时查找。这就是Unity的JavaScript的执行速度比其他JavaScript快了倍左右的原因之一。但是并不是所有变量都能进行类型推导这时Unity将回滚到动态类型化来处理它们。动态类型化时在JavaScript中编写代码变得更加简单但它的执行速度将会变慢。例如:functionStart(){varfoo=GetComponent(MyScript)fooDoSomething()}这儿的foo将进行动态类型化因此调用DoSomething函数会耗时些。因为编译器不知道foo的类型它会去分析foo变量是否有DoSomething函数如果有才进行调用。functionStart(){varfoo:MyScript=GetComponent(MyScript)fooDoSomething()}这里我们强制指定了foo的类型这样会获得更高的性能。、使用#pragmastrict在脚本顶部增加#pragmastrict语句会让Unity编译器在执行该脚本时关闭动态类型化支持强制使用静态类型化。因此如果变量的类型不确定将导致编译错误。例如下面的代码在编译时会产生错误:#pragmastrictfunctionStart(){varfoo=GetComponent(MyScript)fooDoSomething()}、缓存组件查找最优化的另一个方法是缓存组件但它需要编写额外的代码。如果脚本执行的次数很多进行组件的缓存将获得更高的性能这时编写额外的代码显然是值得的。在使用GetComponent函数或存取器变量(AccessorVariable)来访问一个组件时Unity必须从游戏对象中查找相应的组件。这时我们可以使用私有变量来缓存一个指向该组件的引用以便直接进行使用。因此我们可以把:functionUpdate(){transformTranslate(,,)}写成:privatevarmyTransform:TransfromfunctionAwake(){myTransfrom=transform}functionUpdate(){myTransformTranslate(,,)}后面的代码将运行地更快因为Unity不必每帧都在游戏对象中查找transform组件。这对于脚本组件也同样适用。、使用内建数组内建数组的执行非常快因此我们应该使用它。尽管ArrayList和Array类的使用方式都比较简单但它们的处理速度却有很大差别。内建数组都有固定的大小通常事先我们都知道这个最大值。内建数组最大的优势在于它能够在一个紧凑的缓冲区中直接嵌入结构体而不需要存储额外的类型信息。因此在缓存中迭代它时将像在内存的一条线上进行处理非常方便和快捷:privatevarpositions:VectorfunctionAwake(){positions=newVectorfor(vari=i<i){positionsi=Vectorzero}}、避免调用不必要的函数最简单和有效的最优化在于减少不必要的工作。例如当敌人离玩家很远时我们可以让敌人静止不动直到玩家走近它。一种较慢的处理如下:functionUpdate(){Earlyoutiftheplayeristoofarwaryif(VectorDistance(transformposition,targetposition)>)returnperformrealworkwork}这种处理的不足之处在于每帧都要执行Update函数。一种更好的解决方案是在玩家接近敌人时才启用脚本有三种方式来进行实现:()、使用OnBecameVisible和OnBecameInvisible函数:它们的调用取决于渲染系统。当有摄像机能看到对象时将调用OnBecameVisible函数当没有摄像机能看到对象时将调用OnBecameInvisible函数。一般情况下这是有效的但对于Al通常是无效的因为一旦你把摄像机转开敌人时他们可能会变得不可用。functionOnBecameVisible(){enabled=true}functionOnBecameInvisible(){enabled=false}()、使用触发器:使用一个简单的球体触发器时你可以根据球体的范围来调用OnTriggerEnter或OnTriggerExit函数。functionOnTriggerEnter(c:Collider){if(cCompareTag("Player"))enabled=ture}functionOnTriggerExit(c:Collider){if(cCompareTag("Player"))enabled=false}()、使用协同程序:Update函数会在每帧进行调用我们完全可以使用协同程序来在每隔秒钟检查一次距离这将节省很多的资源。第十三章脚本编译(高级)Unity把所有的脚本编译为NETdll文件这些dll文件将在运行时实时地进行汇编。这使得Unity的脚本运行速度非常快比传统的JavaScript快倍左右只比本地C代码慢左右。在保存脚本时Unity便会花极少的时间对它们进行编译在编译的过程中你可以看到在主窗口的右下角会显示一个小型旋转进展图标。脚本编译分为四步进行:、在“StandardAssets”、“ProStandardAssets”和“Plugins”中的脚本首先进行编译。一个文件夹中的脚本不能在另一个文件夹的脚本中直接使用但可以通过使用GameObjectSendMessage来进行交互。、在“StandardAssetsEditor”、“ProStandardAssetsEditor”和“PluginsEditor”中的脚本其次进行编译。如果你想要使用UnityEditor命名空间你必须把脚本放到这些文件夹中。例如增加菜单项或自定义导航你必须把相应的脚本放到这些文件夹中。这些脚本能够访问上级组中的脚本。、在“Editor”中的脚本然后进行编译。跟上步的情况基本相同不同点在于这些脚本不能访问下级组中的脚本。这在编写编辑器代码时会出现一些问题因为你不能编辑下级组中的脚本。可以通过两种方式来解决:()、移动想要访问的脚本到“Plugins”文件夹中。()、利用JavaScript的动态类型化功能。在JavaScript中你不需要知道所使用的类的类型。例如GetComponent函数和SendMessage函数都仅仅使用一个字符串来代替类型。、其他脚本最后进行编译。不在上面提到的文件夹中的脚本将最后进行编译。这些脚本有权访问“StandardAssets”、“ProStandardAssets”和“Plugins”文件夹中的脚本这使得你可以在不同语言脚本之间进行交互。例如如果你想要创建一个JavaScript并在其中使用一个C#脚本。你可以把C#脚本放到“StandardAssets”文件夹中而把JavaScript放到该文件夹的外面。这样javaScript就可以直接访问C#脚本了。第一组(“StandardAssets”、“ProStandardAssets”和“Plugins”文件夹)中的脚本在编译时会比较耗时因为它们在编译的同时第三组(“Editor”文件夹)也在进行预编译。因此如果你想减少编译的时间可以把第一组中的脚本移到第四组中去但我们并不推荐这样做。针对Unity版本进行条件编译Unity增加了一个C#预编译器它能够识别所使用的Unity版本并对特殊功能进行有条件的访问。例如:Specificversiondefineincludingtheminorrevision#ifUNITYUseUnityspecificfeature#endifSpecificversiondefinenotincludingtheminorrevision#ifUNITYUseUnityxspecificfeature#endif这段代码用来在指定的Unity版本中启用一些可用的游戏特性。注意版本条件编译只能用于Unity及其后的版本中。将来Unity会提供一个更适合的定义来在脚本中标识所使用的Unity版本。jxyangyeahnet第一章概述第二章常用操作第三章时间第四章访问组件第五章访问游戏对象第六章向量第七章成员变量和全局变量第八章实例化第九章协同程序和让步第十章使用C#编写脚本第十一章重要类第十二章性能最优化第十三章脚本编译(高级)

用户评价(0)

关闭

新课改视野下建构高中语文教学实验成果报告(32KB)

抱歉,积分不足下载失败,请稍后再试!

提示

试读已结束,如需要继续阅读或者下载,敬请购买!

评分:

/17

VIP

在线
客服

免费
邮箱

爱问共享资料服务号

扫描关注领取更多福利