Unity实现俄罗斯方块游戏

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

一、演示

二、实现思路

创建每一个方块可移动到的位置点,可以理解为创建一个游戏地图,从(0,0)点开始依次向x轴和y轴延伸,例如x最大为9,y最大为19,则创建了一个20行10列的地图

制作每一个形状的预制体,Shape是每一个整体形状,Block是每一块小方块,CenterPos代表这个形状的旋转中心

创建GameController脚本控制游戏逻辑,挂载到面板物体上。创建Shape脚本控制每个形状的操作,挂载到每个形状上

在GameController脚本中编写生成形状的逻辑

//当前方块
public Shape CurShape { get; set; }

private void Update()
{
 if (CurShape == null)
 {
  SpawnBlock();
 }
}

/// <summary>
/// 生成方块
/// </summary>
private void SpawnBlock()
{
 int randomType = Random.Range(1, 8);
 GameObject shape = Instantiate(Resources.Load<GameObject>("Prefabs/Item/Shape" + randomType));
 CurShape = shape.GetComponent<Shape>();
 CurShape.transform.SetParent(transform);
 CurShape.transform.position = new Vector2(4, 20);
}

在Shape脚本中编写形状下落的逻辑

private float fallTimer;//下落的计时器
private float fallTimeval = 0.5f;//下落的时间间隔

private void Update()
{
 //控制下落
 if (fallTimer >= fallTimeval)
 {
  //下落
  Fall();

  fallTimer = 0;
 }
 else
 {
  fallTimer += Time.deltaTime;
 }
}

/// <summary>
/// 下落
/// </summary>
private void Fall()
{
 Vector3 curPos = transform.position;
 Vector3 targetPos = curPos;
 targetPos.y -= 1;
 transform.position = targetPos;
}

但是此时形状是可以无限下落的,到达底部也不会停止下落,所以我们需要编写判定能否下落的方法
在GameController脚本编写方法判定能否下落的逻辑,需要判定两点,每个方块的位置是否到达边界和每个方块的位置是否存在于地图数组中,注意必须对每个方块的位置进行四舍五入取整操作

public const int row_max = 23;
public const int column_max = 10;
public Transform[,] mapArray = new Transform[column_max, row_max];//地图数组(保存每个方块的位置信息)

/// <summary>
/// 判断是否为合法的位置
/// </summary>
/// <param name="shape">每一个形状</param>
/// <returns></returns>
public bool IsValidPos(Transform shape)
{
 foreach (Transform block in shape.transform)
 {
  if (block.GetComponent<SpriteRenderer>() == null) continue;

  Vector2 blockPos = block.transform.position;
  blockPos = new Vector2(Mathf.RoundToInt(blockPos.x), Mathf.RoundToInt(blockPos.y));
  if (IsBorder(blockPos) || mapArray[(int)blockPos.x, (int)blockPos.y] != null)
  {
   return false;
  }
 }
 return true;
}

/// <summary>
/// 判断是否到达边界
/// </summary>
/// <param name="pos">每一个方块的位置</param>
/// <returns></returns>
private bool IsBorder(Vector2 blockPos)
{
 if (blockPos.x < 0 || blockPos.x >= column_max || blockPos.y < 0)
 {
  return true;
 }
 return false;
}

在形状下落的方法中添加判断去控制是否能够下落

private bool canFall = true;//能否下落

/// <summary>
/// 下落
/// </summary>
private void Fall()
{
 if (canFall == false)
 {
  return;
 }

 Vector3 curPos = transform.position;
 Vector3 targetPos = curPos;
 targetPos.y -= 1;
 transform.position = targetPos;

 if (GameController.Instance.IsValidPos(transform) == false)
 {
  targetPos.y += 1;
  transform.position = targetPos;

  FallToBottom();
 }
}

/// <summary>
/// 下落到了底部
/// </summary>
private void FallToBottom()
{
 canFall = false;
 GameController.Instance.CurBlock = null;
}

现在形状与形状之间并不会叠加,而是会重叠在一起,在GameController脚本中编写方法,将每个方块位置添加到地图数组中(注意必须对每个方块的位置进行四舍五入取整操作)

/// <summary>
/// 将每个方块位置添加到地图数组中
/// </summary>
/// <param name="shapeTran"></param>
public void AddEachBlockTransToMapArray(Transform shape)
{
 foreach (Transform block in shape.transform)
 {
  if (block.GetComponent<SpriteRenderer>() == null)
  {
   continue;
  }

  Vector2 blockPos = block.position;
  blockPos = new Vector2(Mathf.RoundToInt(blockPos.x), Mathf.RoundToInt(blockPos.y));
  mapArray[(int)blockPos.x, (int)blockPos.y] = block;
 }
}

当形状下落到底部时,将每一个方块的位置添加到地图数组中

/// <summary>
/// 下落到底部
/// </summary>
private void FallToBottom()
{
 canFall = false;
 GameController.Instance.CurShape = null;

 //方块下落到底部时将每个方块位置添加到地图数组中
 GameController.Instance.AddEachBlockTransToMapArray(transform);
}

控制形状的左右移动

private void Update()
{
 //控制左右移动
 ControlMovement();
}

/// <summary>
/// 控制左右移动
/// </summary>
private void ControlMovement()
{
 float h = 0;
 if (Input.GetKeyDown(KeyCode.RightArrow))
 {
  h = 1;
 }
 else if (Input.GetKeyDown(KeyCode.LeftArrow))
 {
  h = -1;
 }

 Vector3 curPos = transform.position;
 Vector3 targetPos = curPos;
 targetPos.x += h;
 transform.position = targetPos;

 if (GameController.Instance.IsValidPos(transform) == false)
 {
  targetPos.x -= h;
  transform.position = targetPos;
 }
}

控制形状的旋转

private void Update()
{
 //控制旋转
 ControlRotate();
}

/// <summary>
/// 控制旋转
/// </summary>
private void ControlRotate()
{
 if (Input.GetKeyDown(KeyCode.UpArrow))
 {
  Transform rotateTrans = transform.Find("CenterPos");
  transform.RotateAround(rotateTrans.position, Vector3.forward, -90);

  if (GameController.Instance.IsValidPos(transform) == false)
  {
   transform.RotateAround(rotateTrans.position, Vector3.forward, 90);
  }
 }
}

控制加速下落

private void Update()
{
 //控制加速下落
 ControlUpspeed();
}

/// <summary>
/// 控制加速
/// </summary>
private void ControlUpspeed()
{
 if (Input.GetKeyDown(KeyCode.DownArrow))
 {
  fallTimeval = 0.05f;
 }
 if (Input.GetKeyUp(KeyCode.DownArrow))
 {
  fallTimeval = 0.5f;
 }
}

判断整行的消除

先去判断是否有行满了,如果有则进行消除操作,消除时先删除掉当前行的每一个方块再将地图数组中当前行置空,之后再将满的行的上面行依次向下移动一行

/// <summary>
/// 检查是否有行满了
/// </summary>
private void CheckRowFull()
{
 for (int row = 0; row < row_max; row++)
 {
  bool isFull = true;
  for (int column = 0; column < column_max; column++)
  {
   if (mapArray[column, row] == null)
   {
    isFull = false;
    break;
   }
  }

  //如果有行满了
  if (isFull)
  {
   //————————————————————消除操作
   ClearRow(row);
   MoveDownRow(row + 1);
   row--;
  }
 }
}

/// <summary>
/// 清除行
/// </summary>
/// <param name="row">清除的行(满的行)</param>
private void ClearRow(int _row)
{
 for (int coloum = 0; coloum < column_max; coloum++)
 {
  Destroy(mapArray[coloum, _row].gameObject);
  mapArray[coloum, _row] = null;
 }
}

/// <summary>
/// 将清除的行上面的每一行依次向下移动一行
/// </summary>
/// <param name="row">依次移动的起始行(清除的行的上面一行)</param>
private void MoveDownRow(int _row)
{
 for (int row = _row; row < row_max; row++)
 {
  for (int column = 0; column < column_max; column++)
  {
   if (mapArray[column, row] != null)
   {
    mapArray[column, row - 1] = mapArray[column, row];
    mapArray[column, row] = null;
    mapArray[column, row - 1].position -= Vector3.up;
   }
  }
 }
}

判断游戏是否失败

/// <summary>
/// 判断游戏失败
/// </summary>
/// <returns></returns>
private bool IsGameover()
{
 for (int row = row_max - 3; row < row_max; row++)
 {
  for (int column = 0; column < column_max; column++)
  {
   if (mapArray[column, row] != null)
   {
    return true;
   }
  }
 }
 return false;
}

三、完整代码

GameController脚本,控制游戏整体逻辑

using UnityEngine;

public class GameController : MonoSingleton<GameController>
{
 public const int row_max = 23;
 public const int column_max = 10;
 private Transform[,] mapArray;

 //当前方块
 public Shape CurShape { get; set; }

 /// <summary>
 /// 初始化数据
 /// </summary>
 public void InitData()
 {
  mapArray = new Transform[column_max, row_max];
 }

 /// <summary>
 /// 清空地图
 /// </summary>
 public void ClearMap()
 {
  for (int row = 0; row < row_max; row++)
  {
   for (int column = 0; column < column_max; column++)
   {
    if (mapArray[column, row] != null)
    {
     Destroy(mapArray[column, row].gameObject);
     mapArray[column, row] = null;
    }
   }
  }
 }

 private void Update()
 {
  //if (GameManager.Instance.IsPause)
  //{
  // return;
  //}

  if (CurShape == null)
  {
   SpawnBlock();
  }
 }

 /// <summary>
 /// 生成方块
 /// </summary>
 private void SpawnBlock()
 {
  Color randomColor = Random.ColorHSV();
  int randomType = Random.Range(1, 8);
  GameObject shape = Instantiate(Resources.Load<GameObject>("Prefabs/Item/Shape" + randomType));
  CurShape = shape.GetComponent<Shape>();
  CurShape.transform.SetParent(transform);
  CurShape.Init(new Vector2(4, 20), randomColor);
 }

 /// <summary>
 /// 判断是否为合法的位置
 /// </summary>
 /// <param name="shape">每一个形状</param>
 /// <returns></returns>
 public bool IsValidPos(Transform shape)
 {
  foreach (Transform block in shape.transform)
  {
   if (block.GetComponent<SpriteRenderer>() == null) continue;

   Vector2 blockPos = block.transform.position;
   blockPos = new Vector2(Mathf.RoundToInt(blockPos.x), Mathf.RoundToInt(blockPos.y));
   if (IsBorder(blockPos) || mapArray[(int)blockPos.x, (int)blockPos.y] != null)
   {
    return false;
   }
  }
  return true;
 }

 /// <summary>
 /// 判断是否到达边界
 /// </summary>
 /// <param name="pos">每一个方块的位置</param>
 /// <returns></returns>
 private bool IsBorder(Vector2 blockPos)
 {
  if (blockPos.x < 0 || blockPos.x >= column_max || blockPos.y < 0)
  {
   return true;
  }
  return false;
 }

 /// <summary>
 /// 将每个方块位置添加到地图数组中
 /// </summary>
 /// <param name="shapeTran"></param>
 public void AddEachBlockTransToMapArray(Transform shape)
 {
  foreach (Transform block in shape.transform)
  {
   if (block.GetComponent<SpriteRenderer>() == null)
   {
    continue;
   }

   Vector2 blockPos = block.position;
   blockPos = new Vector2(Mathf.RoundToInt(blockPos.x), Mathf.RoundToInt(blockPos.y));
   mapArray[(int)blockPos.x, (int)blockPos.y] = block;
  }

  //检查是否有行满了
  CheckRowFull();
 }

 /// <summary>
 /// 检查是否有行满了
 /// </summary>
 private void CheckRowFull()
 {
  for (int row = 0; row < row_max; row++)
  {
   bool isFull = true;
   for (int column = 0; column < column_max; column++)
   {
    if (mapArray[column, row] == null)
    {
     isFull = false;
     break;
    }
   }

   //如果有行满了
   if (isFull)
   {
    //————————————————————消除操作
    ClearRow(row);
    MoveDownRow(row + 1);
    row--;
   }
  }
 }

 /// <summary>
 /// 清除行
 /// </summary>
 /// <param name="row">清除的行(满的行)</param>
 private void ClearRow(int _row)
 {
  for (int coloum = 0; coloum < column_max; coloum++)
  {
   Destroy(mapArray[coloum, _row].gameObject);
   mapArray[coloum, _row] = null;
  }

  UIManager.Instance.FindUI<UI_Game>().UpdateCurScore();
 }

 /// <summary>
 /// 将清除的行上面的每一行依次向下移动一行
 /// </summary>
 /// <param name="row">依次移动的起始行(清除的行的上面一行)</param>
 private void MoveDownRow(int _row)
 {
  for (int row = _row; row < row_max; row++)
  {
   for (int column = 0; column < column_max; column++)
   {
    if (mapArray[column, row] != null)
    {
     mapArray[column, row - 1] = mapArray[column, row];
     mapArray[column, row] = null;
     mapArray[column, row - 1].position -= Vector3.up;
    }
   }
  }
 }

 /// <summary>
 /// 判断游戏失败
 /// </summary>
 /// <returns></returns>
 public bool IsGameover()
 {
  for (int row = row_max - 3; row < row_max; row++)
  {
   for (int column = 0; column < column_max; column++)
   {
    if (mapArray[column, row] != null)
    {
     return true;
    }
   }
  }
  return false;
 }
}

Shape脚本,控制每个形状的操作

using UnityEngine;

public class Shape : MonoBehaviour
{
 private bool canFall = true;//能否下落

 private float fallTimer;//下落的计时器
 private float fallTimeval = 0.5f;//下落的时间间隔

 /// <summary>
 /// 初始化形状
 /// </summary>
 /// <param name="pos"></param>
 /// <param name="color"></param>
 public void Init(Vector2 pos, Color color)
 {
  transform.position = pos;

  foreach (Transform child in transform)
  {
   if (child.GetComponent<SpriteRenderer>() != null)
   {
    child.GetComponent<SpriteRenderer>().color = color;
   }
  }
 }

 private void Update()
 {
  if (canFall == false)
  {
   return;
  }

  //控制下落
  if (fallTimer >= fallTimeval)
  {
   //下落
   Fall();

   fallTimer = 0;
  }
  else
  {
   fallTimer += Time.deltaTime;
  }

  //控制加速下落
  ControlUpspeed();

  //控制左右移动
  ControlMovement();

  //控制旋转
  ControlRotate();
 }

 /// <summary>
 /// 控制左右移动
 /// </summary>
 private void ControlMovement()
 {
  float h = 0;
  if (Input.GetKeyDown(KeyCode.RightArrow))
  {
   h = 1;
  }
  else if (Input.GetKeyDown(KeyCode.LeftArrow))
  {
   h = -1;
  }

  Vector3 curPos = transform.position;
  Vector3 targetPos = curPos;
  targetPos.x += h;
  transform.position = targetPos;

  if (GameController.Instance.IsValidPos(transform) == false)
  {
   targetPos.x -= h;
   transform.position = targetPos;
  }
 }

 /// <summary>
 /// 控制旋转
 /// </summary>
 private void ControlRotate()
 {
  if (Input.GetKeyDown(KeyCode.UpArrow))
  {
   Transform rotateTrans = transform.Find("CenterPos");
   transform.RotateAround(rotateTrans.position, Vector3.forward, -90);

   if (GameController.Instance.IsValidPos(transform) == false)
   {
    transform.RotateAround(rotateTrans.position, Vector3.forward, 90);
   }
  }
 }

 /// <summary>
 /// 控制加速
 /// </summary>
 private void ControlUpspeed()
 {
  if (Input.GetKeyDown(KeyCode.DownArrow))
  {
   fallTimeval = 0.05f;
  }
  if (Input.GetKeyUp(KeyCode.DownArrow))
  {
   fallTimeval = 0.5f;
  }
 }

 /// <summary>
 /// 下落
 /// </summary>
 private void Fall()
 {
  Vector3 curPos = transform.position;
  Vector3 targetPos = curPos;
  targetPos.y -= 1;
  transform.position = targetPos;

  if (GameController.Instance.IsValidPos(transform) == false)
  {
   targetPos.y += 1;
   transform.position = targetPos;

   FallToBottom();
  }
 }

 /// <summary>
 /// 下落到底部
 /// </summary>
 private void FallToBottom()
 {
  canFall = false;
  GameController.Instance.CurShape = null;

  //方块下落到底部时将每个方块位置添加到地图数组中
  GameController.Instance.AddEachBlockTransToMapArray(transform);

  if (GameController.Instance.IsGameover())
  {
   GameManager.Instance.Gameover();
  }
 }
}

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

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

C++经典小游戏汇总

python经典小游戏汇总

python俄罗斯方块游戏集合

JavaScript经典游戏 玩不停

javascript经典小游戏汇总

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

(0)

相关推荐

  • C#实现俄罗斯方块基本功能

    本文实例为大家分享了C#实现俄罗斯方块的具体代码,供大家参考,具体内容如下 using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; namespace 俄罗斯方块 { public partial class MainForm : Form { public MainForm() { InitializeComponent(); } PictureBox p

  • 基于C#实现俄罗斯方块游戏

    最近在看C#,写了一个很水的方块游戏练手. 代码: namespace game { class Square { public Square() { state = 0; positionY = 0; positionX = 0; } public Square(int InitShapeCnt, int InitState) { state = InitState; positionY = 0; positionX = 0; InitShape(InitShapeCnt); } public

  • C#实现简单俄罗斯方块

    最近在看<.NET游戏编程入门经典 C#篇> 第一章介绍了如何制作俄罗斯方块,自己试了试按照书上的步骤,可算是完成了. 于是写下这篇文章留作纪念. 1.类的设计 在充分分析游戏的特点后,游戏大概可以分为3个类:Square 小正方形,Block 由4个小正方形组合成的一个图形, GameField 游戏的驱动引擎 2.代码的编写 按照从小到大,从简单到复杂的顺序,先完成Square类,Square很简单,只需要提供Show,Hide方法以及Size,Location属性. 接下来是Block,

  • 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.编

  • 用Python编写一个简单的俄罗斯方块游戏的教程

    俄罗斯方块游戏,使用Python实现,总共有350+行代码,实现了俄罗斯方块游戏的基本功能,同时会记录所花费时间,消去的总行数,所得的总分,还包括一个排行榜,可以查看最高记录. 排行榜中包含一系列的统计功能,如单位时间消去的行数,单位时间得分等. 附源码: from Tkinter import * from tkMessageBox import * import random import time #俄罗斯方块界面的高度 HEIGHT = 18 #俄罗斯方块界面的宽度 WIDTH = 10

  • JavaScript实现俄罗斯方块游戏过程分析及源码分享

    观摩一下<编程之美>:"程序虽然很难写,却很美妙.要想把程序写好,需要写好一定的基础知识,包括编程语言.数据结构与算法.程序写得好,需要缜密的逻辑思维能力和良好的梳理基础,而且熟悉编程环境和编程工具." 学了几年的计算机,你有没有爱上编程.话说,没有尝试自己写过一个游戏,算不上热爱编程. 俄罗斯方块曾经造成的轰动与造成的经济价值可以说是游戏史上的一件大事,它看似简单但却变化无穷,令人上瘾.相信大多数同学,曾经为它痴迷得茶不思饭不想. 游戏规则 1.一个用于摆放小型正方形的平

  • JS+Canvas实现的俄罗斯方块游戏完整实例

    本文实例讲述了JS+Canvas实现的俄罗斯方块游戏.分享给大家供大家参考,具体如下: 试玩(没有考虑兼容低版本浏览器): ********************************************************************** 9月3日更新: 修复了隐藏的比较深的BUG 加上暂停.再来一次功能 速度随分数增高而递减 添加log日志 ****************************************************************

  • Python使用pygame模块编写俄罗斯方块游戏的代码实例

    文章先介绍了关于俄罗斯方块游戏的几个术语. 边框--由10*20个空格组成,方块就落在这里面. 盒子--组成方块的其中小方块,是组成方块的基本单元. 方块--从边框顶掉下的东西,游戏者可以翻转和改变位置.每个方块由4个盒子组成. 形状--不同类型的方块.这里形状的名字被叫做T, S, Z ,J, L, I , O.如下图所示: 模版--用一个列表存放形状被翻转后的所有可能样式.全部存放在变量里,变量名字如S_SHAPE_TEMPLATE or J_SHAPE_TEMPLATE 着陆--当一个方块

  • 一个MIDP俄罗斯方块游戏的设计和实现

    文章来源:csdn 作者:陈万飞 作者简介 陈万飞,男,中南大学数软系学士,曾任北京长城软件高级程序员,系统分析师.有较为丰富的j2se,j2ee开发经验.目前致力于j2me研究工作.可通过chen_cwf@163.net与他联系 摘要 本文给出了一个基于MIDP1.0的俄罗斯方块游戏的设计方案,并给出全部实现源代码.该游戏的最大特色是屏幕自适应,无论各种手机,PDA的屏幕大小如何,该游戏总是能获得最佳的显示效果.游戏在J2me wireless toolkit 2.1的4种模拟器上测试通过.

随机推荐