windows程序设计(3):分解扫雷程序之雷区翻盖

假如让你写一个扫雷程序,在没有相关资料的前提下,应该从哪里入手呢?

我们先想想扫雷程序都有什么:菜单,秒表,记雷数,复位,还有雷区等杂七杂八的东西。而整个程序的关键,就在于雷区。因为如果没有秒表,记雷数等功能,扫雷勉强还是可以玩的,但如果没了雷区,就玩不了了。那么雷区又是怎么一回事呢?肯定要有一个数据结构来记录地雷和周围的数,而程序跟你交互的主要部分,就是在雷区上点一下,然后显示一幅图片,还有双击雷区翻开周围一片的操作。说白了,就是在画图。所以整个扫雷程序,就从画图先开始吧。

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HBITMAP hBitmap;
	static int cxClient,cyClient,cxSource,cySource;
	//位图类结构,包含位图的信息
	BITMAP bitmap;
	//设备内容句柄
	HDC hdc,hdcMem;
	//这条消息的实例句柄,因为只在WM_CREATE消息中获得,所以是创建的窗口的实例句柄
	HINSTANCE hInstance;
	int x,y,mx,my;
	//PAINTSTRUCT结构包含了绘制客户区域的信息,第一个参数就是HDC类的
	PAINTSTRUCT ps;


	//控制距离的变量
	int dstx = 100;
	int dsty = 100;
	switch(message)
	{
	//创建窗口消息:第一个消息
	case WM_CREATE:
		//LPCREATESTRUCT是一个指向结构CREATESTRUCT的指针
		//对lParam强制类型转换以后指向结构体,结构体中的hInstance
		hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
		//装载位图:参数为实例句柄,位图,返回值为位图句柄
		//虽然也可以使用位图名称来装载位图,但是通常都是用序号
		hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE  (IDB_BITMAP2));
		//从指定的图形对象中获取信息
		//参数为:图形句柄,对象信息缓冲区的大小,对象信息缓冲区
		GetObject(hBitmap,sizeof(BITMAP),&bitmap);
		//一幅位图的宽
		cxSource = bitmap.bmWidth;
		//一幅位图的高:整个位图由16种不同的图形组成,每幅图形应为整个位图的16分之1
		cySource = bitmap.bmHeight/16;

		return 0;
	//改变窗口大小消息
	case WM_SIZE:
		//32位值的高低位代表客户区的长宽
		//不同的消息间需要保留,所以用的是static
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		return 0;
	//绘制窗口消息
	case WM_PAINT:
		//BeginPaint函数准备画图,向PAINTSTRUCT结构中填充信息
		//参数为:窗口句柄、指向PAINTSTRUCT结构的画图信息,返回值为一个HDC
		hdc = BeginPaint (hwnd, &ps) ;
		//创建内存的设备环境,返回值是一个内存设备环境的句柄
		hdcMem = CreateCompatibleDC(hdc);
		//将一个对象选择到设备环境中,参数为:设备环境句柄,对象句柄
		SelectObject(hdcMem,hBitmap);


		for(y = dsty; y < cyClient-dsty;y += cySource)
		{
			for(x = dstx; x < cxClient-dstx;x += cxSource)
			{
				//贴图函数,参数为:目标DC句柄,贴图点的左上角的x坐标,贴图点的左上角的y坐标,目标和源的长,目标和圆的宽,源DC句柄,源的左上角x坐标,源的左上角y坐标,光栅操作代码(这里是直接从源矩形复制到目标矩形)
				BitBlt(hdc,x,y,cxSource,cySource,hdcMem,0,0,SRCCOPY);
				
			}
		}
		DeleteDC(hdcMem);
		//绘制窗口结束
		EndPaint (hwnd, &ps) ; 
		return 0;


注意到,使用WM_SIZE消息获取了客户区的大小。在WM_PAINT消息的二重循环中,通过它来改变贴多少方砖。

那么如何实现鼠标点一下就翻开呢?

	case WM_LBUTTONDOWN:
		//左键会显示雷区被翻开的效果
		hdc = GetDC(hwnd);
		hdcMem =  CreateCompatibleDC(hdc);
		SelectObject(hdcMem,hBitmap);


		 //使得鼠标点到哪里,就选择那里的雷区,并画一幅翻开的图片
		//鼠标点到雷区外,不起作用
		//鼠标点到雷区里,用这个方块的左上角代替贴图的位置
		//鼠标的位置:
		 mx = (LOWORD(lParam));
		 my = (HIWORD(lParam));

		 //点到雷区里面才有效,雷区的位置:离上下dsty,左右dstx
		 if(mx>dstx && 	mx<cxClient-dstx && my>dsty && my<cyClient-dsty	)
		 {
			//将鼠标的位置改为这个点的对应的雷区的方砖的左上角
			//结果总是在第一块砖上
			mx = ((int)(mx-dstx)/cxSource)*cxSource+dstx;
			my = ((int)(my-dsty)/cySource)*cySource+dsty;
			//贴图函数,参数为:目标DC句柄,贴图点左上角的x坐标,贴图点左上角的y坐标,目标矩形的长,目标矩形的宽,源DC句柄,源的左上角x坐标,源的左上角y坐标,光栅操作代码(这里是直接从源矩形复制到目标矩形)
			BitBlt(hdc,mx,my,cxSource,cySource,hdcMem,0,cySource*15,SRCCOPY);	
		 
		 }					 		
		DeleteDC(hdcMem);
		ReleaseDC(hwnd,hdc);
		return 0;
 //关闭窗口消息
 	case WM_DESTROY:
  		PostQuitMessage (0) ;
  		return 0;
 
 }
 	return DefWindowProc (hwnd, message, wParam, lParam) ; //执行缺省消息
}


就是鼠标点一下,将鼠标点的位置换成它属于的方砖的位置,然后贴上另一种方砖即可。位置的转化原理其实很简单,就是利用C语言中int类型数据整除的性质,举个例子:

如果一块方砖的大小为16*16,你鼠标的位置为(30,50),那么它所属的方砖的左上角,肯定是在30/16 =1, 50/16 = 3就是第一列,第三行的那里,具体的像素就是(1*16,3*16)。

 

你可能感兴趣的:(数据结构,c,windows,语言,callback,图形)