扫雷游戏制作过程(C#描述):第三节、雷区绘制

前言

这里给出教程原文地址
该项目已经放在github上托管。

绘制雷区

这一节我们主要涉及界面中雷区的绘制。绘制雷区需要三个变量来保存雷区行数、列数、以及地雷的数量。而且我们希望能够自动获取上次游戏的设置(初级,中级,高级,雷区的三个变量值不同)。因此这三个变量的值需要保存下来。我们在这里采用Setting文件来保存这些数据。工程创建的时候,系统会自动生成一个Setting文件。因此我们不需要自己创建,只需要使用原有的Setting文件即可。在右方解决方案资源管理器面板中展开Properties,右击Settings.settings,选择打开即可。

按照下图对它进行设置,设置完成后按Ctrl + S进行保存。

我们需要一个paint事件来进行绘制雷区,选中主窗口,在左边的属性面板中,单击事件按钮,并找到Paint事件,双击该条目,系统会自动创建一个事件,我们将在这里绘制雷区。如下图所示:

绘制雷区时我们需要考虑以下几点:

  • 我们需要定义三个基本变量行数、列数、地雷的数量,并初始化。
  • 我们需要一个二重的循环来绘制雷区,我们假定雷区为32×32的小方块,并且四周有一圈宽度为1的留白,用于与其它雷区区别,这样,每个雷区的实际大小为34×34。
  • 整个雷区距离上下左右边缘都应该有个间距,所以需要一个偏移量。
  • 我们需要Form窗口自动调整大小,来适应这个雷区的绘制。
private int Sweep_width;      //雷区中的列数
private int Sweep_high;       //雷区中的行数
private int Sweep_num;        //雷区中的地雷数量
private int nOffsetX;         //雷区绘制时的偏移量,距离窗口左边缘的距离
private int nOffsetY;         //雷区绘制时的偏移量,距离窗口上边缘的距离

public Form_Main()
{
      //初始化操作
      InitializeComponent();
      nOffsetX = 6;                                   //初始化偏移量
      nOffsetY = 6 + UpMenu.Height;                   //初始化偏移量,UpMenu是菜单栏控件
      Sweep_num = Properties.Settings.Default.Sweep_num;      //初始化,从Settings读取地雷数量
      Sweep_high = Properties.Settings.Default.Sweep_high;    //初始化,从Settings读取行数
      Sweep_width = Properties.Settings.Default.Sweep_width;  //初始化,从Settings读取列数
      UpdateSize(Sweep_width,Sweep_high);                     //自适应窗口大小
}
private void Form_Main_Paint(object sender, PaintEventArgs e)
{
      Graphics g = e.Graphics;                                //绘制句柄
      for (int i = 0; i < Sweep_width; i++)
      {
           for (int j = 0; j < Sweep_high; j++)
           {
                g.FillRectangle(Brushes.Violet, new Rectangle(nOffsetX + 34 * i, nOffsetY + 34 * j + 1, 32, 32)); //绘制每一个小方块
           }
      }
}
private void UpdateSize(int width_temp, int high_temp)
{
      //根据雷区行数,列数,来设置整个Form窗口的大小
      int width_update = width_temp * 34 + 12;     
      int high_update = high_temp * 34 + 12;
      Width = width_update + (this.Size.Width - this.ClientSize.Width);
      Height = high_update + UpMenu.Height + TableLayoutPanel_Main.Height + (this.Size.Height - this.ClientSize.Height);
}                

最后按Ctrl + F5编译运行,得到最终结果:

下面我们为了使鼠标移动到雷区上时,能有高亮的效果做一些修改。首先我们需要能找到鼠标当前所在的位置,因此我们需要MouseMove事件,找到MouseMove事件,双击该条目:

我们需要定义新的变量Point来方便我们标记鼠标的位置,鼠标的每次移动并不是都要重新刷新界面,当鼠标从某个32x32的小雷区移动到另一个32x32的小雷区时,我们就需要更改高亮的位置。我们需要采集的是当前鼠标处于哪一个32x32的小雷区,代码如下:
应增加变量:

 private Point mousefocus_new; //鼠标新位置
 private Point mousefocus_old; //鼠标旧位置

应对这两个变量初始化,在public Form_Sweeper(){ }中增加如下代码:

mousefocus_new.X = mousefocus_old.X = 0;            //初始化鼠标位置
mousefocus_new.Y = mousefocus_old.Y = 0;

记录鼠标位置,其中变量mousefocus_new是此刻位置、moursefocus_old是上一时刻位置,两者进行对比,来判断当前鼠标处于的32x32的小雷区是否发生改变。代码如下:

private void Form_Main_MouseMove(object sender, MouseEventArgs e)
{
	//x,y相当于雷区二维数组中的第几行,第几列。
	int x = (e.X - nOffsetX) / 34 + 1;
	int y = (e.Y - nOffsetY) / 34 + 1;
	mousefocus_new.X = x;
	mousefocus_new.Y = y;
	if (e.X < nOffsetX || e.Y < nOffsetY)
	{
		//鼠标位置不在雷区时
		mousefocus_new.X = mousefocus_new.Y = -1;
		Refresh();
	}
	else if (mousefocus_new != mousefocus_old)
	{
		mousefocus_old = mousefocus_new;
		Refresh();
	}
}

Paint事件应做修改,代码如下:

for (int i = 0; i < Sweep_width; i++)
{
	for (int j = 0; j < Sweep_high; j++)
	{
		if (i + 1 == mousefocus_new.X && j + 1 == mousefocus_new.Y)
		{
			g.FillRectangle(new SolidBrush(Color.FromArgb(100, Color.Violet)), new Rectangle(nOffsetX + 34 * i, nOffsetY + 34 * j + 1, 32, 32));  //产生高亮
		}
		else
		{
			g.FillRectangle(Brushes.Violet, new Rectangle(nOffsetX + 34 * i, nOffsetY + 34 * j + 1, 32, 32));
		}
	}
}

此时,按Ctrl + F5编译运行,发现屏幕会闪屏,在初始化模块增加如下代码:

this.DoubleBuffered = true;                         //双缓冲技术,减少屏幕闪屏

最后按Ctrl + F5编译运行,得到最终结果:

原文地址:https://www.cnblogs.com/pengpeng123/p/7443270.html