吃豆子的小游戏

来源:互联网 发布:如何防止网络成瘾 编辑:程序博客网 时间:2024/04/28 19:04

本次吃豆子游戏主要知识点包括以下几个方面

1  CView类中的消息响应

2  控件的消息响应

3  基于CView类内的具体游戏实现

4  游戏图形的实现用CDC类实现

此次吃豆子游戏用的是MFC的开发环境,所以打开VisualC++新建一个MFC APPWizard单文档工程,取名一个Eat Bean1的工程名称。



本次吃豆子基本流程与大体思想

1 定义吃类 和 食物类,初始化 嘴 的各项成员变量,包括嘴的图形出现在屏幕的初始位置,及行走的方向。食物类的定义包括出现的初始位置,以及食物是否被吃掉的判断。

2 在CView类上运用MFC提供的Windows消息中WM_TIMER消息,运用OnTimer()函数让系统提供一个时钟,记录游戏。

4 具体的游戏实现,包括 嘴 撞到墙和吃完豆子自己结束等。

5 具体键盘游戏操作运用到Windows消息响应中的WM_KEYDOWN,用OnKeyDown()来响应玩家的实际操作。

第一部分 

首先在已有的工程"ClassView"中右键CView类添加以下Windows消息

1 WM_KEYDOWN  响应键盘消息

2 WM_RBUTTONDOWN  

3 WM_TIMER    定时器

再右键CView类选择 “ADD VirtualFuncition”选OnlnitialUpdate()

在本次游戏中 OnlnitialUpdate()的主要作用是对 嘴和豆子进行初始化

还要再添加一个成员函数oninit() 对嘴的外形进行初始化


第二步 控件的设计

设计游戏的一些控件来控制 游戏开始 游戏结束 游戏暂停。

我们可以点击 工作空间 的 ResourceView 的 Menu 文件夹 进行控件的具体设计


把IDM_MAINFRAME 中默认的控件全部删除 右键其中的标题栏,点击属性,会得到一个菜单栏标题,建立一个叫做 游戏 菜单栏标题


点击并且在已有控件中的列表点击属性,进行设置。我们分别建立与 标明 对应的ID

游戏开始 IDM_START

游戏暂停 IDM_PAUSE

游戏继续 IDM_CONTINUE

游戏退出 IDM_EXIT

重新开始 IDM_RESTART


成功设置ID之后 我们分别右键对各项属性进行消息响应处理函数的生成

1 右键 游戏开始

2 点击 类向导建立

3 在Messgae Map页面,这里我们选择 IDM_START,具体实现的环境是 CView类,所以我们要把 Classname 的 默认 CMainFrame 改为 CView类,并且在 Messages 设置为命令消息。



最后我们回到原来的ClassView 中去看看我们的所有的函数集合


第三步 具体实现游戏

首先我们在文件开头处分别 定义 嘴 和 食物 的全局变量



class Eat
{
public:
int x,y;
int direct;
}Eat[50];  


class Food
{
public:
int x;
int y;
int isfood;
}Food;


再者

void CEatBean1View::OnInitialUpdate() 
{
CView::OnInitialUpdate();
Eat[0].x=10;
Eat[0].y=10;
Eat[0].direct=1;//初始化 嘴




Food.x=15;
Food.y=15;
Food.isfood=1;//初始化一个豆子


// TODO: Add your specialized code here and/or call the base class

}


代码说明 

对嘴和豆子的初始坐标进行初始化

对 OnKeyDown()具体添加代码



void CEatBean1View::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
// TODO: Add your message handler code here and/or call default
switch(nChar)
{
case VK_UP:if(Eat[0].direct!=2)Eat[0].direct=1;break;
case VK_DOWN:if(Eat[0].direct!=1)Eat[0].direct=2;break;
case VK_LEFT:if(Eat[0].direct!=4)Eat[0].direct=3;break;
case VK_RIGHT:if(Eat[0].direct!=3)Eat[0].direct=4;break;//Eat[0]代表的是嘴,对嘴方向的Eat[0].direct进行判断
}
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}


OnKeyDown函数的第一个参数UNIT nChar是接收用户键入的信息,然后我们用switch进行选择判断

代码说明:

Eat[0]代表的是嘴,我们对嘴的方向Eat[0].direct进行判断。

case VK_UP:if(Eat[0].direct!=2)Eat[0].direct=1;break;意思是 当Eat[0].direct的方向此时并不等于 下 的时候 才能做出 上 的操作动作,否则则忽略用户 向上的操作按键效果,其实就是让嘴在最开始的时候一直向上前进。

对 OnRButtonDown()具体添加代码



void CEatBean1View::OnRButtonDown(UINT nFlags, CPoint point) 
{
// TODO: Add your message handler code here and/or call default
CString str;
str.Format("%d,%d",point.x,point.y);
AfxMessageBox(str);
CView::OnRButtonDown(nFlags,point);
CView::OnRButtonDown(nFlags,point);
CView::OnRButtonDown(nFlags,point);//用鼠标右键屏幕,就会马上显示坐标


CView::OnRButtonDown(nFlags, point);
}


OnRButtonDown()的作用是:用鼠标右键屏幕,就会马上显示当前位置的坐标信息。

对 oninit()具体添加代码

void CEatBean1View::oninit()
{
CDC*pDC=GetDC();
CBrush DrawBrush=(RGB(100,100,100));//定义画刷一个DrawBrush对象,并且用RGB(100,100,100)初始化颜色
CBrush*Drawbrush=pDC->SelectObject(&DrawBrush);
pDC->Pie(Eat[0].x*20,Eat[0].y*20,(Eat[0].x+1)*20,(Eat[0].y+1)*20,(Eat[0].x)*20,(Eat[0].y)*20,(Eat[0].x+1)*20,(Eat[0].y)*20);
       pDC->Ellipse(Food.x*20,Food.y*20,(Food.x+1)*20,(Food.y+1)*20);




    pDC->SelectObject(DrawBrush);


}


利用Windows给我们提供的CDC类进行画图,首先用一个指向CDC类的指针去接受与该窗口关联的DC句柄,然后定义画刷DrawBrush对象,并且用RGB(100,100,100)进行颜色初始化

pDC->Pie(Eat[0].x*20,Eat[0].y*20,(Eat[0].x+1)*20,(Eat[0].y+1)*20,(Eat[0].x)*20,(Eat[0].y)*20,
(Eat[0].x+1)*20,(Eat[0].y)*20);画出一个没有闭合的嘴
       pDC->Ellipse(Food.x*20,Food.y*20,(Food.x+1)*20,(Food.y+1)*20);画出一个圆来表示已经闭合了得嘴

两个函数的公能:Eliiipse()绘制椭圆。椭圆与其外接矩形的中心由 x1,y1,x2,y2 或lpRect指定,椭圆由当前画笔绘制,内部由当前画刷填充。
该函数绘制的图形可以扩充到但并不包括右边及底部坐标,亦即图形的高度是y2-y1,宽度是x2-x1。如果外接椭圆的宽度或高度是0,则不绘制椭圆。

Pie()画一个饼图。由一段椭圆弧形成,其中心点和两个端点已经用线段连接。弧中心是由x1,y1,x2,y2(或lpRect)指定的外接矩形的中心。弧的起点和终点由x3,y3,x4,y4(或ptStart和 ptEnd)指定。弧由选定笔沿逆时针绘制,另外还有两条从端点到中心点的连线。饼图区域由当前画刷填充。如果x3等于x4且y3等于y4,则结果为一个椭圆。它只有一条中心点与点(x3,y3)或点(x4,y4)的连线。该函数绘制的图形延伸到但不包括右边和底部坐标,即图形高度为y2-y1,宽度为x2-x1,外接矩形的宽度和高度都必须大于2单位且小于32,767单位。

对控件添加代码

void CEatBean1View::OnStart() 
{
// TODO: Add your command handler code here
SetTimer(1,1000,NULL);
AfxMessageBox("3秒后游戏开始!");


}


void CEatBean1View::OnPause() 
{
// TODO: Add your command handler code here
KillTimer(1);
    AfxMessageBox("暂停游戏...");
}


void CEatBean1View::OnContinue() 
{
// TODO: Add your command handler code here
SetTimer(1,1000,NULL);


}


void CEatBean1View::OnExit() 
{
// TODO: Add your command handler code here
AfxMessageBox("退出游戏...");
AfxGetMainWnd()->SendMessage(WM_CLOSE);//退出
}


void CEatBean1View::OnRestart() 
{
// TODO: Add your command handler code here

}

解释一下 SetTimer(1,1000,NULL);第一个参数是对应计时器的代号 第二个参数是时间间隔 单位为毫秒 第三个参数作用是使用OnTimer函数
AfxMessageBox的作用是创建消息框。

对 OnDraw()添加代码

void CEatBean1View::OnDraw(CDC* pDC)
{
CEatBean1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CBrush backBrush(RGB(20,0,0));
 CBrush* pOldBrush = pDC->SelectObject(&backBrush);
 CRect rect;
 pDC->GetClipBox(&rect);
 pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(),PATCOPY);
 pDC->SelectObject(pOldBrush);
//pDC->Ellipse(Eat[1].x*20,Eat[1].y*20,(Eat[1].x+1)*20,(Eat[1].y+1)*20);


 pDC->Rectangle(19,19,501,501);


 oninit();


// TODO: add draw code for native data here
}


代码说明 此处使用画刷画出一个背景,并且画出三个矩形

对 OnTime()添加代码

CDC*pDC=GetDC();




     if(Eat[0].x*20<=37||Eat[0].y*20<=37||Eat[0].x*20>=462||Eat[0].y*20>=462)
{


         KillTimer(1);
         AfxMessageBox("游戏结束");
} //进行撞墙判断
if(Food.x*20==Eat[0].x*20&&Food.y*20==Eat[0].y*20)
{
Food.isfood=0;
}//对嘴是否将豆子吃掉进行判断 如果嘴的坐标和豆子的坐标重合 就说明嘴将豆子的坐标吃掉了
 if(
 Food.isfood==0)
{
KillTimer(1);

AfxMessageBox("游戏结束");
 }//吃完豆子结束游戏


pDC->SelectStockObject(WHITE_PEN);
 pDC->Rectangle(Eat[0].x*20,Eat[0].y*20,(Eat[0].x+1)*20,(Eat[0].y+1)*20);


  
     


if(Eat[0].direct==1)
{ Eat[0].y--;
  pDC->SelectStockObject(BLACK_PEN);
  
  CBrush DrawBrush=(RGB(100,100,100));
  CBrush *Drawbrush=pDC->SelectObject(&DrawBrush);
  if  ((Eat[0].x+Eat[0].y)%2==0)pDC->Ellipse(Eat[0].x*20,Eat[0].y*20,(Eat[0].x+1)*20,(Eat[0].y+1)*20);
  else


(pDC->Pie(Eat[0].x*20,Eat[0].y*20,(Eat[0].x+1)*20,(Eat[0].y+1)*20,(Eat[0].x)*20,(Eat[0].y)*20,(Eat[0].x+1)*20,(Eat[0].y)*20));




  pDC->SelectObject(DrawBrush);
  }//


  if(Eat[0].direct==2)
   { Eat[0].y++;
  pDC->SelectStockObject(BLACK_PEN);
  
  CBrush DrawBrush=(RGB(100,100,100));
  CBrush *Drawbrush=pDC->SelectObject(&DrawBrush);
   if  ((Eat[0].x+Eat[0].y)%2==0)pDC->Ellipse(Eat[0].x*20,Eat[0].y*20,(Eat[0].x+1)*20,(Eat[0].y+1)*20);
  else
  pDC->Pie(Eat[0].x*20,Eat[0].y*20,(Eat[0].x+1)*20,(Eat[0].y+1)*20,(Eat[0].x+1)*20,(Eat[0].y+1)*20,(Eat[0].x)*20,(Eat[0].y+1)*20);
  pDC->SelectObject(DrawBrush);
   }


    if(Eat[0].direct==3)
   { Eat[0].x--;
  pDC->SelectStockObject(BLACK_PEN);
  
  CBrush DrawBrush=(RGB(100,100,100));
  CBrush *Drawbrush=pDC->SelectObject(&DrawBrush);
   if  ((Eat[0].x+Eat[0].y)%2==0)pDC->Ellipse(Eat[0].x*20,Eat[0].y*20,(Eat[0].x+1)*20,(Eat[0].y+1)*20);
  else
  pDC->Pie(Eat[0].x*20,Eat[0].y*20,(Eat[0].x+1)*20,(Eat[0].y+1)*20,(Eat[0].x)*20,(Eat[0].y+1)*20,(Eat[0].x)*20,(Eat[0].y)*20);
  pDC->SelectObject(DrawBrush);
   }




if(Eat[0].direct==4)
   { Eat[0].x++;
  pDC->SelectStockObject(BLACK_PEN);
  
  CBrush DrawBrush=(RGB(100,100,100));
  CBrush *Drawbrush=pDC->SelectObject(&DrawBrush);
   if  ((Eat[0].x+Eat[0].y)%2==0)pDC->Ellipse(Eat[0].x*20,Eat[0].y*20,(Eat[0].x+1)*20,(Eat[0].y+1)*20);
  else
  pDC->Pie(Eat[0].x*20,Eat[0].y*20,(Eat[0].x+1)*20,(Eat[0].y+1)*20,(Eat[0].x+1)*20,(Eat[0].y)*20,(Eat[0].x+1)*20,(Eat[0].y+1)*20);
  pDC->SelectObject(DrawBrush);
   } 
       CView::OnTimer(nIDEvent);
}//对嘴的方向进行判断 并且实现嘴的开闭




运行结果图 


总结:这是我做的第一个小游戏,其中还有很多需要改进的地方,谢谢!

1 0