Unity3D俄罗斯方块

游戏展示

截屏2019-12-07下午10.11.49.png
截屏2019-12-07下午10.10.02.png

游戏整体结构

游戏整体结构
整体资源和代码

源码下载地址

俄罗斯方块地址:https://github.com/BackkomCoder/GameSet.git

代码讲解

GameMgr.cs用于整体游戏的控制。

  • 游戏的开始,暂停;
  • 当前方块的生成,删除;
  • 分数的获取和增加。
using UnityEngine;

/// <summary>
/// 游戏管理
/// </summary>
public class GameMgr : MonoBehaviour
{
    public static GameMgr _instance;

    /// <summary>
    /// 方块预设
    /// </summary>
    public Item[] items;

    /// <summary>
    /// 游戏是否暂停
    /// </summary>
    private bool isPause = false;
    /// <summary>
    /// 当前的方块
    /// </summary>
    private Item currentItem;
    /// <summary>
    /// 分数
    /// </summary>
    private int score = 0;

    private void Awake()
    {
        _instance = this;
    }
    private void Update()
    {
        if (isPause)
            return;
        if (currentItem == null)
            //生成方块
            SpawnItem();
    }

    /// <summary>
    /// 开始游戏
    /// </summary>
    public void StartGame()
    {
        isPause = false;
        if (currentItem != null)
        {
            currentItem.Resume();
        }
    }
    /// <summary>
    /// 暂停游戏
    /// </summary>
    public void PauseGame()
    {
        isPause = true;
        if (currentItem != null)
        {
            currentItem.Pause();
        }
    }
    /// <summary>
    /// 当前方块落下完成
    /// </summary>
    public void FallDownComplete()
    {
        currentItem = null;
        //删除无用的方块父物体
        foreach (Transform t in transform)
        {
            if (t.childCount <= 1)
            {
                Destroy(t.gameObject);
            }
        }
        //方块下落完成后,检查游戏是否结束
        if (MapData._instance.IsGameOver())
        {
            PauseGame();
            UIMgr._instance.ShowGameOver();
        }
    }
    /// <summary>
    /// 删除当前方块
    /// </summary>
    public void DeleteCurrentItem()
    {
        if (currentItem != null)
        {
            Destroy(currentItem.gameObject);
            currentItem = null;
        }
    }
    /// <summary>
    /// 增加分数
    /// </summary>
    /// <param name="score">分数</param>
    public void AddScore(int score)
    {
        this.score += score;
        UIMgr._instance.ShowScore(this.score);
    }
    /// <summary>
    /// 设置分数
    /// </summary>
    /// <param name="score">分数</param>
    public void SetScore(int score)
    {
        this.score = score;
        UIMgr._instance.ShowScore(this.score);
    }

    /// <summary>
    /// 生成方块
    /// </summary>
    private void SpawnItem()
    {
        int index = Random.Range(0, items.Length);
        Item item = Instantiate(items[index]);
        item.transform.SetParent(transform);
        currentItem = item;
    }
}

MapData.cs为游戏核心逻辑,地图数据管理。

  • 地图数据的保存;
  • 方块行消除的逻辑判断;
  • 方块位置是否合法
  • 游戏是否结束
using UnityEngine;

/// <summary>
/// 地图数据
/// </summary>
public class MapData : MonoBehaviour
{
    public static MapData _instance;

    /// <summary>
    /// 方块正常下落的行数,大于20行,则表示游戏结束
    /// </summary>
    public const int NORMAL_ROWS = 20;
    /// <summary>
    /// 最大行数
    /// </summary>
    public const int MAX_ROWS = 24;
    /// <summary>
    /// 最大列数
    /// </summary>
    public const int MAX_COLUMNS = 10;
    /// <summary>
    /// 地图数据
    /// </summary>
    private Transform[,] map = new Transform[MAX_COLUMNS, MAX_ROWS];

    private void Awake()
    {
        _instance = this;
    }

    #region 公共方法
    /// <summary>
    /// 方块当前位置是否合法
    /// </summary>
    /// <param name="t">方块的父物体</param>
    /// <returns>位置是否合法</returns>
    public bool IsValidPosition(Transform t)
    {
        foreach (Transform child in t)
        {
            if (!child.CompareTag("Item"))
                continue;
            int x = Mathf.RoundToInt(child.position.x);
            int y = Mathf.RoundToInt(child.position.y);
            //是否在地图内
            if (IsInsideMap(new Vector2(x, y)) == false)
                return false;
            //当前地图位置是否为空
            if (map[x, y] != null)
                return false;
        }
        return true;
    }
    /// <summary>
    /// 方块停止下落后,将方块保存地图中
    /// </summary>
    /// <param name="t">方块的父物</param>
    public void PlaceItem(Transform t)
    {
        foreach (Transform child in t)
        {
            if (!child.CompareTag("Item"))
                continue;
            int x = Mathf.RoundToInt(child.position.x);
            int y = Mathf.RoundToInt(child.position.y);
            map[x, y] = child;
        }
        //检测是否有满行的
        CheckMapRowFull();
    }
    /// <summary>
    /// 游戏是否结束
    /// </summary>
    /// <returns>是否结束</returns>
    public bool IsGameOver()
    {
        for (int i = NORMAL_ROWS; i < MAX_ROWS; I++)
        {
            for (int j = 0; j < MAX_COLUMNS; j++)
            {
                if (map[j, i] != null)
                {
                    return true;
                }
            }
        }
        return false;
    }
    /// <summary>
    /// 清空地图
    /// </summary>
    public void ClearMap()
    {
        for (int i = 0; i < MAX_COLUMNS; I++)
        {
            for (int j = 0; j < MAX_ROWS; j++)
            {
                if (map[i, j] != null)
                {
                    Destroy(map[i, j].gameObject);
                    map[i, j] = null;
                }
            }
        }
    }
    #endregion

    #region 私有方法
    /// <summary>
    /// 当前位置是否在地图内
    /// </summary>
    /// <param name="pos">位置</param>
    /// <returns>是否在地图内</returns>
    private bool IsInsideMap(Vector2 pos)
    {
        return pos.x >= 0 && pos.x < MAX_COLUMNS && pos.y >= 0;
    }
    /// <summary>
    /// 检查地图是否要消除行
    /// </summary>
    private void CheckMapRowFull()
    {
        for (int i = 0; i < MAX_ROWS; I++)
        {
            //检查每一行是否满
            bool isFull = CheckIsRowFull(i);
            if (isFull)
            {
                //增加分数
                GameMgr._instance.AddScore(100);
                //删除满行
                DeleteRow(i);
                //满行以上的方块下落
                MoveDownRowAbove(i + 1);
                I--;
            }
        }
    }
    /// <summary>
    /// 检查行是否满
    /// </summary>
    /// <param name="row">行</param>
    /// <returns>是否行满</returns>
    private bool CheckIsRowFull(int row)
    {
        for (int i = 0; i < MAX_COLUMNS; I++)
        {
            if (map[i, row] == null)
                return false;
        }
        return true;
    }
    /// <summary>
    /// 删除行
    /// </summary>
    /// <param name="row">行</param>
    private void DeleteRow(int row)
    {
        for (int i = 0; i < MAX_COLUMNS; I++)
        {
            Destroy(map[i, row].gameObject);
            map[i, row] = null;
        }
    }
    /// <summary>
    /// 满行以上的方块下落
    /// </summary>
    /// <param name="row">行</param>
    private void MoveDownRowAbove(int row)
    {
        for (int i = row; i < MAX_ROWS; I++)
        {
            MoveDownRow(i);
        }
    }
    /// <summary>
    /// 满行,以上的某一行方块下落
    /// </summary>
    /// <param name="row">行</param>
    private void MoveDownRow(int row)
    {
        for (int i = 0; i < MAX_COLUMNS; I++)
        {
            if (map[i, row] != null)
            {
                map[i, row - 1] = map[i, row];
                map[i, row] = null;
                map[i, row - 1].position += new Vector3(0, -1, 0);
            }
        }
    }
    #endregion
}

Item.cs 控制每一个方块的逻辑。

  • 左右移动,旋转,下落;
  • 方块的下落的速度和暂停。
using UnityEngine;

/// <summary>
/// 每一个方块的控制
/// </summary>
public class Item : MonoBehaviour
{
    /// <summary>
    /// 是否暂停
    /// </summary>
    private bool isPause = false;
    /// <summary>
    /// 下落计时器
    /// </summary>
    private float timer = 0;
    /// <summary>
    /// 下落间隔
    /// </summary>
    private float intervalTime = 0.5f;
    /// <summary>
    /// 旋转点
    /// </summary>
    private Transform pivot;

    private void Start()
    {
        pivot = transform.Find("Pivot");
    }
    private void Update()
    {
        if (isPause)
            return;
        timer += Time.deltaTime;
        if (timer > intervalTime)
        {
            timer = 0;
            //下落
            Fall();
        }
        //方块控制
        InputCtrl();
    }

    /// <summary>
    /// 下落
    /// </summary>
    private void Fall()
    {
        Vector3 pos = transform.position;
        pos.y -= 1;
        transform.position = pos;
        //位置不合法,回复到上一个位置,并暂停下落,将方块保存到地图中
        if (MapData._instance.IsValidPosition(transform) == false)
        {
            pos.y += 1;
            transform.position = pos;
            isPause = true;
            //将方块保存到地图中
            MapData._instance.PlaceItem(transform);
            //当前方块完成下落
            GameMgr._instance.FallDownComplete();
        }
    }
    /// <summary>
    /// 输入控制
    /// </summary>
    void InputCtrl()
    {
        float h = 0;
        //左
        if (Input.GetKeyDown(KeyCode.LeftArrow))
        {
            h = -1;
        }
        //右
        else if (Input.GetKeyDown(KeyCode.RightArrow))
        {
            h = 1;
        }
        //旋转
        else if (Input.GetKeyDown(KeyCode.UpArrow))
        {
            transform.RotateAround(pivot.position, Vector3.forward, -90);
            //位置是否合法检测
            if (MapData._instance.IsValidPosition(transform) == false)
            {
                transform.RotateAround(pivot.position, Vector3.forward, 90);
            }
        }
        //下落
        else if (Input.GetKey(KeyCode.DownArrow))
        {
            intervalTime = 0.1f;
        }
        else
        {
            intervalTime = 0.5f;
        }
        if (h != 0)
        {

            Vector3 pos = transform.position;
            pos.x += h;
            transform.position = pos;
            //位置是否合法检测
            if (MapData._instance.IsValidPosition(transform) == false)
            {
                pos.x -= h;
                transform.position = pos;
            }
        }
    }
    /// <summary>
    /// 暂停
    /// </summary>
    public void Pause()
    {
        isPause = true;
    }
    /// <summary>
    /// 继续
    /// </summary>
    public void Resume()
    {
        isPause = false;
    }

}

UIMgr.cs 用于UI管理。

  • 开始游戏,暂停游戏,重新游戏,游戏结束;
  • 展示分数。
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// UI管理
/// </summary>
public class UIMgr : MonoBehaviour
{
    public static UIMgr _instance;
    /// <summary>
    /// 开始按钮
    /// </summary>
    private Button btnStart;
    /// <summary>
    /// 暂停按钮
    /// </summary>
    private Button btnPause;
    /// <summary>
    /// 重新开始按钮
    /// </summary>
    private Button btnRestart;
    /// <summary>
    /// 分数
    /// </summary>
    private Text textScore;
    /// <summary>
    /// 游戏结束
    /// </summary>
    private Text textGameOver;

    private void Awake()
    {
        _instance = this;
    }

    void Start()
    {
        textGameOver = transform.Find("GameOverText").GetComponent<Text>();
        textScore = transform.Find("ScoreText").GetComponent<Text>();
        btnStart = transform.Find("StartBtn").GetComponent<Button>();
        btnPause = transform.Find("PauseBtn").GetComponent<Button>();
        btnRestart = transform.Find("RestartBtn").GetComponent<Button>();
        //开始游戏
        btnStart.onClick.AddListener(() =>
        {
            if (MapData._instance.IsGameOver())
            {
                return;
            }
            GameMgr._instance.StartGame();
            btnStart.gameObject.SetActive(false);
            btnPause.gameObject.SetActive(true);
        });
        //暂停游戏
        btnPause.onClick.AddListener(() =>
        {
            if (MapData._instance.IsGameOver())
            {
                return;
            }   
            GameMgr._instance.PauseGame();
            btnStart.gameObject.SetActive(true);
            btnPause.gameObject.SetActive(false);
        });
        //重新游戏
        btnRestart.onClick.AddListener(() =>
        {
            btnStart.gameObject.SetActive(false);
            btnPause.gameObject.SetActive(true);
            textGameOver.gameObject.SetActive(false);
            //删除当前方块
            GameMgr._instance.DeleteCurrentItem();
            //清空地图
            MapData._instance.ClearMap();
            //重置分数
            GameMgr._instance.SetScore(0);
            //重新游戏
            GameMgr._instance.StartGame();
        });
    }
    /// <summary>
    /// 展示分数
    /// </summary>
    /// <param name="score">分数</param>
    public void ShowScore(int score)
    {
        textScore.text = score.ToString();
    }
    /// <summary>
    /// 游戏结束
    /// </summary>
    public void ShowGameOver()
    {
        textGameOver.gameObject.SetActive(true);
    }
}

结束语

大学的时候就想着要做个俄罗斯方块,现在终于完成了(哈哈,真是汗颜💧)
有什么问题,下方评论区随时联系我哦!
喜欢的话,点个关注支持一下!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351

推荐阅读更多精彩内容