首页 VC++打地鼠游戏

VC++打地鼠游戏

举报
开通vip

VC++打地鼠游戏第5章 老鼠和滚球                                     1. 1.        实现思想 老鼠和滚球游戏,是我根据自己的意愿,是为了我将要说明的下面有关内容而设置的。由于没有经过具体实践,我并不能保证其趣味性和吸引力,我们学习的是它实现过程中的一些思想。希望对读者有所帮助。 其实,这种游戏,在街头电子游戏中是常见的。但由于电脑的渐渐普及,大规模游戏的迅速出现,已经不适合玩者的需求了。但是,我们可以以小见大,在学习小游戏的基础上,慢慢培养设计大游...

VC++打地鼠游戏
第5章 老鼠和滚球                                     1. 1.        实现思想 老鼠和滚球游戏,是我根据自己的意愿,是为了我将要说明的下面有关内容而设置的。由于没有经过具体实践,我并不能保证其趣味性和吸引力,我们学习的是它实现过程中的一些思想。希望对读者有所帮助。 其实,这种游戏,在街头电子游戏中是常见的。但由于电脑的渐渐普及,大规模游戏的迅速出现,已经不适合玩者的需求了。但是,我们可以以小见大,在学习小游戏的基础上,慢慢培养设计大游戏的一些方法和技巧。   关于游戏的角色:   老鼠,是游戏的主角,它有三条生命,它为了生存,必须能够逃脱灾难性的局面(被滚球撞到),它在经过所谓的适者生存的淘汰之后,学会了使用子弹;而且是一种能够消灭滚球的子弹,以达到防身的目的。每打中一个球得一分,每被撞一下失去一条生命。 滚球,一共有50个,但为了维持生态平衡,最多只能出来六个。滚球,是为了消灭老鼠而存在的,它将会在现代科技的伪随机函数中不定向地出现。 红心,生命的象征,只要你能碰到它,你就幸运了,因为由此你会得到一条生命。当然,生存需要竞争,需要你自己去创造;只要你在消灭了很多滚球之后,你才有可能取得。正因为这个原因,我把它安排在滚球出现的地方。   关于游戏的实现:   游戏,经常都是全屏的,但是由于我们的目的不仅仅在于游戏本身,我们的宗旨是利用游戏的趣味性和吸引力,来增强读者学习程序语言的信心。所以,在前面我们都没有用到全屏显示,我们不需要。 另外说明:本游戏的全屏只适应于800*600的分辨率。 全屏,是这个游戏的一点要学习的。多位图的移动,是本游戏要学习的第二个内容,上面的最多只有两个位图可以移动,这是由于游戏本身决定的。当然,这个游戏的多位图移动,也是游戏本身决定的,但却是我之所以选择它为第五章的原因。当然,我们没有那么多的手去操作键盘,去移动位图。我们必须设置我们的程序,让程序自己去执行,去移动,去显示计算机的优越性。 这个游戏,比起上面来,它是一个更加复杂的社会,它不再只是玩者一个人充当角色。所以,我们新建了各自的类。   新建工程5_1,为单文档默认设置。                 2. 2.        制作位图   既然游戏是复杂的,它就需要很多位图,但由于我们在前面已经用了很多位图,我们这里就不多说了。只是说明一下位图的数量和大小。   背景位图:800*600 1张 IDB_BITMAP1 老鼠位图:50*50 4张 IDB_BITMAP2 (两只老鼠两个方向) IDB_BITMAP3 IDB_BITMAP7 IDB_BITMAP8 滚球位图:50*50 1张 IDB_BITMAP4 红心位图:50*50 1张 IDB_BITMAP11 子弹位图:50*50 4张 IDB_BITMAP5 (两种颜色两个方向) IDB_BITMAP6 IDB_BITMAP9 IDB_BITMAP10     3. 3.        变量和函数     老鼠:   它有位置,分数,方向,生命力,子弹和它自己。其中它自己的图像及它射出来的子弹都有前后方向。我们必须为它定义一个类,以让两个老鼠共用。   新建类:CMouse   添加变量如下:   CPoint point;//位置 CBitmap bitmapa;//向后图像 CBitmap bitmapb;//向前图像 CBitmap shota;//向后子弹 CBitmap shotb;//向前子弹 int score;//分数 short direction;//方向 short lifes;//生命       滚球:   它也有位置,也有它自己的图像。由于一共有五十个球,最多会显示六个球,我们也定义它为一共类。   新建类:CBall   添加变量如下:   CPoint point; CBitmap bitmap;//由于对称,我们只要一个位图   游戏:   由于它的复杂性,程序,由于它的复杂性,为了不使它们更加复杂,我们也为游戏的实现本身建立一个类。   新建类:CGame   添加变量如下:   CBall ball[6];//六个滚球 short ballnum;//滚球数:50 CMouse m1,m2;//两只老鼠 int yy[16][12];//屏幕数组 CBitmap shot;//子弹位图 bool heart1,heart2;//两个红心是否显示 CBitmap heart;//红心位图   既然是游戏本身,它必定要实现很多功能,我们添加如下函数:   void GetHeart();//老鼠得到红心 void KillBall();//老鼠打中滚球 void KillMouse();//滚球杀死老鼠 void Draw(CDC*pDC);//画界面 void DrawScore(CDC*pDC);//画分数 void BallMove();//使球滚动   最后,到了主程序,为了实现全屏,我们在CMainFrame 中添加如下变量和函数:   CRect m_FullScreenRect;//全屏显示时的窗口位置 void OnFullScreen();//全屏显示     在CMy5_1View中添加如下变量和函数:   CGame game;//游戏对象 CBitmap cloud;//背景云图   OnTimer(UINT nIDEvent) OnCreate(LPCREATESTRUCT lpCreateStruct) OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)           4. 4.        具体实现   全屏显示:   首先,我们必须使单文档以全屏形式出现,并设置为不能改动大小,没有工具栏和状态栏,没有菜单。 添加OnFullScreen()函数如下: void CMainFrame::OnFullScreen() { // TODO: Add your command handler code here CRect WindowRect; GetWindowRect(&WindowRect); CRect ClientRect; RepositionBars(0,0xffff,AFX_IDW_PANE_FIRST,reposQuery,&ClientRect); ClientToScreen(&ClientRect); //获取屏幕的分辨率 int nFullWidth=GetSystemMetrics(SM_CXSCREEN); int nFullHeight=GetSystemMetrics(SM_CYSCREEN); m_FullScreenRect.left=WindowRect.left-ClientRect.left; m_FullScreenRect.top=WindowRect.top-ClientRect.top; m_FullScreenRect.right=WindowRect.right-ClientRect.right+nFullWidth; m_FullScreenRect.bottom=WindowRect.bottom-ClientRect.bottom+nFullHeight; //进入全屏显示状态 WINDOWPLACEMENT wndpl; wndpl.rcNormalPosition=m_FullScreenRect; SetWindowPlacement(&wndpl); }   再改变函数OnCreate(LPCREATESTRUCT lpCreateStruct)和 PreCreateWindow(CREATESTRUCT& cs)如下:     int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;   OnFullScreen();//全屏显示 return 0; }   BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { if( !CFrameWnd::PreCreateWindow(cs) ) return FALSE; // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs   cs.style=WS_SYSMENU; cs.dwExStyle=WS_EX_TOPMOST; //窗口置于最前   return TRUE; }   这样,全屏就显示出来了。   构造函数:   接着,就要把位图显示出来,在CMy5_1View()函数添加语句如下:   CMy5_1View::CMy5_1View() { // TODO: add construction code here cloud.LoadBitmap(IDB_BITMAP1); }   显示背景:   在OnDraw(CDC* pDC)函数里面添加语句如下:   void CMy5_1View::OnDraw(CDC* pDC) { CMy5_1Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here CDC Dc; if(Dc.CreateCompatibleDC(pDC)==FALSE) AfxMessageBox("Can't create DC"); //在不同位置显示位图 Dc.SelectObject(cloud); pDC->StretchBlt(0,0,800,600,&Dc,0,0,800,600,SRCCOPY); game.Draw(pDC); } 由于真正的画图是在类Game里面实现的,我们添加game.Draw(pDC)函数。但是,为了防止某些变量的赋值和初始化问 快递公司问题件快递公司问题件货款处理关于圆的周长面积重点题型关于解方程组的题及答案关于南海问题 ,我们先添加CGame()函数的语句如下:   构造函数:   CGame::CGame() { int i,j; heart.LoadBitmap(IDB_BITMAP11); m1.bitmapb.LoadBitmap(IDB_BITMAP2); m2.bitmapb.LoadBitmap(IDB_BITMAP3); m1.bitmapa.LoadBitmap(IDB_BITMAP7); m2.bitmapa.LoadBitmap(IDB_BITMAP8); m1.shotb.LoadBitmap(IDB_BITMAP5); m2.shotb.LoadBitmap(IDB_BITMAP9); m1.shota.LoadBitmap(IDB_BITMAP6); m2.shota.LoadBitmap(IDB_BITMAP10); //生命数量为3 m1.lifes=3; m2.lifes=3; //红心显示 heart1=true; heart2=true; //滚球数量50 ballnum=50;   for(i=0;i<6;i++) ball[i].bitmap.LoadBitmap(IDB_BITMAP4); //滚球开始出现的位置 for(i=0;i<3;i++) { ball[i].point.x=4+i; ball[i].point.y=0; ball[i+3].point.x=9+i; ball[i+3].point.y=0; } //老鼠开始出现的位置 m1.point.x=0; m1.point.y=10; m2.point.x=15; m2.point.y=10; //老鼠开始的方向 m1.direction=1; m2.direction=-1; //清理背景数组,或1或0 for(i=0;i<16;i++) for(j=0;j<11;j++) yy[i][j]=0; yy[4][1]=1; yy[5][1]=1; yy[6][1]=1; yy[9][1]=1; yy[10][1]=1; yy[11][1]=1; for(i=0;i<16;i++) yy[i][3]=1; yy[5][3]=0; yy[10][3]=0; for(i=5;i<11;i++) yy[i][5]=1; for(i=4;i<12;i++) yy[i][7]=1; for(i=0;i<5;i++) yy[i][9]=1; for(i=11;i<16;i++) yy[i][9]=1; for(i=0;i<16;i++) yy[i][11]=1;   }   显示前景:   再添加如下函数:   void CGame::Draw(CDC *pDC) { int i,j; CDC Dc; if(Dc.CreateCompatibleDC(pDC)==FALSE) AfxMessageBox("Can't create DC"); //在不同位置显示位图 //显示老鼠位图 if(m2.lifes>0) { Dc.SelectObject(m2.direction==1?m2.bitmapb:m2.bitmapa); pDC->BitBlt(m2.point.x*50,m2.point.y*50,50,50,&Dc,0,0,SRCCOPY); } if(m1.lifes>0) { Dc.SelectObject(m1.direction==1?m1.bitmapb:m1.bitmapa); pDC->BitBlt(m1.point.x*50,m1.point.y*50,50,50,&Dc,0,0,SRCCOPY); } //画滚球 //大于六个的画六个 j=ballnum-m1.score; j=j-m2.score; if(j>6) j=6; for(i=0;iBitBlt(ball[i].point.x*50,ball[i].point.y*50,50,50,&Dc,0,0,SRCCOPY); } //检查界面数组 //2 关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf 示m2的子弹,3表示m1的子弹 for(i=0;i<16;i++) for(j=0;j<12;j++) { if(yy[i][j]==2) { Dc.SelectObject(m2.direction==1?m2.shotb:m2.shota); pDC->BitBlt(i*50,j*50,50,50,&Dc,0,0,SRCCOPY); } if(yy[i][j]==3) { Dc.SelectObject(m1.direction==1?m1.shotb:m1.shota); pDC->BitBlt(i*50,j*50,50,50,&Dc,0,0,SRCCOPY); } } //画老鼠的生命数量 for(i=0;iStretchBlt(i*40+10,0,30,30,&Dc,0,0,50,50,SRCCOPY); } for(i=0;iStretchBlt(760-i*40,0,30,30,&Dc,0,0,50,50,SRCCOPY); } //显示分数,函数在下面! DrawScore(pDC); //显示红心 Dc.SelectObject(heart); if(heart1) pDC->BitBlt(250,0,50,50,&Dc,0,0,SRCCOPY); if(heart2) pDC->BitBlt(500,0,50,50,&Dc,0,0,SRCCOPY);   }   显示分数信息:   由于这个函数在前面已经出现,只是添加如下:   void CGame::DrawScore(CDC *pDC) { int nOldDC=pDC->SaveDC(); //设置字体 CFont font; if(0==font.CreatePointFont(250,"Comic Sans MS")) { AfxMessageBox("Can't Create Font"); } pDC->SelectObject(&font); //设置字体颜色及其背景颜色 CString str; pDC->SetTextColor(RGB(0,10,244)); pDC->SetBkColor(RGB(0,255,0)); //输出数字 str.Format("%d",m1.score); if(m2.score>=0) pDC->TextOut(40,28,str); str.Format("%d",m2.score); if(m2.score>=0) pDC->TextOut(740,28,str);   pDC->RestoreDC(nOldDC);   }   滚球实现:   老鼠的移动是由我们操作的,我们后面再实现,而滚球是程序驱动的,它们是怎样实现的呢?添加如下函数:   void CGame::BallMove() { int i,j,k; j=ballnum-m1.score; j=j-m2.score; if(j>6) j=6; for( i=0;i15) ball[i].point.x=0; //下面空,下掉 if(yy[ball[i].point.x][ball[i].point.y+1]!=1) for(k=ball[i].point.y;k<12;k++) if(yy[ball[i].point.x][k]==1) { ball[i].point.y=k-1; break; } //到了最底面 if((ball[i].point.x==15)&&(ball[i].point.y==10)) { //在上面出现 if(i==1) { ball[i].point.x=10; ball[i].point.y=-1; } else { ball[i].point.x=5; ball[i].point.y=-1; } } } //同上 else if((i==2)||(i==4)||(i==0)) { ball[i].point.x--; if(ball[i].point.x<0) ball[i].point.x=15; if(yy[ball[i].point.x][ball[i].point.y+1]!=1) for(k=ball[i].point.y;k<12;k++) if(yy[ball[i].point.x][k]==1) { ball[i].point.y=k-1; break; } if((ball[i].point.x==0)&&(ball[i].point.y==10)) { if(i==2) { ball[i].point.x=5; ball[i].point.y=0; } else { ball[i].point.x=10; ball[i].point.y=0; }   } }   } }       计时器函数:   那么,现在应该添加计数器函数了。添加函数OnCreate(LPCREATESTRUCT lpCreateStruct)和 OnTimer(UINT nIDEvent)如下:   int CMy5_1View::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; // TODO: Add your specialized creation code here SetTimer(1,250,NULL); return 0; }   void CMy5_1View::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default //经常是否有以下事件发生 game.BallMove(); game.KillMouse(); game.KillBall(); game.GetHeart(); //重画 OnDraw(GetDC()); int i,j; //如果有子弹,清除子弹 for(i=0;i<16;i++) for(j=0;j<12;j++) if(game.yy[i][j]==2||game.yy[i][j]==3) game.yy[i][j]=0; CView::OnTimer(nIDEvent); }   其中的game.KillMouse(), game.KillBall(),game.GetHeart()函数分别表示是否有老鼠被杀,是否有球被打,是否有红心被拿。分别介绍如下:   得到红心: void CGame::GetHeart() { //是哪个老鼠是得到了哪个红心 //第一只老鼠 if((m1.point.x==5)&&(m1.point.y==0)) //第一只红心 if(heart1) { m1.lifes++; heart1=false; } if((m2.point.x==5)&&(m2.point.y==0)) if(heart1) { m2.lifes++; heart1=false; } if((m1.point.x==10)&&(m1.point.y==0)) if(heart2) { m1.lifes++; heart2=false; } if((m2.point.x==10)&&(m2.point.y==0)) if(heart2) { m2.lifes++; heart2=false; } }   射中滚球:   void CGame::KillBall() { for(int i=0;i<6;i++) //m2的子弹 if(yy[ball[i].point.x][ball[i].point.y]==2) { //m2的分数添加 m2.score++; //滚球消失 ball[i].point.x=5; ball[i].point.y=-1; } //同上 else if(yy[ball[i].point.x][ball[i].point.y]==3) { m1.score++; ball[i].point.x=10; ball[i].point.y=-1; } }   杀死老鼠:   void CGame::KillMouse() { int i; for(i=0;i<6;i++) { //老鼠的位置和滚球的位置 if(m1.point==ball[i].point) { //生命减少 m1.lifes--; //老鼠重新出现 m1.point.x=7; m1.point.y=6; } //同上 if(m2.point==ball[i].point) { m2.lifes--; m2.point.x=8; m2.point.y=6; }   } }     键盘操作:   最后,就剩下键盘操作函数了,实现如下:   void CMy5_1View::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { // TODO: Add your message handler code here and/or call default int i; switch(nChar) { //子弹按钮 //数字键盘3 case VK_NUMPAD3: //界面数组位置为2 game.yy[game.m2.point.x+game.m2.direction][game.m2.point.y]=2; break; case VK_LEFT: //向左移动1 game.m2.point.x--; //老鼠方向为左 game.m2.direction=-1; //如果是边界,从右边出现 if(game.m2.point.x<0) game.m2.point.x=15; //如果下面空,下掉 if(game.yy[game.m2.point.x][game.m2.point.y+1]!=1) for(i=game.m2.point.y;i<12;i++) if(game.yy[game.m2.point.x][i]==1) { game.m2.point.y=i-1; break; } break; //道理同上 case VK_RIGHT: game.m2.point.x++; game.m2.direction=1; if(game.m2.point.x>15) game.m2.point.x=0; if(game.yy[game.m2.point.x][game.m2.point.y]!=1) for(i=game.m2.point.y;i<12;i++) if(game.yy[game.m2.point.x][i]==1) { game.m2.point.y=i-1; break; } break; //上跳 case VK_UP: //如果上面能站住,上移 if(game.yy[game.m2.point.x][game.m2.point.y-1]==1) game.m2.point.y=game.m2.point.y-2; //否则,如果前面能够站住,上移 else if(game.yy[game.m2.point.x+game.m2.direction][game.m2.point.y-1]==1) { game.m2.point.x+=game.m2.direction; game.m2.point.y=game.m2.point.y-2; } //否则,如果后面能够,上移 else if(game.yy[game.m2.point.x-game.m2.direction][game.m2.point.y-1]==1) { game.m2.point.x-=game.m2.direction; game.m2.point.y=game.m2.point.y-2; game.m2.direction=-game.m2.direction; } break; //下跳 case VK_DOWN: //下掉 for(i=game.m2.point.y+2;i<12;i++) if(game.yy[game.m2.point.x][i]==1) { game.m2.point.y=i-1; break; } break; //道理同上 case 75: game.yy[game.m1.point.x+game.m1.direction][game.m1.point.y]=3; break; case 65: game.m1.point.x--; game.m1.direction=-1; if(game.m1.point.x<0) game.m1.point.x=15; if(game.yy[game.m1.point.x][game.m1.point.y+1]!=1) for(i=game.m1.point.y;i<12;i++) if(game.yy[game.m1.point.x][i]==1) { game.m1.point.y=i-1; break; } break; case 68: game.m1.point.x++; game.m1.direction=1; if(game.m1.point.x>15) game.m1.point.x=0; if(game.yy[game.m1.point.x][game.m1.point.y]!=1) for(i=game.m1.point.y;i<12;i++) if(game.yy[game.m1.point.x][i]==1) { game.m1.point.y=i-1; break; } break; case 87: if(game.yy[game.m1.point.x][game.m1.point.y-1]==1) game.m1.point.y=game.m1.point.y-2; else if(game.yy[game.m1.point.x+game.m1.direction][game.m1.point.y-1]==1) { game.m1.point.x+=game.m1.direction; game.m1.point.y=game.m1.point.y-2; } else if(game.yy[game.m1.point.x-game.m1.direction][game.m1.point.y-1]==1) { game.m1.point.x-=game.m1.direction; game.m1.point.y=game.m1.point.y-2; game.m1.direction=-game.m1.direction; } break; case 83: for(i=game.m1.point.y+2;i<12;i++) if(game.yy[game.m1.point.x][i]==1) { game.m1.point.y=i-1; break; } break; } //重画 CDC* pDC=GetDC(); OnDraw(pDC); ReleaseDC(pDC);   CView::OnKeyDown(nChar, nRepCnt, nFlags); }   现在,游戏完成,运行,看看效果。         5. 5.        附加内容     程序的调整:   编程序,我们不可能一开始就以最好的方法,最好的思想去实现它,成功地去完成它。我们总是会遇到各种难题,出现各种差错,碰到各种思想障碍,以致我们在编程序的时候不能按照正常的形式编写,那么,我们是不是该检查我们刚写的程序,并做一些必要的调整呢? 不难看出,我们上面最后一个函数太复杂了,我们是不该不那么多的代码写在一个函数里面,不该可以用函数实现的代码直接写出来,也不该让可以不重复的代码重复出现! 那么,我们应该任何实现这个函数的调整? 首先,我们容易发现,m1和m2的方向操作几乎一样,就只是m1和m2的差别而已,是否可以把它们用代码重用的想法,合二为一。 第二,合二为一,就必须添加一个函数来给m1和m2共用,那么,这个函数应该添加在哪里?是在view类,还是在game类?由于函数里面的变量都是用game.开头的,显然,完成可以添加在game类里面。而至于参数m1和m2的差别,我们应该在添加的move()函数里添加它们的标志参数:CMouse m。另外,方向也是应该有参数的,分别用1,2,3,4表示。最后函数的形式如下: void CGame::Move(CMouse m,int direct) 第三,把OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)函数里面的相应内容移到新函数里面,去掉不必要的game.,并把m1,m2改为m,而在OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)函数里面添加新函数的相应调用。 更改完毕,你是否去试试!     支持多分辨率:   可以制作多种位图,依据对不同分辨率的判断,相应显示位图,并在draw()里面编写多种位图的显示。   6. 6.        小结   这个程序,我们学习了另外一种常见的游戏。我们还学习了全屏显示的算法,虽然这里的全屏显示并不完全;但是它说明了一个道理:   圆是不存在的,因为它太完美了!   所有的程序,所有的算法都是不完美的,都是有待改进的。我们必须承认它的存在,当然,我们也必须努力改进它,尽量使它完美。
本文档为【VC++打地鼠游戏】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_243914
暂无简介~
格式:doc
大小:380KB
软件:Word
页数:16
分类:互联网
上传时间:2012-07-28
浏览量:81