書名: Unity 4 3D開發實戰詳解作者名: 峰 杜化美 張月霞 索依娜編著本章字數: 1052字更新時間: 2019-01-01 21:14:59
3.4 Unity腳本的基本語法
通過前面兩節的介紹,讀者應該對Unity 中專用JavaScript和C#腳本有了一些簡單的了解,下面就分別對Unity中專用JavaScript和C#腳本的基本語法進行介紹說明。
3.4.1 常用操作
Unity 中很多對游戲對象的操是通過腳本來修改對象的 Transform(變換屬性)與 Rigidbody (剛體屬性)參數來實現的。上述屬性的參數可以非常方便地通過腳本編程實現修改,例如讓物體繞x軸順時針旋轉20°,則可以使用如下的JavaScript代碼片段來實現。
1 function Update(){ //聲明Update方法
2 this.transform.Rotate(20, 0, 0); //繞x軸上旋轉20°
3 }
腳本開發完成后,將這個腳本掛載到需要旋轉的游戲對象上,在項目運行時即可實現所需功能。當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明一個example類
4 void Update() { //重寫Update函數
5 transform.Rotate(20, 0, 0); //繞x軸旋轉20°
6 }}
如果希望游戲對象沿z軸正方向移動,則可以使用如下的JavaScript代碼片段來實現。
1 var gameobject : GameObject; //聲明一個游戲對象
2 function Update(){ //聲明Update方法
3 gameobject.transform.Translate(0, 0, 1);//實現gameobject每幀向前移動1個單位長度
4 }
上述代碼運行時可以實現gameobject游戲對象每幀向前移動1個單位。當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明一個example類
4 void Update() { //重寫Update函數
5 transform.Translate(0, 0, 1); //實現gameobject每幀向前移動1個單位長度
6 }}
提示
一般情況下,在Unity中,x軸為紅色的軸表示左右,y軸為綠色的軸表示上下,z軸為藍色的軸表示前后。
3.4.2 記錄時間
在Unity中記錄時間需要用到Time類。Time類中比較重要的變量為deltaTime(只讀),它指的是從最近一次調用Update()或者FixUpdate()到現在的時間。
說明
系統在繪制每一幀時,都會回調一次Update函數,因此,如果想在系統繪制每一幀時都做相同的工作,可以把對應的代碼寫在Update函數中。
如果想勻速地旋轉一個物體,不考慮幀速率的情況下,可以乘以 Time.deltaTime,具體可以使用如下的JavaScript代碼片段來實現。
1 var gameobject : GameObject; //聲明一個游戲對象
2 function Update(){ //聲明Update方法
3 gameobject.transform.Rotate(10* Time.deltaTime,0 , 0); //繞x軸均勻旋轉
4 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明一個example類
4 void Update() { //重寫Update函數
5 transform.Rotate(10 * Time.deltaTime, 0, 0); //繞x軸均勻旋轉
6 }}
同樣地,也可以使用類似的方法來移動物體,具體可以使用如下的JavaScript代碼片段來實現。
1 var gameobject : GameObject; //聲明一個游戲對象
2 function Update(){ //聲明Update方法
3 gameobject.transform.Translate(0, 0, 1 * Time.deltaTime); //沿z軸向前移動
4 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明一個example類
4 void Update() { //重寫Update函數
5 transform.Translate(0, 0, 1 * Time.deltaTime);//沿z軸均勻平移
6 }}
如果想每秒增加或者減少一個值,需要乘以 Time.deltaTime,同時也要明確在游戲中是需要每秒1個單位還是每幀1個單位的效果。如果是乘以Time.deltaTime,那么,游戲對象就會按固定的節奏運動而不是依賴游戲的幀速率,因此,游戲對象的運動變得更容易控制。
例如想讓游戲對象(Gameobject)沿 y 軸正方向每秒上升 5 個單位,具體可以使用如下的JavaScript代碼片段來實現。
1 var gameobject : GameObject; //聲明一個游戲對象
2 function Update(){ //聲明Update方法
3 gameobject.transform.position.y+=5*Time.deltaTime;//游戲對象沿y軸每秒上升5個單位長度
4 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明一個example類
4 public GameObject gameobject; //聲明一個游戲對象
5 void Update() { //重寫Update函數
6 gameobject.transform.position.y+=5*Time.deltaTime; //沿y軸每秒上升5個單位
7 }}
如果涉及剛體時,可以寫在FixUpdate函數里面,這樣就不需要乘以Time.deltaTime,例如,讓一個剛體沿y軸正方向每秒上升2個單位,可以使用如下的JavaScript代碼片段來實現。
1 var gameobject : Rigidbody; //聲明一個游戲對象
2 function FixUpdate() { //聲明Update方法
3 gameobject.rigidbody.transform.position.y+=2;//剛體沿y軸每秒上升2個單位長度
4 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明一個example類
4 public GameObject gameobject; //聲明一個游戲對象
5 void FixUpdate() { //重寫Update函數
6 gameobject.rigidbody.transform.position.y+=2;//剛體沿y軸每秒上升2個單位長度
7 }}
說明
FixUpdate方法是按固定的物理時間被系統回調執行的,其中的代碼的執行和游戲的幀速率無關。
如果涉及剛體的力的時候,通常也不需要乘以Time.deltaTime,因為Unity引擎系統內部事先處理好了。
3.4.3 訪問其他組件
組件屬于游戲對象,比如把一個 Renderer(渲染器)組件附加到游戲對象上,可以使游戲對象顯示到游戲場景中;把Camera(攝像機)組件附加到游戲對象上可以使該對象具有攝像機的所有屬性。由于所有的腳本都是組件,因此一般的腳本都可以附加到游戲對象上。
常用的組件可以通過簡單的成員變量取得,下面介紹了一些常見的成員變量,如表3-1所示。
表3-1 常見的成員變量

提示
這里的組件體現在屬性查看器中,而變量是在腳本中體現的。一個游戲對象的所有組件及其所帶的屬性參數都能夠在屬性查看器中查看。如果想通過掛載在游戲對象上的腳本代碼來實現獲得該游戲對象上的對應組件及其屬性,可以通過變量名來獲得,例如,獲得游戲對象( gameobject )上的 Transform 組件,可這樣寫gameobject.transform;如果想進一步獲得其 position 屬性,則可這樣寫gameobject.transform.position。
如果想查看所有的預定義成員變量,可以查看關于 Component、Behavior 和 MonoBehaviour類的文檔,本書不再一一介紹。如果游戲對象中沒有想要取得的值,那么上面的變量將為null。
在 Unity 中,附加到游戲對象上的組件可以通過 GetComponent 獲得,具體可以使用如下的JavaScript代碼片段來實現。
1 var gameobject : Rigidbody; //聲明游戲對象
2 function Update(){ //聲明Update方法
3 gameobject.transform.Translate(1, 0, 0); //沿x軸移動一個單位
4 gameobject.GetComponent(Transform).Translate(1, 0, 0);//沿x軸移動一個單位
5 }
第4行和第5行代碼作用是一樣的,都是使游戲對象沿x軸正方向移動。當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明一個example類
4 void Update() { //重寫Update函數
5 transform.Translate(1, 0, 0); //沿x軸移動一個單位
6 GetComponent<Transform>().Translate(1, 0, 0); //沿x軸移動一個單位
7 }}
提示
注意transfom和Transform之間大小寫的區別,前者是變量(小寫),后者是類或腳本名稱(大寫)。大小寫不同使你能夠從類和腳本名中區分變量。
同樣地,也可以通過GetComponent獲取其他的腳本。比如有一個HelloWorld腳本,里面有一個sayHello函數。HelloWorld腳本要與調用它的腳本附加在同一游戲對象上,具體可以使用如下的JavaScript代碼片段來實現。
1 var otherScript : HelloWorld; //聲明一個HelloWorld腳本
2 function Update (){ //聲明Update方法
3 otherScript = GetComponent (HelloWorld); //調用HelloWorld腳本
4 otherScript.sayHello(); //執行sayHello方法
5 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明一個example類
4 void Update() { //重寫Update函數
5 OtherScript otherScript = GetComponent<OtherScript>(); //得到其他腳本組件
6 otherScript.sayHello(); //執行sayHello方法
7 }}
提示
聲明otherScript變量時也可以不指定類型,但是指定類型后,Unity就不需要進行邏輯判斷otherScript,這樣可以優化性能,提高運行速度。
3.4.4 訪問其他游戲對象
大部分腳本不單單控制一個游戲對象。Unity 腳本中有很多方法訪問它們的游戲對象和游戲組件。比如有一個Test腳本,并將其附加到一個游戲對象上,具體可以使用如下的JavaScript代碼片段來實現。
1 var otherScript : Test; //聲明一個Test腳本
2 function Update (){ //聲明Update方法
3 otherScript = GetComponent(Test); //調用Test腳本
4 otherScript.DoSomething(); //執行Test腳本中的DoSomething方法
5 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明一個example類
4 void Update() { //重寫Update函數
5 OtherScript otherScript = GetComponent<OtherScript>(); //得到其他腳本組件
6 otherScript.DoSomething(); //調用腳本組件中的DoSomething()方法
7 }}
1.通過屬性查看器指定參數
可以通過屬性查看器來確定一些變量的值,具體可以使用如下的JavaScript代碼片段來實現。
1 // 將要轉換的對象拖曳到target位置
2 var target : Transform; //聲明一個游戲對象
3 function Update (){ //聲明Update方法
4 target.Translate(0, 0, 2); //沿z軸每幀移動2個單位
5 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 // 將要平移的對象拖曳到target位置
2 using UnityEngine;
3 using System.Collections; //引入系統包
4 public class example : MonoBehaviour { //聲明一個example類
5 public Transform target; //定義一個Transform類型的target變量
6 void Update() { //重寫Update函數
7 target.Translate(0, 0, 2); //在z軸上平移2個單位
8 }}
讀者也能將參數顯示在屬性查看器,然后就可以拖曳Test腳本到屬性查看器的gameobject位置,具體可以使用如下的JavaScript代碼片段來實現。
1 //設置a DoSomething到target變量指定在屬性查看器
2 var target : Test; //聲明一個游戲對象
3 function Update (){ //聲明Update方法
4 //設置Test對象的a變量
5 target.a = 2;
6 //調用Test的Dosomething
7 target.DoSomething("Hello");
8 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 //設置foo DoSomething到target變量指定在屬性查看器
2 using UnityEngine;
3 using System.Collections; //引入系統包
4 public class example : MonoBehaviour { //聲明一個example類
5 public Test target; //定義一個Test類型的target變量
6 void Update() { //重寫Update函數
7 target.a = 2; //設置target對象的a變量
8 target.DoSomething("Hello"); //調用target的DoSomething方法
9 }}
2.確定對象的層次關系
可以通過Transform組件去找到它的子對象和父對象,具體可以使用如下的JavaScript代碼片段來實現。
1 //獲得子游戲對象“Hand”
2 var gameobject : GameObject; //聲明一個游戲對象
3 function Update (){ //聲明Update方法
4 gameobject.transform.Find("Hand").Translate(0, 0, 1);//找到子對象“Hand”并沿Z軸每幀移動1個單位
5 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明一個example類
4 void Update() { //重寫Update函數
5 //找到子對象“Hand”,并沿z軸每幀移動1個單位
6 transform.Find("Hand").Translate(0, 0, 1);
7 }}
一旦讀者在Hierarchy面板找到Transform,讀者就可以通過GetComponent獲得其他腳本,例如有一個Test.js腳本掛載在子對象Hand上,具體可以使用如下的JavaScript代碼片段來實現。
1 var gameobject : GameObject;
2 function Update (){
3 //找到子對象 "Hand"
4 //獲取Test,設置a為2
5 gameobject.transform.Find("Hand").GetComponent(Test).a = 2;
6 //獲得子對象"Hand"
7 //調用附屬于它的Test的DoSomething
8 gameobject.transform.Find("Hand").GetComponent(Test).DoSomething("Hello");
9 //獲得子對象"Hand"
10 //加一個力到剛體上
11 gameobject.transform.Find("Hand").rigidbody.AddForce(0, 0, 2);
12 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明一個example類
4 void Update() { //重寫Update函數
5 //找到子對象 "Hand",并得到該對象上的Test腳本組件,同時設置a為2
6 transform.Find("Hand").GetComponent<Test>().a = 2;
7 //找到子對象 "Hand",并得到該對象上的Test腳本組件,同時調用DoSomething函數
8 transform.Find("Hand").GetComponent<Test>().DoSomething("Hello");
9 //找到子對象 "Hand",在該對象的剛體屬性上加一個沿z軸的大小為2的力
10 transform.Find("Hand").rigidbody.AddForce(0, 0, 2);
11 }}
也可以使用腳本來循環到所有的子對象,然后對子對象做某種操作,如平移。具體可以使用如下的JavaScript代碼片段來實現。
1 //y軸正方向移動所有的子對象5個單位
2 for (var child : Transform in transform) {
3 child.Translate(0, 5, 0);
4 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 //y軸正方向移動所有的子對象5個單位
2 foreach (Transform child in transform) {
3 child.Translate(0, 5, 0);
4 }
如果想要得到更多關于Transform的信息,可以參考相關的文檔。
3.指定名字或標簽
可以使用GameObject.FindWithTag和GameObject.Find GameObjectsWithTag搜索指定標簽的游戲對象;使用GameObject.Find搜索指定名字的游戲對象,具體可以使用如下的JavaScript代碼片段來實現。
1 function Start (){
2 //通過名字搜索
3 var name = GameObject.Find("SomeName");
4 name.transform.Translate(0, 0, 1);
5 //通過標簽搜索
6 var tag = GameObject.FindWithTag("SomeTag");
7 tag.transform.Translate(0, 0, -1);
8 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明一個example類
4 void Start() { //重寫Start函數
5 //找到名稱為SomeName的游戲對象
6 GameObject go = GameObject.Find("SomeName");
7 go.transform.Translate(0, 0, 1); //沿z軸平移1
8 //找到Tag為SomeTag的游戲對象
9 GameObject tag = GameObject.FindWithTag("SomeTag");
10 tag.transform.Translate(0, 0, -1); //沿z軸平移-1
11 }}
這樣,通過 GetComponent 就能得到指定游戲對象上的任意腳本或組件,具體可以使用如下的JavaScript代碼片段來實現。
1 function Start (){
2 //通過名字搜索
3 var name = GameObject.Find("SomeName");
4 name.GetComponent(Test).DoSomething();
5 //通過標簽搜索
6 var tag = GameObject.FindWithTag("SomeTag");
7 tag.GetComponent(Test).DoSomething();
8 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明一個example類
4 void Start() { //重寫Start函數
5 //找到名稱為SomeName的游戲對象
6 GameObject go = GameObject.Find("SomeName");
7 //得到go對象上的Test腳本組件,并調用腳本中的方法
8 go.GetComponent<Test>().DoSomething();
9 //找到Tag為SomeTag的游戲對象
10 GameObject tag = GameObject.FindWithTag("SomeTag");
11 //得到go對象上的Test腳本組件,并調用腳本中的方法
12 tag.GetComponent<Test>().DoSomething();
13 }}
但是這里也有一些特殊的變量,比如主攝像機(Camera.main),即第一個有效的攝像機被標示為主攝像機(只讀),如果當前游戲場景中沒有主攝像機則返回null。
4.傳遞參數
一些事件中包含了特殊的信息,例如觸發碰撞事件的Collider組件。在OnTiggerStay函數中有一個碰撞體參數,通過這個參數,能得到碰撞的剛體,具體可以使用如下的 JavaScript 代碼片段來實現。
1 function OnTriggerStay( other : Collider ) {
2 //如果碰撞體是一個剛體
3 //則給它一個向前的力
4 if (other.rigidbody)
5 other.rigidbody.AddForce(0, 0, 2);
6 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine; //引入系統包
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour {
4 void OnTriggerStay(Collider other) {
5 if (other.rigidbody) //如果碰撞體是剛體,則給它一個向前的力
6 other.rigidbody.AddForce(0, 0, 2);
7 }}
或者通過Collider得到這個對象上掛載的腳本,具體可以使用如下的JavaScript代碼片段來實現。
1 function OnTriggerStay( other : Collider ) {
2 //一般碰撞體沒有附腳本,所以讀者需要首先檢查是否為null
3 if (other.GetComponent(Test))
4 //如果其他的碰撞體附加了Test,則調用它的DoSomething方法
5 other.GetComponent(Test).DoSomething();
6 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour {
4 void OnTriggerStay(Collider other) {
5 //如果other對象上存在Test腳本組件,就調用該腳本的方法
6 if (other.GetComponent<Test>())
7 other.GetComponent<Test>().DoSomething();
8
9 }}
說明
在上面的代碼中使用后綴方式訪問其他變量,也同樣能訪問到碰撞對象所包含的任意組件。
5.某個類型的腳本
可以使用FindObjectsOfType函數找到特定的組件,具體可以使用如下的JavaScript代碼片段來實現。
1 function Start (){
2 //獲得附加在場景里的游戲對象的Test組件
3 var test : Test = FindObjectOfType(Test);
4 test.DoSomething();
5 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour {
4 void Start() { //重寫Start函數
5 //找到Test類型的組件
6 Test test = FindObjectOfType(typeof(Test));
7 test.DoSomething(); //調用腳本中的DoSomething函數
8 }}
說明
如果使用FindObjectsOfType函數得到的Test類型的組件不止一個,則返回第一個Test類型腳本組件。
3.4.5 向量
在Unity中使用Vector3表示空間中的所有向量,具體可以使用如下的JavaScript代碼片段來實現。
1 var position : Vector3 ;
2 position.x = 3; //x軸分量
3 position.y = 4; //y軸分量
4 position.z = 5; //z軸分量
也可以直接給position賦值,不必分別給x、y、z賦值,具體可以使用如下的JavaScript代碼片段來實現。
1 var position : Vector3 ;
2 position = Vector3(3,4,5);
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine; //引入系統包
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour {
4 public Vector3 position = new Vector3(3, 4, 5); //new一個新向量
5 }
Vector3也自定義了一些固定的值,例如Vector3.up等同于Vector3(0,1,0),Vector3.zero等同于Vector3(0,0,0)等,這樣可以簡化代碼。
Vector3類中有很多實用的方法,例如想要獲得兩點之間的距離時,可以使用Vector3.Distance()方法,具體可以使用如下的JavaScript代碼片段來實現。
1 var position1 : Vector3 = Vector3(2,1,3); //聲明向量position1
2 var position2 : Vector3 = Vector3(3,4,5); //聲明向量position2
3 var theDistance = Vector3.Distance(position1, position2);//求向量position1和position2之間的距離
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour {
4 public Vector3 position1 = new Vector3(2, 1, 3); //聲明向量
5 public Vector3 position2 = new Vector3(3, 4, 5); //聲明向量
6 //求兩個向量之間的距離
7 public float theDistance = Vector3.Distance(position1, position2);
8 }
Vector3也支持運算符,比如:JavaScript中 var position= position1+ position2,C#中 public float theDistance = position1+ position2。
3.4.6 成員變量和全局變量
一般情況下,定義在方法體外的變量是成員變量,這個變量可以在屬性查看器查看到,若進行修改,而且它會隨著項目一起自動保存,如在JavaScript中:
var a : int = 1;
而在C#腳本中:
public int a = 1;
在屬性查看器中可以看到這個變量,名字為“a”,值為“1”,讀者可以修改它的值。
如果聲明的是一個組件類型的變量(類似GameObject、Transform、Rigidbody等),需要在屬性查看器拖曳游戲對象到變量處并確定它的值,具體可以使用如下的JavaScript代碼片段來實現。
1 //聲明一個敵人對象
2 var enemy: GameObject;
3 function Update(){
4 //如果敵人和初始點的距離小于10
5 if ( Vector3.Distance( enemy.position, Vector3.zero ) < 10 ) {
6 print("I sense the enemy is near!");
7 } }
這樣,在屬性查看器中直接拖曳敵人對象到 enemy 變量即可。當然,也可以使用如下的 C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明example類
4 public Transform enemy; //聲明一個Transform
5 void Update() { //重寫Update方法
6 //如果enemy和transform的距離小于10,則打印出一句話
7 if (Vector3.Distance(enemy.position, transform.position) < 10)
8 print("I sense the enemy is near!");
9 }}
可以通過private創建私有變量,這樣在屬性查看器中就不會顯示該變量,避免錯誤地修改。具體可以使用如下的JavaScript代碼片段來實現。
1 private var lastCollider : Collider;
2 //碰撞檢測事件
3 function OnCollisionEnter(collisionInfo : Collision ) {
4 lastCollider = collisionInfo.collider;
5 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明example類
4 private Collider lastCollider; //聲明一個Collider
5 //碰撞檢測事件
6 void OnCollisionEnter(Collision collisionInfo) {
7 lastCollider = collisionInfo.collider;
8 }}
也可以通過 static 來創建全局變量,這樣就可以在不同腳本間調用這個變量,具體可以使用如下的JavaScript代碼片段來實現。
1 //腳本里的靜態變量名為 'test'
2 static var test : int = 5;
3 //讀者可以像普通變量一樣去調用它
4 print(test);
5 test = 1;
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明example類
4 public static int test = 5; //聲明一個靜態整型變量
5 print(test); //打印
6 test = 1; //賦值
7 }
如果想從另外一個腳本調用變量 test,讀者可以通過“腳本名.變量名”的方式調用,具體可以使用如下的JavaScript代碼片段來實現。
1 //腳本名稱為“Hello.js”
2 print(Test.test);
3 Test.test = 10;
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class hello : MonoBehaviour { //聲明example類
4 void Start() { //重寫Start方法
5 print(Test.test); //打印Test.cs腳本中的變量
6 Test.test = 10; //給Test.cs腳本中的變量賦值
7 }}
3.4.7 實例化
Unity 中如果要創建很多相同的物體(比如射擊出去的子彈,保齡球瓶等)時,可以通過實例化(Instantiate)快速實現。而且實例化出來的對象包含了這個對象所有的屬性,這樣就能保證原封不動地創建所需的對象。實例化在 Unity 中有很多用途,充分使用它非常必要。實例化常和預制件(Prefabs)一起使用。
例如,創建一個腳本“Hit.js”,當一個碰撞體撞擊到一個物體時,銷毀這個物體,并創建一個損壞的物體,具體可以使用如下的JavaScript代碼片段來實現。
1 var broke : Transform ;
2 //當一個碰撞發生自毀
3 //在相同位置實例化一個代替物體
4 function OnCollisionEnter (){
5 //撞擊發生1s后銷毀物體
6 Destroy (broke.gameObject,1);
7 var brokes : Transform ;
8 //在物體原來的位置創建一個和原來物體姿態一樣的物體
9 theClonedExplosion = Instantiate(brokes,broke.position, broke.rotation);
10 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明example類
4 public Transform explosion; //聲明explosion變量
5 void OnCollisionEnter() { //重寫OnCollisionEnter方法
6 Destroy(gameObject,1); //撞擊發生1s后銷毀對象
7 Transform theClonedExplosion; //聲明變量
8 //在物體原來的位置創建一個和原來物體姿態一樣的物體
9 theClonedExplosion = Instantiate(explosion, transform.position,
10 transform.rotation) as Transform;
11 }}
說明
Destroy(gameobject,n)是在n秒后銷毀物體,也可以使用DestroyImmediate(gameobject,boolean),這樣就可以根據布爾值,判斷是否直接銷毀物體。
3.4.8 協同程序和中斷
如果通過寫代碼去實現游戲的連續步驟的話,有時會產生大量重復的工作,具體可以使用如下的JavaScript代碼片段來實現。
1 private var state = 0;
2 function Update() {
3 if (state == 0) {
4 //做步驟0
5 state = 1;
6 return;
7 }
8 if (state == 1) {
9 // 做步驟1
10 state = 2;
11 return;
12 }
13 // ...
14 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明example類
4 private int state = 0; //聲明state變量
5 void Update() { //重寫Update函數
6 if (state == 0) { //做步驟0
7 state = 1;
8 return;
9 }
10 if (state == 1) { // 做步驟1
11 state = 2;
12 return;
13 }}}
這時候,如果使用中斷可以更方便,具體可以使用如下的JavaScript代碼片段來實現。
1 while(true) {
2 // 做步驟0
3 yield;
4 // 等待一幀
5 // 做步驟1
6 yield;
7 // 等待一幀
8 // ...
9 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明example類
4 IEnumerator Example() { //聲明Example函數
5 while (true) {
6 yield return null; // 等待一幀
7 yield return null; // 等待一幀
8 }}}
說明
在腳本中,中斷語句是一個特殊的返回類型,它可以使函數的執行跳到中斷語句的下一行。
也可以傳遞時間值到中斷語句,Update函數會在中斷時間結束后執行下一語句,具體可以使用如下的JavaScript代碼片段來實現。
1 //...
2 yield WaitForSeconds (2.0); //等待2s
3 //...
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 //...
2 yield return new WaitForSeconds(2.0F); //等待2s
3 //...
下面的代碼片段先執行 Do()函數,但是 Do()函數之后的 print 語句也會立即執行,具體JavaScript代碼片段來實現。
1 Do (); //執行Do函數
2 print ("This is printed immediately"); //打印提示信息
3 function Do (){ //聲明Do函數
4 print("Do now"); //打印提示信息
5 yield WaitForSeconds (2); //休眠2s
6 print("Do 2 seconds later"); //打印提示信息
7 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明example類
4 IEnumerator Do() { //聲明Do函數
5 print("Do now"); //打印
6 yield return new WaitForSeconds(2); //等待2s
7 print("Do 2 seconds later"); //再次打印
8 }
9 void Example() { //聲明Exame函數
10 Do(); //調用Do方法
11 print("This is printed immediately"); //打印
12 }}
修改上面的代碼,使用連接協同程序,那么程序將先執行Do函數,等待,執行完Do函數之后再執行其他語句,具體可以使用如下的JavaScript代碼片段來實現。
1 //連接協同程序
2 yield StartCoroutine("Do");
3 print("Also after 2 seconds"); //打印提示信息
4 print ("This is after the Do coroutine has finished execution"); //打印提示信息
5 function Do (){ //聲明D函數
6 print("Do now"); //打印提示信息
7 yield WaitForSeconds (2); //休眠2s
8 print("Do 2 seconds later"); //打印提示信息
9 }
當然,也可以使用如下的C#代碼片段來實現相同的功能。
1 using UnityEngine;
2 using System.Collections; //引入系統包
3 public class example : MonoBehaviour { //聲明example類
4 IEnumerator Do() { //聲明Example函數
5 print("Do now"); //打印
6 yield return new WaitForSeconds(2); //等待2s
7 print("Do 2 seconds later"); //再次打印
8 }
9 IEnumerator Example() { //聲明Example函數
10 yield return StartCoroutine("Do"); //啟動Do協同程序
11 print("Also after 2 seconds"); //打印
12 print("This is after the Do coroutine has finished execution");//打印
13 }}
說明
任何時間處理程序都是協同程序,但是在Update()和FixUpdate()中不能使用協同程序。
3.4.9 一些重要的類
本小節將向讀者介紹Unity腳本中的一些重要的類,由于篇幅的限制,所以本小節只對這些類中的比較常用的變量和函數進行簡單的介紹說明,其他具體的信息讀者可參考官方腳本參考手冊。
1.MonoBehaviour類
MonoBehaviour 是每個腳本的基類,其繼承自 Behaviour 類。每個 JavaScript 腳本自動繼承MonoBehaviour,使用C#時,需要顯式繼承MonoBehaviour。
MonoBehaviour 類中的方法可以重寫,這些方法在兩種腳本中只是在方法聲明上有一點差別外,其他大致相同。下面介紹一下主要可重寫的方法,如表3-2所示。
表3-2 MonoBehaviour類中主要可重寫的函數

MonoBehaviour類中有很多的繼承函數,下面將介紹主要的繼承函數,如表3-3所示。
表3-3 MonoBehaviour類中主要的繼承函數

MonoBehaviour類中還有很多繼承類函數,下面將介紹主要的繼承類函數,如表3-4所示。
表3-4 MonoBehaviour類中主要的繼承類函數

說明
讀者可能對“繼承函數”和“繼承類函數”兩個概念比較疑惑。“繼承函數”指的是其函數都是從父類直接繼承或者從父類間接繼承而來的函數,而“繼承類函數”指的是其函數均從根類Object中間接繼承而來,而非從其他父類繼承而來。
2.Transform類
場景中的每一個物體都有一個Transform。用于儲存并操控物體的位置、旋轉和縮放。每一個Transform可以有一個父級,允許你分層次應用位置、旋轉和縮放。可以在Hierarchy面板查看層次關系。Transform類中包含了很多變量,下面將介紹主要的變量,如表3-5所示。
表3-5 Transform類中主要的變量

Transform類中同時還有很多函數,下面將介紹主要的函數,如表3-6所示。
表3-6 Transform類中主要的函數

3.Rigidbody類
Rigidbody可以模擬物體在物理效果下的狀態。它可以讓物體接收力和扭矩,讓物體相對真實地移動。如果一個物體想被重力所約束,其必須含有Rigidbody屬性。Rigidbody類中包含很多變量,下面將介紹主要的變量,如表3-7所示。
Rigidbody類中包含很多函數,下面將介紹主要的函數,如表3-8所示。
表3-7 Rigidbody類中主要的變量

表3-8 Rigidbody類中主要的函數

4.CharacterController類
角色控制器用于第三人稱或者第一人稱游戲主角控制。它可以根據碰撞檢測判斷是否能夠移動,而不必給角色控制器添加Rigidbody。而且角色控制器不會受到力的影響,當調用Move函數時,系統會先檢查角色控制器是否和其他物體發生碰撞,然后才決定移動與否。
在屬性查看器中可以查看到其屬性,具體的屬性如表3-9所示。
表3-9 Inspector面板中角色控制器的主要屬性

CharacterController類中包含很多變量,下面將介紹一下CharacterController類中的主要變量,如表3-10所示。
表3-10 CharacterControler類中的主要變量

CharacterController類中包含很多函數,下面將介紹一下CharacterController類中的主要函數,如表3-11所示。
表3-11 CharacterControler類中的主要函數

3.4.10 性能優化
Unity 中專用的 JavaScript 對性能進行了大量的優化措施,比如使用靜態類型,使用#pragma strict,緩存組件查詢,使用內建數組以及盡量少調用函數等措施。下面將對各個措施進行介紹。
1.使用靜態類型
在JavaScript中,很重要的優化就是使用靜態類型代替動態類型。Unity中有一種邏輯推理的技術自動將JavaScript轉換為靜態腳本,無需做額外的工作。例如:
var a = 1;
此時,Unity會推斷a為整形,不需要使用動態名稱查找等技術,節省了大量的時間。但是,有些變量是無法被推斷的,比如下面的代碼片段所示:
1 function Start (){ //聲明Start方法
2 var test = GetComponent(Test); //調用Test腳本
3 test.DoSomething(); //執行Test腳本中的DoSomething方法
4 }
因為 test 是未知類型,所以在調用 DoSomeThing()函數之前,Unity 需要檢查 test 是否支持DoSomeThing()函數,所以很耗時間。
如果想要獲得更好的性能,可將上面的代碼改成如下代碼片段:
1 function Start () { //聲明Start方法
2 var test: Test = GetComponent(Test); //調用Test腳本
3 test.DoSomething(); //執行Test腳本中的DoSomething方法
4 }
說明
在腳本中,聲明變量時明確變量的類型,避免了Unity系統對類型的檢查,可以獲得更快的速度,這對于移動開發更重要。
2.使用#pragma strict
有時可能會在無意識的情況下寫出未知類型的變量,如果在腳本的開始加入#pragma strict語句后,Unity就會禁用JavaScript的動態類型,如果腳本中有動態類型的聲明,Unity就會報告編譯錯誤。例如,在禁用動態類型的情況下,下面的代碼片段在Unity中編譯就會報錯。
var test = GetComponent(Test);
3.緩存組件查詢
這種優化不一定是必須的,但是如果想更高的提升性能的話,優化組件緩沖也是可以考慮的。當通過GetComponent查詢一個組件時,可以設置一個私有變量去儲存這個組件。這樣,Unity無需在每一幀中去查詢Transform類型的組件。實現方式可以參考如下代碼片段:
1 private var myTransform : Transform ; //聲明靜態變量
2 function Awake (){ //聲明Awake方法
3 myTransform = transform;
4 }
5 function Update (){ //聲明Update方法
6 myTransform.Translate(0, 0, 2); //沿z軸每幀移動2個單位
7 }
說明
這種先聲明一個私有變量,然后在Awake方法中賦值,把對應的組件對象存放在私有變量的方式同樣適用于腳本組件。
4.使用內建數組
雖然ArrayList和Array很容易使用,而且很方便,但是相比內建數組而言,他們的速度還是有很大的差異。內建數組直接嵌入 struct 數據類型存入第一個緩沖區里,不需要其他類型信息或者其他資源。因此做緩存遍歷更快捷,如:
1 private var positions : Vector3 []; //聲明靜態向量
2 function Awake (){ //聲明Awake方法
3 positions = new Vector3 [100]; //創建一個向量
4 for (var i=0;i<100;i++){ //執行for循環
5 positions[i] = Vector3.zero ; //為每個向量賦值
6 } }
5.盡量少調用函數
最簡單和最有效的優化就是干最少的工作。Unity中Update()函數每一幀都在運行,所以減少Update()函數里面工作量,可以大量優化性能。讀者通過協調程序或者加入標志位就能實現。
說明
在實際開發中,一般把標志位檢查放在函數外面,這樣就無需每一幀都檢查標志位,減少了系統性能的消耗。
3.4.11 腳本編譯
Unity 可以把腳本編譯為.dll 文件,.dll 文件將在運行時編譯運行。這樣做可以提高執行的速度,比傳統的JavaScript腳本要快20倍左右。
腳本具體的編譯需要以下4步。
(1)所有的“Standard Assert”、“Pro Standard Assert”或者“Plugins”文件夾里的腳本會被首先編譯。
(2)所有的“Standard Assert/Editor”、“Pro Standard Assert/Editor”或者“Plugins/Editor”文件夾里的腳本會被首先編譯。
(3)所有在“Editor”文件夾里面的腳本接著被編譯。
(4)其他腳本在最后編譯。所有這一步里編譯的腳本,可以訪問第一組提到的所有腳本(即“Standard Assets”、“Pro Standard Assets”或者“Plugins”文件夾里的腳本)。
提示
最后編譯的腳本可以訪問最先編譯的腳本,實現了不同的腳本語言之間的溝通。例如,想在一個JavaScript腳本中引用一個C#腳本,可以將C#腳本放到“Standard Assets”文件夾下,然后將JavaScript腳本放在此文件夾之外,JavaScript腳本便可以直接引用C#腳本。在“WebPlayer Templates”文件夾下的腳本不會被編譯。
根據Unity的版本進行編譯。在腳本的開始寫入一些代碼:
1 // 判斷Unity的版本
2 #if UNITY_2_6_0
3 //使用2.6.0特性
4 #endif
5 // 判斷Unity的版本
6 #if UNITY_2_6
7 // 使用2.6.x特性
8 #endif
說明
上面的代碼寫在腳本的開始處,然后,當Unity編譯器在編譯時,是從Unity2.6版本開始的。
這樣,就能確保游戲的特性只在對應的版本中使用。這樣也可以標記腳本,確保在對應的Unity版本中才能使用。
3.4.12 泛化方法
手冊中的一些方法可以進行泛化通過方法名字后面加T或者.<T>。如下面的代碼片段:
function FuncName.<T>(): T;
這些就是泛化方法。這對于腳本的意義就是,當調用這個方法時,可以指定參數的類型或者返回值的類型。在JavaScript中,泛化方法可以用于動態類型的局限性:
1 //類型推斷正確由于被定義為函數調用
2 var obj = GetComponent.<Rigidbody>();
手冊中任何一種方法都可以通過特殊的調用語句進行泛化。