井子游戏解题报告(B11040723).doc
实 验 报 告( 2011/2012 学年 第 1 学期)课程名称Windows高级软件开发实验名称井子游戏实验时间2012年12月12日指导单位计算机学院软件工程系指导教师王子元学生姓名高阳阳班级学号B11040723学院(系)计算机学院专 业软件工程一、实验名称: 井子游戏二、实验要求: 练习相应鼠标消息,练习鼠标的绘制,谁先连成三个在一条直线上谁就算赢.三、实验目的:通过此次试验,可以让同学们更深入的了解windows的内部机制,熟悉API函数的应用,鼠标响应的机制等。四、实验环境(设备)安装有VC6.0或VC2008版本的PC机。五、概要设计 在这里,我主要以文字语言叙述我的整体的思路,在详细的代码设计里,我会给出自己具体是怎样实现操作的。题目要求实现一个简单的井子游戏,主要是熟悉鼠标响应的操作。首先,我需要绘制基本的棋盘及棋子,借鉴QQ小游戏的五子棋的棋盘模式,我以咖啡色为背景色,并以黑色细线绘制棋盘的九个格子,同时,我利用黑色画刷和白色画刷,分别绘制了代表黑方的黑棋和代表白方的白棋。而在基本的棋盘和棋子设计思路完成后,就是具体的功能的实现。 在我们熟知的基本的棋类操作及功能中,主要的操作有:游戏开始的操作,悔棋操作,认输操作,重新开始的操作。主要的功能有每次游戏结束后相应的游戏进行的时间的显示。为了实现相应的操作及显示时间的功能,我利用绘图工具绘制了分别写有“重新开始” “认输” “悔棋”“开始”“时间”的bmp图片,并通过相应函数加载到窗口中,再通过鼠标相应,实现相应的功能。同时,我也利用资源文件,实现了“新游戏”“说明”“游戏指导”等基本的菜单功能。六、详细设计1、首先是对窗口显示区域中棋盘及相应按钮的基本的绘制。 在这里我利用GetSystemMetrics(SM_CXSCREEN)函数和GetSystemMetrics(SM_CYSCREEN)分别获取电脑屏幕的宽度和高度并将值分别赋给变量xSize,ySize,在利用CreatWindow()函数创建窗口时,我将窗口的起点坐标的x,y值分别赋值为(xSize-400)/2,(ySize-400)/2,并将窗口的宽度和高度分别设为600,400.同时在窗口风格上,我将窗口风格设置为WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX。这样就使得每一次运行时,窗口都能显示在屏幕的中央,并且防止了因窗口最大化而造成的棋盘的不美观。而为了绘制棋盘,我设置了变量DIVISIONS, 并利用宏定义将其值定义为3,在每次绘制棋盘前,我通过在WM_SIZE消息响应中利用变量cxBlock = LOWORD (lParam)/ (DIVISIONS+1) 获得棋盘中每个格子的宽度,cyBlock = HIWORD (lParam) / DIVISIONS 获得棋盘中每个格子的高度。在每次利用 WM_PAINT绘制棋盘时,我通过两层循环利用 Rectangle()函数绘制棋盘。Rectangle()函数是基本API函数,用来进行矩形的绘制。其第一个参数是当前设备语柄,其后四个参数被别为其绘制的矩形的左上角的x,y坐标和右下角的x,y坐标。2,、然后是按钮的显示。为了使界面看起来更加友好和人性化,同时也是避免因过多的按钮控键的利用而增加不必要的代码量,我选择了利用加载图片和鼠标点击相结合的功能实现模式。我定义了hdc类型的数组hMemDC5, BITMAP 类型的数组bitmap5以及HBITMAP类型的数组 hBitmap5,分别用于获得内存设备语柄,载入位图和将位图选入目标设备。3、 菜单的显示。 菜单的显示相对比较简单,我是通过在资源文件里编写菜单部分的代码,并通过在WNDCLASS窗口类的对象中定义 wndclass.lpszMenuName =L"MENU"实现将MENU作为窗口的缺省菜单。 4、图标的绘制及显示 在图标的绘制中,我利用了VC6.0自带的功能。在菜单栏中点击插入选项,然后在下拉菜单中点击资源,出现如下图所示的窗口。双击Icon即可进行图标的绘制。图标绘制好后,我在WNDCLASS窗口类的对象中通过wndclass.hIcon =LoadIcon(wndclass.hInstance,MAKEINTRESOURCE(IDI_ICON1);加载图标。 这样,基本的窗口显示就实现了。运行程序一个还没有实现功能的友好界面就出现了。如图: 然后就是具体功能的实现了。为了实现具体的功能,有几个重要的变量在这里强调一下。bool Huiqi =false;/判断是否进行悔棋操作,初始化为false;bool Start=false;/判断是否开始游戏int second=0;/记录游戏运行时间int WW=0,WW1=0,WW2=0;/分别记录赢家,悔棋过度的一方 ,一用来判 断施行哪一个对话框int huiqi1=0,huiqi2=0;/分别记录双方的悔棋的次数 int fStateDIVISIONSDIVISIONS ;/如果fStateij=1;则在对应的棋盘的格子里绘制黑棋,如果fStateij=2;则在对应的棋盘的格子里绘制白棋int X1=2000,Y1=2000;/用X1,Y1标记当点击(“悔棋”)按钮时的前一个位置int OK1,OK2;/标注哪一个获胜int C=0;/标注双击悔棋的操作是否是当前最后落棋的位置int count=0;/记录当前棋盘的棋子的个数6、 对单击鼠标左键的消息的响应 在我的所用功能的实现中,响应鼠标左键单击的功能最多。 根据鼠标在窗口的不同位置,通过单击鼠标左键,可以实现下棋,悔棋,认输,开始,重新开始等操作。 在下棋的单击操作中,当鼠标点击在当前的尚未下棋的棋盘的位置时,根据fState xy的值进行棋子的绘制。如果fState xy的值为1,则绘制黑色的棋子,如果fState xy的值为2,则进行白色棋子的绘制。绘制棋子的方法很简单,直接利用Ellipse(hdc,xLeft,yTop,xRight,yBottom)函数即可。 每下一个棋子,程序会根据当前棋盘的棋子的数目和位置判双方的输赢情况。每一方都有八种可以获胜的棋子的结合状态。 一旦双方获胜,会根据获胜的一方来 显示相应的对话框。在对话框中也会显示相应的时间。为了在对话框中显示时间,我用了编辑控件。每次弹出对话框时在对话框的初始化中将时间录入编辑控件。核心代码部分如下: wsprintf (tSecond,TEXT("%2d"), second);/将int型的数据转换为tchar输出 wcscat(tSecond,L"秒");/串连接函数 SetDlgItemText(hdlg,IDC_TITLE,tSecond);/录入时间到编辑框中获胜的对话框(以白方为例):当棋盘中棋子达到9个时,而双方却都没有获胜时,也会弹出相应的对话框。在程序运行时你可以看到。如图:关于悔棋的操作。 关于悔棋,我设置了两种悔棋方式。一种方式是直接双击左键进行悔棋操作,一种是通过点击棋盘右边的悔棋按钮进行悔棋操作,如图: 我实现悔棋操作的核心是通过响应 WM_LBUTTONDBLCLK消息实现的。当你没有通过双击棋子进行悔棋,而是通过点击悔棋按钮进行悔棋时,当点击悔棋按钮时,我通过函数SendMessage(hwnd,WM_LBUTTONDBLCLK,0,0);发送WM_LBUTTONDBLCLK消息进行悔棋操作。进行悔棋的操作很简单,对可以进行悔棋的操作,只需要将其对应的fState xy的值改为0.,并且重新进行窗口的绘制即可,这样在窗口的重新绘制时,fStatexy为0的格子就不会绘制棋子。而对悔棋次数大于等于2的悔棋操作则会视为无效,并弹出相应的对话框进行提示。具体实现可以参考我的代码。悔棋过度时弹出的对话框如图:关于认输操作.重新开始的操作开始操作的原理很简单。通过分别响应相应的鼠标消息即可。没有其他很难理解的部分,在这里就不多累赘。最后,是关于资源菜单的加载与利用。、我在资源文件里定义了如下几个菜单:MENU MENU DISCARDABLE BEGIN POPUP "游戏" BEGIN MENUITEM "新游戏(&N)", IDM_NEW MENUITEM "说明(&S)", IDM_SAY MENUITEM "退出(&X)", IDM_EXIT END POPUP "帮助" BEGIN MENUITEM "游戏指导(&O)", IDM_GUIDE ENDENDMYMENUACCEL ACCELERATORSBEGIN"N", IDM_NEW,ASCII,NOINVERT"S", IDM_SAY,ASCII,NOINVERT"X", IDM_EXIT,ASCII,NOINVERTEND并在menu.h文件里定义了其值。然后通过在WNDCLASS窗口类的对象中设置wndclass.lpszMenuName =L"MENU"实现将MENU作为窗口的缺省菜单。加载成功后,通过在WndProc消息响应函数里响应WM_COMMAND消息实现其操作。 关于对话框的设置。关于对话框的设置,我在这次实验中用的较少。主要是PUSHBUTTON、DEFPUSHBUTTON、CTEXT、LTEXT四种控件的利用。其中前两种是按钮控件,主要使用来判断是退出游戏还是继续游戏。后两种是编辑控件,CTEXT为静态的,主要是用开显示已经存在的文字内容,如游戏介绍,游戏提示等,而LTEXT为动态的编辑控件,主要是用来显示时间。关于时间怎样在编辑控件里显示,我在前面的文字里已经有所叙述,在这里就不多累赘。这样,一个基本实现菜单功能,开始,悔棋,认输等按钮功能的井子游戏就已经完成了。通过运行exe文件,我们可以得到一个友好的井子游戏的界面,在此界面上可以进行相应的游戏操作。七、 实验总结(心得)做此次大作业时,当时还没有讲MFC应用,所以基本上每一个代码都是我自己一个一个敲出来的。在编写的过程中不免遇到了更多的麻烦。从十一月初开始,基本上每天晚上自习都会编写一会,直到十一月二十九号才正式完成。在编写过程中遇到的麻烦很多,有语法上的,但更多的是因为对window编程的基本的内部机制和API函数及消息响应原理的不清楚而引起的。就拿双击鼠标进行悔棋的操作来说,起初我一直以为双击鼠标后只响应WM_LBUTTONDBLCLK消息,所以导致了在悔棋操作时总是出现本应该是白棋悔棋却总是将黑棋去掉,或本应该悔黑棋却总是再重绘时将白棋去掉的错误,最后我又认真看了一下书才明白进行双击操作时是先响应WM_LBUTTONDOWN消息后响应WM_LBUTTONDBLCLK消息的。在向对话框的编辑窗口输入数字时,因为不知道怎样将数字转化为字符串输出也纠结了好久,最后发现了wsprintf()函数可以很轻松的做到,心里高兴的很。而在实现位图的加载的过程中,因为起初不太明白怎样实现多个位图的载入(将多个位图载入一个窗口),导致每次加载完毕后,窗口中总是出现多个一样的按钮图片,后来我试着将内存环境语柄,位图语柄等分别设置为数组的形式,每次加载一个位图我都重新获得内存环境,最终解决了这个问题吧。在编写的过程中还遇到了许多其他的问题,在这里就不多叙述,但更多的是收获,通过此次实验,我更加深入的了解了API函数及window编程的内部机制。也更加喜欢上了编程本身。我会更加努力,再接再厉,在程序设计上,在自己的专业上越走越远。也谢谢老师的教导。加油!