俄羅斯方塊
文章目錄
- 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-2for (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];elsereturn 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);//左右邊界重合,預判斷posPosition 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();