• MFC实现简单飞机大战(含游戏声音)


    1 实验内容

    本实验主要是实现简单的飞机大战游戏,包含游戏声音、碰撞后爆炸效果,有大小敌机等。所用到的知识点如下:

    1.贴图技术

    2.飞机类、子弹类实现

    3.位图移动

    4.碰撞判断,实现爆炸效果

    5.插入声音

    此实验的大概设计:游戏画面保持有一架大敌机,五架小敌机,一架战机,30颗子弹。如果子弹击落了敌机,那么敌机对象不会被delete,而仅仅是改变它的位置,让它重新从上面飞下,姑且可以叫做“假摧毁”吧。同理,子弹飞出界面或打到敌机,该子弹对象也不会被delete,而是从底部重新上升。击落敌机时会有爆炸效果;战机被敌机摧毁会有更大的爆炸效果,且游戏会停1秒再继续。

    2 实验环境

    win10+vs2015

    3 实验步骤

    3.1 添加游戏资源

    从网上下载自己喜欢的飞机和子弹图片(.png文件),下载游戏背景音乐(.wav文件),把上述资源导入本工程的res文件,复制res文件到Debug文件中,这样便可以直接打开zsr_简单飞机大战.exe

    打开res文件,可以看到下图(当然啦,飞机、子弹和游戏声音选你喜欢的就行啦,不用跟我的一样):


    上图中倒数第二个黑色的飞机就是我的程序图标。

    3.2 修改程序图标

    点击[这里]了解一下。

    3.3 设置类

    右键‘项目’,点击‘添加类’,就可以添加类啦。本实验设置4个类:大敌机(DADIJI),小敌机(XIAODIJI),战机(ZHANJI),子弹(ZIDAN)。并且在类中实现贴图。

    3.3.1 大敌机类(DADIJI)

    添加大敌机类,类名为'DADIJI'。并在类中添加数据成员和成员函数,修改构造函数。具体如下:

    //在类中加入:
    CImage m_hero;//图片类对象
    	int diji_x;//图片横坐标
    	int diji_y;//图片轴坐标
    	void JIDRAW(CDC *cDC) {  //设定大敌机的大小
    		m_hero.Draw(*cDC, diji_x, diji_y, 100, 50);//图片的宽为100,高50
    	}
    
    //修改构造函数
    DADIJI::DADIJI()
    {
     //加载图片
    	CString imgPath = _T("res\LXPlane.png");
    	m_hero.Load(imgPath);
     //初始化大敌机的位置
    	diji_x = 300;
    	diji_y = 0;
    }

    3.3.2 小敌机类(XIAODIJI)

    添加内容和大敌机类似,如下:

    //类中加入:
    	CImage m_hero;
    	int diji_x;
    	int diji_y;
    	//设定小敌机的大小
    void JIDRAW(CDC *cDC) {
    		m_hero.Draw(*cDC, diji_x, diji_y, 50,50); //小敌机宽50,高50
    	}
    //修改构造函数:
     XIAODIJI::XIAODIJI()
    {
     //加载图片
    	CString imgPath = _T("res\BluePlane.png");
    	m_hero.Load(imgPath);
     //设定小敌机的初始位置
    	diji_x = 30;
    	diji_y = 0;
    }
    

    3.3.3 战机类(ZHANJI)

    修改内容和大敌机差不多。这里说明一下,我的战机位置也用了diji_x和diji_y来表示,其实为了代码的可读性,这里用zhanji_x和zhanji_y比较好。(但我比较懒,这里就不修改了。)具体改动的代码如下:

    //在类中加入:
    CImage m_hero;
    	int diji_x;
    	int diji_y;
    	CImage m_hero1;
    //设定战机的大小
    	void JIDRAW(CDC *cDC) {
    		m_hero.Draw(*cDC, diji_x, diji_y, 80, 80); 
    	}
    //加入爆炸效果函数
    	void JIDRAW1(CDC *cDC, int x, int y) {
    		m_hero1.Draw(*cDC, x, y, 100, 100);//战机被敌机碰撞将变成爆炸状
    	}
    //修改构造函数:
    ZHANJI::ZHANJI()
    {
    //加载战机图片
    	CString imgPath = _T("res\XPlane.png");
    	m_hero.Load(imgPath);
    //加载爆炸图片
    	CString imgPath1 = _T("res\ZHANJIBAOZHA.png");
    	m_hero1.Load(imgPath1);
    //设定战机的初始位置
    	diji_x = 330;
    	diji_y = 500;
    }
    

    3.3.4 子弹类(ZIDAN)

    这里的解释,和战机类的解释一样。具体代码如下:

    //在类中加入:
    	CImage m_hero;
    	CImage m_hero1;
    	int diji_x;
    	int diji_y
    //设置子弹的大小;
    	void JIDRAW(CDC *cDC) {
    		m_hero.Draw(*cDC, diji_x, diji_y, 20, 20); 
    	}
      //设置爆炸效果
    	void JIDRAW1(CDC *cDC,int x,int y) {
    		m_hero1.Draw(*cDC, x, y, 50, 50);//子弹碰到敌机,将会变成爆炸状
    	}
    修改构造函数:
    ZIDAN::ZIDAN()
    {
    //加载子弹图片
    	CString imgPath = _T("res\zidan.png");
    	m_hero.Load(imgPath);
    //加载爆炸图片
    	CString imgPath1 = _T("res\baozha.png");
    	m_hero1.Load(imgPath1);
    设定子弹初始位置
    	diji_x = 320;
    	diji_y = 600;
    }
    

    3.4 游戏重要变量

    //在view类中加入下面变量:
    	DADIJI  DA1;//大敌机1台
    	XIAODIJI   XIAO1[5];//保持游戏界面有小敌机5台
    	ZIDAN ZI[30];//保持游戏界面有子弹30颗
    	ZHANJI ZHAN;//战机一台

    3.5 设计游戏开始界面

    //修改view中的Oncreat()函数,如下:
    int Czsr_简单飞机大战View::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    	if (CView::OnCreate(lpCreateStruct) == -1)
    		return -1;
    	
    	// TODO:  在此添加您专用的创建代码
    	PlaySound(_T("res\youxi.wav"), NULL, SND_FILENAME | SND_ASYNC|SND_LOOP);//循环播放背景音乐
    	this->SetTimer(1, 100, NULL);//设置一个定时器
    	int x_num = 30;
    	for (int j3 = 0;j3 < 5;j3++)
    	{
    		XIAO1[j3].diji_x += x_num;//开始时,小飞机的横坐标相隔30
    		x_num += 30;
    		XIAO1[j3].diji_y -= x_num;//开始时,小飞机的纵坐标相隔30,起到延迟出现的效果
    	}
     
    	int num = 0;
    	for (int j2 = 0;j2 < 30;j2++)
    	{
    		ZI[j2].diji_y-= num;//开始时,子弹的纵坐标相隔25,起到延迟出现的效果
    		ZI[j2].diji_x = ZHAN.diji_x+30;//子弹的横坐标在战机的正中间
    		num += 25;
    		;
    	}
     
    	return 0;
    }
    

    3.6 位图移动和碰撞

    看到3.5的修改中已经设置了一个计时器,这里响应Ontimer()函数,如下:

    //设计ontimer()函数(如下):
    void Czsr_简单飞机大战View::OnTimer(UINT_PTR nIDEvent)
    {
    	// TODO: 在此添加消息处理程序代码和/或调用默认值
    
    		CDC *cDC = this->GetDC();   //获得当前窗口的DC     
    		GetClientRect(&m_client);   //获得窗口的尺寸  
    		CRect rect;
    		GetClientRect(rect);
    		cDC->FillSolidRect(rect, RGB(255, 255, 255));
     
    		ZHAN.JIDRAW(cDC);//画战机
    		
    
    		//子弹
    		for (int j1 = 0;j1 < 30;j1++)
    		{
    			if (ZI[j1].diji_x < DA1.diji_x+100&& ZI[j1].diji_x >DA1.diji_x - 100&&ZI[j1].diji_y < DA1.diji_y+50&& ZI[j1].diji_y >DA1.diji_y - 50)//判断子弹是否和大战机碰撞
    			{
    				int xx = DA1.diji_x;
    				int yy = DA1.diji_y;
    				ZI[j1].JIDRAW1(cDC,xx,yy );//若两者产生碰撞,则出现爆炸效果
    				ZI[j1].diji_y = ZHAN.diji_y;
    				ZI[j1].diji_x = ZHAN.diji_x + 30;//发生爆炸的子弹,重新回到战机的子弹口
    				DA1.diji_x = rand() % 400;  //大敌机从上面的随机位置重新出现
    				while (DA1.diji_x<20)
    					DA1.diji_x = rand() % 400;
    				DA1.diji_y = 0;
    			}
    			else if (ZI[j1].diji_y < 0)//如果子弹飞到最高处,重回战机子弹口
    			{
    				ZI[j1].diji_y = ZHAN.diji_y;
    				ZI[j1].diji_x = ZHAN.diji_x + 30;
    			}
    			else
    			ZI[j1].diji_y = ZI[j1].diji_y-10;//否则,继续上升
    			for (int i1 = 0;i1 < 5;i1++)
    			{
    				if (ZI[j1].diji_x < XIAO1[i1].diji_x + 50 && ZI[j1].diji_x >XIAO1[i1].diji_x - 50 && ZI[j1].diji_y <XIAO1[i1].diji_y + 50 && ZI[j1].diji_y >XIAO1[i1].diji_y - 50)//判断每台小敌机是否和子弹碰撞
    				{
    					int xx1 = XIAO1[i1].diji_x;
    					int yy1 = XIAO1[i1].diji_y;
    					XIAO1[i1].diji_x = rand() % 400;//若碰撞,小敌机重新从上面的随机位置降落
    					while (XIAO1[i1].diji_x<20)
    						XIAO1[i1].diji_x = rand() % 400;
    					XIAO1[i1].diji_y = -30;
    			
    					ZI[j1].JIDRAW1(cDC, xx1, yy1);//爆炸状态
    					ZI[j1].diji_y = ZHAN.diji_y;
    					ZI[j1].diji_x = ZHAN.diji_x + 30;//子弹重新回到战机子弹口
    				}
    			}
    			ZI[j1].JIDRAW(cDC);//画子弹
    		}
    
    		//小敌机
    		for (int j = 0;j < 5;j++)
    		{
    			if (ZHAN.diji_x < XIAO1[j].diji_x + 50 && ZHAN.diji_x >XIAO1[j].diji_x -50 && ZHAN.diji_y < XIAO1[j].diji_y + 50 && ZHAN.diji_y >XIAO1[j].diji_y - 50)//判断战机是否和小战机碰撞
    			{
    				int xxx = ZHAN.diji_x;
    				int yyy = ZHAN.diji_y;
    				ZHAN.JIDRAW1(cDC, xxx, yyy);//若碰撞,发生战机爆炸
    				Sleep(500);//停止500ms
    				XIAO1[j].diji_x = rand() % 400;
    				while (DA1.diji_x<20)
    					XIAO1[j].diji_x = rand() % 400;//小敌机重新随机降落
    				DA1.diji_y = 0;
    				ZHAN.diji_x = 300;
    				ZHAN.diji_y = 500;//战机回到初始位置
    			}
    			else if (XIAO1[j].diji_y > 690)//若小敌机走出游戏界面,则重新随机降落
    			{
    				XIAO1[j].diji_x = rand() % 400;
    				while(XIAO1[j].diji_x<20)
    					XIAO1[j].diji_x = rand() % 400;
    				XIAO1[j].diji_y = 0;
    			}
    			XIAO1[j].diji_y += 10;//小战机下降
    			XIAO1[j].JIDRAW(cDC);
    		}
    
    		//大敌机
    		if (ZHAN.diji_x < DA1.diji_x + 100 && ZHAN.diji_x >DA1.diji_x - 100 && ZHAN.diji_y < DA1.diji_y + 80 && ZHAN.diji_y >DA1.diji_y - 80)//判断战机是否和大敌机发生碰撞
    		{
    			int xxx = ZHAN.diji_x;
    			int yyy = ZHAN.diji_y;
    			ZHAN.JIDRAW1(cDC, xxx, yyy);//若碰撞,战机爆炸,大敌机重新随机降落
    			Sleep(500);//停止500ms
    			DA1.diji_x = rand() % 400;
    			while (DA1.diji_x<20)
    				DA1.diji_x = rand() % 400;
    			DA1.diji_y = 0;
    			ZHAN.diji_x = 300;//战机回到初始位置
    			ZHAN.diji_y = 500;
    		}
    		if (DA1.diji_y < 650)//大敌机下降
    			DA1.diji_y = DA1.diji_y + 10;
    		else
    			DA1.diji_y = 0;//若大飞机飞出游戏界面 ,重新随机降落
    		DA1.JIDRAW(cDC); //画大敌机
    		ReleaseDC(cDC);
     
    	CView::OnTimer(nIDEvent);
    }
    

    3.7 战机移动

    为方便,这里的战机只能左右移动。下面响应一个键盘左右键的函数:

    void Czsr_简单飞机大战View::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
    {
    	// TODO: 在此添加消息处理程序代码和/或调用默认值
    	if (nChar == 37)//向左移
    	{
    		if (ZHAN.diji_x > 0)
    			ZHAN.diji_x -= 10;
    	}
    
    			else 	
    			{
    				if (nChar == 39)//向右移
    				if (ZHAN.diji_x <450)
    					ZHAN.diji_x += 10;
    			}
     
    	CView::OnKeyDown(nChar, nRepCnt, nFlags);
    }
    

    3.8 删除状态栏

    在框架类的OnCreate()函数中删除响应代码即可。

    3.9  删除菜单栏,固定窗口大小

    修改框架类的PreCreateWindow()函数:
    BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
    {
    	if( !CFrameWnd::PreCreateWindow(cs) )
    		return FALSE;
    	// TODO: 在此处通过修改
    	//  CREATESTRUCT cs 来修改窗口类或样式
    	
    	//zsr,删除菜单项
    	cs.hMenu = NULL;
    	//禁止改变窗口大小
    	cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
    
    	cs.style &= ~WS_MAXIMIZEBOX; //禁止窗口最大化
    
    	cs.style &= ~WS_MINIMIZEBOX; //禁止窗口最小化
    
    	cs.style &= ~WS_THICKFRAME;//使窗口不能用鼠标改变大小
     
    	return TRUE;
    }
    

    4 游戏展示


                初始样式                                                                    游戏中

     

                小敌机爆炸                                                                 大敌机、战机爆炸

    5 总结

    这个实验太过简单,飞机数量已经固定且没有计分功能。


  • 相关阅读:
    基于go语言的消息推送系统架构分析
    golang IDE 工具liteide打开报错解决方法
    centos 安装ffmpeg 并生成文件截图
    android 微信分享
    函数编程之闭包漫谈(Closure)
    2017-06-26
    本周总结
    一周总结
    一周总结
    一周总结
  • 原文地址:https://www.cnblogs.com/surecheun/p/9648966.html
Copyright © 2020-2023  润新知