(本篇文章網誌版:http://shineright.blogspot.tw/2016/12/day-20-ui.html)
遊遊核心機制已大致完成。現在最重要的是讓主角在碰觸敵人或敵人子彈時損失生命,以及顯示玩家的分數。
兩者我都會以Unity的UI系統完成。Unity的UI系統讓處理按鈕、選單、文字、卷軸、影像等常用的UI元件變得非常簡單。我會以Unity UI的影像(Image)顯示生命值,以文字(Text)顯示分數。
在Hierarchy欄點Create→UI→Canvas新增一個Canvas。Unity會自動建立兩個Game Object──Canvas和EventSystem。Canvas和EventSystem是使用Unity UI不可或缺的元件。UI物件必須顯示於Canvas上,而UI物件的觸發事件(Event)如按扭的點擊則由EventSystem來處理。
在Canvas的Inspector中,把Canvas Scaler (Script) Component的Scale Mode選為「Scale With Screen Size」,使UI隨螢幕大小縮放,以適應不同像素的螢幕。(預設的「Constant Pixel Size」無論在任何螢幕大小,都維持同樣的像素大小,所以就算遊戲在大螢幕運行,UI原件也不會放大)
先來處理主角的生命值。在Hierarchy欄為Canvas建立一個新的Game Object,命名為「Life Indicator」。UI元件不同於一般Game Object,它們沒有Transform Component,取而代之的是Rect Transform Component。Rect Transform Component簡單來說,代表UI Canvas中的一塊矩形範圍。Rect Transform看似簡單,卻擁有許多複雜的功能。例如,它可以設定Anchors(在Scene中顯示的四個小三角形),使該UI元件隨父元件(parent)的比例縮放。
為了使UI在不同螢幕都能維持正常比例,我習慣在Canvas底下再建立一個新的Game Object,名為「Aspect Ratio Fitter」。把剛剛建立的「Life Indicator」拖至「Aspect Ratio Fitter」,成為它的子元件。並在Inspector為「Aspect Ratio Fitter」建立一個Aspect Ratio Fitter (Script) Component。把Aspect Mode設為Fit In Parent,Aspect Ratio設為0.5625。Fit In Parent選項使該Game Object延展以適應parent,也就是Canvas(也就是螢幕大小)的最大範圍。Aspect Ratio欄可以設定該Game Object的長寬比,使該Game Object在不同長寬比的螢幕中都能維持固定的比例。因為我的遊戲主要做給手機(螢幕比例大多是9:16),所以設為9÷16=0.5625。
接著在Scene視窗中把「Life Indicator」四個代表Pivot的小三角形移到螢幕左上方,形成一個小長方形。
並把它的Rect Transform的Left, Top, Right, Bottom都設為0。這樣一來在任何螢幕大小,Life Indicator都會維持一定比例。(因為四個「Pivot在父物件的位置」到四個「物件矩形的角」的距離是固定的)
這個就是用來顯示生命值的範圍了。
在Hierarchy中為Life Indicator新建三個UI→Image,並在Inspector中的Image (Script) Component把Source Image改為愛心的圖案。並像剛剛調整Life Indicator的Pivot一樣,把愛心的Pivot調到要顯示愛心的位置的四個角,再到Inspector把Rect Transform的Left, Top, Right, Bottom都設為0。生命值的顯示位置就設定好了。
設定完成後,就可以來寫Script了。建立一個名為「LifeIndicator.cs」的C# Script。
public class LifeIndicator : MonoBehaviour
{
//三個愛心的Image
public GameObject[] lifeImages;
//剩餘生命
private int currentLife;
void Start()
{
currentLife = lifeImages.Length;
}
public void DecreaseLife()
{
//減少生命值
currentLife—;
//讓愛心的圖案消失
lifeImages [currentLife].SetActive (false);
if (currentLife == 0) {
print ("Game Over");
}
}
}
型態為Game Object陣列的lifeImages
是用來儲存剛剛在Scene上建立的三個愛心。currentLife
代表剩餘的生命值。currentLife
在Start()
函數中被初始化為lifeImages.Length
,也就是愛心的總數(之後會在Inspector設定)。
public void DecreaseLife()
是主角在受到傷害時會呼叫的函數。它會減少生命值,使一個愛心的圖案消失,並檢查生命值是否已到達0。這裡暫時以print(“Game Over”)
取代顯示遊戲結束的視窗。
接著到Character.cs,在OnTriggerEnter2D(...){}
的if
判斷式內加入下行程式碼:
GameObject.FindObjectOfType<LifeIndicator> ().DecreaseLife ();
GameObject.FindObjectOfType<>()
和GameObject.FindGameObjectWithTag()
頗為相似,只是前者以型態來找尋物件,並回傳該型態的物件。後者以Game Object的Tag來尋找Game Object,且回傳型態是GameObject。
因為在Scene上只有一個LifeIndicator物件,所以可以用GameObject.FindObjectOfType<>()
找到該物件,並直接呼叫DecreaseLife()
來減少生命值。
回到Unity Editor,為「Life Indicator」Game Object新增Life Indicator (Script) Component,把Life Images的Size設為3,並把三個愛心的Image分別拉入Element 0-2中。
進入Play Mode測試一下,現在主角被物件打到後不只會改變表情,生命值也會正常降低了。
待續。