2012年2月4日星期六

Unity 3D : 使用 GUI 製作時鐘倒數效果


倒數計時的功能,在遊戲中相當常見,表現方式很多,其中以圓形時鐘指針那樣縮減倒數的方式尤其常見,例如技能的冷卻時間倒數;但是當我們要使用 GUI 製作這種效果時,會發現不容易做到,因為 GUI 不能指定區塊將圖片在該區塊範圍內的圖素遮擋為透明的,此時,我們必須使用其他技巧來完成這個目的。


首先我們必須將底圖切割為半圓的圖,而倒數效果的圖則是與底圖相同大小的白色半透明圖片。

 在【Unity 的 GUI 深度控制】一文中,我們瞭解 GUI 有深度關係,所以我們以圖層的概念為這四張圖重新排列成為五個圖層,如下圖所示的前後順序排列它們的深度,然後以圓心座標為半透明圖的旋轉樞紐,使 GUI 依序做以下幾個動作就可完成效果..


  • 剛開始時先將最前面層的那個半圓圖及左右半透明圖隱藏起來(關閉 script 的執行或使該 GUI.color.a 為 0)。
  • 當開始倒數時先顯示左右半透明圖。
  • 右半透明圖開始旋轉,旋轉過程如果從正面看可以看到部份被左底圖擋住。
  • 當右半透明圖旋轉 180 度後,將完全被左底圖檔住,此時顯示最前面層的右半圓圖。
  • 左半透明圖開始旋轉,旋轉過程如果從正面看可以看到部份被最前面層的右半圓圖擋住。
  • 當左半透明圖旋轉 180 度後,將完全被最前面層的右半圓圖擋住,此時即完成倒數。
  • 最後再將最前面層的那個半圓圖及左右半透明圖隱藏起來,並將左右半透明圖的旋轉角度恢復為 0 度。

以上是動作原理,接下來我們利用【Unity 的 GUI 如何旋轉?】、【Unity 的 GUI 深度控制】、【Unity 3D : 利用 Animation View 製作動態的 GUI】文章所提到過的方法來實作效果,實作步驟為:

  • 撰寫 GUI 專用的程式碼,如下...

public class MyDrawTexture : MonoBehaviour {

//貼圖
public Texture2D texture;
//大小位置
public Rect position;
//深度
public int depth;
//角度
public float angle;
//旋轉樞紐
public Vector2 pivot;

void OnGUI(){

if(angle != 0) GUIUtility.RotateAroundPivot(angle, pivot);

GUI.depth = depth;
GUI.DrawTexture(position , texture , ScaleMode.StretchToFill);
}
}

  • 建立空物件(GameObject / Create Empty),將此程式拉給此物件附加上成為物件的 Component。



  • 在 Hierarchy 中複製出另外四個物件並更改名稱,並建一個空物件做為父物件,把這五個 GUI 物件都拉進去做為子物件。


  • 設置各物件的大小位置(假設圖的大小為 22*44)、深度(在前面的數值較小)、旋轉樞紐座標。






  • 開啟 Animation View (Window / Animation) 並在 Hierarchy 中選擇這些 GUI 物件的父物件,在 Animation View 新增動畫剪輯,新增完成後,在 Inspector 可以看到多出 Animation Component,在 Animation View 中會發現可對此物件及其子物件編輯動畫曲線。




  • 在 Hierarchy 點擊名為 front 的物件,並在 Animation View 中將它的 Enable 欄位設定 0 秒的值為 0 及 0.5 秒(0:30) 的值為 1 的線段。


  • 在 Hierarchy 點擊名為 modalLeft 的物件,並在 Animation View 中將它的 Angle 欄位設定 0.5 秒(0:30)的值為 0 及 1 秒(1:00) 的值為 180 的線段。


  • 在 Hierarchy 點擊名為 modalRight 的物件,並在 Animation View 中將它的 Angle 欄位設定 0 秒的值為 0 及 0.5 秒(0:30) 的值為 180 的線段。


  • 接下來執行遊戲就能看到像時鐘轉動的效果。


到這邊算是完成效果的實現了,但是離實際運用還有一些事情要考慮... 每個 GUI 物件都是個別設定位置,但這個效果至少要五個物件在一起,如果要移動它的位置不就要反覆做五次修改座標的動作,這樣的話,好像也不是很方便;所以我們可以使用 GUI.BeginGroup() 及 GUI.EndGroup() 將它們包起來統一設置,另外就是旋轉樞紐也有類似的情形,所以我們也可將它調整到一個地方設置就好,做法如下...
  • 將上面程式碼中的 public int depth; 、 public Vector2 pivot; 及 OnGUI() 整個刪除掉,因為用不到了。
  • 撰寫 GUI 的 Group 程式碼如下:

public class MyDrawTextureGroup : MonoBehaviour {

public Rect position;

public Vector2 pivot;

public MyDrawTexture[] guis;

void OnGUI(){

GUI.BeginGroup(position);

foreach(MyDrawTexture _gui in guis){

if(_gui == null || !_gui.enabled) continue;

Matrix4x4 _matrix = GUI.matrix;

if(_gui.angle != 0) GUIUtility.RotateAroundPivot(_gui.angle, pivot);

GUI.DrawTexture(_gui.position , _gui.texture , ScaleMode.StretchToFill);

GUI.matrix = _matrix;
}

GUI.EndGroup();
}      
}

  • 把這支程式拉給這五個 GUI 物件的父物件,設置寬高及旋轉樞紐座標後,依前後關係將五個 GUI 物件拉到 Inspector 中的 Guis 欄位的陣列元素中。


此時再次執行遊戲,獲得的效果是一樣的,所不同的是,當改變位置時只需要在父物件調整即可。

最後,我們可能想要調整效果動作的時間,希望速度要快一點或慢一點,那麼只需要點擊一下父物件,在 Animation View 把有設置動畫線段的物件展開來 ( front、 modalLeft、modalRight )就能同時看到它們的線段及 key,此時只要在上方的橫條拖曳滑鼠框選該時間範圍內各線段的 key,然後拉動橫條上的 key 就能任意調整多條線段的關鍵時間點,這樣就能很方便的調整速度。