本文由BlueCoder编写   转载请说明出处:

http://blog.csdn.net/crocodile__/article/details/13505997

我的邮箱:bluecoder@yeah.net    欢迎大家和我交流编程心得

我的微博:BlueCoder_黎小华    欢迎光临^_^

前面两篇回顾了一些MFC编程要领(后期还会随带着讲一点儿,不过不是重点了),今天就进入游戏主题,搞一个小游戏

前段时间,看见Yorhom朋友用html5+Qt开发了一款游戏——SpaceWar,感觉挺有意思的——刚好最近一直在研究了MFC以及GDI+,可以着手将这款游戏移植到MFC中(本来可以提前几天发布的,但是前两天参加了一个竞赛,也就耽搁了一下)——这里还是很感谢Yorhom分享的素材,这位朋友很不错,html5游戏开发爱好者

接下来,我就一步一步介绍这款MFC版游戏的实现流程

————————————————————————————————————————————————————————————

————————————————————————————————————————————————————————————

一、游戏界面以及玩法的介绍

(1). 游戏开始界面

(2). 游戏界面

(3). 游戏结束界面

二、主要的实现细节分析

这里,我将详细讲解我在写这个游戏中用到的主要原理、思想

(1). 首先是游戏背景的移动(这个就是类似于幻灯片播放的原理), 我做了一个示意图,先来看看:

(ps: 红框区域的1、2分别表示在内存中绘制的两张连续的背景 , 蓝色区域表示窗口客户区)

如此循环,给人视角效果就是这两张背景在连续的变换

(2). 子弹的移动——要保证在不同方向上的移动速度相同, 因此需要对移动速度在水平方向(x轴)和垂直方向(y轴)求分量,这个是简单的物理问题,大家应该能明白:

(下图是示意图)

(3). 音效播放的处理

1>. 点击子弹以及爆炸音效都使用PlaySound,但是必须采用异步播放模式,以免游戏界面出现卡顿现象。因为如果采用同步播放,会播放完后才会返回;异步播放会直接返回,不会耽搁时间

2>. 游戏背景音乐要重复播放,除非玩家不再玩了

(4). 碰撞检测

首先要使用一个函数:PtInRect(CPoint pt)-->检测点pt是否在一个矩形区域Rect中

而对于子弹与怪物机的碰撞检测,就需要检测子弹的矩形区域的四个点(左上、下,右上、下),只要有一个点在怪物机的矩形区域内,就表示子弹射击到了一个怪物机

三、代码详解

(1). 类视图

(2). 先来浏览一下头文件SpaceWarView.h

//计时器ID
#define ID_BULLET   100//发子弹
#define ID_MONSTER  101//处理怪物
#define ID_MOUVEBK  102//移动背景//英雄机和怪物机
typedef struct img
{CImage png;CRect   rect;int        speed;bool  isOut;
}IMG;//子弹
typedef struct blt
{CImage png;CRect   rect;int        speed;bool  isOut;int       ix;//x移动坐标int       iy;//y移动坐标
}BULLET;//显示状态:   开始   游戏中   结束
enum    STATE{START, RUNNING, END};class CSpaceWarView : public CView
{
protected: // 仅从序列化创建CSpaceWarView();DECLARE_DYNCREATE(CSpaceWarView)// 属性
public:CSpaceWarDoc* GetDocument() const;// 操作
public:// 重写
public:virtual void OnDraw(CDC* pDC);  // 重写以绘制该视图virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:// 实现
public:virtual ~CSpaceWarView();
#ifdef _DEBUGvirtual void AssertValid() const;virtual void Dump(CDumpContext& dc) const;
#endifprotected://数据成员
private://显示状态STATE m_state;CDC     m_bufferDC;//缓冲DCCBitmap    m_bufferBmp;//缓冲BmpCSize    m_sClient;//客户区大小CPoint m_ptBltOrg;//子弹的起点//开始背景struct sbk{CImage bk;CImage normal;CImage selected;CRect  rect;bool   isSelected;}m_startBk;//游戏背景struct gbk{CImage fir;//第一张背景CImage   sec;//第二张背景BOOL isFir;//标记左边是否是第一张int       curx;//左边背景的x坐标}m_gameBk;//结束背景struct ebk{CImage bk;CImage gameover;CImage againNor;//重新开始CImage againSel;CImage exitNor;//退出CImage exitSel;CRect  rGo;//gameover的rectCRect  rRes;//restart的rectCRect  rExit;//exit的rectbool   isExitSel;bool   isResSel;}m_endBk;//暂停与继续struct sg{CImage   img;BOOL    isStop;CRect    rect;}m_stopGoOn;IMG                m_hero;//英雄机vector<BULLET>    m_bullet;//子弹vector<IMG>      m_vecMons;//怪物机size_t       m_score;//存储分数-每射击成功一次, 加5分const int    m_max;//标记怪物最多个数//成员函数
private://游戏的三种状态void StartUI();//开始void RunningUI();//游戏中void EndUI();//结束//开始游戏void StartGame();//结束游戏void EndGame();//暂停游戏void PauseGame();//继续游戏void GoOnGame();//由于调用次数很多, 设为内联函数inline void StickBk();//贴背景inline void StickBullet();//贴子弹inline void BulletOut();//排除出界的子弹inline void MoveMonster();//移动怪物机inline void MoveBullet();//移动子弹inline int  Beat(int i);//返回子弹i射击到的怪物机j//重新启动怪物机inline void RestartMonster(bool isStart, int i);//为索引为i的子弹求水平、垂直速度inline void GetXY(CPoint org, CPoint end, int i);// 生成的消息映射函数
protected:DECLARE_MESSAGE_MAP()
public:afx_msg void OnTimer(UINT_PTR nIDEvent);afx_msg void OnDestroy();afx_msg void OnLButtonDown(UINT nFlags, CPoint point);afx_msg void OnMouseMove(UINT nFlags, CPoint point);
};

(3). 通过头文件可以看见,我定义了一个枚举变量,用来控制游戏界面的状态

//显示状态:      开始   游戏中   结束
enum    STATE{START, RUNNING, END};

根据游戏状态绘制游戏界面

void CSpaceWarView::OnDraw(CDC* pDC)
{CSpaceWarDoc* pDoc = GetDocument();ASSERT_VALID(pDoc);if (!pDoc)return;// TODO: 在此处为本机数据添加绘制代码//创建内存位图、内存DC以便在内存中绘图, 实现双缓冲m_bufferDC.CreateCompatibleDC(NULL);m_bufferBmp.CreateCompatibleBitmap(pDC,m_gameBk.fir.GetWidth() * 2, m_gameBk.fir.GetHeight());m_bufferDC.SelectObject(m_bufferBmp);switch(m_state){//绘制开始界面case START:StartUI();break;//绘制游戏界面case RUNNING:RunningUI();break;//绘制结束界面case END:EndUI();break;}//将内存中的图贴到客户区中pDC->BitBlt(0, 0, m_sClient.cx, m_sClient.cy, &m_bufferDC, 0, 0, SRCCOPY);//释放内存位图和内存DCm_bufferBmp.DeleteObject();m_bufferDC.DeleteDC();
}

(4). 动态贴游戏背景图片

//贴背景
void CSpaceWarView::StickBk()
{//如果到了左边界,就换图if(m_gameBk.curx == -m_gameBk.fir.GetWidth()){m_gameBk.curx  = 0;m_gameBk.isFir = m_gameBk.isFir? 0 : 1;}int width = m_gameBk.fir.GetWidth();//如果第一张图在前面,就先绘制第一张if(m_gameBk.isFir){m_gameBk.fir.BitBlt(m_bufferDC, m_gameBk.curx, 0, SRCCOPY);m_gameBk.sec.BitBlt(m_bufferDC, m_gameBk.curx + width, 0, SRCCOPY);}//第二张图在前面,先绘制第二张else{m_gameBk.sec.BitBlt(m_bufferDC, m_gameBk.curx, 0, SRCCOPY);m_gameBk.fir.BitBlt(m_bufferDC, m_gameBk.curx + width, 0, SRCCOPY);}
}

(5). 对速度求解x、y轴方向的速度分量

//为索引为i的子弹求水平、垂直速度
void CSpaceWarView::GetXY(CPoint org, CPoint end, int i)
{//求得子弹的中心位置org.x += m_bullet[i].png.GetWidth();org.y += m_bullet[i].png.GetHeight();//如果两点在一条垂直直线上if(org.x == end.x){m_bullet[i].ix = 0;m_bullet[i].iy = m_bullet[i].speed;if(org.y < end.y)m_bullet[i].iy = - m_bullet[i].iy;}//如果两点在一条水平直线上else if(org.y == end.y){m_bullet[i].iy = 0;m_bullet[i].ix = m_bullet[i].speed;if(org.x > end.x)m_bullet[i].ix = - m_bullet[i].ix;}else{//求斜率k(正切)、倾斜角angle、cos(余弦)、sin(正弦)double k        = (org.y - end.y) * 1.0 / (org.x - end.x);double angle = atan(k);double cosine    = cos(angle);double sine       = sin(angle);/*将直线上的速度speed分解为x、y轴上的分量分别表示x上的移动速度、y上的移动速度*/m_bullet[i].ix = (int)fabs(m_bullet[i].speed * cosine);if(org.x > end.x)m_bullet[i].ix = - m_bullet[i].ix;m_bullet[i].iy = (int)fabs(m_bullet[i].speed * sine);if(org.y > end.y)m_bullet[i].iy = - m_bullet[i].iy;}
}

(6). 点击鼠标左键发送子弹

//如果没有暂停游戏
if(!m_stopGoOn.isStop)
{//按下鼠标左键, 发送子弹for(size_t i=0; i<m_bullet.size(); i++){if(m_bullet[i].isOut){m_bullet[i].speed = 20;int x = m_ptBltOrg.x;int y = m_ptBltOrg.y;m_bullet[i].rect.SetRect(x, y, m_bullet[i].png.GetWidth() + x,m_bullet[i].png.GetHeight() + y);GetXY(m_ptBltOrg, point, i);m_bullet[i].isOut = false;break;}}//异步播放射击音乐PlaySound (TEXT ("res\\music\\attack.wav"), NULL, SND_FILENAME | SND_ASYNC);
}

(7). 在移动怪物机时检测怪物机是否出了边界

//在移动怪物机过程中判断怪物机是否出了边界
void CSpaceWarView::MoveMonster()
{for(size_t i=0; i<m_vecMons.size(); i++){//水平移动怪物机只需要变化rect的left和rightm_vecMons[i].rect.left -= m_vecMons[i].speed;//如果怪物机出了边界, 进入游戏结束状态if(m_vecMons[i].rect.left < -m_vecMons[i].rect.Width()){m_state = END;EndGame();InvalidateRect(NULL, FALSE);break;}m_vecMons[i].rect.right -= m_vecMons[i].speed;}
}

(8). 检测子弹是否射击到了怪物机

//返回子弹i射击到的怪物机j(-1表示未射击到)
int CSpaceWarView::Beat(int i)
{CRect r = m_bullet[i].rect;for(size_t j=0; j<m_vecMons.size(); j++){if(m_vecMons[j].rect.PtInRect(CPoint(r.left, r.top)) ||m_vecMons[j].rect.PtInRect(CPoint(r.right, r.top)) ||m_vecMons[j].rect.PtInRect(CPoint(r.left, r.bottom)) ||m_vecMons[j].rect.PtInRect(CPoint(r.right, r.bottom)) ){return j;//射击到了索引为j的怪物机}}//未射击到怪物机return -1;
}

(9). 在移动子弹的时检测是否射击到了怪物机

//移动子弹
void CSpaceWarView::MoveBullet()
{for(size_t i=0; i<m_bullet.size(); i++){if(!m_bullet[i].isOut){//返回当前子弹射击到的怪物机索引int index_beat = Beat(i);//如果子弹射击到了怪物机if(index_beat != -1){//计分m_score += 5;//重新启动当前怪物机RestartMonster(false, index_beat);//标记当前子弹出界了m_bullet[i].isOut = true;//异步播放爆炸音乐PlaySound (TEXT ("res\\music\\die.wav"), NULL, SND_FILENAME | SND_ASYNC);continue;}//移动子弹m_bullet[i].rect.left += m_bullet[i].ix;m_bullet[i].rect.right += m_bullet[i].ix;m_bullet[i].rect.top += m_bullet[i].iy;m_bullet[i].rect.bottom += m_bullet[i].iy;}}
}

(10). 释放游戏所使用的所有资源

//善后工作
void CSpaceWarView::OnDestroy()
{CView::OnDestroy();/*程序退出之前, 释放所有内存资源*/EndGame();//释放开始界面资源m_startBk.bk.ReleaseGDIPlus();m_startBk.normal.ReleaseGDIPlus();m_startBk.selected.ReleaseGDIPlus();//释放游戏界面资源m_gameBk.fir.ReleaseGDIPlus();m_gameBk.sec.ReleaseGDIPlus();m_hero.png.ReleaseGDIPlus();for(size_t i=0; i<m_bullet.size(); i++){m_bullet[i].png.ReleaseGDIPlus();}for(size_t i=0; i<m_vecMons.size(); i++){m_vecMons[i].png.ReleaseGDIPlus();}//释放结束界面资源m_endBk.bk.ReleaseGDIPlus();m_endBk.gameover.ReleaseGDIPlus();
}

四、免费资源下载

点击下载源代码

点击下载游戏安装程序

【VC++游戏开发#三】2D篇 —— 游戏之一:空中大战(SpaceWar)相关推荐

  1. 【游戏开发】2D RPG游戏

    前言 通过对游戏<原神>的功能复刻来学习游戏开发 截止到10月,本项目已经开发的差不多了,不是开发的完善了,而是通过这个项目已经学会了Unity开发游戏的技巧,就不继续开发了. 这里展示一 ...

  2. 游戏开发心得——书籍篇——《游戏引擎框架》-导论

    游戏开发心得--书籍篇--<游戏引擎框架>-导论 FOR THE SIGMA FOR THE GTINDER FOR THE ROBOMASTER 简介: 学习<游戏引擎框架> ...

  3. 游戏开发面试答案篇(一)-- C++篇​

    游戏开发程序岗面试题答案版C++篇,后续继续更新游戏逻辑篇.unity篇.图形学篇,并整理成文档,可在公号[游戏君五尘]获取 原文链接 游戏开发面试答案篇(一)-- C++篇​ 目录 一.基础语法 二 ...

  4. Cocos2D手机游戏开发之优化篇

    Cocos2D手机游戏开发之优化篇 在这个手机游戏盛行已久的年代,一款产品想要博得更多用户的喜爱就要在细节上做得更加到位.而游戏的优化在这里面起到了非常关键的作用.试想下,一款画面和玩法都深受用户喜欢 ...

  5. 游戏开发总结-java篇

    游戏开发总结-java篇 前言 网络通信 数据存储 逻辑开发 逻辑开发一般遇到的问题有: Java游戏服务器方面的开发要掌握的技术: java服务器目前主流框架技术 前言 Java语言,由于学习成本低 ...

  6. 开发html5 2d 赛车游戏以及打包发布为手机APP 第一话 工欲善其事

    按照昔日做给上头拿去找汽车商卖钱的一个赛车游戏APP的经验来说明这个例子(不过当然只说有关游戏的部分) 思路:一幅赛道画面,赛道上面有一辆主角车,可以由玩家用手指拖拽去控制赛车的移动 设置计时器,画面 ...

  7. 微信小游戏开发教程-2D游戏原理讲解

    微信小游戏开发教程-2D游戏原理讲解 原理 为了更加形象的描述,这里先上一张图: 背景 a. 首先,我们看到背景好像是一张无限长的图片在向下移动.实际则不然,这是一张顶部和底部刚好重叠的图片.这是一种 ...

  8. Unity 2D游戏开发教程之为游戏场景添加多个地面

    Unity 2D游戏开发教程之为游戏场景添加多个地面 为游戏场景添加多个地面 显然,只有一个地面的游戏场景太小了,根本不够精灵四处活动的.那么,本节就来介绍一种简单的方法,可以为游戏场景添加多个地面. ...

  9. CutJS – 用于 HTML5 游戏开发的 2D 渲染引擎

    CutJS 是轻量级的,快速的,基于 Canvas 开发的 HTML5  2D 渲染引擎,可以用于游戏开发.它是开源的,跨平台的,与现代的浏览器和移动设备兼容.CutJS 提供了一个类似 DOM 树的 ...

  10. HTML5游戏开发(三):使用webpack构建TypeScript应用

    <HTML5游戏开发>系列文章的目的有:一.以最小的成本去入门egret小项目开发,官方的教程一直都是面向中重型:二.egret可以非常轻量:三.egret相比PIXI.js和sprite ...

最新文章

  1. 精通python网络爬虫-精通python网络爬虫
  2. hadoop学习-倒排索引
  3. 修饰符(public/private/default/protected)
  4. struts2 jsp跳转action 404_Struts2 学习笔记(三)
  5. 在maven项目中打开jsp_零基础在intellij中打开一个项目复制粘贴内容即可运行的java拼图...
  6. 对python的功能和扩展功能的认知_Python基础-基础认知和库了解
  7. 数据结构与算法之树的进阶
  8. 【组合数学】递推方程 ( 有重根递推方程求解问题 | 问题提出 )
  9. static与final关键字
  10. cad卸载工具_如何卸载AutoCAD 附上清理注册表方法
  11. 水滴动态IP:自动换IP软件哪个好用?换IP的原理是什
  12. 《手把手教你读财报》- 读书总结
  13. 奶茶封口膜-市场现状及未来发展趋势
  14. python服务器环境搭建_python服务器环境搭建(2)——安装相关软件
  15. 苹果电脑卸载顽固图标
  16. 从零开始设计RISC-V处理器——单周期处理器的设计
  17. 解决kubelet报错:kubelet.go:2183] node “k8s-20-52“ not found
  18. bullmind在线思维导图软件
  19. 领克全球化战略扩展至中东;万豪国际集团2023年将在全球开设超过35家奢华酒店 | 美通企业日报...
  20. IP route 和 route的区别

热门文章

  1. 活动延期 | Triton Meetup 2022
  2. struts2漏洞学习记录
  3. 取一个数的各个数位的数字
  4. 【布局优化】基于改进粒子群算法实现充电桩选址优化问题附matlab代码
  5. 爱奇艺校招模拟类型题目区间表达式
  6. 在 Nuxt.js 和 Vue.js 项目中引入第三方字体或艺术字
  7. AcrGIS Pro一键出图
  8. RK3568 ov5695摄像头
  9. SolidWorks2022安装步骤
  10. java 时间类_Java日期和时间类简介