Unity實現(xiàn)俄羅斯方塊
本文實例為大家分享了Unity實現(xiàn)俄羅斯方塊的具體代碼,供大家參考,具體內(nèi)容如下
一、使用SpriteRenderer作為小方塊圖片,創(chuàng)建7種由若干個小方塊圖片組成的方塊,如下圖:

Shape-1是一個空物體,其子物體Block、Block(1)、Block(2)、Block(3)是小方塊,Pivot是錨點(空物體),錨點用作于旋轉(zhuǎn)中心點,方塊旋轉(zhuǎn)是以它為中心進(jìn)行旋轉(zhuǎn)的。旋轉(zhuǎn)方塊的代碼如下:
transform.RotateAround(pivot.position, Vector3.forward, -90);
二、通過測試劃分出一個俄羅斯方塊操作區(qū)域(游戲區(qū)域),在z軸相同 的xy平面上的 每個坐標(biāo)作為二維數(shù)組map的索引,如:map[1,0]保存(1,0,z)坐標(biāo)上的小方塊物體的Transform組件,游戲區(qū)域上x是橫軸、y是縱軸,左下角的小方塊坐標(biāo)(0,0),右上角是的小方塊坐標(biāo)(x-1,y-1)。這樣,將游戲區(qū)域劃分成 一個map數(shù)組后,就可以管理全部小方塊,實現(xiàn)判斷整行滿并消除行,方塊是否可以下落一行,方塊是否可以變形,方塊是否可以水平移動等功能,下面貼出相關(guān)代碼。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Model : MonoBehaviour
{
public const int NORMAL_ROWS = 20;//最大行數(shù)(這個也是用于判斷游戲結(jié)束邏輯)
public const int MAX_ROWS = 23;//最大行數(shù)+3(用于判斷游戲結(jié)束邏輯)
public const int MAX_COLUMNS = 10;//最大列數(shù)
private Transform[,] map = new Transform[MAX_COLUMNS, MAX_ROWS];//地圖數(shù)組map
//下面這些不用管,是UI的一些參數(shù)
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)的位置是否合理,參數(shù)t是方塊的Transform
public bool IsValidMapPosition(Transform t)
{
foreach (Transform child in t)
{
//子物體帶"Block"字符的標(biāo)簽tag都是"Block",它們就是小方塊
if (child.tag != "Block") continue;
//如果遍歷到的子物體是小方塊的話進(jìn)行下面的邏輯判斷
Vector2 pos = child.position.Round();//Round是我擴(kuò)展的方法,==貼出來.
//若不在地圖范圍內(nèi),則不可用
if (IsInsideMap(pos) == false) return false;
//有其他圖形占用著,則不可用
if (map[(int)pos.x, (int)pos.y] != null)
{
return false;//不可用
}
}
return true;
}
//不在地圖范圍內(nèi)返回false,在返回true
private bool IsInsideMap(Vector2 pos)
{
return pos.x >= 0 && pos.x < MAX_COLUMNS && pos.y >= 0;
}
/// <summary>
/// 在方塊進(jìn)行檢查位置不合理后,會固定自身位置 放入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;
}
//放進(jìn)map后需要檢查整張地圖是否需要消行
return CheckMap();
}
/// <summary>
/// 檢查地圖 是否需要消除行
/// </summary>
private bool CheckMap()
{
int count = 0;//消行數(shù)
for (int i = 0; i < MAX_ROWS; i++)
{
//若第i行填滿小方塊
if (CheckIsRowFull(i))
{
count++;
DeleteRow(i);//消除第i行
MoveDownRowsAbove(i + 1);//第i+1行及其上方的所有行向下移動一行(從下到上移動)
i--; //因為上面的那一行已經(jīng)移動下來了,這里將i--,目的是為了繼續(xù)檢查上面那一行是否滿,因為for會將i++,這樣就抵消了i++,讓i繼續(xù)保持原樣
}
}
//下面不用管,是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);//物理上的移動
}
}
}
//判斷游戲結(jié)束邏輯
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;//錨點(旋轉(zhuǎn)中心點)
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進(jìn)入if
if(ctrl.model.IsValidMapPosition(this.transform)==false)
{
pos.y += 1;//因為當(dāng)前位置是不可用的,而上一個位置肯定是可用的,所以這里移動回到上一個位置處
transform.position = pos;
isPause = true;//暫停當(dāng)前shape的下移
bool isLineclear = ctrl.model.PlaceShape(this.transform);//使用Model的方法固定shape,即放入map數(shù)組保存小方塊信息,并且檢查地圖消除行
if (isLineclear) ctrl.audioManager.PlayLineClear();//isLineclear是true代表消除行了,播放聲音
gameManager.FallDown();//設(shè)置GameManager中的currentShape=null,這樣就會實例化下一個shape方塊,并且更新分?jǐn)?shù)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();
}
}
//按鍵盤↑對方塊進(jìn)行旋轉(zhuǎn)(即變形)
if(Input.GetKeyDown(KeyCode.UpArrow))
{
//以pivot位置為旋轉(zhuǎn)中心,繞z軸旋轉(zhuǎn)-90°
transform.RotateAround(pivot.position, Vector3.forward, -90);
//旋轉(zhuǎn)后自然也要檢查下位置是否合理
if (ctrl.model.IsValidMapPosition(this.transform) == false)
{
//不合理,旋轉(zhuǎn)90° 變回之前那樣子(注意:這個過程是瞬間發(fā)生的,玩家不會看到這個變化過程?。?
transform.RotateAround(pivot.position, Vector3.forward, 90);
}
else
{
ctrl.audioManager.PlayControl();
}
}
//按鍵盤↓會進(jìn)行加速方塊下移
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;//是否暫停狀態(tài)
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;
}
//如果當(dāng)前方塊為空,生成一個方塊(這個currentShape是由Shape
if (currentShape == null)
{
SpawnShape();
}
}
/// <summary>
/// 刪除已經(jīng)沒有小方塊Block的sharp方塊物體,即childCount小于等于1的
/// 并且判斷游戲結(jié)束邏輯
/// </summary>
public void FallDown()
{
currentShape = null;//將當(dāng)前方塊設(shè)置為空,這一步是為了生成新的方塊
//更新UI邏輯
if(ctrl.model.isDataUpdate)
{
ctrl.view.UpdateGameUI(ctrl.model.Score, ctrl.model.HighScore);
}
//刪除已經(jīng)沒有小方塊的sharp物體
foreach(Transform t in blockHolder)
{
if(t.childCount<=1)
{
Destroy(t.gameObject);
}
}
//判斷游戲結(jié)束
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()
{
//隨機(jī)方塊類型
int index = Random.Range(0, shapes.Length);//random create a shape
//隨機(jī)顏色
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>
/// 刪除當(dāng)前正在下落的方塊
/// </summary>
public void ClearShape()
{
if(currentShape!=null)
{
Destroy(currentShape.gameObject);
currentShape = null;
}
}
}
大致上,俄羅斯方塊的核心邏輯都總結(jié)在這3個腳本,Sharp是處理了方塊的移動邏輯,Model是真正的核心處理方塊的位置是否合理、消行、消行后將上方方塊整體下移一行等邏輯,GameManager是處理游戲結(jié)束之類的邏輯。
更多俄羅斯方塊精彩文章請點擊專題:俄羅斯方塊游戲集合 進(jìn)行學(xué)習(xí)。
更多有趣的經(jīng)典小游戲?qū)崿F(xiàn)專題,分享給大家:
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
經(jīng)典排序算法之冒泡排序(Bubble sort)代碼
這篇文章主要介紹了經(jīng)典排序算法之冒泡排序(Bubble sort)代碼的相關(guān)資料,非常不錯具有參考借鑒價值,需要的朋友可以參考下2016-06-06
C#調(diào)用海康工業(yè)相機(jī)SDK采集圖像并在Halcon窗口中顯示方式
這篇文章主要介紹了C#調(diào)用??倒I(yè)相機(jī)SDK采集圖像并在Halcon窗口中顯示方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02
關(guān)于C#10 新特性 Lambda 優(yōu)化
這篇文章主要介紹了C# 10 新特性 Lambda 優(yōu)化,C# 10 對于 Lambda 做了很多的優(yōu)化,我們可以在 C# 中更加方便地使用委托和 Lambda 了,下面就來看一些示例,需要的朋友也可以參考一下2021-11-11
WinForm實現(xiàn)鼠標(biāo)拖動控件跟隨效果
這篇文章主要為大家詳細(xì)介紹了WinForm實現(xiàn)鼠標(biāo)拖動控件跟隨效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-03-03
c#使用linq把多列的List轉(zhuǎn)化為只有指定列的List
這篇文章主要介紹了c#使用linq把多列的List轉(zhuǎn)化為只有指定列的List,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
c#訪問this關(guān)鍵字和base關(guān)鍵字示例
this關(guān)鍵字引用類的當(dāng)前實例。靜態(tài)成員方法中不能使用this關(guān)鍵字,this關(guān)鍵字只能在實例構(gòu)造函數(shù)、實例方法或?qū)嵗L問器中使用。base關(guān)鍵字用于從派生類中訪問基類的成員。下面學(xué)習(xí)一下這二個關(guān)鍵字的使用方法2014-01-01

