当今游戏产业中,经典游戏的复刻一直是一项受欢迎且具有挑战性的任务。俄罗斯方块是一个深入人心、令人上瘾的经典游戏,在过去几十年里一直享有广泛的流行度。其简单而富有策略性的玩法吸引了无数玩家的关注。因此,我决定利用Unity引擎来复刻这款经典游戏,以让更多的人重新体验其中的乐趣。
通过使用Unity引擎,我能够利用其强大的工具和功能,从头开始构建一个与原版俄罗斯方块游戏相似的游戏。我将努力保持原版游戏的核心要素,包括七种不同形状的方块(俄罗斯方块),玩家可以通过旋转和移动这些方块来填充完整的水平行,完成消除并得分的目标。
除了保留原版玩法外,我还计划为游戏添加一些额外的功能和改进。例如,增加多样化的难度级别,使得游戏适合任何玩家的技能水平。我还计划添加特殊的道具或技能,使游戏更加丰富有趣。此外,我还将注重游戏的视觉效果和音效,以提升玩家的沉浸感。
我的目标是创造一个令人难以抗拒的游戏体验,让玩家们在回忆经典之余,也能感受到崭新的乐趣。无论是单人挑战高分,还是与朋友们一较高下,这个复刻版的俄罗斯方块游戏都将带给玩家们小时候的回忆和喜悦。
我非常兴奋能够应用Unity引擎来实现这个愿望,并期待将来能与大家分享这款复刻版俄罗斯方块游戏。在这个过程中,我将努力改进和完善游戏,以确保它可以在各种平台上流畅运行,并为玩家们带来最佳的游戏体验。
谢谢大家的支持和关注!让我们一起回味经典,畅享游戏的乐趣吧!
先来看看实现的最终效果
泛型单例
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SingleBase<T> : MonoBehaviour where T : class
{
public static T Instance;
protected virtual void Awake()
{
Instance = this as T;
}
}
游戏管理类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : SingleBase<GameManager>
{
protected override void Awake()
{
base.Awake();
}
private void Start()
{
OnStartGame();
}
//开始游戏
void OnStartGame()
{
//网格生成
MapManager.Instance.InitMap();
}
}
简单工厂类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
//简单工厂
///
public static class SimpleFactory
{
//创建Resources/Model中的物体
public static GameObject CreateModel(string name, Transform parent)
{
return Object.Instantiate(Resources.Load("Model/" + name), parent) as GameObject;
}
}
网格地图生成
//常量类
public static class Defines
{
public static readonly int RowCount = 15;//网格行数
public static readonly int ColCount = 10;//网格列数
public static readonly float Offset = 0.9f;//格子间隔
}
网格地图管理器
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//网格地图管理器
public class MapManager : SingleBase<MapManager>
{
//初始化网格地图
public void InitMap()
{
for (int row = 0; row < Defines.RowCount; row++)
{
for (int col = 0; col < Defines.ColCount; col++)
{
GameObject obj = SimpleFactory.CreateModel("block", transform);
obj.transform.localPosition = new Vector3(col * Defines.Offset, -row * Defines.Offset, 0);
}
}
}
}
新建Block 方块脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum BlockType
{
Empty,//空
Static,//不动
Model,//模型
}
//方块脚本
public class Block : MonoBehaviour
{
public BlockType type;
public int RowIndex;
public int ColIndex;
public SpriteRenderer sp;
public Sprite normalSprite;//默认的图片
public Sprite modelSprite;//怪物图片
public void Init(int row, int col, BlockType type)
{
this.type = type;
this.RowIndex = row;
this.ColIndex = col;
}
private void Awake()
{
sp = GetComponent<SpriteRenderer>();
normalSprite = sp.sprite;
modelSprite = Resources.Load<Sprite>("Icon/gbl");
}
private void Start()
{
SetTypeToSp();
}
public void SetTypeToSp()
{
switch (this.type)
{
case BlockType.Empty:
sp.sprite = normalSprite;
sp.color = Color.white;
break;
case BlockType.Static:
sp.color = Color.red;
break;
case BlockType.Model:
sp.sprite = modelSprite;
sp.color = Color.white;
break;
}
}
}
网格地图管理器MapManager 调用
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//网格地图管理器
public class MapManager : SingleBase<MapManager>
{
public Block[,] blockArr;
//初始化网格地图
public void InitMap()
{
blockArr = new Block[Defines.RowCount,Defines.ColCount];
for (int row = 0; row < Defines.RowCount; row++)
{
for (int col = 0; col < Defines.ColCount; col++)
{
GameObject obj = SimpleFactory.CreateModel("block", transform);
obj.transform.localPosition = new Vector3(col * Defines.Offset, -row * Defines.Offset, 0);
Block b = obj.AddComponent<Block>();
b.Init(row, col, BlockType.Model);
//存储到二维数组
blockArr[row, col] = b;
}
}
}
}
MapManager新增方法
//切换对应下标的方块的类型
public void ChangeType(int row, int col, BlockType type)
{
Block b = blockArr[row, col];
b.type = type;
b.SetTypeToSp();
}
俄罗斯方块基类
//俄罗斯方块基类
public class TetrisBase
{
public int rowIndex;//对应整个网格的行坐标
public int colIndex;//对应整个网格的列坐标
public int rowCount;//存储形状的行总数
public int colCount;//存储形状的列总数
public BlockType[,] blockArr;//存储枚举的二维数组
//初始化
public virtual void Init()
{
}
//画自己(更改网格对应的图片精灵)
public virtual void DrawMe()
{
for (int row = 0; row < rowCount; row++)
{
for (int col = 0; col < colCount; col++)
{
if (blockArr[row, col] != BlockType.Empty)
{
int map_rowIndex = rowIndex + row;
int map_colIndex = colIndex + col;
MapManager.Instance.ChangeType(map_rowIndex, map_colIndex, blockArr[row, col]);
}
}
}
}
//擦除自己
public virtual void WipeMe()
{
for (int row = 0; row < rowCount; row++)
{
for (int col = 0; col < colCount; col++)
{
if (blockArr[row, col] != BlockType.Empty)
{
int map_rowIndex = rowIndex + row;
int map_colIndex = colIndex + col;
MapManager.Instance.ChangeType(map_rowIndex, map_colIndex, BlockType.Empty);
}
}
}
}
//向下移动
public virtual void MoveDown()
{
rowIndex++;
}
//左移动
public virtual void MoveLeft()
{
colIndex--;
}
//右移动
public virtual void MoveRight()
{
colIndex++;
}
}
T形状的俄罗斯方块
//T形状的俄罗斯方块
public class Tetris_T : TetrisBase
{
public override void Init()
{
rowCount = 2;
colCount = 3;
blockArr = new BlockType[,]{
{BlockType.Model,BlockType.Model,BlockType.Model},
{BlockType.Empty,BlockType.Model,BlockType.Empty}
};
}
}
GameManager调用,绘制方块形状
TetrisBase currentTetris;//当前操作的俄罗斯
//开始游戏
void OnStartGame()
{
//网格生成
MapManager.Instance.InitMap();
currentTetris = CreateTetris();
//画出来
currentTetris.DrawMe();
}
//创建
TetrisBase CreateTetris()
{
TetrisBase t = new Tetris_T();
t.Init();
t.colIndex = Defines.ColCount / 2 - t.colCount / 2;
return t;
}
定义移动方向枚举
//移动方向
public enum Direction
{
None,
Right,
Left,
Down
}
修改俄罗斯方块基类TetrisBase
//根据方向移动
public virtual void MoveByDir(Direction dir)
{
//擦除自己
WipeMe();
switch (dir)
{
case Direction.None:
break;
case Direction.Right:
MoveRight();
break;
case Direction.Left:
MoveLeft();
break;
case Direction.Down:
MoveDown();
break;
}
DrawMe();//画自己
}
GameManager新增用户操作
void Update(){
InputCtl();
}
//用户操作
public void InputCtl()
{
if (Input.GetKeyDown(KeyCode.A))
currentTetris.MoveByDir(Direction.Left);
if (Input.GetKeyDown(KeyCode.D))
currentTetris.MoveByDir(Direction.Right);
if (Input.GetKeyDown(KeyCode.S))
currentTetris.MoveByDir(Direction.Down);
}
俄罗斯方块基类TetrisBase新增方法
//是否能移动的方向
public virtual bool IsCanMove(Direction dir)
{
int _rowIndex = rowIndex;
int _colIndex = colIndex;
switch (dir)
{
case Direction.None:
break;
case Direction.Right:
_colIndex++;
break;
case Direction.Left:
_colIndex--;
break;
case Direction.Down:
_rowIndex++;
break;
}
//超出网格
if (_colIndex < 0 || _colIndex + colCount > Defines.ColCount || _rowIndex + rowCount > Defines.RowCount)
{
return false;
}
return true;
}
GameManager调用
//用户操作
public void InputCtl()
{
if (Input.GetKeyDown(KeyCode.A))
{
if (currentTetris.IsCanMove(Direction.Left))
{
currentTetris.MoveByDir(Direction.Left);
}
}
if (Input.GetKeyDown(KeyCode.D))
{
if (currentTetris.IsCanMove(Direction.Right))
{
currentTetris.MoveByDir(Direction.Right);
}
}
if (Input.GetKeyDown(KeyCode.S))
{
if (currentTetris.IsCanMove(Direction.Down))
{
currentTetris.MoveByDir(Direction.Down);
}
}
}
Defines新增常用类
public static readonly float downTime = 1;//下落时间间隔
修改GameManager
float timer;
//开始游戏
void OnStartGame()
{
timer = Defines.downTime;
}
void Update()
{
AutoMoveDown();
}
//自动下落
public void AutoMoveDown()
{
timer -= Time.deltaTime;
if (timer <= 0)
{
timer = Defines.downTime;
if (currentTetris.IsCanMove(Direction.Down))
{
currentTetris.MoveByDir(Direction.Down);
}
else
{
//不能移动重新创建
currentTetris = CreateTetris();
currentTetris.DrawMe();
}
}
}
效果
MapManager新增方法
//设置不可移动后的俄罗斯方块对应的位置为Static类型
public void SetStatic(TetrisBase t)
{
for (int row = 0; row < t.rowCount; row++)
{
for (int col = 0; col < t.colCount; col++)
{
if (t.blockArr[row, col] != BlockType.Empty)
{
int map_rowIndex = row + t.rowIndex;
int map_colIndex = col + t.colIndex;
ChangeType(map_rowIndex, map_colIndex, BlockType.Static);
}
}
}
}
GameManager调用
if (!currentTetris.IsCanMove(Direction.Down))
{
//设置不可移动类型
MapManager.Instance.SetStatic(currentTetris);
}
运行效果
解决方块下落重叠的问题
修改俄罗斯方块基类TetrisBase的IsCanMove方法
遍历当前方块的每个单元格,如果单元格不为空且对应最近的地图单元格是静态的(即已经被其他方块占据),则返回false表示不能移动,否则返回true表示可以移动
//是否能移动的方向
public virtual bool IsCanMove(Direction dir)
{
int _rowIndex = rowIndex;
int _colIndex = colIndex;
switch (dir)
{
case Direction.None:
break;
case Direction.Right:
_colIndex++;
break;
case Direction.Left:
_colIndex--;
break;
case Direction.Down:
_rowIndex++;
break;
}
//超出网格
if (_colIndex < 0 || _colIndex + colCount > Defines.ColCount || _rowIndex + rowCount > Defines.RowCount)
{
return false;
}
//检查当前方块是否可以向指定方向移动
for (int row = 0; row < rowCount; row++)
{
for (int col = 0; col < colCount; col++)
{
if (blockArr[row, col] != BlockType.Empty)
{
int map_rowIndex = _rowIndex + row;
int map_colIndex = _colIndex + col;
Block b = MapManager.Instance.blockArr[map_rowIndex, map_colIndex];
if (b.type == BlockType.Static)
{
return false;
}
}
}
}
return true;
}
效果
TetrisBase俄罗斯方块基类新增控制旋转方法
//旋转
public void Rotate()
{
//二维数组互换
int new_rowCount = colCount;
int new_colCount = rowCount;
//互换行列后是否超出网格
if (rowIndex + new_rowCount > Defines.RowCount || colIndex + new_colCount > Defines.ColCount) return;
BlockType[,] tempArr = new BlockType[new_rowCount, new_colCount];
for (int row = 0; row < new_rowCount; row++)
{
for (int col = 0; col < new_colCount; col++)
{
tempArr[row, col] = blockArr[new_colCount - 1 - col, row];
if (tempArr[row, col] != BlockType.Empty)
{
//对应位置是静态类型static不能旋转
if (MapManager.Instance.blockArr[row + this.rowIndex, col + this.colIndex].type == BlockType.Static) return;
}
}
}
//擦除
WipeMe();
rowCount = new_rowCount;
colCount = new_colCount;
blockArr = tempArr;
DrawMe();//画
}
GameManager调用
if (Input.GetKeyDown(KeyCode.W)) currentTetris.Rotate();
GameManager新增方法
# 调用
//检测删除行
CheckDelete();
//检测删除行
public void CheckDelete()
{
//最后一行开始遍历
for (int row = Defines.RowCount - 1; row >= 0; row--)
{
int count = 0;//静态类型的个数
for
(int col = 0; col < Defines.ColCount; col++)
{
BlockType type = MapManager.Instance.blockArr[row, col].type;
if (type == BlockType.Static)
count++;
}
if
(count == Defines.ColCount)
{
for (int dropRow = row; dropRow > 1; dropRow--)
{
for (int dropCol = 0; dropCol < Defines.ColCount; dropCol++)
{
//上一行类型覆盖当前行
BlockType type = MapManager.Instance.blockArr[dropRow - 1, dropCol].type;
MapManager.Instance.ChangeType(dropRow, dropCol, type);
}
}
row++;
}
}
}
修改GameManager代码
bool isStop = false;//游戏结束标识
//开始游戏
void OnStartGame()
{
isStop = false;
}
void Update()
{
if (isStop == true)
{
return;
}
}
//当前俄罗斯生成的时候对应位置是不可移动(覆盖)说明游戏结束
public bool IsGameOver()
{
for (int row = 0; row < currentTetris.rowCount; row++)
{
for (int col = 0; col < currentTetris.colCount; col++)
{
BlockType type = currentTetris.blockArr[row, col];
if (type != BlockType.Empty)
{
int map_rowIndex = row + currentTetris.rowIndex;
int map_colIndex = col + currentTetris.colIndex;
if (MapManager.Instance.blockArr[map_rowIndex, map_colIndex].type == BlockType.Static) return true;
}
}
}
return false;
}
调用
//自动下落
public void AutoMoveDown()
{
timer -= Time.deltaTime;
if (timer <= 0)
{
timer = Defines.downTime;
if (currentTetris.IsCanMove(Direction.Down))
{
currentTetris.MoveByDir(Direction.Down);
}
else
{
//设置不可移动类型
MapManager.Instance.SetStatic(currentTetris);
//检测删除行
CheckDelete();
//不能移动重新创建
currentTetris = CreateTetris();
if (IsGameOver() == true)
{
isStop = true;
Debug.Log("game over");
return;
}
currentTetris.DrawMe();
}
}
}
修改GameManager代码
//检测删除行
public void CheckDelete()
{
//最后一行开始遍历
for (int row = Defines.RowCount - 1; row >= 0; row--)
{
//。。。
if(count == Defines.ColCount)
{
for (int dropCol = 0; dropCol < Defines.ColCount; dropCol++)
{
//当前行生成哥布林
SimpleFactory.CreateModel("gbl", null).transform.position = MapManager.Instance.blockArr[row, dropCol].transform.position;
}
//。。。
}
}
}
怪物触碰boss造成伤害和特效,还有游戏结束效果,就自己扩展了,也很简单,还有不同形状的方块
效果
要啥源码,好好看,好好学!
【视频】https://www.bilibili.com/video/BV1Fr4y1x7mx
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注
,以便我第一时间收到反馈,你的每一次支持
都是我不断创作的最大动力。当然如果你发现了文章中存在错误
或者有更好的解决方法
,也欢迎评论私信告诉我哦!
好了,我是向宇
,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~