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