您需要 登录 才可以下载或查看,没有账号?注册
x
准备在我的项目中,我创建了这些脚本: · 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); } }} 这样单步撤销就完成了,如果你想要实现一个多步撤销,用栈来存储撤销信息即可。 |