转载请标明是引用于 http://blog.csdn.net/chenyujing1234
例子代码:
n
游戏在 6× 6 格子的棋盘中进行,可排出55种不同的组合图案。主要开发人的抽象思维能力、空间想象能力、动手能力、几何构建能力。游戏运行时功能如下。
n
(1)实现用鼠标拖动拼块,拼块任意位置摆放。
n
(2)绕拼块的中心点旋转(旋转由鼠标右键操作实现)。
n
(3)拼块水平翻转(由鼠标双击操作实现)
n百变方块游戏效果如图22-1所示。用户拖动棋盘周围的8种拼块到棋盘中,直到棋盘所有空白方块格子被填满则此关游戏胜利。单击“新方块图案”按钮则进入下一关游戏。如果玩家无法完成则可以单击“参考答案”按钮查看参考拼图方案。
地图信息存储
n
地图信息采用文本文件map.txt存储保存。根据目标图案按列存放,每关占一行。0代表固定不变的绿色填充方格,1代表蓝色填充的方格(即需要用户的8种拼块填充的方格)。
1,1,1,1,1,1
,
1,1,1,1,1,1
,
0,0,0,0,0,1
,
0,1,0,1,0,1
,
1,1,1,1,1,1
,
1,1,1,1,1,1
,
1,1,1,1,1,1,1,1,0,1,0,1,1,0,0,0,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0,1,1,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
n
游戏在 6× 6 格子的棋盘中进行,每关开始时从文本文件map.txt读取相应关所对应行的字符串,
分割后将数据按列将目标图案存储到二维数组
OrgMap[6,6]
,
而用户移动拼块后的图案按列存储到二维数组Map[6,6]中。通过对比两个数组知道是否成功完成此关。
1.拼块类(CChip.cs)
字段
m_nType
存储拼块的类型代号,总计有7个拼块。分别用
1
—
8
代表图22-3的七个拼块。
m_nPointCount
存储拼块的顶点个数,
m_pointList
存储拼块的顶点坐标。
myPath
是形成拼块的路径。
class CChip
{
Point []m_pointList; //顶点坐标
int m_nPointCount; //顶点个数
int m_nType; //类型代号
private GraphicsPath myPath;
…….
}
拼块的参数设置方法SetChip
拼块类提供对拼块的参数设置方法SetChip,该方法完成拼块类型代号的设置,拼块图案顶点坐标初始化,最终形成拼块的路径。
public void SetChip(int type, Point []ppointlist, int count)
{
m_nType = type;
m_nPointCount = count;
m_pointList = new Point[m_nPointCount];
for(int i=0; i<count; i++)
m_pointList[i] = ppointlist[i];
myPath = new GraphicsPath();
myPath.AddLines(m_pointList);
myPath.CloseFigure();//CloseFigure方法闭合当前图形并开始新图形。
}
拼块的平移
Move(int x_offset, int y_offset)应用Matrix实现GraphicsPath路径的平移。
public void Move(int x_offset, int y_offset)
{
Matrix matrix = new Matrix();
matrix.Translate(x_offset, y_offset); //追加平移
myPath.Transform(matrix);//应用变形
myPath.CloseFigure();
}
Move2(int x_offset, int y_offset)不用Matrix实现GraphicsPath路径的平移,而是直接对路径的每个顶点一一平移。
public void Move2(int x_offset, int y_offset)
{
myPath.Reset(); //清空路径
for (int i = 0; i < m_nPointCount; i++) // 平移各顶点
{
m_pointList[i].X += x_offset;
m_pointList[i].Y += y_offset;
}
myPath.AddLines(m_pointList);
myPath.CloseFigure();
}
拼块的旋转
Rotation()应用Matrix实现GraphicsPath路径的旋转,每次旋转45度。
public void Rotation()
{
Matrix matrix = new Matrix();
RectangleF rect = new RectangleF();
rect = myPath.GetBounds();
// 计算旋转中心坐标(x,y)
double x = rect.Left + rect.Width / 2;
double y = rect.Top + rect.Height / 2;
//matrix.Rotate(45.0f); //旋转顺时针45度
matrix.RotateAt(45.0f, new Point((int)x, (int)y)); //旋转顺时针45度
}
拼块的旋转(2)
Rotation2()不用Matrix实现GraphicsPath路径的旋转,而是获取myPath的矩形区域,计算旋转中心,从而计算出每个顶点的新坐标。
public void Rotation2()
{
RectangleF rect=new RectangleF() ;
rect=myPath.GetBounds();
myPath.Reset(); //清空路径
double x = rect.Left + rect.Width/2; // 计算旋转中心
double y = rect.Top + rect.Height/ 2;
double dx, dy;
for (int i = 0; i < m_nPointCount; i++) // 旋转各顶点
{
dx = m_pointList[i].X - x;
dy = m_pointList[i].Y - y;
m_pointList[i].X = (int)(x + dx * 0.7071 - dy * 0.7071);
m_pointList[i].Y = (int)(y + dx * 0.7071 + dy * 0.7071);
}
myPath.AddLines(m_pointList);
myPath.CloseFigure();
}
拼块水平反转
ReverseTurn()获取myPath的矩形区域,计算旋转中心,从而计算出水平翻转后每个顶点的新坐标。
public void ReverseTurn() // 水平反转
{
RectangleF rect = new RectangleF();
rect = myPath.GetBounds();
myPath.Reset();
double x = rect.Left + rect.Width / 2; // 计算旋转中心
double y = rect.Top + rect.Height / 2;
for (int i = 0; i < m_nPointCount; i++) // 水平反转各点
{
m_pointList[i].X = (int)(2 * x - m_pointList[i].X);
m_pointList[i].Y = m_pointList[i].Y;
}
myPath.AddLines(m_pointList);
myPath.CloseFigure();
}
DrawChip( )
在
Graphics
对象上画出拼块。每个拼块设置不同的填充颜色。
public void DrawChip(Graphics g)//画拼块
{
Pen myPen = new Pen(Color.Black, 1);
g.DrawPath(myPen, myPath);
int alpha = 140; //透明度
Color c= Color.FromArgb(alpha, 255, 255, 255);
switch (m_nType)
{
case 1:
c = Color.FromArgb(alpha, 127, 127, 127);
break;
case 2:
c = Color.FromArgb(alpha, 255, 0, 0);
break;
case 3:
c = Color.FromArgb(alpha, 200, 255, 0);
break;
.......
case 8:
c = Color.FromArgb(alpha, 228, 128, 128);
break;
}
SolidBrush brushNew = new SolidBrush(c);
g.FillPath(brushNew, myPath);
}
对用户移动的拼块进行位置校正
Verity(int CHIP_WIDTH)对用户移动的拼块进行位置校正,方法是判断拼块的第一个顶点坐标m_pointList[0]接近棋盘中那个方格,则计算出偏移量后,对拼块的所有顶点进行修正,保证拼块移动到适当的方格位置处。
public void Verity(int CHIP_WIDTH)
{
myPath.Reset(); //清空路径
int x_offset=0, y_offset=0;
int d;
d=m_pointList[0].X % CHIP_WIDTH;
if (d != 0)
x_offset -= (d < CHIP_WIDTH / 2 ? d:d- CHIP_WIDTH);
d = m_pointList[0].Y % CHIP_WIDTH;
if (d != 0)
y_offset -= (d < CHIP_WIDTH / 2 ? d:d- CHIP_WIDTH);
for (int i = 0; i < m_nPointCount; i++) // 平移各点
{
m_pointList[i].X += x_offset;
m_pointList[i].Y += y_offset;
}
myPath.AddLines(m_pointList);
myPath.CloseFigure();
}
2.设计窗体类(Form1.cs)
窗体加载事件
int [,]Map=new int[6,6];
int[,] OrgMap = new int[6, 6];//初始化目标地图OrgMap
窗体加载事件中,调用Reset()对8个拼块的顶点坐标m_chipList 数组初始化。调用ReadOrgMap(1)方法读出第一关目标图案的地图信息到OrgMap二维数组中。
private void Form1_Load(object sender, EventArgs e)
{
Reset();// 初始化拼图块
//以下两句是为了设置控件样式为双缓冲,这可以有效减少闪烁的问题
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
this.UpdateStyles();
ReadOrgMap(1); //读出第一关目标图案的地图信息到OrgMap二维数组中
Draw_AllChip();//画出所有拼块
}
初始化拼块
Reset()初始化拼图块顶点坐标m_chipList 数组,并形成拼块的图形路径。
private void Reset()
{
for (int i = 0; i < CHIP_COUNT; i++)
m_chipList[i] = new CChip();
Point[] pointList = new Point[MAX_POINTS];
int sx=250, sy=0;
pointList[0] = new Point(sx , sy );
pointList[1] = new Point(sx + 2* CHIP_WIDTH, sy );
pointList[2] = new Point(sx + 2* CHIP_WIDTH, sy + 2 * CHIP_WIDTH);
pointList[3] = new Point(sx , sy + 2 * CHIP_WIDTH);
m_chipList[0].SetChip(1, pointList, 4); //m_chipList[0]为一个2W*2W的正方形
sx =170 ; sy = 90;
pointList[0] = new Point(sx + CHIP_WIDTH * 2, sy);
pointList[1] = new Point(sx + CHIP_WIDTH * 6, sy);
pointList[2] = new Point(sx + CHIP_WIDTH * 6, sy + CHIP_WIDTH);
pointList[3] = new Point(sx + CHIP_WIDTH * 2, sy + CHIP_WIDTH);
m_chipList[1].SetChip(2, pointList, 4); //m_chipList[1]为一个4W*1W的长方形
……
}
读出第n关的地图信息
ReadOrgMap()方法从map.txt文本文件按行读出第n关目标图案的地图信息到OrgMap二维数组中。
private void ReadOrgMap(int n)
{
string filename = "map.txt";
FileStream fs = File.OpenRead(filename);
StreamReader sr = new StreamReader(fs, Encoding.Default);
string t = null;
while (n > 0)
{
t = sr.ReadLine();
n--;
}
sr.Close();
fs.Close();
string [] a = new string[36 + 1];
a = t.Split(',');
for (int i = 1; i < 7; i++)
for (int j = 1; j < 7; j++)
OrgMap[i-1, j-1]=Convert.ToInt16(a[(i-1)*6+j-1]);
}
“新方块图案”按钮单击事件
计算出新图案序号n,调用Reset()对8个拼块m_chipList 数组初始化并调用Draw_AllChip()画出所有8个拼块。
private void button1_Click(object sender, EventArgs e)//“新图案”按钮
{
n++;
if (n > max)
{
MessageBox.Show("没有新图案了");
n--;
return;
}
ReadOrgMap(n);//读目标地图OrgMap文件
Map = new int[6, 6];
Reset(); // 初始化拼图块
Draw_AllChip(); //画出所有拼块
}
Draw_AllChip()画出所有拼块
Draw_AllChip()根据新图案序号n缩半画出640*480的目标图案,通过循环调用拼块类Cchip. DrawChip()
方法画出所有拼块。
private void Draw_AllChip() ///画出所有拼块及目标图案
{
Bitmap bmp = new Bitmap(this.Width, this.Height);
this.BackgroundImage = bmp;
//Graphics g = this.CreateGraphics();
Graphics g = Graphics.FromImage(bmp);
g.Clear(this.BackColor);
string r=n.ToString()+".jpg";
Bitmap s = (Bitmap)Image.FromFile(r);
//g.DrawImage(s,new Point(400, 0));
g.DrawImage(s, new Rectangle(450, 10, 320, 240),
new Rectangle(0, 0, 640, 480),GraphicsUnit.Pixel); //在(450,10)处显示320*240图案
for (int i = 0; i < CHIP_COUNT; i++)
m_chipList[i].DrawChip(g);
}
窗体鼠标按下事件
在窗体鼠标按下事件中,首先获取鼠标坐标Point(e.X, e.Y),判断是否在某拼块区域中,如果在记录用户选中的拼块序号。并通过e.Button判断用户是否右键单击,如果右键单击则将用户选中的拼块顺时针旋转90度,重画所有拼块。
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
Point p = new Point(e.X, e.Y);
for (int i = 0; i < CHIP_COUNT; i++)
if (m_chipList[i].PtInChip(p) == true)
{
m_nCurrIndex = i; //记录用户选中的拼块
Drag_PictBox = true; //移动标志赋值为真
oldx = e.X;
oldy = e.Y;
break;
}
if (e.Button == MouseButtons.Right) //右键旋转
{
m_chipList[m_nCurrIndex].Rotation2(); //旋转顺时针45度
m_chipList[m_nCurrIndex].Rotation2(); //旋转顺时针45度
Draw_AllChip();//画出所有拼块
}
}
窗体鼠标移动事件
在窗体鼠标移动中,首先获取鼠标坐标Point(e.X, e.Y),显示在窗体上,然后计算偏移量传给拼块类Cchip.Move2方法,改变用户选中拼块m_nCurrIndex的各点坐标,重画所有拼块。
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
Point p = new Point(e.X, e.Y);
label1.Text = p.ToString();
if (Drag_PictBox == true)
{
Cursor.Current = Cursors.Hand;
m_chipList[m_nCurrIndex].Move2(e.X - oldx, e.Y - oldy); //移动
Draw_AllChip(); //画出所有拼块
}
oldx = e.X;
oldy = e.Y;
}
窗体鼠标松开事件
在窗体鼠标松开事件中,对用户移动的m_nCurrIndex拼块进行位置校正,保证移动到适当的方格位置处。并判断游戏是否成功。
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
Drag_PictBox = false;
//对用户选中的m_nCurrIndex拼块坐标进行修正,放置到正确位置
m_chipList[m_nCurrIndex].Verity(CHIP_WIDTH);
Draw_AllChip(); //画出所有拼块
if(Win()) MessageBox.Show("成功完成此关") ; //判断是否成功
}
窗体的鼠标双击事件
在窗体的鼠标双击事件中,对相应拼块进行水平翻转。
private void Form1_MouseDoubleClick(object sender, MouseEventArgs e)
{
Point p = new Point(e.X, e.Y);
for (int i = 0; i < CHIP_COUNT; i++)
if (m_chipList[i].PtInChip(p) == true)
{
m_nCurrIndex = i; //记录用户选中的拼块
Drag_PictBox = true;
oldx = e.X;
oldy = e.Y;
break;
}
m_chipList[m_nCurrIndex].ReverseTurn(); 水平反转
Draw_AllChip();//画出所有拼块
}
窗体重绘事件
在窗体重绘事件中,绘制棋盘和本关已固定的绿色方块。
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics gp = e.Graphics;
SolidBrush myBrush = new SolidBrush(Color.Green);
for (int i = 0; i < 6; i++) //绘制本关已固定的绿色方块
for (int j = 0; j < 6; j++) {
if(OrgMap[i,j]==0)
gp.FillRectangle(myBrush, i * CHIP_WIDTH,
j * CHIP_WIDTH, CHIP_WIDTH, CHIP_WIDTH);
}
Pen p = new Pen(Color.Brown, 1);
for (int i = 1; i < 7; i++) //绘制棋盘
for (int j = 1; j < 7; j++) {
gp.DrawLine(p, 1, j * CHIP_WIDTH, CHIP_WIDTH*6, j * CHIP_WIDTH);
gp.DrawLine(p, i * CHIP_WIDTH, 1, i * CHIP_WIDTH, CHIP_WIDTH * 6);
}
}