最重要的就是記住翻過的卡片位置,當然也記住那張圖案是什麼,避免因為忘記再重複翻之前翻過的卡片浪費時間,遊戲就是越快把卡片全部移除越好。
製作使用的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