天天看點

unity協程_「Unity」開發2048小遊戲

原文連結:「Unity」開發2048小遊戲

簡介

2048

作為一款出色的休閑遊戲,基本沒有人沒聽過他的名字。然而我在手機自帶的應用商店中找不到一款純淨的

2048

,于是打算使用

Unity

做一款自己的

2048

完成的效果看起來像是這個樣子:

unity協程_「Unity」開發2048小遊戲

準備

在我的項目中,我建立了這些腳本:

  • GameManager,遊戲的核心邏輯,接收使用者輸入
  • CellData,用于存儲每個格子的資訊
  • CellAnimation:格子的動畫
  • RevokeData,存儲網格資訊,用于撤銷
  • UIManager,管理UI界面

随機生成

随機生成的代碼很簡單,即查詢空格子,在空格子中随機生成:

public void RandomSpawn() {
    // 查找空格子
    List<CellData> emptyPos = new List<CellData>();
    foreach (var c in cells) {
        // 将這個格子在本輪已經合并設定為false,下面會講具體作用
        c.merged = false;
        if (c.empty) emptyPos.Add(c);
    }

    // 如果沒有空格子
    if (emptyPos.Count == 0) {
        return ;
    }
    CellData cell = emptyPos[Random.Range(0, emptyPos.Count)];
    // 随機生成
    cell.Spawn(Random.Range(0, 5) == 0 ? 2 : 1, cellPrefab);
}
           

遊戲的思路

如果你還沒有玩過

2048

,可以先去其網頁版試玩。

以向左滑動為例

最左邊的第一列顯然是不需要移動的,接着我們從第二列往後一列一列的移動,移動的時候,先向左查詢第最左邊的可以移動到的空格子,然後交換兩個格子的屬性,然後判斷這個格子是否能夠和他左邊的格子合并(即值是否一樣,如果所有格子滿了并且每一個格子都不能合并,遊戲結束)

public void Merge(CellData spw, CellData des) {
        if (!CanMerge(spw, des)) return;
        int pow = spw.power + 1;
        // 銷毀兩個遊戲對象
        des.Remove(); spw.Remove();
        // 合并,其代碼在下面
        spw.Merge(pow, cellPrefab);
        score += spw.value;
    }
    public void MoveLeft() {
        for (int i = 0; i < size; i++) {
            for (int j = 1, k; j < size; j++) {
                if (cells[i, j].empty) continue;
                for (k = j; k - 1 >= 0 && cells[i, k - 1].empty; k--);
                if (k != j) cells[i, k].CopySwap(cells[i, j]);
                if (k - 1 >= 0) Merge(cells[i, k - 1], cells[i, k]);
            }
        }
    }
           

網格資訊

每個格子都至少應該擁有其對應的數值和格子上存儲的

game object

(用于顯示數字)。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CellData {

    // 圖塊的值是2的 power 次方
    public int power;
    // 2^power對應的值
    public int value;
    // 圖塊
    public GameObject cell;
    // 在 World 中的坐标
    private Vector2 cellPos;
    public Vector2 position {
        get {
            return cellPos;
        }
    }

    // 如果這個網格上沒有遊戲物體,那麼就是空的格子(當然也可以判斷 power 是不是 < 1,即格子的值小于 2^1)
    public bool empty {
        get {
            return cell == null;
        }
    }

    // 是否已經合并
    public bool merged {
        get; set;
    }
    public CellData(Vector2 pos) {
        power = 0;
        cell = null;
        cellPos = pos;
    }

    // 根據power建立物體
    public void Create(int pow, GameObject prefab = null) {
        value = 1; power = pow;
        for (int i = 0; i < pow; i++) value *= 2;
        merged = false;
        if (prefab == null) return;
        cell = GameObject.Instantiate(prefab, position, Quaternion.identity);
        if (cell.GetComponent<SpriteRenderer>() == null) cell.AddComponent<SpriteRenderer>();
        UpdateCellSprite();
    }

    public void UpdateCellSprite() {
        //Debug.Log(cell);
        cell.GetComponent<SpriteRenderer>().sprite = GameManager.instance.sprites[power - 1];
    }

    public void UpdateCellPosition() {
        cell.GetComponent<CellAnimation>().MoveTo(position);
    }

    // 清空
    public void Clear() {
        cell =  null;
        power = 0;
        value = 0;
    }
    // 銷毀格子上的 gameObject
    public void Remove() {
        GameObject.Destroy(cell);
        Clear();
    }

    // 生成
    public void Spawn(int pow, GameObject obj) {
        Create(pow, obj);
        cell.GetComponent<CellAnimation>().Create();
    }
    // 交換兩個格子的資料,用于移動
    public void CopySwap(CellData data) {
        if (data == this) return;
        Create(data.power);
        cell = data.cell;
        UpdateCellSprite();
        UpdateCellPosition();
        data.Clear();
    }

    // 合并
    public void Merge(int pow, GameObject obj) {
        Create(pow, obj);
        merged = true;
        cell.GetComponent<CellAnimation>().Merge();
    }

}
           

播放動畫

說到動畫,自然而然的就能想到

iTween

,但我們這裡使用

iTween

顯然小題大做,是以我們使用

協程

來完成這個工作,以移動動畫為例:

public float moveSpeed = 35;
private IEnumerator MoveToCoroutine(Vector3 pos) {
    for (; transform.position != pos; ) {
        transform.position = Vector3.MoveTowards(transform.position, pos, Time.deltaTime * moveSpeed);
        yield return null;
    }
}
           

撤銷功能

RevokeData

會記錄在上一步操作中的網格資訊,記錄的時間點是這裡:

if ((direction == Direction.down || Input.GetKeyDown(KeyCode.DownArrow)) && CanMoveDown()) {
    SaveData();
    MoveDown();
    RandomSpawn();
    CheckForFinish();
}
           

恢複即把網格資訊恢複:

public void Revoke() {
    if (!revoke.saved) return;
    score = revoke.score;
    for (int i = 0; i < size; i++) {
        for (int j = 0; j < size; j++) {
            cells[i, j].Remove();
            if (revoke.grids[i, j] > 0) cells[i, j].Spawn(revoke.grids[i, j], cellPrefab);
        }
    }
}
           

這樣單步撤銷就完成了,如果你想要實作一個多步撤銷,用棧來存儲撤銷資訊即可。

下面是github位址:

Grapedge/Unity-2048​github.com

unity協程_「Unity」開發2048小遊戲

歡迎大家通路我的部落格,我部落格中的内容也會陸續搬運到知乎中。

Grapedge​blog.grapedge.top

unity協程_「Unity」開發2048小遊戲