[计算机] unity 2048

查看:815 |回复:3 | 2021-5-8 09:53:47

您需要 登录 才可以下载或查看,没有账号?注册

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);
        }
    }}
这样单步撤销就完成了,如果你想要实现一个多步撤销,用栈来存储撤销信息即可。
2021-5-8 09:53:47  
 赞 赞 1

使用道具 登录

3个回答,把该问题分享到群,邀请大神一起回答。
2#
回复 收起回复
2023-3-1 12:10:11   回复
 赞 赞 1

使用道具 登录

3#
回复 收起回复
2023-8-30 09:40:47   回复
 赞 赞 0

使用道具 登录

CG 游戏行业专业问题

程序逻辑文章算法
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表