2016年12月21日 星期三

Day 22: 暫停、遊戲結束畫面

昨天做完開始畫面和分數後,UI就剩暫停和遊戲結束畫面了。不同於遊戲開始畫面,製作暫停和結束畫面都不必新開一個Scene,而是直接在Script中Instantiate一個Canvas Prefab就好了。

馬上來做暫停鈕和暫停畫面。和之前一樣建立一個UI→Button,換成暫停鈕的圖案,並在Scene中排好位置。



接著建立一個名為「InstantiateCanvasOnClick.cs」的C# Script:

using UnityEngine;

using System.Collections;

using UnityEngine.UI;



public class InstantiateCanvasOnClick : MonoBehaviour 

{
    //要產生的canvas

    public GameObject canvasPrefab;

    
    void Start()

    {
        //按下按鈕時,呼叫ClickEvent()

        GetComponent< Button > ().onClick.AddListener (()  =>  {

            ClickEvent ();

        });

    }



    void ClickEvent()

    {
        //生產canvasPrefab

        Instantiate (canvasPrefab, Vector2.zero, Quaternion.identity);

    }

}

上段程式碼十分簡單,只是讓玩家在點擊按鈕後產生canvasPrefab而已。

回到Unity Editor,為暫停鈕加上Instantiate Canvas On Click (Script) Component。

接著來做暫停畫面的Canvas。在Hierarchy欄建立一個新的UI→Canvas,命名為「Pause Canvas」,並為之再新增一個名為「Background」的Image子物件。



在它的Image Component把它的Color設成稍稍透明的黑色,如此在暫停時還能隱約看到遊戲場景。此外,還得為它加上Aspect Ratio Fitter (Script) Component,設成Fit In Parent、0.5625,以確保暫停畫面在不同螢幕下維持一定比例。


在Background內再建立兩個子物件。一個是擁有Text Component的暫停文字,一個是擁有Button Component的開始按鈕。在Scene中調整一下位置。


開始按鈕的Script和暫停鈕的Script完全相反,是要在玩家點擊後,把現存的暫停Canvas從螢幕上移除:

using UnityEngine;

using System.Collections;

using UnityEngine.UI;



public class DestroyCanvasOnClick : MonoBehaviour

{
    //要移除的canvas

    public GameObject canvas;

    void Start()

    {
        //按下按鈕後,呼叫ClickEvent()

        GetComponent< Button > ().onClick.AddListener (()  =>  {

            ClickEvent ();

        });

    }



    void ClickEvent()

    {
        //刪掉canvas

        Destroy (canvas);

    }

}

這段程式碼相當簡單,應該不用多作解釋。


回到Unity Editor,為開始鈕加上Destroy Canvas On Click (Script) Component,並把整個Pause Canvas拖到要移除的Canvas欄。


將整個Pause Canvas拖到Project欄成為Prefab後便可把它從Scene移除。把剛做好的Pause Canvas Prefab拖到暫停鈕Instantiate Canvas On Click (Script) Component的Canvas Prefab欄後,進入Play Mode測試一下。

在Play Mode下,暫停鈕和開始鈕都可正常生產和移除暫停畫面。然而,暫停畫面下遊戲還是正常運行。

要使遊戲真正暫停,只要為Pause Canvas加上一小段Script就可以了:

public class PauseOnCanvas : MonoBehaviour

{

    void OnEnable()

    {
        //時間暫停

        Time.timeScale = 0f;

    }



    void OnDisable()

    {
        //時間以正常速度運行

        Time.timeScale = 1f;

    }

}

Time.timeScale可以調整遊戲引擎中時間運行的速度比例。任何和時間有關的函數都會受到影響(如Update()CoroutineInvoke()等等)。Time.timeScale1f時,時間以正常速度運行。

當產生暫停畫面時,OnEnable()會被呼叫,我將Time.timeScale設為0f,使時間在出現暫停畫面時暫停。

而暫停畫面被移除時,OnDisable()會被呼叫,將Time.timeScale調回1f,讓遊戲回復正常運行。

結束畫面和暫停畫面大同小異。一樣建立一個新的Canvas,新增一個背景圖案的Image,加上Aspect Ratio Fitter (Script)和Pause On Canvas (Script) Component(畢竟遊戲結束時,不必讓遊戲繼續在後面默默運行)。





結束畫面有幾個和暫停畫面不一樣的UI。一個是顯示分數的佈告欄,一個是「再玩一局」的按鈕。

「再玩一局」按鈕非常簡單,只要為它加上Go To Scene On Click (Script),並把Scene Index設為開始畫面的Build Index(也就是0),就完成了。


顯示分數的佈告欄也相當簡單,但我希望在顯示分數之外,還可以放一張主角俯看摩天大樓的圖案,並跟據不同的高度顯示不同的大樓。



先在昨天寫的「ScoreText.cs」加一小段:

public int CurrScore {

    get {

        return currScore;

    }

}

讓其他Script可以簡單地以GameObject.FindObjectOfType< ScoreText >().CurrScore取得分數。

接著新建一個名為「GameOverCanvas.cs」的C# Script:

using UnityEngine;

using System.Collections;

using UnityEngine.UI;



public class GameOverCanvas : MonoBehaviour

{

    public Text finalScoreText;  //顯示分數的Text

    public Image buildingImage;  //顯示大樓


    //大樓Sprite陣列(從最矮排到最高)

    public Sprite[] buildingSprites;

    //大樓的高度(從最矮排到最高)

    public int[] buildingHeights;


    //分數

    private int score;



    void Start()

    {
        //從ScoreText取得分數

        score = GameObject.FindObjectOfType< ScoreText > ().CurrScore;


        //設定顯示分數的文字

        finalScoreText.text = score.ToString() + "cm";


        //判斷要顯示哪一棟大樓

        int spriteIndex = 0;


        while (buildingHeights [spriteIndex] < score) {

            spriteIndex++;

        }


        //設定大樓的Sprite

        buildingImage.sprite = buildingSprites [spriteIndex - 1];

    }

}

finalScoreTextbuildingImage這兩個變數分別代表遊戲結束畫面佈告欄上的分數文字和主角俯看的大樓的Image。

buildingSpritesbuildingHeights則用來存儲各大樓的sprite和高度。例如,buildingSprites[3]是101大樓、buildingHeights[3]是50920、buildingHeights[4]是82800,如果分數在50920到82800之間,就會顯示101大樓。

Start()函數先以GameObject.FindObjectOfType< ScoreText >().CurrScore取得玩家最終的分數,再設定佈告欄上finalScoreText顯示的text。下面的while式決定該用哪一棟大樓的sprite(若分數大於當前大樓的高度,則繼續比較下一棟大樓的高度),最後再以buildingImage.sprite設定大樓的Image。

回Unity Editor,為Game Over Canvas加上Game Over Canvas (Script) Component。並新建UI→Text做為分數的Text和UI→Image做為建築物的Image。把分數Text拉至Final Score Text欄、建築物Image拉至Building Image。設定完Building Sprites和Building Heights後,便可把Game Over Canvas整個拉到Project欄成為Prefab。



最後,打開之前寫好的LifeIndicator.cs。新增一個用來儲存Game Over Canvas Prefab的public變數:

public GameObject gameOverCanvasPrefab;

並把先前的print(“Game Over”);改成產生Game Over Canvas的

Instantiate (gameOverCanvasPrefab, Vector2.zero, Quaternion.identity);

把Game Over Canvas的Prefab拖到Life Indicator中Life Indicator (Script) Component的Game Over Canvas Prefab欄,遊戲結束畫面就能在耗盡生命時出現了!


待續。

沒有留言 :

張貼留言