最重要的就是記住翻過的卡片位置,當然也記住那張圖案是什麼,避免因為忘記再重複翻之前翻過的卡片浪費時間,遊戲就是越快把卡片全部移除越好。
製作使用的Unity版本為5.2.2f1。
實作測試 (WebGL build,稍微花點時間載入) (滑鼠點擊卡片翻牌,兩張卡片一樣就消除,全部消除遊戲結束)
1、卡片
先來製作數張卡片物件,這邊稍微偷個懶,做一個空物件然後直接把卡片的背面跟正面的Sprite放進去,然後我稍稍地把卡片正面圖的Z軸位置往前移動一點點,這樣形成一個正反兩面不同的物件。
之後要做翻牌效果就直接轉最上層物件的Y軸角度,就可以做出類似翻牌的感覺了。
再來做一個簡單的卡片Component,這個Component不做什麼用,單純紀錄卡片群組號碼,用來辨識兩張卡片是否同一個群組,也可以用其他方法來判斷點選的兩張卡片是否相同,不過這邊就隨意啦。
public class Card : MonoBehaviour { public int group; }
要先把這個Component加到卡片物件上也是可以,不過我是在Board物件中建立卡片的時候加上去順便設定群組號碼,所以這邊只要製作完這個Script就可以了,不用再做其他動作。
所以這邊我每個卡片的Prefab只有在最上層的物件加上一個Box Collider 2D而已,依賴之後的Board物件加上別的Component。這邊可以預測到一個問題,就是如果卡片數量多的話,需要製作的Prefab也多,另外一種做法就是指製作一個Prefab,然後Instantiate物件的時候去切換正面的Sprite圖片。
2、板子
開始來製作板子Component,同樣這次也是讓所有的判斷都寫在這裡,同時因為我只有使用16張卡片,所以有些數值以及做法是寫死的,如果要改變卡片的數量,就需要調整一些部分。
public class Board : MonoBehaviour { public GameObject[] card; //卡片prefab private List<GameObject> cardList = new List<GameObject>(); //場上建立的所有卡片物件 private List<Card> pickCard = new List<Card>(); //拾取的卡片 private bool animFinished = true; //卡片翻面動作是否結束 private int cardCount; //場上剩餘卡片計數 void Start() { //簡易產生一組16個亂數順序 int[] order = new int[16] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; for (int i = 0; i < 16; ++i) { int random = Random.Range(0, 16); int backup = order[i]; order[i] = order[random]; order[random] = backup; } //這邊寫死只產生16張卡片 for (int i = 0; i < 8; ++i) //卡片種類 { for (int j = 0; j < 2; ++j) //每種產生兩張 { GameObject newCard = GameObject.Instantiate(card[i]); newCard.AddComponent<Card>().group = i; //設定兩張卡片同一個group id newCard.transform.parent = this.transform; //把卡片物件放在Board下 //設定卡片在4x4中的位置,我這邊產生的位置是用local座標 //所以第一張卡的(0,0)會在Board物件的位置,開始往右跟往上增加卡片,形成4x4的排列,卡片的位置安排也要看自己的需求修改 float posX = order[(i * 2 + j)] % 4; float posY = order[(i * 2 + j)] / 4; newCard.transform.localPosition = new Vector2(posX, posY); cardList.Add(newCard); //卡片加入列表記錄起來 } } cardCount = cardList.Count; //紀錄場上卡片數量 } void Update() { //滑鼠點左鍵,檢查卡片 if (Input.GetKeyDown(KeyCode.Mouse0)) { RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero); if (hit) { Card target = hit.transform.GetComponent<Card>(); if (target != null) { CheckCard(target); //開始檢查卡片 } } } } private void CheckCard(Card target) { if (!animFinished) return; //如果翻卡片動作還沒結束就不執行 //如果拾取卡片沒有目前點的卡片,就把卡片加入列表,並且讓卡片翻過來 if (!pickCard.Contains(target)) { pickCard.Add(target); StartCoroutine(FlipAnim(target, pickCard)); } } //簡易翻卡動作,直接轉物件的Y軸角度 private IEnumerator FlipAnim(Card card, List<Card> pickList) { animFinished = false; while (true) { float angle = Mathf.MoveTowards(card.transform.localEulerAngles.y, 180, 360 * Time.deltaTime); card.transform.localRotation = Quaternion.Euler(new Vector3(0, angle, 0)); if (angle == 180) break; yield return null; } //如果拾取卡片有兩張,就來比對 if (pickList.Count == 2) { yield return new WaitForSeconds(0.3f); if (pickList[0].group == pickList[1].group) //如果兩張卡片是同組的 { //關閉這兩張卡片的顯示 pickList[0].gameObject.SetActive(false); pickList[1].gameObject.SetActive(false); cardCount -= 2; //卡片數量計數少兩張 if (cardCount <= 0) { //如果卡片計數為0表示場上的卡片都被清空了,遊戲結束 Debug.Log("GameOver"); } } else //兩張卡片不同組,翻回背面 { float angle = 180; while (true) { angle = Mathf.MoveTowards(angle, 0, 360 * Time.deltaTime); foreach(Card c in pickList) c.transform.localRotation = Quaternion.Euler(new Vector3(0, angle, 0)); if (angle == 0) break; yield return null; } } pickList.Clear(); //清空拾取卡片記錄 } animFinished = true; } }
製作完後在場上做一個空物件把這個放上去,接著就可以開始把先前作好的卡片Prefab開始拉進來card這個列表。
這邊我多放幾個Prefab在裡面,至少卡片的種類要大於場上卡片數量除以2的,避免同一種卡片超過兩張,這邊我產生卡片是寫死16張,所以至少要8種卡片,修改產生卡片的數量也要避免卡片種類不足。
到這邊就完成了,剩下的就是UI顯示,像是剩餘卡片數量,做一個計時器,或是當完成遊戲後的結束畫面之類的。
算是滿常見的遊戲,機制也很簡單,就單純翻兩張牌比對,相同就消去,全部消除完就結束,也可以簡單的一個Component就完成。
如果有任何錯誤歡迎提出。
4 comments:
版主,如果要讓牌一開始顯示正面,過幾秒變成背面用什麼方法會比較好
那可以用一個Coroutine來跑,當玩家按下Start的時候執行這個Coroutine,先全部把卡片轉過來,再轉回背面接著才開始遊戲
例如:
private IEnumerator StartGame()
{
foreach(GameObject card in Cards)
{
//旋轉每張卡片的角度到正面
}
//等待N秒
foreach(GameObject card in Cards)
{
//轉回背面
}
//遊戲正式開始,開始計時
}
請問in Cards這裡的Cards是指卡片的群組嗎(public GameObject[] card; //卡片prefab 這個)
另外旋轉的程式碼是放這個?
float angle = Mathf.MoveTowards(card.transform.localEulerAngles.y, 180, 360*Time.deltaTime);
card.transform.localRotation = Quaternion.Euler(new Vector3(0, angle, 0));
if (angle == 180) break;
那個是放卡片prefab物件用的阿, 開遊戲隨機取卡片就會從這裡面取prefab物件來instantiate到場上
旋轉是這個沒錯啊, 這個旋轉就旋轉卡片y軸180度, 角度到了就跳出那個while, 也有別的方式可以做, 不過這邊有達到目的就好
Post a Comment