原文連結:「Unity」開發2048小遊戲
簡介
2048
作為一款出色的休閑遊戲,基本沒有人沒聽過他的名字。然而我在手機自帶的應用商店中找不到一款純淨的
2048
,于是打算使用
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-2048github.com
歡迎大家通路我的部落格,我部落格中的内容也會陸續搬運到知乎中。
Grapedgeblog.grapedge.top