Unity - PuzzleGame 1 (9宮格總和15)

  接下來做做幾個益智遊戲,很久以前就有玩到許多這類型的遊戲,有些機制還滿有趣的,試著複製幾個類型來做看看就是了,不過有許多算是偷懶的部分就隨意吧。


  我想先從簡單又算經典的九宮格總和15來做,機制很容易懂,就是九宮格的三個橫向、三個直向以及交叉對角線的總和都是15,記得第一次玩到這個的時候感覺有點難,不過在了解了以後其實是滿容易的,雖然版面排列不只一種,但是基本上都差不多就是了。

  製作使用的Unity版本為5.2.2f1。




  實作測試 (WebGL build,似乎會花點時間載入),用滑鼠拖拉數字方塊至九宮格上,直到各行列跟交叉線的總和都是15便完成遊戲






1、製作數字格子。

  首先製作9個數字方塊物件,這邊用空白方塊圖片拉9個到場景中,這樣就有9個方塊Sprite物件,接著在每個方塊下放一個數字,這個數字當然就是1到9。


  每個物件都加上一個Box Collider 2D的Component,因為之後要來使用內建的OnMouseDown()、OnMouseUp()來做簡易的拖拉擺放。

  接著在每個物件上再加上一個自己做的Component,用來記錄這個格子代表的數字,以及拖曳的運作。

public class Unit : MonoBehaviour
{
    public int number; //這個方塊代表的數字
    private Vector3 startPosition; //紀錄起始位置
    private bool isDrag; //是否拖曳中

    void Start ()
    {
        //紀錄方塊在開始時的位置,之後可以讓方塊回到這裡
        startPosition = this.transform.position;
    }

    void Update ()
    {
        //如果這個方塊在拖曳中,就讓它一直跟隨滑鼠
        if (isDrag)
        {
            Vector2 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition); //把滑鼠座標轉換為場景座標
            this.transform.position = mousePos;
        }
    }

    //當滑鼠按下的時候
    void OnMouseDown()
    {
        StopAllCoroutines(); //停止移動的Coroutine
        isDrag = true; //此物件拖曳中

        //因為只有簡單一個機制,所以直接用Singleton來呼叫
        Board.GetInstance().RemoveUnit(this); //嘗試移除板子上的這個物件,如果板子上沒有這個物件就忽略
    }

    //當滑鼠放開
    void OnMouseUp()
    {
        isDrag = false; //取消拖曳

        //檢查放開的瞬間是否放在板子上
        bool isOnBoard = false;
        RaycastHit2D[] hits = Physics2D.RaycastAll(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
        foreach (RaycastHit2D hit in hits)
        {
            //用Raycast的方式來檢查所有的Collider,只取Tag是Board的物件
            if (hit.transform.tag == "Board")
            {
                //如果放開的時候滑鼠位置有碰到板子,就放到板子上
                Board.GetInstance().AddUnit(this, hit.transform.gameObject); //同樣用Singleton方式呼叫
                StartCoroutine("ReturnPosition", hit.transform.position); //把數字格子移動到板子格子的位置
                isOnBoard = true;
            }
        }

        //如果滑鼠座標沒有接觸到板子,就送回起始位置
        if(!isOnBoard) StartCoroutine("ReturnPosition", startPosition);
    }

    //簡單的移動位置方法
    IEnumerator ReturnPosition(Vector3 pos)
    {
        Vector3 target = new Vector3(pos.x, pos.y, this.transform.position.z);
        while (true)
        {
            this.transform.position = Vector3.MoveTowards(this.transform.position, target, 10f * Time.deltaTime);
            if (this.transform.position == target) break;
            yield return null;
        }
    }

    //讓方塊移動回起始位置
    public void ReturnStartPosition()
    {
        StopAllCoroutines();
        StartCoroutine("ReturnPosition", startPosition);
    }
}





2、製作九宮格板子。

  數字方塊到這邊就簡單的做完了,接下來要製作九宮格版面的部分。



  同樣使用空白方塊圖片拉9個Sprite物件到場景中,我這邊依照鍵盤的數字鍵來排列並且命名物件,同樣每個物件上都加上一個Box Collider 2D,到時候判斷滑鼠位置。這邊特別設定每個格子物件的Tag為Board,到時候用來判斷滑鼠座標的Raycast碰到的Collider是不是這個。

  把所有方格都放到一個空白的Board之下,簡單做個整理,之後要整組調整位置也方便。接著這個Board物件上放上一個自己做的Component,算是做為整個遊戲的主要運作中心。


public class Board : MonoBehaviour
{
    public GameObject[] positionObj; //九宮格板子的格子物件    
    private Unit[] unitArr; //數字格子物件陣列
    private static Board instance;

    public static Board GetInstance()
    {
        if (instance == null)
        {
            instance = GameObject.FindObjectOfType<board>();
            if (instance == null) instance = new GameObject().AddComponent<board>();
        }
        return instance;
    }

    void Start ()
    {
        unitArr = new Unit[9];
        ReCalculate();
    }

    //增加一個Unit物件到板子上,用板子格子的物件來判斷位置
    public void AddUnit(Unit unit, GameObject boardObj)
    {
        int index = -1;
        //尋找板子格子
        for (int i = 0; i < positionObj.Length; ++i)
        {
            if (boardObj == positionObj[i])
                index = i;
        }

        if (index == -1)
        {
            //沒有這個位置物件,把Unit送回起始點
            unit.ReturnStartPosition();
        }
        else
        {
            //有這個位置物件,這個位置上已經有一個Unit了
            if (unitArr[index] != null)
            {
                if (unitArr[index] != unit)
                {
                    //把舊的Unit送回起始點,並把新的放上去
                    unitArr[index].ReturnStartPosition();
                    unitArr[index] = unit;
                }
            }
            else
            {
                //有這個位置物件,這個位置上是空的,直接把Unit設定上去
                unitArr[index] = unit;
            }
        }

        ReCalculate(); //重新計算各行列總和
    }

    //從板子上移除Unit物件
    public void RemoveUnit(Unit unit)
    {
        for (int i = 0; i < unitArr.Length; ++i)
        {
            if (unit != null && unitArr[i] != null && unitArr[i].Equals(unit))
                unitArr[i] = null;
        }

        ReCalculate(); //重新計算各行列總和
    }

    //簡單計算行列總和
    private void ReCalculate()
    {
        int n1 = (unitArr[0] == null) ? 0 : unitArr[0].number;
        int n2 = (unitArr[1] == null) ? 0 : unitArr[1].number;
        int n3 = (unitArr[2] == null) ? 0 : unitArr[2].number;
        int n4 = (unitArr[3] == null) ? 0 : unitArr[3].number;
        int n5 = (unitArr[4] == null) ? 0 : unitArr[4].number;
        int n6 = (unitArr[5] == null) ? 0 : unitArr[5].number;
        int n7 = (unitArr[6] == null) ? 0 : unitArr[6].number;
        int n8 = (unitArr[7] == null) ? 0 : unitArr[7].number;
        int n9 = (unitArr[8] == null) ? 0 : unitArr[8].number;

        //三個直的,三個橫的,加上交叉,總共有8個數值
        int[] result = new int[8];
        result[0] = n7 + n8 + n9;
        result[1] = n4 + n5 + n6;
        result[2] = n1 + n2 + n3;
        result[3] = n3 + n5 + n7;
        result[4] = n3 + n6 + n9;
        result[5] = n2 + n5 + n8;
        result[6] = n1 + n4 + n7;
        result[7] = n1 + n5 + n9;

        //判斷是否遊戲結束,只有其中一個不是15就不算完成遊戲
        bool isGameOver = true;
        for (int i = 0; i < result.Length; ++i)
        {
            if (result[i] != 15) isGameOver = false;
        }

        if (isGameOver)
        {
            //顯示遊戲結束畫面
            Debug.Log("Game Over");
        }
    }
}

  完成了這個Component並且把它掛在Board物件上之後,在Inspector面板中把九宮格的格子依序放到PositionObj裡面,同時調整Z軸,稍稍往後移一點點,避免九宮格的Collider跟Unit物件重疊,因為Unit物件並不是用Raycast的方式判斷點擊,所以Collider重疊就會影響OnMouseDown的運作







3、完成

   到這邊遊戲的機制就算完成了,當然還有介面的製作、數字總和的顯示,不過這部分就要看自己的需求去製作了。

  或許有些部分不是最好的方式,拖曳跟擺放的判斷當然也有其他的方式,不過這邊主要的目的在於簡單(偷懶)製作,同時利用Unity的功能來完成,希望是有達到這個目標就是了,如果有錯誤的話請告訴我。


No comments:

Post a Comment