一、設計思路
一個背景數組,一個方塊數組。將方塊數組放入背景數組時,需要一個座標進行定位,即在兩個座標系之間存在一個映射關係。
二、form內代碼
ErLuoSiClass elsClass = new ErLuoSiClass(); /*遊戲開始 按鈕事件*/ private void button1_Click(object sender, EventArgs e) { elsClass.CellWidthCount = 12; elsClass.CellHeightCount = 16; elsClass.InitErLuoSi(); elsClass.winHandle = elsPanel.Handle; elsPanel.Width = elsClass.Width; elsPanel.Height = elsClass.Height; elsClass.ErLuoSiDraw(); button1.Enabled = false; //隨機產生當前 block elsClass.GetInitBlockData(); //定時器開始 timer1.Enabled = true; } /*俄羅斯方塊重繪事件*/ private void elsPanel_Paint(object sender, PaintEventArgs e) { if (elsClass.elsStartFlag) elsClass.ErLuoSiDraw(); } /*鍵盤操作*/ private void Form1_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Down) { //先判斷是否可以下移 if(elsClass.CanDown()) elsClass.currentX++;//下移 } else if (e.KeyCode == Keys.Left) { //先判斷是否可以左移 if (elsClass.CanLeft()) elsClass.currentY--;//左移 } else if (e.KeyCode == Keys.Right) { //先判斷是否可以右移 if (elsClass.CanRight()) elsClass.currentY++;//右移 } else if (e.KeyCode == Keys.Up) { //先判斷是否可以變換 if(elsClass.CanChange()) elsClass.ChangeBlock();//變換 } else if (e.KeyCode == Keys.Space) { //空格鍵暫停、開始 if (timer1.Enabled) timer1.Enabled = false; else timer1.Enabled = true; } //重繪 此處也重繪 在介面上反映快點 elsClass.ErLuoSiDraw(); elsPanel.Focus(); } /*定時器事件*/ private void timer1_Tick(object sender, EventArgs e) { if (elsClass.CanDown()) { elsClass.currentX++; } else //假如不能再移動了 { //將block中的值放入背景數組中 elsClass.SetData(); //檢查並刪除行 elsClass.CheckAndDeleteLines(); //檢查是否到頂 if (elsClass.CheckToTop()) { timer1.Enabled = false; MessageBox.Show("到頂了!"); } //重新設定起始狀態 elsClass.currentX = -4; elsClass.currentY = 5; //隨機產生新的block elsClass.GetInitBlockData(); } //重繪圖 elsClass.ErLuoSiDraw(); }
三、核心控制類
public class ErLuoSiClass { #region 參數 public System.IntPtr winHandle; //遊戲開關標誌 public bool elsStartFlag = false; //遊戲畫板對應數組 預設全是0 public int[,] RushData; #region 小圖案的狀態,暫且7種,每種都用四個變化 (7*4)*4*4 public int[, ,] Blocks = { //逆時針旋轉 #region 橫杆的4種狀態 { {1,0,0,0}, {1,0,0,0}, {1,0,0,0}, {1,0,0,0} }, { {1,1,1,1}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0} }, { {1,0,0,0}, {1,0,0,0}, {1,0,0,0}, {1,0,0,0} }, { {1,1,1,1}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0} }, #endregion #region 方框的4種狀態 { {1,1,0,0}, {1,1,0,0}, {0,0,0,0}, {0,0,0,0} }, { {1,1,0,0}, {1,1,0,0}, {0,0,0,0}, {0,0,0,0} }, { {1,1,0,0}, {1,1,0,0}, {0,0,0,0}, {0,0,0,0} }, { {1,1,0,0}, {1,1,0,0}, {0,0,0,0}, {0,0,0,0} }, #endregion #region T字的4種狀態 { {1,1,1,0}, {0,1,0,0}, {0,0,0,0}, {0,0,0,0} }, { {1,0,0,0}, {1,1,0,0}, {1,0,0,0}, {0,0,0,0} }, { {0,1,0,0}, {1,1,1,0}, {0,0,0,0}, {0,0,0,0} }, { {0,1,0,0}, {1,1,0,0}, {0,1,0,0}, {0,0,0,0} }, #endregion #region 7字的4種狀態 { {1,1,0,0}, {0,1,0,0}, {0,1,0,0}, {0,0,0,0} }, { {1,1,1,0}, {1,0,0,0}, {0,0,0,0}, {0,0,0,0} }, { {1,0,0,0}, {1,0,0,0}, {1,1,0,0}, {0,0,0,0} }, { {0,0,1,0}, {1,1,1,0}, {0,0,0,0}, {0,0,0,0} }, #endregion #region 反7字的4種狀態 { {1,1,0,0}, {1,0,0,0}, {1,0,0,0}, {0,0,0,0} }, { {1,0,0,0}, {1,1,1,0}, {0,0,0,0}, {0,0,0,0} }, { {0,1,0,0}, {0,1,0,0}, {1,1,0,0}, {0,0,0,0} }, { {1,1,1,0}, {0,0,1,0}, {0,0,0,0}, {0,0,0,0} }, #endregion #region Z字的4種狀態 { {1,1,0,0}, {0,1,1,0}, {0,0,0,0}, {0,0,0,0} }, { {0,1,0,0}, {1,1,0,0}, {1,0,0,0}, {0,0,0,0} }, { {1,1,0,0}, {0,1,1,0}, {0,0,0,0}, {0,0,0,0} }, { {0,1,0,0}, {1,1,0,0}, {1,0,0,0}, {0,0,0,0} }, #endregion #region 反Z字的4種狀態 { {0,1,1,0}, {1,1,0,0}, {0,0,0,0}, {0,0,0,0} }, { {1,0,0,0}, {1,1,0,0}, {0,1,0,0}, {0,0,0,0} }, { {0,1,1,0}, {1,1,0,0}, {0,0,0,0}, {0,0,0,0} }, { {1,0,0,0}, {1,1,0,0}, {0,1,0,0}, {0,0,0,0} }, #endregion }; #endregion /*當前操作的block塊的內部組成*/ public int[,] CurrentBlock = new int[4, 4]; //當前操作的block起點在整個數組中的位置,起點在左上方 public int currentX = -4; public int currentY = 5; //當前方格數、方格狀態數 public int CellNum = 0; public int CellState = 0; //一個格子的大小 public int CellSize = 15; //在寬度、高度方向上格子數目 public int CellWidthCount; public int CellHeightCount; //地區寬度、高度 public int Width; public int Height; #endregion /*初始化*/ public void InitErLuoSi() { RushData = new int[CellHeightCount, CellWidthCount]; Width = CellWidthCount * (CellSize + 1) + 1; Height = CellHeightCount * (CellSize + 1) + 1; elsStartFlag = true; //遊戲狀態設定為開始 } /*畫遊戲圖*/ public void ErLuoSiDraw() { Image myImage = new Bitmap(Width, Height); Graphics g = Graphics.FromImage(myImage); g.FillRectangle(new SolidBrush(Color.Gray), 0, 0, Width, Height); //寬度方向上走,畫豎線 for (int i = 0; i <= CellWidthCount; i++) { g.DrawLine(new Pen(Color.Black), (CellSize+1) * i, 0, (CellSize+1) * i, Height); } //高度方向上走,畫橫線 for (int i = 0; i <= CellHeightCount; i++) { g.DrawLine(new Pen(Color.Black), 0, (CellSize + 1) * i, Width, (CellSize + 1) * i); } SolidBrush brush = new SolidBrush(Color.Gray); //預設灰色背景畫刷 FontFamily fontFamily = new FontFamily("Arial"); Font font = new Font(fontFamily, 12, FontStyle.Regular, GraphicsUnit.Pixel); string strTemp = ""; //預設需要寫的字 int fontLeft = CellSize/5; //畫所有方格 for (int i = 0; i < CellHeightCount; i++) { for (int j = 0; j < CellWidthCount; j++) { brush = new SolidBrush(Color.Gray); //灰色背景畫刷 if (RushData[i, j] == 1) brush = new SolidBrush(Color.Gold); //金色背景畫刷 strTemp = RushData[i, j].ToString(); g.FillRectangle(brush, (CellSize + 1) * j + 1, (CellSize + 1) * i + 1, CellSize, CellSize); g.DrawString(strTemp, font, new SolidBrush(Color.Black), (CellSize + 1) * j + fontLeft, (CellSize + 1) * i + fontLeft); } } //畫當前移動的方格 for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (currentX + i < 0) continue; brush = new SolidBrush(Color.Gray); //預設灰色背景畫刷 strTemp = CurrentBlock[i, j].ToString(); if (CurrentBlock[i, j] == 1) { brush = new SolidBrush(Color.Gold); //金色背景畫刷 } if (currentX + i >= 0 && currentX + i <= CellHeightCount - 1 && currentY+j >= 0 && currentY + j <= CellWidthCount - 1 && RushData[currentX+i,currentY+j]==1) { brush = new SolidBrush(Color.Gold); //金色背景畫刷 strTemp = "1"; } g.FillRectangle(brush, (CellSize + 1) * (currentY + j) + 1, (CellSize + 1) * (currentX + i) + 1, CellSize, CellSize); g.DrawString(strTemp, font, new SolidBrush(Color.Black), (CellSize + 1) * (currentY + j) + fontLeft, (CellSize + 1) * (currentX + i) + fontLeft); } } Graphics gg = Graphics.FromHwnd(winHandle); gg.DrawImage(myImage, 0, 0); } /*隨機產生當前的block*/ public void GetInitBlockData() { Random ran = new Random(); CellNum = ran.Next(0, 6); CellState = ran.Next(0, 4); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { CurrentBlock[i, j] = Blocks[CellNum * 4 + CellState, i, j]; } } } /*是否可以向下移動*/ public bool CanDown() { for(int i=3; i>=0; i--) { for (int j = 0; j <4 ; j++) { //小方格內為1 則需要判斷是否可以向下 if (CurrentBlock[i, j] == 1) { if (currentX + i + 1 >= CellHeightCount) { return false; //不能向下移動了 } else if (currentX + i + 1 >= 0 && currentX + i + 1 < CellHeightCount && currentY + j >= 0 && currentY + j < CellWidthCount && RushData[currentX + i + 1, currentY + j] == 1) { return false; //不能向下移動了 } } } } return true; } /*是否可以向左移動*/ public bool CanLeft() { for (int i = 0; i <4 ; i++) { for (int j = 0; j < 4; j++) { if (CurrentBlock[ i, j] == 1) { if( currentY + j - 1 < 0) { return false; //不能向下移動了 } else if (currentX + i >= 0 && currentX + i < CellHeightCount && currentY + j - 1 >= 0 && currentY + j - 1 < CellWidthCount && RushData[currentX + i , currentY + j- 1] == 1) { return false; //不能向下移動了 } } } } return true; } /*是否可以向右移動*/ public bool CanRight() { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (CurrentBlock[i, j] == 1) { if (currentY + j + 1 >= CellWidthCount) { return false; //不能向下移動了 } else if (currentX + i >= 0 && currentX + i < CellHeightCount && currentY + j + 1 >= 0 && currentY + j + 1 < CellWidthCount && RushData[currentX + i , currentY + j + 1] == 1) { return false; //不能向下移動了 } } } } return true; } /*是否可以旋轉*/ public bool CanChange() { CellState = (CellState + 1) % 4; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { //主要看為1的方格旋轉後的位置是否衝突 if (Blocks[CellNum * 4 + CellState, i, j] == 1) { //如果在範圍內 if (currentX + i >= 0 && currentX + i < CellWidthCount && currentY + j >= 0 && currentY + j < CellHeightCount) { if (RushData[i, j] == 1) return false; } //如果不在範圍內 else { return false; } } } } return true; } /*旋轉當前的方格*/ public void ChangeBlock() { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { CurrentBlock[i, j] = Blocks[CellNum * 4 + CellState, i, j]; } } } /*置數組:將已經不能再移動的block中的值放入背景數組中 */ public void SetData() { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (CurrentBlock[i, j] == 1) { if (currentX + i >= 0 && currentX + i < CellHeightCount && currentY + j >= 0 && currentY + j < CellWidthCount ) RushData[currentX + i,currentY + j] = 1; } } } } /*檢查行滿,並銷毀行、計分*/ public void CheckAndDeleteLines() { int lines = 0; //行數 用於計分 int[] linesFlag = new int[CellHeightCount]; //記錄每行是否填滿 //尋找滿的行 for (int i = CellHeightCount-1; i >=0 ; i--) { int count = 0; //本行滿的數目 for (int j = CellWidthCount-1; j >=0 ; j--) { if (RushData[i, j] == 1) { count++; } } if (count == CellWidthCount) { linesFlag[i] = 1; lines++; } } //移動行 for (int i = 0; i < CellHeightCount; i++) { //如果本行滿 if (linesFlag[i] == 1) { //從最大序號的行開始移動 for (int j = i; j >0 ; j--) { for (int k = 0; k < CellWidthCount; k++) { RushData[j, k] = RushData[j - 1, k]; } } for (int k = 0; k < CellWidthCount; k++) { RushData[0, k] = 0; } } } } //檢查是否到頂 public bool CheckToTop() { //列迴圈 for (int j = 0; j < CellWidthCount; j++) { if (RushData[0, j] == 1 && currentX < 0) return true; //到頂 } return false; } }