手把手教“MFC版贪吃蛇教程”写在前面的话本次贪吃蛇教程主要知识点包括以下几个方面1CView类中的消息响应2控件的消息响应3基于CView类内的具体游戏实现4数组5游戏图形的实现用CDC类实现。本版游戏的具体实现是在CViewl类中实现,所以其他类不用添加任何代码。由于此次贪吃蛇需要MFC的开发环境,所以打开VisualC++新建一个MFCAppWizard单文档工程,随意取名一个工程名称。本次贪吃蛇基本流程与大体思想1定义蛇类和食物类,初始化贪吃蛇各项成员变量,包括图像的出现在屏幕的初始位置,长度,以及蛇的行走方向。食物类的定义包括出现的初始位置,以及食物是否被吃掉的判断。2用数组初始化长度为3的贪吃蛇,并且默认食物未出现3在CView类上运用MFC提供的Windows消息中WM_TIMER消息,运用OnTimer()函数让系统提供一个时钟节拍,更新游戏4具体游戏实现,包括蛇撞到自己和围墙都将使游戏结束,判断吃豆等,其中还包括根据蛇的长度来进行游戏难度的改变。5具体键盘游戏操作运用到Windows消息响应中的WM_KEYDOWN,用OnKeyDown()来响应玩家的实际操作。///////////////////////////////////////////////////////////////////////第一部分首先在已有工程下的“ClassView”中右键CView类添加以下Windows信息1WM_KEYDOWN2WM_RBUTTONDOWN3WM_TIMER再右键CView类选择“ADDVirtualFunciton”选OnInitialUpdate()OnInitialUpdate()的功能如下:视图窗口完全建立后第一个被框架调用的函数。框架在第一次调用OnDraw前会调用OnInitialUpdate,因此OnInitialUpdate是设置滚动视图的逻辑尺寸和映射模式的最合适的地方。时间上,两者先后顺序不同,构造函数生成本类的对象,但没有产生窗口,OnCreate后窗口产生,然后才是视图的OnInitialUpDate,一般在这里对视图的显示做初始化。简单点,就是ONCREATE只是产生VIEW的基本结构和变量而在OnInitialUpDate()中,主要初始化视图中控件等。对各个变量进行初始化操作所以我们要用这个函数来进行贪吃蛇的初始化工作。再者还要添加一个成员函数oninit()进行贪吃蛇外观的初始化控件的设计再者是设计游戏的一些控件来控制“游戏开始”“游戏结束”和“游戏暂停”。我们可以点击“工作空间”的“ResourceView”进行控件的具体设计,这里我们在Menu文件夹中把“IDR_MAINFRAME”中默认的控件全部删除右键其中的标题栏,点击属性,会得到一个菜单栏标题,我们分别建立1个菜单栏标题。这里我们分别建“游戏”。点击并且在已有控件中的列表中点击属性,进行“菜单项目属性”的设置。我们本别建立的属性“标明”与对应的ID有游戏开始IDM_START游戏暂停IDM_PAUSE游戏继续IDM_CONTINUE游戏退出IDM_EXIT此处控件的设计是“可见即可得”的控件操作成功设置ID之后我们分别右键各项属性进行消息响应处理函数的生成具体方法操作例子如下1右键“游戏开始”2点击“类向导建立”3在MessageMaps页面,在要进行消息响应的控件ID列表ObjectIDs中上选择对应的ID,这里我们选择IDM_START,具体实现的环境是CView类,所以我们必须把“Classname”的默认“CMainFrame”改为“CView类”,并且在“Messages”类型设置中,用“COMMAND”设置为其为命令消息。其余各项也按照同理进行设置。PS:Windows消息的分类有3种,标准消息,命令消息,通告消息1标准消息除COMMAND之外,所以WM_开头都是标准消息2命令消息就是COMMAND3通告消息由控件产生的消息,这类信息也能以WM_COMMAND类型出现最后我们回到原来的ClassView去看看我们一共添加的函数所有的函数集合全部有以下在此处我们可以看到我们添加的Windows消息所有的消息响应函数的声明//////////////////////////////////////////////////////////第二部分具体实现游戏、Step1首先我们在文件开头处分别定义蛇和食物的全局变量structSnake{intx,y;intlen;intdirect;}Snake[50];structFood{intx;inty;intisfood;}Food;再者voidCSNAKEView::OnInitialUpdate(){CView::OnInitialUpdate();Snake[0].x=10;Snake[0].y=10;Snake[1].x=11;Snake[1].y=10;Snake[2].x=12;Snake[2].y=10;Snake[0].direct=3;Snake[0].len=3;Food.isfood=1;//TODO:Addyourspecializedcodehereand/orcallthebaseclass}代码说明:初始化贪吃蛇起初有3个节点,长度为3,起始坐标;食物默认为1无0有当然我更加喜欢0无1有Step2对OnKeyDown()具体添加代码voidCSNAKEView::OnKeyDown(UINTnChar,UINTnRepCnt,UINTnFlags){//TODO:Addyourmessagehandlercodehereand/orcalldefaultswitch(nChar){caseVK_UP:if(Snake[0].direct!=2)Snake[0].direct=1;break;caseVK_DOWN:if(Snake[0].direct!=1)Snake[0].direct=2;break;caseVK_LEFT:if(Snake[0].direct!=4)Snake[0].direct=3;break;caseVK_RIGHT:if(Snake[0].direct!=3)Snake[0].direct=4;break;}CView::OnKeyDown(nChar,nRepCnt,nFlags);CView::OnKeyDown(nChar,nRepCnt,nFlags);}OnKeyDown函数的第一个参数UINTnChar是接收用户键入的信息,然后我们用switch进行选择判断代码说明:Snake[0]代表的是蛇头,我们对蛇头的方向Snake[0].direct进行判断。caseVK_UP:if(Snake[0].direct!=2)Snake[0].direct=1;break;意思就是当Snake[0].direct的方向此时并不等于“下”的时候,才能做出“上”的操作动作,否则则忽略用户“向上”的操作按键效果Step3对OnRButtonDown()具体添加代码voidCSNAKEView::OnRButtonDown(UINTnFlags,CPointpoint){//TODO:Addyourmessagehandlercodehereand/orcalldefault//TODO:Addyourmessagehandlercodehereand/orcalldefaultCStringstr;str.Format(%d,%d,point.x,point.y);AfxMessageBox(str);CView::OnRButtonDown(nFlags,point);CView::OnRButtonDown(nFlags,point);CView::OnRButtonDown(nFlags,point);}这个函数功能是:用鼠标右键屏幕,就会马上显示当前位置的坐标信息。其实这个函数并不是本游戏中必要添加的函数,只是为了在后面游戏页面的设计的时候可以用鼠标右键屏幕了解大概的屏幕坐标信息,才特地做的这里补充一个知识点WIN32坐标系有3种1世界坐标系2页面坐标系3设备坐标系世界坐标系与页面坐标系称谓“逻辑空间”,实际上就等同于我们现实生活中的数学坐标系一样。设备坐标系则不然,电脑屏幕就是一个用设备坐标系的,特征为以用户区窗口左上角为(0,0)原点,X坐标向右为正,Y坐标向下为正。Step4voidCSNAKEView::oninit(){CDC*pDC=GetDC();CBrushDrawBrush=(RGB(100,100,100));CBrush*Drawbrush=pDC-SelectObject(&DrawBrush);for(inti=0;i=snake[0].l-1;i++)pDC-Rectangle(snake[i].x*20,snake[i].y*20,(snake[i].x+1)*20,(snake[i].y+1)*20);pDC-SelectObject(DrawBrush);}代码说明:利用Windows给我们提供的CDC类来进行画图,我们首先用一个指向CDC类的指针去接受与该窗口相关联的DC句柄,然后用定义画刷一个DrawBrush对象,并且用RGB(100,100,100)来给画刷初始化颜色。并且用SelectObject(&DrawBrush);函数把对象画刷选入到设备描述表中,用for循环依次把贪吃蛇的3个节点画出来。voidfarrectangle(intleft,inttop,intright,intbottom);这个函数的的功能根据函数功能:该函数画一个矩形,用当前的画笔画矩形轮廓,用当前画刷进行填充.Step5控件添加代码voidCSNAKEView::OnStart(){//TODO:AddyourcommandhandlercodehereSetTimer(1,3000,NULL);AfxMessageBox(3秒后开始游戏!);}voidCSNAKEView::OnPause(){//TODO:AddyourcommandhandlercodehereKillTimer(1);AfxMessageBox(暂停游戏...);}voidCSNAKEView::OnExit(){//TODO:AddyourcommandhandlercodehereAfxMessageBox(退出游戏...);exit0;}voidCSNAKEView::OnContinue(){//TODO:AddyourcommandhandlercodehereSetTimer(1,10,NULL);}代码说明:由于之前我们设定了WM_TIMER消息,我们能运用计时器功能用WM_TIMER来设置定时器先请看SetTimer这个API函数的原型UINT_PTRSetTimer(HWNDhWnd,//窗口句柄UINT_PTRnIDEvent,//定时器ID,多个定时器时,可以通过该ID判断是哪个定时器UINTuElapse,//时间间隔,单位为毫秒TIMERPROClpTimerFunc//回调函数);SetTimer(m_hWnd,1,1000,NULL);//一个1秒触发一次的定时器在MFC程序中SetTimer被封装在CWnd类中,调用就不用指定窗口句柄了所以我们这里可以只去后3个参数写成SetTimer(1,10,NULL);1000为1秒关于afxMessageBox的研究在本博客中有写有,这里就不在赘述Step6对OnDraw()的添加代码OnDraw()函数众所周知视图类中的输出.视图类的输出基本上都是在视图类的OnDraw函数中处理的,系统会准备好入参,然后调用OnDraw函数本人也没有过多的去研究过次函数,仅知道一些皮毛。不过这里有个知识点那便是OnPaint()与OnDraw()的区别,OnPaint()派生于CWnd类,响应WM_PAINT消息。OnDra