Unity3D脚本编程--基本概念
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); }值得注意的是用于进行拷贝的对象并不一定须要放置在场景中。
更普遍的做法是将一个预设(Prefab)拖到脚本的相应公有成员变量上,实例化的时候直接对这个成员变量进行实例化就可以。
// 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中按一定时间进行调用,而不是每一帧都调用。
參考:http://game.ceeger.com/Script/