1. 简介
在Unity3D中,游戏对象(GameObject)的行为是由附加其上的脚本来控制的,游戏开发者通过编写脚本来控制游戏中的所有对象,如移动Camera等。GameObject可以被附加不同类型的组件,但每种类型的组件只能有一个或没有。脚本本质上也是一种组件。
在Unity3D中默认的脚本代码如下所示:
- <span style="font-family:Arial;"><span style="font-family:Arial;">// ***** C# script *****
- using UnityEngine;
- using System.Collections;
- public class ffc : MonoBehaviour {
- // Use this for initialization
- void Start () {
- }
- / Update is called once per frame
- void Update () {
- }
- }
- //***** Java script *****
- #pragma strict
- function Start () {
- }
- function Update () {
- }
- </span></span>
由此可见,脚本代码与大家熟悉的Java代码类似,即都是由以下两部分组成:
• 变量
• 函数
• 其它代码:在任何函数之外的代码在物体被加载的时候运行。这个可以用来初始化脚本状态。
MonoBehaviour是所有脚本的基类,每个Javascript脚本自动继承MonoBehaviour,使用C#或Boo时,需要显式继承MonoBehaviour.
1.1 脚本成员变量
脚本变量就是指类的成员变量(即在JavaScript或C#或Boo中定义的成员变量,而不是基类MonoBehaviour中定义的变量),在Unity3D中将成员变量设为公有的时候,当把它附加到游戏对象后,可以在游戏对象的监视面板中的脚本组件那栏里面看到该“公有变量”,即可以在编辑器中直接对该公有变量进行赋值,同时在Debug状态下也可以在面板中看到它的值。
1.2 基类可被继承的成员变量(内置变量)
变量名 | 描述 |
transform | The Transform attached to this GameObject (null if there is none attached). |
rigidbody | The Rigidbody attached to this GameObject (null if there is none attached). |
camera | The Camera attached to this GameObject (null if there is none attached). |
light | The Light attached to this GameObject (null if there is none attached). |
animation | The Animation attached to this GameObject (null if there is none attached). |
constantForce | The ConstantForce attached to this GameObject (null if there is none attached). |
renderer | The Renderer attached to this GameObject (null if there is none attached). |
audio | The AudioSource attached to this GameObject (null if there is none attached). |
guiText | The GUIText attached to this GameObject (null if there is none attached). |
networkView | The NetworkView attached to this GameObject (Read Only). (null if there is none attached) |
guiTexture | The GUITexture attached to this GameObject (Read Only). (null if there is none attached) |
collider | The Collider attached to this GameObject (null if there is none attached). |
hingeJoint | The HingeJoint attached to this GameObject (null if there is none attached). |
particleEmitter | The ParticleEmitter attached to this GameObject (null if there is none attached). |
gameObject | The game object this component is attached to. A component is always attached to a game object. |
tag | The tag of this game object. |
1.3 基类可被继承的类函数
类函数 | 描述 |
Destroy | Removes a gameobject, component or asset. |
DestroyImmediate | Destroys the object obj immediately. It is strongly recommended to use Destroy instead. |
DontDestroyOnLoad | Makes the object target not be destroyed automatically when loading a new scene. |
FindObjectOfType | Returns the first active loaded object of Type type. |
FindObjectsOfType | Returns a list of all active loaded objects of Type type. |
Instantiate | Clones the object original and returns the clone. |
1.4 基类可被继承的函数
类函数 | 描述 |
GetComponent | Returns the component ofType type if the game object has one attached, null if it doesn't. function GetComponent (type :Type) : Component |
GetComponent | Returns the component withnametype if the game object has one attached, null if it doesn't. function GetComponent (type :string) : Component |
GetComponentInChildren | Returns the component of Type type in the GameObject or any of its children using depth first search. |
GetComponentsInChildren | Returns all components of Type type in the GameObject or any of its children. |
GetComponents | Returns all components of Type type in the GameObject. |
CompareTag | Is this game object tagged tag? |
SendMessageUpwards | Calls the method named methodName on every MonoBehaviour in this game object and on every ancestor of the behaviour |
SendMessage | Calls the method named methodName on every MonoBehaviour in this game object. |
BroadcastMessage | Calls the method named methodName on every MonoBehaviour in this game object or any of its children. |
GetInstanceID | Returns the instance id of the object. |
ToString | Returns the name of the game object. |
1.5 基类可重写函数
1.5.1 常规更新事件
函数名 | 描述 |
Update | Update is called every frame, if the MonoBehaviour is enabled. |
LateUpdate | LateUpdate is called every frame, if the Behaviour is enabled. LateUpdate is called after all Update functions have been called. This is useful to order script execution. For example a follow camera should always be implemented in LateUpdate because it tracks objects that might have moved inside Update. |
FixedUpdate | This function is called every fixed framerate frame, if the MonoBehaviour is enabled. |
1.5.2 初始化事件
函数名 | 描述 |
Awake | Awake is called when the script instance is being loaded. |
Start | Start is called just before any of the Update methods is called the first time. |
Reset | Reset to default values. |
1.5.3 GUI元素或Collider碰撞体事件
函数名 | 描述 |
OnMouseEnter | nMouseEnter is called when the mouse entered the GUIElement or Collider. |
OnMouseOver | OnMouseOver is called every frame while the mouse is over the GUIElement or Collider. |
OnMouseExit | OnMouseExit is called when the mouse is not any longer over the GUIElement or Collider. |
OnMouseDown | OnMouseDown is called when the user has pressed the mouse button while over the GUIElement or Collider. |
OnMouseUp | OnMouseUp is called when the user has released the mouse button. |
OnMouseUpAsButton | OnMouseUpAsButton is only called when the mouse is released over the same GUIElement or Collider as it was pressed. |
OnMouseDrag | OnMouseDrag is called when the user has clicked on a GUIElement or Collider and is still holding down the mouse. |
1.5.4 Collider碰撞体事件
函数名 | 描述 |
OnTriggerEnter | OnTriggerEnter is called when the Collider other enters the trigger. |
OnTriggerExit | OnTriggerExit is called when the Collider other has stopped touching the trigger. |
OnTriggerStay | OnTriggerStay is called once per frame for every Collider other that is touching the trigger. |
1.5.5 Collider碰撞体或rigibody刚体事件
函数名 | 描述 |
OnCollisionEnter | OnCollisionEnter is called when this collider/rigidbody has begun touching another rigidbody/collider. |
OnCollisionExit | OnCollisionExit is called when this collider/rigidbody has stopped touching another rigidbody/collider. |
OnCollisionStay | OnCollisionStay is called once per frame for every collider/rigidbody that is touching rigidbody/collider. |
2. 控制游戏对象(GameObject)
在Unity3D中,可以在监视器面板中修改物体的组件属性,但是更多的时候,需要使用脚本来进行动态操作。
2.1 访问组件
最常见的一个情形是需要使用脚本访问附加到相同游戏对象(GameObject)上的另一个组件(当前脚本就是一个组件,其他的组件也就是另一个组件了)。一个组件实质上是一个类的实例,因而首先需要做的是获取想要操作的组件实例的引用。这个通过GetComponent函数来实现。典型的,可能会想要将一个组件赋值给一个变量,如下代码所示:
- void Start () {
- Rigidbody rb = GetComponent<Rigidbody>();
- }
一旦获取了组件实例的引用,就可以对它的属性进行想要的操作,同时也可以调用它的一些功能函数。
如果想要访问另一个脚本文件,也可以使用GetComponent,只需使用脚本的类名作为该函数的组件类型参数(因为脚本本来就也是一个组件)。
- // You can access script components in the same way as other components.
- function Start () {
- var someScript : ExampleScript;
- someScript = GetComponent (ExampleScript);
- someScript.DoSomething ();
- }
由于一些组件类型经常使用,unity提供了一些内置的变量来访问它们,参见1.2(内置变量),其示例代码如下:
- void Start () {
- transform.position = Vector3.zero;
- }
2.2 访问其他对象
虽然游戏对象(GameObject)都有各自的组件(包括脚本)进行处理,使用代码进行跟踪其他物体是常有的事。例如,一个追赶的敌人可能需要知道玩家的位置,Unity提供了一系列的方法来获取其他对象,以适合不同的场合。
2.2.1 将对象【静态】链接到公有成员变量
最直接的办法是将一个游戏对象添加到脚本的公有成员变量上,直接在编辑器中将需要访问的游戏对象拖到对应脚本组件的那个公有成员变量上,Unity3D会自动根据变量的类型将添加的游戏对象中相同的组件类型映射到该变量。
例如将一个游戏对象拖给一个Transform的成员变量,就会自动的将游戏对象的Transform组件和该变量映射起来。
直接将对象和变量链接起来在处理需要有永久链接的对象的时候是最有用的方法。同时也可以使用一个数组变量和几个相同类型的对象链接起来,但是这种链接必须在Unity3D编辑器中完成,而不能在运行时进行。
2.2.2 【动态】定位其它对象
2.2.2.1 查找子物体
如果一个游戏场景中有很多同一类型的对象,例如敌人、路点(waypoints)和障碍物。这些对象在游戏中需要由一个特定的脚本来监视和响应。这个时候使用变量来链接这些对象太过麻烦。对于这种情况,通常更好的方法是将一系列的对象添加到一个父对象下面,这些子对象可以通过使用父对象的Transfrom组件来获得。
- public class WaypointManager : MonoBehaviour {
- public Transform waypoints;
- void Start() {
- waypoints = new Transform[transform.childCount];
- int i = 0;
- for (Transform t in transform) {
- waypoints[i++] = t;
- }
- }
- }
同时也可以使用Tranfrom.Find来查找某个具体的子对象。使用Transform来进行对象查找操作是因为每一个游戏对象都有Transfrom组件。
2.2.2.2 通过名称或标签访问对象
只要有一些信息,在层级场景中的任何位置定位到该游戏对象是可能的。单个对象可以通过GameObject.Find函数进行查找。如下:
- GameObject player;
- void Start() {
- player = GameObject.Find("MainHeroCharacter");
- }
某个对象或者一系列的对象也可以分别通过GameObject.FindWithTag和GameObject.FindObjectsWidthTag函数进行定位。
2.2.2.3 查找特定类型的对象
static Object FindObjectOfType(Type type)返回指定类型对象中的第一个活动的加载对象, 需要注意的是这个函数很慢(可能是由于要在整个场景中进行遍历),不推荐每一帧都使用这个函数,在大多数情况下可以使用单件模式,例如:
Camera cam = FindObjectOfType(typeof(Camera)) as Camera;
由于该函数返回的类型是Object,所以需要使用as进行一下强制转换。
static Object[ ] FindObjectsOfType(Type type);
返回指定类型的加载活动对象的列表,速度也慢
HingeJoint[ ] hinges = FindObjectsOfType(typeof(HingeJoint)) as HingeJoint[ ];
3. 创建和销毁对象
在运行时创建和销毁对象是常有的事。在Unity3D中,可以使用Instantiate函数对现有的一个对象做一个拷贝来创建一个新的游戏对象。
实例化更多通常用于实例投射物(如子弹、榴弹、破片、飞行的铁球等),AI敌人,粒子爆炸或破坏物体的替代品。
- // Instantiates 10 copies of prefab each 2 units apart from each other
- var prefab : Transform;
- for (var i : int = 0;i < 10; i++) {
- Instantiate (prefab, Vector3(i * 2.0, 0, 0), Quaternion.identity);
- }
- // Instantiate a rigidbody then set the velocity
- var projectile : Rigidbody;
- function Update () {
- // Ctrl was pressed, launch a projectile
- //按Ctrl发射炮弹
- if (Input.GetButtonDown("Fire1")) {
- // Instantiate the projectile at the position and rotation of this transform
- //在该变换位置和旋转,实例化炮弹
- var clone : Rigidbody;
- clone = Instantiate(projectile, transform.position, transform.rotation);
- // Give the cloned object an initial velocity along the current object's Z axis
- //沿着当前物体的Z轴给克隆的物体一个初速度。
- clone.velocity = transform.TransformDirection (Vector3.forward * 10);
- }
- }
实例化也能直接克隆脚本实例,整个游戏物体层次将被克隆,并且返回被克隆脚本的实例。
同时也有一个Destroy函数在帧更新函数完成后或设定的一个延时时间后销毁一个对象。
- // Kills the game object
- //销毁游戏物体
- Destroy (gameObject);
- // Removes this script instance from the game object
- //从游戏物体删除该脚本实例
- Destroy (this);
- // Removes the rigidbody from the game object
- //从游戏物体删除刚体
- Destroy (rigidbody);
- // Kills the game object in 5 seconds after loading the object
- //加载物体5秒后销毁游戏物体
- Destroy (gameObject, 5);
- // When the user presses Ctrl, it will remove the script
- // named FooScript from the game object
- //当按下Ctrl将从游戏物体删除名为FooScript的脚本
- function Update () {
- if (Input.GetButton ("Fire1") && GetComponent (FooScript))
- Destroy (GetComponent (FooScript));
- }
注意到Destroy函数可以销毁单独的组件而不对游戏对象本身产生影响,一个通常易犯的错误是Destroy(this); 这句代码仅仅销毁脚本组件,而不销毁该脚本所附加在的对象。
4. 协程(Coroutines)
一个coroutine就像一个可以暂停执行并将控制权返回给Unity3D的函数,但是在下一帧的时候又可以在它停止的位置继续执行。在C#中,这样声明一个coroutine:
- IEnumerator Fade() {
- for (float f = 1f; f <= 0; f -= 0.1f) {
- Color c = renderer.material.color;
- c.alpha = f;
- renderer.material.color = c;
- yield return;
- }
- }
实质上它是一个返回类型为IEnumerator的函数,同时在函数体中增加了yield return这句代码。yield return这行就是会在执行的时候暂停、在下一帧的时候恢复执行的位置。要启动coroutine,需要使用StartCorutine函数。
- void Update() {
- if (Input.GetKeyDown("f")) {
- StartCoroutine("Fade");
- }
- }
默认的情况下,一个coroutine在它暂停后的下一帧恢复,但是也可以使用WaitFroSeconds来引入一个延时。
- IEnumerator Fade() {
- for (float f = 1f; f <= 0; f -= 0.1f) {
- Color c = renderer.material.color;
- c.alpha = f;
- renderer.material.color = c;
- yield return new WaitForSeconds(.1f);
- }
- }
这个可以用于产生一个随时间变化的效果,同时也是一个用于进行优化的有效方法。游戏中的许多人物需要周期性的执行,最常用的做法是将它们包含在Update函数中。但是Update函数通常每秒会调用多次。当有的任务并不需要这么频繁的被调用的时候,可以将它放在一个coroutine中按一定时间进行调用,而不是每一帧都调用。