2013年9月8日 星期日

Unity3D 教學 ﹥16 Unity JavaScript 腳本

http://www.cg.com.tw/Unity/htm/Unity_016.asp

Unity JavaScrtip 教學
您可以使用 Unity 的編輯工具或是 3D 軟體製作真實的遊戲世界,然而許多遊戲元素與互動行為,必須透過撰寫腳本程式來產生。
Unity 可以使用 JavaScript、C#、Boo 撰寫遊戲的腳本程式,而且在一個遊戲專案中也可以混合使用,這表示遊戲開發團隊的成員可以選擇自已熟悉語言撰寫腳本。對於初次接觸程式設計的同學來說 JavaScript 應該是比較容易上手的,本段教學將採用 JavaScript 來撰寫腳本程式。

準備練習用場景
我們將會利用幾個簡單的範例,說明 Unity 撰寫腳本的步驟、程式語法,與重要的觀念。首先建立新的遊戲場景,新增一個 Plane 當作地面,再新增一個 Cube 方塊,而稍後撰寫的腳本將會套用到方塊來改變其行為。

資料輸出
資料的輸出對於撰寫程式很重要的一部份,經常用於於程式的除錯或是判斷遊戲執行的狀態。使用 print( ) 可以輸出字串或是變數內容,實際在發佈的遊戲中,玩家們並不會看到輸出的內容,只會顯示在 Unity 的訊息視窗。
點選功能表 [ Assets > Create > Javascript ] 即可建立一個新的腳本程式,專案視窗將會出現新增的腳本程式,請將腳本的名稱修改為「ShowMessage 」,由於 Unity 只能檢視而無法修改腳本的內容,使用滑鼠雙擊剛才建立的腳本,即可開啟腳本編輯器修改腳本內容。
Unity 預設的腳本編輯器是一套稱為 MonoDevelop 的程式,您也可以從 Unity 的偏好設定改用其他的程式來編輯腳本(例如 Unitron、Uniscite、記事本等),當您在 MonoDevelop 修改腳本並存檔後,只要切換回 Unity 時腳本就會自動更新,無需重新載入!

此外,適當的設定腳本的名稱,可以讓您(或是遊戲開發團隊的其他成員)瞭解其功能,減少搜尋與溝通的時間。腳本名稱的設定有其限制,開頭的字元必須是英文字元,後面的字元可以使用英文或數字,請注意不可使用中文、空白與特殊符號。
滑鼠點圖片可檢視腳本
Start () 函數、Update() 函數
新建立的腳本時僅有預設的 Start ( ) 函數與 Update ( ) 函數,若要執行特定的動作,可以將程式寫在函數裡面。
這兩個函數的差別是 Start ( ) 函數只有在開始時執行一次,而 Update ( ) 函數則是每個影格都執行一次。

撰寫的腳本必須指定給遊戲物件,進入遊戲時才會被執行。您可以使用滑鼠將腳本拖曳到 Hierarchy 階層視窗的物件,也可以拖曳到 Scene 場景視窗的物件,或是在物件選取的狀態拖曳到 Inspector 屬性檢視器,再按下播放按鈕進行測試。
以上三種套用腳本的操作步驟,其結果是相同的,不過當遊戲物件含有父子階層物件時,使用拖曳腳本到 Scene 場景視窗中物件的方式,需要特別注意腳本是否套用到正確的物件上。例如原本想套用在父物件的腳本,不小心套用到子物件的話,就會發生執行錯誤的情形。
執行的結果,在 Console 視窗顯示 "Hello World" 訊息。

程式註解
在一些 JavaScript 程式的範例中,您可能注意到程式碼中出現的 // 雙斜線,這就是程式的「註解」。
註解 ( comment ) 屬於不執行的程式碼,主要是於程式碼的說明,有助於日後的記憶提取,以下是單行註解與多行註解的寫法:
var RotateAngle : int; // 旋轉角度
function Update() {
 transform.Rotate(0, RotateAngle*Time.deltaTime, 0);
}

程式的撰寫與測試階段,有時想要暫時停用一段程式,其實不需要刪除這些程式碼,只要在前後加上斜線與星號,使程式碼變成多行註解,遊戲引擎就不會執行這個部份,稍後您只要刪除註解即可,不需要重新輸入一次。


多數的情況下,遊戲物件可以利用變形 ( Transform ) 或剛體 ( Rigidbody ) 進行操縱,而透過腳本程式的成員變數可以對遊戲物件進行個別的存取。例如透過以下的腳本程式,可以在遊戲執行時讓物件移到特定的座標位置:
遊戲執行時,方塊移到 Vector3 ( 0,0,0 ) 的原點位置:
若是需要讓遊戲物件朝向指定的軸向移動的話,可以將程式改為以下的寫法:
遊戲執行時,物件朝著 X 軸方向(自身座標)移動。

Time 類別包含了一個稱為「deltaTime」的重要變數。此變數包含了從呼叫 Undate 或 FixedUpdate 的時間長度(決定於實際使用的類型)。

若您想要讓物件每 1 秒鐘旋轉 30 度,請使用以下腳本:
旋轉到指定角度的腳本:
遊戲執行時,方塊旋轉的結果:
物件旋轉是以「度」為單位,不過若是輸入 360 度,實際上並不是轉一圈,而是完全靜止不動。因為執行時將會比對旋轉的角度,若是輸入 361 度與輸入 1 度的結果相同,輸入 359 度則與輸入 -1 的結果相同。

變數 ( Variable )
接著改用變數的方式來寫
var RotateAngle : int;
function Update() {
 transform.Rotate(0, RotateAngle*Time.deltaTime, 0);
}
變數 RotateAngle 用於設定物件每秒旋轉的角度。

變數的宣告與使用
程式語言幾乎都有變數的觀念,變數的功用是保存程式執行時的變動資訊,例如遊戲的分數、玩家的等級、經驗值。
使用變數的一項優點,就是可直接在 Unity 的屬性檢視器進行設定,不需要透過腳本編輯器修改程式碼。這對於遊戲設計團隊來說是很理想的,程式設計人員只要在撰寫遊戲腳本時宣告變數,並且將變數的用法告知團隊的其他設計人員,讓設計人員們只需要設定這些變數值,而不需要深入瞭解程式運作的原理,達到分工合作的目地。

變數的資料型態
String 字串:文字與數字,儲存於引號之間,例如 "Unity 3D" 。
int 整數:沒有小數點的整數值。
float 浮點數:包含小數點的數值。
boolean 布林值: 只有 true 或是 false ,主要用於儲存某種狀態。
Vector3 向量座標:空間的 XYZ 座標值。

附註: Vector3 是比較特殊的變數資料型態,以下是使用範例:
function PlayerDeath() {
 transform.position = Vector3(0, 0, 0);
}

變數的類型
變數宣告時,可以指定變數的類型。
public 公用變數
private 私用變數
static 靜態全域變數

陣列 ( Array )
「陣列」與「變數」的功能很類似,不過陣列可以儲存多個值,以下是一個陣列使用的基本範例:

設定屬性
遊戲物件的屬性可透過腳本程式存取與控制,記得我們在前面講過修動物件位置的範例嗎?
腳本中的 gameObject.transform.position 是指物件的位置,請注意這部份全部使用小寫。

存取其他元件
元件是附加到遊戲物件的,例如附加 Light 元件到遊戲物件可產生燈光的照明效果,附加 Camera 元件可以讓該物件變成攝影機物件。實際上腳本也屬於一種元件,可以附加到遊戲物件。
大部份的共用元件可使用一般的成員變數進行存取
Transform transform
Rigidbody rigidbody (剛體元件)
Renderer renderer (著色器元件)
Camera camera(攝影機元件)
Light light(燈光元件)
Animation animation
Collider collider

燈光控制的範例
新增一個 JavaScript 程式腳本並命名為「LightControl」,使用腳本編輯器撰寫腳本程式,並套用到擁有燈光元件的遊戲物件。
加上控制燈光的顏色
var lightRange:float = 10;
var lightIntensity:float = 1;
var lightRed:float;
var lightGreen:float;
var lightBlue:float;
function Update () {
gameObject.light.range=lightRange;
gameObject.light.intensity=lightIntensity;
gameObject.light.color=Color(lightColorR,lightColorG,lightColorB);
}

OnMouseDown() 函數
當玩家使用滑鼠點選場景中的遊戲物件或 GUI 元素時,就會呼叫 OnMouseDown() 函數。
我們使用前一個燈光控制的範例,加上 OnMouseDown() 函數與 if 判斷式,製作一個可以開關的燈光:
滑鼠點圖片可檢視腳本

粒子發射器控制的範例

Unity 輸入控制
使用 Application.LoadLevel 方法載入場景,注意場景 Scene_01 必須先加到發佈清單中,否則會出現無法載入場景的訊息。
滑鼠點圖片可檢視腳本

Unity 攝影機控制
滑鼠點圖片可檢視腳本
滑鼠點圖片可檢視腳本

Instantiate 動態生成
當我們製作遊戲的內容時,並不是所有的物件在進入遊戲時就會立即出現場景中,例如射擊遊戲中的子彈,或是俄羅斯方塊遊戲中從上方掉落的各種方塊,都是在遊戲執行階段重生 ( spawn ) 或再製 ( duplicate ) 產生的物件。
將一個預製物件指定給 myPrefab 變數,遊戲開始時動態生成。
請注意程式碼行號 3 的部份,變數的型態從 GameObject 遊戲物件改為 Rigidbody 剛體物件,可以限制選取的物件必須附加 Rigidbody 剛體元件。

if 條件判斷
if 敘述用於判斷條件,若是條件符合就執行 { } 括弧內的指令,而在不符合條件時,則執行 else { } 中的指令。
var grounded : boolean = false;
function Update() {
 if ( grounded == true ){
  // 執行符合條件時的指令
 }
 else{
  // 執行不符合條件時的指令
 }
}

無論是日常生活或是遊戲的世界,經常需要進行條件的判斷。舉個列子,出門時若下大雨,那就開車上班,否則騎車就好。下面是腳本的觀念,但是實際上不能這樣寫,因為 JavaScript 的變數不能用中文喔!
var 下大雨 : boolean;
function Update() {
 if ( 下大雨 == 是 ){
  開車上班;
 }
 else{
  騎車上班;
 }
}

多重條件
條件判斷式使用 && 符號標記時,設定的多項條件必須全部成立,才會執行指定的動作。

條件判斷式使用 || 符號標記時,設定的多項條件只要其中一項條件成立,就會執行指定的動作。

迴圈
迴圈用於需要重複執行的指令,以下的程式碼開始執行時,將會重複執行 10 次,列印從 1 ~ 10 的數字。


任何附加到遊戲物件的元件或腳本,皆可透過 GetComponent 存取。
transform.Translate(0, 1, 0);
// is equivalent to
GetComponent(Transform).Translate(0, 1, 0);

需要注意的是 transform 與 Transform 的差異,前者是一個變數(英文小寫),而後者是一個類別或腳本名稱(英文大寫)。

分類標籤
分類標籤的功能是讓遊戲引擎辨別不同類別的物件,您可以為場景中的特定遊戲物件加上分類標籤,實際上標籤的觀念很容易理解,以下舉個例子說明:
假設您正在進行第一人稱射擊遊戲的設定,玩家的武器與彈藥可以從場景中取得,彈藥的補給來自地面上的彈藥箱,而這些彈藥箱分為幾種不同的規格,紅色彈藥箱可增加 10 枚手榴彈、綠色彈藥箱可增加子彈 100 發、藍色彈藥箱則…。遊戲引擎如何判斷玩家拾獲的物件是何種類型的彈藥箱呢?您可以在為同一類型的物件指定分類標籤。

碰撞偵測
function OnControllerColliderHit(hit:ControllerColliderHit){
if(hit.gameObject.tag == "Fire"){
Debug.Log("Very hot~");
}
}

Application.LoadLevel("game");
134

偵聽鍵盤
使用 GetKey 判斷玩家按下的鍵盤按鈕。
function Update () {
if(Input.GetKey("1"))
guiText.text="攻擊";

if(Input.GetKey("2"))
guiText.text="魔法";

if(Input.GetKey("3"))
guiText.text="防禦";

if(Input.GetKey("4"))
guiText.text="道具";

if(Input.GetKey("5"))
guiText.text="逃跑";

}

gameObject.active = false; 讓遊戲物件停止動作。

使用 OnMouseDown() 開啟網頁
若在 Unity 編輯器或單機版遊戲執行時,將會使用預設的瀏覽器(例如 Internet Explorer、Google Chrome 等)開啟指定的網頁,並將瀏覽器視窗移到最前面的位置。而在使用 web player 方式執行遊戲時,瀏覽器將會立即導向指定的網址。