Unity實(shí)現(xiàn)俄羅斯方塊游戲
本文實(shí)例為大家分享了Unity實(shí)現(xiàn)俄羅斯方塊游戲的具體代碼,供大家參考,具體內(nèi)容如下
一、演示

二、實(shí)現(xiàn)思路
創(chuàng)建每一個(gè)方塊可移動(dòng)到的位置點(diǎn),可以理解為創(chuàng)建一個(gè)游戲地圖,從(0,0)點(diǎn)開(kāi)始依次向x軸和y軸延伸,例如x最大為9,y最大為19,則創(chuàng)建了一個(gè)20行10列的地圖

制作每一個(gè)形狀的預(yù)制體,Shape是每一個(gè)整體形狀,Block是每一塊小方塊,CenterPos代表這個(gè)形狀的旋轉(zhuǎn)中心

創(chuàng)建GameController腳本控制游戲邏輯,掛載到面板物體上。創(chuàng)建Shape腳本控制每個(gè)形狀的操作,掛載到每個(gè)形狀上
在GameController腳本中編寫(xiě)生成形狀的邏輯
//當(dāng)前方塊
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腳本中編寫(xiě)形狀下落的邏輯
private float fallTimer;//下落的計(jì)時(shí)器
private float fallTimeval = 0.5f;//下落的時(shí)間間隔
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;
}
但是此時(shí)形狀是可以無(wú)限下落的,到達(dá)底部也不會(huì)停止下落,所以我們需要編寫(xiě)判定能否下落的方法
在GameController腳本編寫(xiě)方法判定能否下落的邏輯,需要判定兩點(diǎn),每個(gè)方塊的位置是否到達(dá)邊界和每個(gè)方塊的位置是否存在于地圖數(shù)組中,注意必須對(duì)每個(gè)方塊的位置進(jìn)行四舍五入取整操作
public const int row_max = 23;
public const int column_max = 10;
public Transform[,] mapArray = new Transform[column_max, row_max];//地圖數(shù)組(保存每個(gè)方塊的位置信息)
/// <summary>
/// 判斷是否為合法的位置
/// </summary>
/// <param name="shape">每一個(gè)形狀</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>
/// 判斷是否到達(dá)邊界
/// </summary>
/// <param name="pos">每一個(gè)方塊的位置</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;
}
現(xiàn)在形狀與形狀之間并不會(huì)疊加,而是會(huì)重疊在一起,在GameController腳本中編寫(xiě)方法,將每個(gè)方塊位置添加到地圖數(shù)組中(注意必須對(duì)每個(gè)方塊的位置進(jìn)行四舍五入取整操作)
/// <summary>
/// 將每個(gè)方塊位置添加到地圖數(shù)組中
/// </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;
}
}
當(dāng)形狀下落到底部時(shí),將每一個(gè)方塊的位置添加到地圖數(shù)組中
/// <summary>
/// 下落到底部
/// </summary>
private void FallToBottom()
{
canFall = false;
GameController.Instance.CurShape = null;
//方塊下落到底部時(shí)將每個(gè)方塊位置添加到地圖數(shù)組中
GameController.Instance.AddEachBlockTransToMapArray(transform);
}
控制形狀的左右移動(dòng)
private void Update()
{
//控制左右移動(dòng)
ControlMovement();
}
/// <summary>
/// 控制左右移動(dòng)
/// </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;
}
}
控制形狀的旋轉(zhuǎn)
private void Update()
{
//控制旋轉(zhuǎn)
ControlRotate();
}
/// <summary>
/// 控制旋轉(zhuǎn)
/// </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;
}
}
判斷整行的消除
先去判斷是否有行滿(mǎn)了,如果有則進(jìn)行消除操作,消除時(shí)先刪除掉當(dāng)前行的每一個(gè)方塊再將地圖數(shù)組中當(dāng)前行置空,之后再將滿(mǎn)的行的上面行依次向下移動(dòng)一行
/// <summary>
/// 檢查是否有行滿(mǎn)了
/// </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;
}
}
//如果有行滿(mǎn)了
if (isFull)
{
//————————————————————消除操作
ClearRow(row);
MoveDownRow(row + 1);
row--;
}
}
}
/// <summary>
/// 清除行
/// </summary>
/// <param name="row">清除的行(滿(mǎn)的行)</param>
private void ClearRow(int _row)
{
for (int coloum = 0; coloum < column_max; coloum++)
{
Destroy(mapArray[coloum, _row].gameObject);
mapArray[coloum, _row] = null;
}
}
/// <summary>
/// 將清除的行上面的每一行依次向下移動(dòng)一行
/// </summary>
/// <param name="row">依次移動(dòng)的起始行(清除的行的上面一行)</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;
//當(dāng)前方塊
public Shape CurShape { get; set; }
/// <summary>
/// 初始化數(shù)據(jù)
/// </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">每一個(gè)形狀</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>
/// 判斷是否到達(dá)邊界
/// </summary>
/// <param name="pos">每一個(gè)方塊的位置</param>
/// <returns></returns>
private bool IsBorder(Vector2 blockPos)
{
if (blockPos.x < 0 || blockPos.x >= column_max || blockPos.y < 0)
{
return true;
}
return false;
}
/// <summary>
/// 將每個(gè)方塊位置添加到地圖數(shù)組中
/// </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;
}
//檢查是否有行滿(mǎn)了
CheckRowFull();
}
/// <summary>
/// 檢查是否有行滿(mǎn)了
/// </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;
}
}
//如果有行滿(mǎn)了
if (isFull)
{
//————————————————————消除操作
ClearRow(row);
MoveDownRow(row + 1);
row--;
}
}
}
/// <summary>
/// 清除行
/// </summary>
/// <param name="row">清除的行(滿(mǎn)的行)</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>
/// 將清除的行上面的每一行依次向下移動(dòng)一行
/// </summary>
/// <param name="row">依次移動(dòng)的起始行(清除的行的上面一行)</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腳本,控制每個(gè)形狀的操作
using UnityEngine;
public class Shape : MonoBehaviour
{
private bool canFall = true;//能否下落
private float fallTimer;//下落的計(jì)時(shí)器
private float fallTimeval = 0.5f;//下落的時(shí)間間隔
/// <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();
//控制左右移動(dòng)
ControlMovement();
//控制旋轉(zhuǎn)
ControlRotate();
}
/// <summary>
/// 控制左右移動(dòng)
/// </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>
/// 控制旋轉(zhuǎn)
/// </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;
//方塊下落到底部時(shí)將每個(gè)方塊位置添加到地圖數(shù)組中
GameController.Instance.AddEachBlockTransToMapArray(transform);
if (GameController.Instance.IsGameover())
{
GameManager.Instance.Gameover();
}
}
}
更多俄羅斯方塊精彩文章請(qǐng)點(diǎn)擊專(zhuān)題:俄羅斯方塊游戲集合 進(jìn)行學(xué)習(xí)。
更多有趣的經(jīng)典小游戲?qū)崿F(xiàn)專(zhuān)題,分享給大家:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C#使用偽隨機(jī)數(shù)實(shí)現(xiàn)加密用戶(hù)密碼的方法
這篇文章主要介紹了C#使用偽隨機(jī)數(shù)實(shí)現(xiàn)加密用戶(hù)密碼的方法,對(duì)于開(kāi)發(fā)C#會(huì)員系統(tǒng)或者程序安全問(wèn)題都有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-07-07
C#使用有道ip地址查詢(xún)接口方法實(shí)例詳解
這篇文章主要介紹了C#使用有道ip地址查詢(xún)接口方法,實(shí)例分析了有道IP地址查詢(xún)接口的使用方法與數(shù)據(jù)返回格式,需要的朋友可以參考下2015-05-05
C#使用Socket進(jìn)行簡(jiǎn)單的通訊的示例代碼
Socket 類(lèi)是基于與 Linux、macOS 或 Windows 的本機(jī)互操作性提供的托管代碼版本的套接字服務(wù),提供了一系列的接口來(lái)支持應(yīng)用層的調(diào)用,下面我們就來(lái)學(xué)習(xí)一下如何使用Socket進(jìn)行簡(jiǎn)單的通訊,需要的可以參考下2023-12-12
C#中實(shí)現(xiàn)任意List的全組合算法代碼
這篇文章主要是介紹了.net C# 實(shí)現(xiàn)任意List的全組合算法實(shí)現(xiàn)代碼,需要的朋友可以參考下2013-05-05
System.Data.OleDb.OleDbException: 未指定的錯(cuò)誤的完美解決方法
本文給大家?guī)?lái)三種有關(guān)System.Data.OleDb.OleDbException: 未指定的錯(cuò)誤的完美解決方法,每種方法都很不錯(cuò),需要的朋友可以參考下2016-09-09
asp.net core mvc權(quán)限控制:在視圖中控制操作權(quán)限
本文主要介紹了asp.net core mvc權(quán)限控制:在視圖中控制操作權(quán)限。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-02-02

