俄罗斯方块
文章目录
- Test1_场景切换相关
- BeginScene.cs
- BegionOrEndScene.cs
- EndScene.cs
- Game.cs
- GameScene.cs
- ISceneUpdate.cs
- Test2_绘制对象基类和枚举信息
- DrawObject.cs
- IDraw.cs
- Position.cs
- Test3_地图相关
- Map.cs
- Test4_坐标信息类
- BlockInfo.cs
- Test5_板砖工人类
- BlockWorker.cs
- Test6_输入模块
- InputThread.cs
- Program.cs
Test1_场景切换相关
BeginScene.cs
namespace CSharp俄罗斯方块
{
internal class BeginScene : BegionOrEndScene
{
public BeginScene()
{
strTitle = "俄罗斯方块";
strOne = "开始游戏";
}
public override void EnterJDoSomthing()
{
if (nowSelIndex == 0)
{
Game.ChangeScene(E_SceneType.Game);
}
else
{
Environment.Exit(0);
}
}
}
}
BegionOrEndScene.cs
namespace CSharp俄罗斯方块
{
abstract internal class BegionOrEndScene : ISceneUpdate
{
protected int nowSelIndex = 0;
protected string strTitle;
protected string strOne;
public abstract void EnterJDoSomthing();
public void Update()
{
Console.ForegroundColor = ConsoleColor.White;
Console.SetCursorPosition(Game.w / 2 - strTitle.Length, 5);
Console.Write(strTitle);
Console.SetCursorPosition(Game.w / 2 - strOne.Length, 8);
Console.ForegroundColor = nowSelIndex == 0 ? ConsoleColor.Red : ConsoleColor.White;
Console.Write(strOne);
Console.SetCursorPosition(Game.w / 2 - 4, 10);
Console.ForegroundColor = nowSelIndex == 1 ? ConsoleColor.Red : ConsoleColor.White;
Console.Write("结束游戏");
switch(Console.ReadKey(true).Key)
{
case ConsoleKey.W:
nowSelIndex--;
if (nowSelIndex < 0)
{
nowSelIndex = 1;
}
break;
case ConsoleKey.S:
nowSelIndex++;
if (nowSelIndex > 1)
{
nowSelIndex = 0;
}
break;
case ConsoleKey.J:
EnterJDoSomthing();
break;
}
}
}
}
EndScene.cs
namespace CSharp俄罗斯方块
{
internal class EndScene : BegionOrEndScene
{
public EndScene()
{
strTitle = "结束游戏";
strOne = "回到开始界面";
}
public override void EnterJDoSomthing()
{
if(nowSelIndex == 0)
{
Game.ChangeScene(E_SceneType.Begin);
}
else
{
Environment.Exit(0);
}
}
}
}
Game.cs
namespace CSharp俄罗斯方块
{
enum E_SceneType
{
Begin,
Game,
End,
}
internal class Game
{
public const int w = 50;
public const int h = 35;
public static ISceneUpdate nowScene;
public Game()
{
Console.CursorVisible = false;
Console.SetWindowSize(w, h);
Console.SetBufferSize(w, h);
ChangeScene(E_SceneType.Begin);
}
public void start()
{
while (true)
{
if (nowScene != null)
{
nowScene.Update();
}
}
}
public static void ChangeScene(E_SceneType type)
{
Console.Clear();
switch (type)
{
case E_SceneType.Begin:
nowScene = new BeginScene();
break;
case E_SceneType.Game:
nowScene = new GameScene();
break;
case E_SceneType.End:
nowScene = new EndScene();
break;
}
}
}
}
GameScene.cs
namespace CSharp俄罗斯方块
{
internal class GameScene : ISceneUpdate
{
Map map;
BlockWorker blockWorker;
//bool isRunning;
//新开线程
//Thread inputThread;
public GameScene()
{
map = new Map(this);
blockWorker = new BlockWorker();
InputThread.Instance.inputEvent += CheckInputThread;
//isRunning = true;
//inputThread = new Thread(CheckInputThread);
设置成后台线程(生命周期随主线程)
//inputThread.IsBackground = true;
开启线程
//inputThread.Start();
}
public void StopThread()
{
//isRunning = false;
//inputThread = null;
//移除输入事件监听
InputThread.Instance.inputEvent -= CheckInputThread;
}
private void CheckInputThread()
{
//while (isRunning)
//{
//检测输入,防止输入卡程序
if (Console.KeyAvailable)
{
//避免影响主线程,在输入后加锁
lock (blockWorker)
{
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.LeftArrow:
//判断变形
if (blockWorker.CanChange(E_Change_Type.Left, map))
blockWorker.Change(E_Change_Type.Left);
break;
case ConsoleKey.RightArrow:
if (blockWorker.CanChange(E_Change_Type.Right, map))
blockWorker.Change(E_Change_Type.Right);
break;
case ConsoleKey.A:
if (blockWorker.CanMoveR_L(E_Change_Type.Left, map))
blockWorker.MoveR_L(E_Change_Type.Left);
break;
case ConsoleKey.D:
if (blockWorker.CanMoveR_L(E_Change_Type.Right, map))
blockWorker.MoveR_L(E_Change_Type.Right);
break;
case ConsoleKey.S:
if (blockWorker.CanDown(map))
blockWorker.AutoMove();
break;
}
}
}
//}
}
public void Update()
{
lock (blockWorker)
{
map.Draw();
blockWorker.Draw();
if (blockWorker.CanDown(map))
blockWorker.AutoMove();
}
//线程休眠减速
Thread.Sleep(100);
}
}
}
ISceneUpdate.cs
namespace CSharp俄罗斯方块
{
/// <summary>
/// 场景更新接口
/// </summary>
internal interface ISceneUpdate
{
void Update();
}
}
Test2_绘制对象基类和枚举信息
DrawObject.cs
namespace CSharp俄罗斯方块
{
/// <summary>
/// 绘制类型
/// </summary>
enum E_DrawType
{
/// <summary>
/// 墙壁
/// </summary>
Wall,
/// <summary>
/// 正方形方块
/// </summary>
Cube,
/// <summary>
/// 直线
/// </summary>
Line,
/// <summary>
/// 坦克
/// </summary>
Tank,
/// <summary>
/// 左梯子
/// </summary>
Left_Ladder,
/// <summary>
/// 右梯子
/// </summary>
Right_Ladder,
/// <summary>
/// 左长梯子
/// </summary>
Left_Long_ladder,
/// <summary>
/// 右长梯子
/// </summary>
Right_Long_ladder,
}
internal class DrawObject : IDraw
{
public Position pos;
public E_DrawType type;
public DrawObject(E_DrawType type)
{
this.type = type;
}
public DrawObject(E_DrawType type, int x, int y) : this(type)
{
pos = new Position(x, y);
}
public void Draw()
{
if (pos.y < 0)
return;
Console.SetCursorPosition(pos.x, pos.y);
switch (type)
{
case E_DrawType.Wall:
Console.ForegroundColor = ConsoleColor.Red;
break;
case E_DrawType.Cube:
Console.ForegroundColor = ConsoleColor.Blue;
break;
case E_DrawType.Line:
Console.ForegroundColor = ConsoleColor.Green;
break;
case E_DrawType.Tank:
Console.ForegroundColor = ConsoleColor.Cyan;
break;
case E_DrawType.Left_Ladder:
case E_DrawType.Right_Ladder:
Console.ForegroundColor = ConsoleColor.Magenta;
break;
case E_DrawType.Left_Long_ladder:
case E_DrawType.Right_Long_ladder:
Console.ForegroundColor = ConsoleColor.Yellow;
break;
}
Console.Write("■");
}
//擦除
public void ClearDraw()
{
if (pos.y < 0)
return;
Console.SetCursorPosition(pos.x, pos.y);
Console.Write(" ");
}
/// <summary>
/// 附着墙壁方法
/// </summary>
/// <param name="type"></param>
public void ChangeType(E_DrawType type)
{
this.type = type;
}
}
}
IDraw.cs
namespace CSharp俄罗斯方块
{
internal interface IDraw
{
void Draw();
}
}
Position.cs
namespace CSharp俄罗斯方块
{
internal struct Position
{
public int x;
public int y;
public Position(int x, int y)
{
this.x = x;
this.y = y;
}
public static bool operator ==(Position left, Position right)
{
if (left.x == right.x && left.y == right.y) { return true; }
return false;
}
public static bool operator !=(Position left, Position right)
{
if (left.x == right.x && left.y == right.y) { return false; }
return true;
}
public static Position operator +(Position left, Position right)
{
return new Position(left.x + right.x, left.y + right.y);
}
}
}
Test3_地图相关
Map.cs
namespace CSharp俄罗斯方块
{
internal class Map : IDraw
{
//固定墙壁
private List<DrawObject> walls = new List<DrawObject>();
//动态墙壁
public List<DrawObject> dynamicWalls = new List<DrawObject>();
private GameScene nowGameScene;
//为了外部能快速得到地图边界
//动态墙壁的宽容量,小方块横向个数
public int w;
public int h;
//记录每一行有多少个小方块的容器,索引就是行号
private int[] recordInfo;
public Map(GameScene scene)
{
nowGameScene = scene;
h = Game.h - 6;
recordInfo = new int[h];
w = 0;
//绘制横向墙壁
for (int i = 0; i < Game.w; i += 2)
{
walls.Add(new DrawObject(E_DrawType.Wall, i, h));
w++;
}
w -= 2;
for (int i = 0; i < h; i++)
{
walls.Add(new DrawObject(E_DrawType.Wall, 0, i));
walls.Add(new DrawObject(E_DrawType.Wall, Game.w - 2, i));
}
}
public void Draw()
{
for (int i = 0; i < walls.Count; i++)
{
walls[i].Draw();
}
for (int i = 0; i < dynamicWalls.Count; i++)
{
dynamicWalls[i].Draw();
}
}
public void ClearDraw()
{
for (int i = 0; i < dynamicWalls.Count; i++)
{
dynamicWalls[i].ClearDraw();
}
}
public void AddWalls(List<DrawObject> walls)
{
for (int i = 0; i < walls.Count; i++)
{
//掉下来的方块变成墙壁类型
walls[i].ChangeType(E_DrawType.Wall);
dynamicWalls.Add(walls[i]);
if (walls[i].pos.y<=0)
{
nowGameScene.StopThread();
Game.ChangeScene(E_SceneType.End);
return;
}
//进行添加动态墙壁行的计数
recordInfo[h - walls[i].pos.y - 1]++;
}
ClearDraw();
CheckClear();
Draw();
}
/// <summary>
/// 检测是否跨层
/// </summary>
public void CheckClear()
{
//遍历时安全移除,添加待移除数组
List<DrawObject> delList = new List<DrawObject>();
//要选择记录行中有多少个方块的容器
//数组判断这一行是否满
//遍历数组,检测数组里面存的数是不是w-2
for (int i = 0; i < recordInfo.Length; i++)
{
//如果这行满了需要移除
if (recordInfo[i] == w)
{
//1、这一行的所有方块移除
for (int j = 0; j < dynamicWalls.Count; j++)
{
//通过动态方块的y计算它在哪一行
if (i == h - dynamicWalls[j].pos.y - 1)
{
//待移除添加到记录列表
delList.Add(dynamicWalls[j]);
}
//2、要这一行之上的所有方块下移
else if (h - dynamicWalls[j].pos.y - 1 > i)
{
dynamicWalls[j].pos.y++;
}
}
//移除待删小方块
for (int j = 0;j < delList.Count; j++)
{
dynamicWalls.Remove(delList[j]);
}
//3、记录小方块数量的数组从上到下迁移
for (int j = i; j < recordInfo.Length-1; j++)
{
recordInfo[j] = recordInfo[j + 1];
}
//置空最顶层
recordInfo[recordInfo.Length - 1] = 0;
//利用递归,一层一层检测
CheckClear();
break;
}
}
}
}
}
Test4_坐标信息类
BlockInfo.cs
namespace CSharp俄罗斯方块
{
internal class BlockInfo
{
private List<Position[]> list;
public BlockInfo(E_DrawType type)
{
list = new List<Position[]>();
switch (type)
{
case E_DrawType.Cube:
list.Add(new Position[3] { new Position(2, 0), new Position(0, 1), new Position(2, 1) });
break;
case E_DrawType.Line:
list.Add([new Position(0, -1), new Position(0, 1), new Position(0, 2)]);
list.Add([new Position(-4, 0), new Position(-2, 0), new Position(2, 0)]);
list.Add([new Position(0, -2), new Position(0, -1), new Position(0, 1)]);
list.Add([new Position(-2, 0), new Position(2, 0), new Position(4, 0)]);
break;
case E_DrawType.Tank:
list.Add([new Position(-2, 0), new Position(2, 0), new Position(0, 1)]);
list.Add([new Position(0, -1), new Position(-2, 0), new Position(0, 1)]);
list.Add([new Position(0, -1), new Position(-2, 0), new Position(2, 0)]);
list.Add([new Position(0,-1), new Position(2, 0), new Position(0, 1)]);
break;
case E_DrawType.Left_Ladder:
list.Add([new Position(0, -1), new Position(2, 0), new Position(2, 1)]);
list.Add([new Position(2, 0), new Position(-2, 1), new Position(0, 1)]);
list.Add([new Position(-2, -1), new Position(-2, 0), new Position(0, 1)]);
list.Add([new Position(0, -1), new Position(2, -1), new Position(-2, 0)]);
break;
case E_DrawType.Right_Ladder:
list.Add([new Position(0, -1), new Position(-2, 0), new Position(-2, 1)]);
list.Add([new Position(-2, -1), new Position(0, -1), new Position(2, 0)]);
list.Add([new Position(2, -1), new Position(2, 0), new Position(0, 1)]);
list.Add([new Position(-2, 0), new Position(0, 1), new Position(2, 1)]);
break;
case E_DrawType.Left_Long_ladder:
list.Add([new Position(-2, -1), new Position(0, -1), new Position(0, 1)]);
list.Add([new Position(2, 1), new Position(-2, 0), new Position(2, 0)]);
list.Add([new Position(0, -1), new Position(0, 1), new Position(2, 1)]);
list.Add([new Position(-2, 0), new Position(2, 0), new Position(-2, 1)]);
break;
case E_DrawType.Right_Long_ladder:
list.Add([new Position(0, -1), new Position(2, -1), new Position(0, 1)]);
list.Add([new Position(-2, 0), new Position(2, 1), new Position(2, 0)]);
list.Add([new Position(0, -1), new Position(0, 1), new Position(-2, 1)]);
list.Add([new Position(-2, 0), new Position(2, 0), new Position(-2, -1)]);
break;
default:
break;
}
}
public Position[] this[int index]
{
get
{
if (index < 0)
return list[0];
else if (index >= list.Count)
return list[list.Count - 1];
else
return list[index];
}
}
public int Count { get => list.Count; }
}
}
Test5_板砖工人类
BlockWorker.cs
namespace CSharp俄罗斯方块
{
enum E_Change_Type
{
Left,
Right,
}
internal class BlockWorker : IDraw
{
//方块们
private List<DrawObject> blocks;
//用Dictionary方便查找
private Dictionary<E_DrawType, BlockInfo> blockInfoDic;
//随机创建的方块具体形态信息
private BlockInfo nowBlockInfo;
//当前形态的索引
private int nowInfoIndex;
public BlockWorker()
{
blockInfoDic = new Dictionary<E_DrawType, BlockInfo>()
{
{ E_DrawType.Cube,new BlockInfo(E_DrawType.Cube)},
{ E_DrawType.Line,new BlockInfo(E_DrawType.Line)},
{ E_DrawType.Tank,new BlockInfo(E_DrawType.Tank)},
{ E_DrawType.Left_Ladder,new BlockInfo(E_DrawType.Left_Ladder)},
{ E_DrawType.Right_Ladder,new BlockInfo(E_DrawType.Right_Ladder)},
{ E_DrawType.Left_Long_ladder,new BlockInfo(E_DrawType.Left_Long_ladder)},
{ E_DrawType.Right_Long_ladder,new BlockInfo(E_DrawType.Right_Long_ladder)},
};
RandomCreatBlock();
}
public void Draw()
{
for (int i = 0; i < blocks.Count; i++)
{
blocks[i].Draw();
}
}
/// <summary>
/// 随机创建方块
/// </summary>
public void RandomCreatBlock()
{
Random r = new Random();
E_DrawType type = (E_DrawType)r.Next(1, 8);
blocks = new List<DrawObject>()
{
new DrawObject(type),
new DrawObject(type),
new DrawObject(type),
new DrawObject(type),
};
//定义0,0相对坐标
blocks[0].pos = new Position(24, -5);
nowBlockInfo = blockInfoDic[type];
//随机当前形态
nowInfoIndex = r.Next(0, nowBlockInfo.Count);
//取出原点坐标信息
Position[] pos = nowBlockInfo[nowInfoIndex];
for (int i = 0; i < pos.Length; i++)
{
//通过原点坐标信息绘制四个方块
blocks[i + 1].pos = pos[i] + blocks[0].pos;
}
}
//擦除方法
public void ClearDraw()
{
for (int i = 0; i < blocks.Count; i++)
{
blocks[i].ClearDraw();
}
}
/// <summary>
/// 方块变形
/// </summary>
/// <param name="type">左,右</param>
public void Change(E_Change_Type type)
{
//擦除之前的方块
ClearDraw();
switch (type)
{
case E_Change_Type.Left:
nowInfoIndex--;
if (nowInfoIndex < 0)
nowInfoIndex = nowBlockInfo.Count - 1;
break;
case E_Change_Type.Right:
nowInfoIndex++;
if (nowInfoIndex >= nowBlockInfo.Count)
nowInfoIndex = 0;
break;
}
//得到索引,取出来
Position[] pos = nowBlockInfo[nowInfoIndex];
for (int i = 0; i < pos.Length; i++)
{
blocks[i + 1].pos = pos[i] + blocks[0].pos;
}
//擦除之后绘制
Draw();
}
/// <summary>
/// 判断能否变形
/// </summary>
/// <param name="type">地图方向</param>
/// <param name="map">地图墙壁</param>
/// <returns></returns>
public bool CanChange(E_Change_Type type, Map map)
{
//临时变量用来模拟
int nowIndex = nowInfoIndex;
switch (type)
{
case E_Change_Type.Left:
nowIndex--;
if (nowIndex < 0)
nowIndex = nowBlockInfo.Count - 1;
break;
case E_Change_Type.Right:
nowIndex++;
if (nowIndex >= nowBlockInfo.Count)
nowIndex = 0;
break;
}
//临时索引信息判断是否和墙壁重合
Position[] nowPos = nowBlockInfo[nowIndex];
//判断是否超出地图边界
Position tempPos;
for (int i = 0; i < nowPos.Length; i++)
{
tempPos = blocks[0].pos + nowPos[i];
if (tempPos.x < 2 || tempPos.x >= Game.w - 2 || tempPos.y >= map.h)
{
return false;
}
}
//判断是否和其他方块重合
for (int i = 0; i < nowPos.Length; i++)
{
tempPos = blocks[0].pos + nowPos[i];
for (int j = 0; j < map.dynamicWalls.Count; j++)
{
if (tempPos == map.dynamicWalls[j].pos)
{
return false;
}
}
}
return true;
}
/// <summary>
/// 方块左右移动
/// </summary>
/// <param name="type">左,右</param>
public void MoveR_L(E_Change_Type type)
{
//移动之前擦除
ClearDraw();
//根据传入的类型,决定左右移动,得到偏移位置
Position movePos = new Position(type == E_Change_Type.Left ? -2 : 2, 0);
for (int i = 0; i < blocks.Count; i++)
{
blocks[i].pos += movePos;
}
//移动之后绘制
Draw();
}
/// <summary>
/// 判断能否移动
/// </summary>
/// <param name="type">左,右移动</param>
/// <returns></returns>
public bool CanMoveR_L(E_Change_Type type, Map map)
{
Position movePos = new Position(type == E_Change_Type.Left ? -2 : 2, 0);
//左右边界重合,预判断pos
Position pos;
for (int i = 0; i < blocks.Count; i++)
{
pos = blocks[i].pos + movePos;
if (pos.x < 2 || pos.x >= Game.w - 2)
return false;
}
//动态方块重合
for (int i = 0; i < blocks.Count; i++)
{
pos = blocks[i].pos + movePos;
for (int j = 0; j < map.dynamicWalls.Count; j++)
{
if (pos == map.dynamicWalls[j].pos)
return false;
}
}
return true;
}
//方块自动下落
public void AutoMove()
{
ClearDraw();
Position downMove = new Position(0, 1);
for (int i = 0; i < blocks.Count; i++)
{
blocks[i].pos += downMove;
}
Draw();
}
public bool CanDown(Map map)
{
Position downPos = new Position(0, 1);
//预下落位置
Position pos;
//边界
for (int i = 0; i < blocks.Count; i++)
{
pos = blocks[i].pos + downPos;
if (pos.y >= map.h)
{
//碰到地图,此时变成地图的一部分
map.AddWalls(blocks);
//创建新的随机方块
RandomCreatBlock();
return false;
}
}
//动态方块
for (int i = 0; i < blocks.Count; i++)
{
pos = blocks[i].pos + downPos;
for (int j = 0; j < map.dynamicWalls.Count; j++)
{
if (pos == map.dynamicWalls[j].pos)
{
//碰到方块,此时变成地图的一部分
map.AddWalls(blocks);
RandomCreatBlock();
return false;
}
}
}
return true;
}
}
}
Test6_输入模块
InputThread.cs
namespace CSharp俄罗斯方块
{
internal class InputThread
{
//线程成员变量
Thread inputThread;
//输入检测事件
public event Action inputEvent;
private static InputThread instance = new InputThread();
public static InputThread Instance
{
get
{
return instance;
}
}
private InputThread()
{
inputThread = new Thread(InputCheck);
inputThread.IsBackground = true;
inputThread.Start();
}
private void InputCheck()
{
while (true)
{
inputEvent?.Invoke();
}
}
}
}
Program.cs
using CSharp俄罗斯方块;
Game game = new Game();
game.start();