c#俄罗斯方块游戏
一、设计思路
一个背景数组,一个方块数组。将方块数组放入背景数组时,需要一个坐标进行定位,即在两个坐标系之间存在一个映射关系。
二、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; } }