Unity实现俄罗斯方块

本文实例为大家分享了Unity实现俄罗斯方块的具体代码,供大家参考,具体内容如下

一、使用SpriteRenderer作为小方块图片,创建7种由若干个小方块图片组成的方块,如下图:

Shape-1是一个空物体,其子物体Block、Block(1)、Block(2)、Block(3)是小方块,Pivot是锚点(空物体),锚点用作于旋转中心点,方块旋转是以它为中心进行旋转的。旋转方块的代码如下:

transform.RotateAround(pivot.position, Vector3.forward, -90);

二、通过测试划分出一个俄罗斯方块操作区域(游戏区域),在z轴相同 的xy平面上的 每个坐标作为二维数组map的索引,如:map[1,0]保存(1,0,z)坐标上的小方块物体的Transform组件,游戏区域上x是横轴、y是纵轴,左下角的小方块坐标(0,0),右上角是的小方块坐标(x-1,y-1)。这样,将游戏区域划分成 一个map数组后,就可以管理全部小方块,实现判断整行满并消除行,方块是否可以下落一行,方块是否可以变形,方块是否可以水平移动等功能,下面贴出相关代码。

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

public class Model : MonoBehaviour
{
 public const int NORMAL_ROWS = 20;//最大行数(这个也是用于判断游戏结束逻辑)
 public const int MAX_ROWS = 23;//最大行数+3(用于判断游戏结束逻辑)
 public const int MAX_COLUMNS = 10;//最大列数
 private Transform[,] map = new Transform[MAX_COLUMNS, MAX_ROWS];//地图数组map
 //下面这些不用管,是UI的一些参数
 private int score = 0;
 private int highScore = 0;
 private int numbersGame = 0;

 public int Score { get { return score; } }
 public int HighScore { get { return highScore; } }
 public int NumbersGame { get { return numbersGame; } }

 public bool isDataUpdate = false;

 private void Awake()
 {
 LoadData();
 }
 //检查一个方块(如Sharp-1)的位置是否合理,参数t是方块的Transform
 public bool IsValidMapPosition(Transform t)
 {
 foreach (Transform child in t)
 {
 //子物体带"Block"字符的标签tag都是"Block",它们就是小方块
 if (child.tag != "Block") continue;
 //如果遍历到的子物体是小方块的话进行下面的逻辑判断
 Vector2 pos = child.position.Round();//Round是我扩展的方法,==贴出来.
 //若不在地图范围内,则不可用
 if (IsInsideMap(pos) == false) return false;
 //有其他图形占用着,则不可用
 if (map[(int)pos.x, (int)pos.y] != null)
 {
 return false;//不可用
 }
 }
 return true;
 }
 //不在地图范围内返回false,在返回true
 private bool IsInsideMap(Vector2 pos)
 {
 return pos.x >= 0 && pos.x < MAX_COLUMNS && pos.y >= 0;
 }
 /// <summary>
 /// 在方块进行检查位置不合理后,会固定自身位置 放入map[,]保存小方块Block的Transform组件
 /// </summary>
 /// <param name="t">shape的父物体transform</param>
 public bool PlaceShape(Transform t)
 {
 foreach (Transform child in t)
 {
 if (child.tag != "Block") continue;
 Vector2 pos = child.position.Round();
 map[(int)pos.x, (int)pos.y] = child;
 }
 //放进map后需要检查整张地图是否需要消行
 return CheckMap();
 }
 /// <summary>
 /// 检查地图 是否需要消除行
 /// </summary>
 private bool CheckMap()
 {
 int count = 0;//消行数
 for (int i = 0; i < MAX_ROWS; i++)
 {
 //若第i行填满小方块
 if (CheckIsRowFull(i))
 {
 count++;
 DeleteRow(i);//消除第i行
 MoveDownRowsAbove(i + 1);//第i+1行及其上方的所有行向下移动一行(从下到上移动)
 i--; //因为上面的那一行已经移动下来了,这里将i--,目的是为了继续检查上面那一行是否满,因为for会将i++,这样就抵消了i++,让i继续保持原样
 }
 }
 //下面不用管,是UI部分的逻辑
 if (count > 0)
 {
 score += count * 100;
 if(score>highScore)
 {
 highScore = score;
 }
 isDataUpdate = true;
 return true;
 }
 else
 {
 return false;
 }

 }
 //检查第row行是否满
 private bool CheckIsRowFull(int row)
 {
 for (int i = 0; i < MAX_COLUMNS; i++)
 {
 if (map[i, row] == null)
 {
 return false;
 }
 }
 return true;
 }
 private void DeleteRow(int row)
 {
 for (int i = 0; i < MAX_COLUMNS; i++)
 {
 Destroy(map[i, row].gameObject);
 map[i, row] = null;
 }
 }
 //将索引row行至最上面的行都往下移动一行(从下开始移动)
 public void MoveDownRowsAbove(int row)
 {
 for (int i = row; i < MAX_ROWS; i++)
 {
 MoveDownRow(i);
 }
 }
 //将row行 下移一行,处理了逻辑位置 和 物体位置
 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);//物理上的移动
 }
 }
 }
 //判断游戏结束逻辑
 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)
 {
  numbersGame++;
  SaveData();
  return true;
 }
 }
 }
 return false;
 }
 public void Restart()
 {
 for(int i=0;i<MAX_COLUMNS;i++)
 {
 for(int j=0;j<MAX_ROWS;j++)
 {
 if(map[i,j])
 {
  Destroy(map[i, j].gameObject);
  map[i, j] = null;
 }
 }
 }
 score = 0;
 }
 private void LoadData()
 {
 highScore= PlayerPrefs.GetInt("HighScore", 0);
 numbersGame = PlayerPrefs.GetInt("NumbersGame",0);
 }
 private void SaveData()
 {
 PlayerPrefs.SetInt("HighScore", highScore);
 PlayerPrefs.SetInt("NumbersGame", numbersGame);
 }
 public void ClearData()
 {
 score = 0;
 highScore = 0;
 numbersGame = 0;
 SaveData();
 }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Shape : MonoBehaviour {

 private GameManager gameManager;
 private Ctrl ctrl;
 private bool isPause = false;
 private bool isSpeedUp = false;
 private float timer = 0;
 private float stepTime = 0.8f;//0.8s方块下落一行
 private Transform pivot;//锚点(旋转中心点)
 private int multiple = 15; 

 private void Awake()
 {
 pivot = transform.Find("Pivot");
 }

 private void Update()
 {
 if (isPause) return;
 timer += Time.deltaTime;
 if(timer>stepTime)
 {
 timer = 0;
 Fall();
 }
 InputControl();
 }
 /// <summary>
 /// 方块下落一行逻辑
 /// </summary>
 private void Fall()
 {
 //先尝试将方块下移一行
 Vector3 pos = transform.position;
 pos.y -= 1;
 transform.position = pos;
 //使用Model的方法检查方块位置是否合理,若不合理返回false进入if
 if(ctrl.model.IsValidMapPosition(this.transform)==false)
 {
 pos.y += 1;//因为当前位置是不可用的,而上一个位置肯定是可用的,所以这里移动回到上一个位置处
 transform.position = pos;
 isPause = true;//暂停当前shape的下移
 bool isLineclear = ctrl.model.PlaceShape(this.transform);//使用Model的方法固定shape,即放入map数组保存小方块信息,并且检查地图消除行
 if (isLineclear) ctrl.audioManager.PlayLineClear();//isLineclear是true代表消除行了,播放声音
 gameManager.FallDown();//设置GameManager中的currentShape=null,这样就会实例化下一个shape方块,并且更新分数UI
 return;
 }
 ctrl.audioManager.PlayDrop();//方块固定的声音
 }
 //输入控制
 private void InputControl()
 {
 // if (isSpeedUp) return;
 float h=0;
 if(Input.GetKeyDown(KeyCode.LeftArrow))
 {
 h = -1;
 }
 else if(Input.GetKeyDown(KeyCode.RightArrow))
 {
 h = 1;
 }
 if(h!=0)
 {
 //这里和Fall下移逻辑处理手法一样,不再阐述!
 Vector3 pos = transform.position;
 pos.x += h;
 transform.position = pos;
 if (ctrl.model.IsValidMapPosition(this.transform) == false)
 {
 pos.x -= h;
 transform.position = pos;
 }
 else
 {
 ctrl.audioManager.PlayControl();
 }
 }

 //按键盘↑对方块进行旋转(即变形)
 if(Input.GetKeyDown(KeyCode.UpArrow))
 {
 //以pivot位置为旋转中心,绕z轴旋转-90°
 transform.RotateAround(pivot.position, Vector3.forward, -90);
 //旋转后自然也要检查下位置是否合理
 if (ctrl.model.IsValidMapPosition(this.transform) == false)
 {
 //不合理,旋转90° 变回之前那样子(注意:这个过程是瞬间发生的,玩家不会看到这个变化过程!)
 transform.RotateAround(pivot.position, Vector3.forward, 90);
 }
 else
 {
 ctrl.audioManager.PlayControl();
 }
 }
 //按键盘↓会进行加速方块下移
 if(Input.GetKeyDown(KeyCode.DownArrow))
 {
 isSpeedUp = true;
 stepTime /= multiple;
 }
 }
 public void Init(Color color,Ctrl ctrl,GameManager gameManager)
 {
 this.gameManager = gameManager;
 this.ctrl = ctrl;
 foreach(Transform t in transform)
 {
 if(t.tag=="Block")
 {
 t.GetComponent<SpriteRenderer>().color = color;
 }
 }
 }
 public void Pause()
 {
 isPause = true;
 }
 public void Resume()
 {
 isPause = false;
 }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour {

 private Ctrl ctrl;
 private bool isPause = true;//是否暂停状态
 private Shape currentShape = null;
 public Shape[] shapes;//shape prefabs
 public Color[] colors;//shape colors
 private Transform blockHolder;
 private void Awake()
 {
 ctrl = GetComponent<Ctrl>();
 blockHolder = transform.Find("BlockHolder");
 } 

 void Update () {
 if(isPause)
 {
 return;
 }
 //如果当前方块为空,生成一个方块(这个currentShape是由Shape
 if (currentShape == null)
 {
 SpawnShape();
 }
 }
 /// <summary>
 /// 删除已经没有小方块Block的sharp方块物体,即childCount小于等于1的
 /// 并且判断游戏结束逻辑
 /// </summary>
 public void FallDown()
 {
 currentShape = null;//将当前方块设置为空,这一步是为了生成新的方块
 //更新UI逻辑
 if(ctrl.model.isDataUpdate)
 {
 ctrl.view.UpdateGameUI(ctrl.model.Score, ctrl.model.HighScore);
 }
 //删除已经没有小方块的sharp物体
 foreach(Transform t in blockHolder)
 {
 if(t.childCount<=1)
 {
 Destroy(t.gameObject);
 }
 }
 //判断游戏结束
 if(ctrl.model.isGameOver())
 {
 PauseGame();
 ctrl.view.ShowGameOverUI(ctrl.model.Score);
 }
 }
 public void StartGame()
 {
 isPause = false;
 if (currentShape != null)
 {
 currentShape.Resume();
 }
 }
 public void PauseGame()
 {
 isPause = true;
 if(currentShape!=null)
 {
 currentShape.Pause();
 }
 }
 /// <summary>
 /// 生成一个方块sharp
 /// </summary>
 void SpawnShape()
 {
 //随机方块类型
 int index = Random.Range(0, shapes.Length);//random create a shape
 //随机颜色
 int indexColor = Random.Range(0, colors.Length);//random shape color
 //实例化方块
 currentShape = GameObject.Instantiate(shapes[index]);
 //放入blockholder物体下
 currentShape.transform.parent = blockHolder;
 //初始化其方块颜色等工作
 currentShape.Init(colors[indexColor],ctrl,this);

 }
 /// <summary>
 /// 删除当前正在下落的方块
 /// </summary>
 public void ClearShape()
 {
 if(currentShape!=null)
 {
 Destroy(currentShape.gameObject);
 currentShape = null;
 }
 }
}

大致上,俄罗斯方块的核心逻辑都总结在这3个脚本,Sharp是处理了方块的移动逻辑,Model是真正的核心处理方块的位置是否合理、消行、消行后将上方方块整体下移一行等逻辑,GameManager是处理游戏结束之类的逻辑。

更多俄罗斯方块精彩文章请点击专题:俄罗斯方块游戏集合 进行学习。

更多有趣的经典小游戏实现专题,分享给大家:

C++经典小游戏汇总

python经典小游戏汇总

python俄罗斯方块游戏集合

JavaScript经典游戏 玩不停

javascript经典小游戏汇总

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Unity实现俄罗斯方块游戏

    本文实例为大家分享了Unity实现俄罗斯方块游戏的具体代码,供大家参考,具体内容如下 一.演示 二.实现思路 创建每一个方块可移动到的位置点,可以理解为创建一个游戏地图,从(0,0)点开始依次向x轴和y轴延伸,例如x最大为9,y最大为19,则创建了一个20行10列的地图 制作每一个形状的预制体,Shape是每一个整体形状,Block是每一块小方块,CenterPos代表这个形状的旋转中心 创建GameController脚本控制游戏逻辑,挂载到面板物体上.创建Shape脚本控制每个形状的操作,挂

  • Unity实现俄罗斯方块

    本文实例为大家分享了Unity实现俄罗斯方块的具体代码,供大家参考,具体内容如下 一.使用SpriteRenderer作为小方块图片,创建7种由若干个小方块图片组成的方块,如下图: Shape-1是一个空物体,其子物体Block.Block(1).Block(2).Block(3)是小方块,Pivot是锚点(空物体),锚点用作于旋转中心点,方块旋转是以它为中心进行旋转的.旋转方块的代码如下: transform.RotateAround(pivot.position, Vector3.forwa

  • Unity实现俄罗斯方块(三)

    本文实例为大家分享了Unity实现俄罗斯方块第3部分,供大家参考,具体内容如下 解决穿透问题 逻辑部分 1.在物体进行移动的过程中更新格子的信息,原来的所占据的位置信息进行置空操作,现在所占据的格子进行赋值操作. 2.在移动后的位置进行判断,首先移动后的位置不能是null,且不能不是移动后的物体 代码部分 void updateGrid() { //去除移动以前的位置信息 for (int y = 0; y < Grid.height; y++) { for (int x = 0; x < G

  • Unity实现俄罗斯方块(二)

    本文实例为大家分享了Unity实现俄罗斯方块第2部分,供大家参考,具体内容如下 代码部分 1. 实现物体自由降落(在有关于物体的脚本中编写) 1).使用循环调用方法实现 public float speed = 0.3f;//物体下落 // Start is called before the first frame update void Start() { InvokeRepeating("Move", 0, speed); } void Move() { transform.po

  • Unity实现俄罗斯方块(一)

    本文实例为大家分享了Unity实现俄罗斯方块第一部分,供大家参考,具体内容如下 准备工作 1.新建一个2D项目,新建成功以后设置相机的一些参数 2.导入所要用的图片,修改图片的一些属性 3.了解俄罗斯方块的几种模型 4.根据模型制作预制体(由多个小方块通过修改位置制作预制体) 5.设置游戏边界 注:当制作完预制体以后应当要保存项目save project以及游戏边界宽度应当要设置成可以放几个方块的宽度 搭建计时器 1.新建一个UI Text重命名为Timer,其中Text内容为00:00 2.编

  • Unity实现见缝插针小游戏

    本文实例为大家分享了Unity实现见缝插针游戏的具体代码,供大家参考,具体内容如下 控制小球旋转 using System.Collections; using System.Collections.Generic; using UnityEngine; public class RotateSelf : MonoBehaviour { //每秒旋转90度 public float speed = 90; // Update is called once per frame void Updat

  • unity实现贪吃蛇游戏

    unity贪吃蛇基本原理实现,供大家参考,具体内容如下 原理: 1.每个身体跟着前面的身体移动: 2.蛇头自动一直向前走,可以向左或者向右转弯. 思想: 贪吃蛇的身体有若干个,每个身体有共同的特性,就是跟着前面的身体移动,这里把蛇的身体抽象出出来,用一个SnackBody类来表达,每一节身体都new出一个SnackBody对象,然后操作这个对象实现功能:蛇头可以看做特殊的蛇身体.应该有一个管理器来管理所有的蛇身体,所以有个SnackController类来管理.每段蛇身都有Front,self,

  • Unity实现3D贪吃蛇的移动代码

    本文实例为大家分享了Unity实现3D贪吃蛇移动的具体代码,供大家参考,具体内容如下 记录一下前段时间写到的一个3D贪吃蛇的移动代码. 链接:Unity实现3D贪吃蛇 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class GameManager : MonoBehaviour { List<Transform> bodyL

  • 一篇文章教会你用Unity制作网格地图生成组件

    目录 前言 1,创建组建出网格的基本单元 2,编辑网格创建脚本 3,地图生成案例 总结 前言 如果你玩过三国志这种类型的战旗游戏或者模拟城市.部落冲突.海岛奇兵这种模拟经营类的游戏,那么你对网格地图一定不会陌生.在这些游戏中,所有地图场景中的物体都是基于整齐的网格来记录位置等信息.如下图: 如果你还是感知不到什么是网格地图.俄罗斯方块或者贪吃蛇你一定不会陌生,物体的存在是依托于规整的网格地图而存在的. 还是一如既往,本篇文章为零基础小白文,如果你是小萌新,并且对网格地图感兴趣的话,可以学习本片文

  • JavaScript实现简洁的俄罗斯方块完整实例

    本文实例讲述了JavaScript实现简洁的俄罗斯方块.分享给大家供大家参考,具体如下: 先来看看运行效果图: 完整实例代码如下: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>俄罗斯方块</title> <style ty

随机推荐