本文由BlueCoder编写 转载请说明出处:
http://blog.csdn.net/crocodile__/article/details/14556113
我的邮箱:bluecoder@yeah.net 欢迎大家和我交流编程心得
我的微博:BlueCoder_黎小华 欢迎光临^_^
前段时间在网上看见了一个小游戏——看看你能坚持多少秒——考你的敏捷性,我玩了几次,然后居然超过了18秒
事后,我用MFC模拟了该游戏中方块"撞墙反弹"的效果——撞墙反弹效果
今天呢,我就继续借用上次模拟的效果,来实现这款小游戏(我简化了游戏的难度,玩起来更易上手)
下面详细讲述本游戏制作细节
一、游戏介绍及效果演示
(1). 运行程序出现一个对话框,点击"开始"按钮开始游戏
(2). 用鼠标控制红色方块的移动,要避免和蓝色方块接触,这是有些结束的判断依据
(3). 当游戏结束后,会弹出一个对话框(如下)。选择"继续"按钮继续游戏,"退出按钮退出游戏"
(4). 之前,有很多朋友说运行不起单独的exe程序——ok,这次人性化点儿,给一个提示,你就知道了
(5). 演示效果(蓝色方块的移动速度会根据你坚持的时间来改变, 10秒改变一次,20秒改变一次)
二、类视图
三、编程思想的改变
(1). 之前的风格:
之前写的游戏都是在View类中一通搞定,所以导致×××View.h和×××View.cpp文件都比较大,而且游戏的各个部分分工不明确,可读性也不太好(尽管我添加了很多注释)
(2). 现在的风格:
本次游戏的编程思想采用C++类的封装、多态、继承等特性,主要封装了两个类——CDiamond, CTimer
其中CDiamond是方块类——继承于CImage类,代表游戏中的方块,负责方块的各种操作;CTimer是计时器类——别误解,它是计时间用的类(游戏上方显示的时间),这个类很简单,就是起计时的作用
四、代码剖析
由于本次是采用面向对象的编程思想,故也就逐个类的讲解
(1).CTimer类——前面已介绍过,此类主要负责时间计时作用
定义了三个成员变量和三个成员方法:
class CTimer { private: UINT m_sec;//秒级 UINT m_ten;//十分位 UINT m_pct;//百分位(percent) public: CTimer(void); //重载运算符++来自加 CTimer& operator++(int); //获取秒数 UINT GetSecond(); //清零 void Clear(); //以字符串形式返回计时器信息 CString ToString(); ~CTimer(void); };
其中三个成员变量的作用示意图如下:
三个成员函数的代码如下:
//重载运算符++来自加 CTimer& CTimer::operator++(int)//int表示是重载的后缀运算符 { m_pct++; if(m_pct == 10)//等于10, 就需要进位 { m_ten++; if(m_ten == 10)//等于10, 就需要进位 { m_sec++; m_ten = 0; } m_pct = 0; } return *this; } //获取秒数 UINT CTimer::GetSecond() { return m_sec; } //清零 void CTimer::Clear() { m_sec = 0; m_ten = 0; m_pct = 0; } //以字符串形式返回计时器信息 CString CTimer::ToString() { CString time; time.Format(_T("%u.%u%us"), m_sec, m_ten, m_pct); return time; }
(2).CDiamond类——此类用于管理方块的各个操作(代码中注释已经够清晰了,所以我就直接贴出来)
Diamond.h头文件
#include<atlimage.h>//用到了CImage类 //方块类——继承于CImage class CDiamond : public CImage { private: CSize m_sBorder; //边界区域 CSize m_sMove; //移动位移 CPoint m_ptDmd; //方块左上角点 CRect m_rtDmd; //方块所在区域 public: CDiamond(void); //默认构造函数 void SetBorder(CSize border);//设置边界 bool IsOutBorder(CPoint pt);//判断方块是否出界 //判断是否与指定的方块相交 bool IsIntersect(CRect rect); void SetMove(CSize move);//设置移动位移 void ExpandMove(int n);//移动位移扩大n倍 void SetDmdPt(CPoint pt);//设置方块左上角点 CPoint GetDmdPt(); //获取方块左上角点 void SetDmdRect(); //设置方块所在区域 void SetDmdRect(CRect rect);//重载 CRect GetDmdRect(); //获取方块所在区域 void ChangeMove(); //改变移动位移 void MoveDiamond(); //移动方块 ~CDiamond(void); };
Diamond.h头文件Diamond.cpp#include "stdafx.h" #include "Diamond.h" //默认构造函数 CDiamond::CDiamond(void) { m_sBorder.SetSize(0, 0); m_sMove.SetSize(1, 1); m_ptDmd.SetPoint(0, 0); m_rtDmd.SetRect(0, 0, 0, 0); } //设置边界 void CDiamond::SetBorder(CSize border) { m_sBorder = border; } //判断指定的点是否出边界 bool CDiamond::IsOutBorder(CPoint pt) { //出界返回true if(pt.x < 0 || pt.x > (m_sBorder.cx - m_rtDmd.Width()) || pt.y < 0 || pt.y > (m_sBorder.cy - m_rtDmd.Height())) { return true; } //未出界, 返回false return false; } //判断是否与指定的方块相交 bool CDiamond::IsIntersect(CRect rect) { if(rect.PtInRect(CPoint(m_rtDmd.left, m_rtDmd.top)) || rect.PtInRect(CPoint(m_rtDmd.right, m_rtDmd.top)) || rect.PtInRect(CPoint(m_rtDmd.left, m_rtDmd.bottom)) || rect.PtInRect(CPoint(m_rtDmd.right, m_rtDmd.bottom))) { return true; } return false; } //设置移动位移 void CDiamond::SetMove(CSize move) { m_sMove = move; } //扩大移动位移 void CDiamond::ExpandMove(int n) { int old = (int)fabs(m_sMove.cx * 1.0); m_sMove.cx /= old; m_sMove.cy /= old; m_sMove.cx *= n; m_sMove.cy *= n; } //设置左上角点 void CDiamond::SetDmdPt(CPoint pt) { m_ptDmd = pt; } //获取左上角点 CPoint CDiamond::GetDmdPt() { return m_ptDmd; } //设置矩形区域 void CDiamond::SetDmdRect() { m_rtDmd.SetRect(m_ptDmd.x, m_ptDmd.y, m_ptDmd.x + GetWidth(), m_ptDmd.y + GetHeight()); } //重载设置矩形区域 void CDiamond::SetDmdRect(CRect rect) { m_rtDmd = rect; } //获取矩形区域 CRect CDiamond::GetDmdRect() { return m_rtDmd; } //改变移动位移 void CDiamond::ChangeMove() { //如果出了左右边界,水平反向 if(m_ptDmd.x < 0 || m_ptDmd.x > (m_sBorder.cx - GetWidth())) { m_sMove.cx = -m_sMove.cx; } //如果出了上下边界,垂直反向 if(m_ptDmd.y < 0 || m_ptDmd.y > (m_sBorder.cy - GetHeight())) { m_sMove.cy = -m_sMove.cy; } } //移动方块 void CDiamond::MoveDiamond() { m_ptDmd.x += m_sMove.cx; m_ptDmd.y += m_sMove.cy; //改变方块所在区域 SetDmdRect(); } CDiamond::~CDiamond(void) { }
(3).CHoldOnView类——用于整个游戏UI的布局和运行管理
定义了两个计时器
//定义计时器ID #define ID_MOVE 100 //控制方块移动 #define ID_INTERSECT 101//判断是否相交
成员变量
//成员变量 private: CTimer m_timer;//计时 CImage m_bk;//背景 CDiamond m_leftUp;//左上角方块 CDiamond m_rightUp;//右上角方块 CDiamond m_leftDown;//左下角方块 CDiamond m_rightDown;//右下角方块 CDiamond m_redDmd;//控制的红色方块 CPoint m_ptMoveStart;//记录鼠标移动的起点 bool m_isTwo, m_isThree;//标记位移是否扩大2、3倍 CSize m_sClient;//客户区大小
成员函数
//成员函数 private: void LoadLeftUp(); //加载左上角方块 void LoadRightUp(); //加载右上角方块 void LoadLeftDown();//加载左下角方块 void LoadRightDown();//加载右下角方块 void LoadRedDmd();//加载红色控制方块 public: void RestartGame();//重新开始游戏
初始化
CHoldOnView::CHoldOnView() { //设置客户区大小 m_sClient.SetSize(294, 414); //一开始标记位移未进行任何扩大 m_isTwo = m_isThree = false; //加载背景 m_bk.Load(_T("res\\bk.png")); if(m_bk.IsNull()) { MessageBox(_T("png加载失败, 请将程序和res文件夹放在同目录下!")); exit(0); } //加载并初始化左上角方块 LoadLeftUp(); //加载并初始化右上角方块 LoadRightUp(); //加载并初始化左下角方块 LoadLeftDown(); //加载并初始化右下角方块 LoadRightDown(); //加载红色控制方块 LoadRedDmd(); //加载开始对话框 CStartDlg startDlg; startDlg.DoModal(); }
响应鼠标消息以用鼠标控制红色方块
void CHoldOnView::OnLButtonDown(UINT nFlags, CPoint point) { //设置移动起点为当前鼠标左键按下的位置 m_ptMoveStart = point; CView::OnLButtonDown(nFlags, point); } void CHoldOnView::OnMouseMove(UINT nFlags, CPoint point) { //如果在按下了鼠标左键时拖动鼠标,那么就移动红色方块 if(nFlags == MK_LBUTTON) { //先获取红色方块所在的点 CPoint ptRed = m_redDmd.GetDmdPt(); //移动红色所在的点 ptRed += (point - m_ptMoveStart); //下一次移动的起点就是当前的终点 m_ptMoveStart = point; if(!m_redDmd.IsOutBorder(ptRed)) { //设置红色方块的位置 m_redDmd.SetDmdPt(ptRed); m_redDmd.SetDmdRect(); } } CView::OnMouseMove(nFlags, point); }
双缓冲贴图
void CHoldOnView::OnDraw(CDC* pDC) { CHoldOnDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; //创建缓冲DC、bmp CDC bufferDC; bufferDC.CreateCompatibleDC(NULL); CBitmap bufferBmp; bufferBmp.CreateCompatibleBitmap(pDC, m_sClient.cx, m_sClient.cy); bufferDC.SelectObject(bufferBmp); //贴背景 bufferDC.SetStretchBltMode(COLORONCOLOR); m_bk.StretchBlt(bufferDC, 0, 0, m_sClient.cx, m_sClient.cy, SRCCOPY); //贴各个方向上的方块 m_leftUp.BitBlt(bufferDC, m_leftUp.GetDmdPt(), SRCCOPY); m_rightUp.BitBlt(bufferDC, m_rightUp.GetDmdPt(), SRCCOPY); m_leftDown.BitBlt(bufferDC, m_leftDown.GetDmdPt(), SRCCOPY); m_rightDown.BitBlt(bufferDC, m_rightDown.GetDmdPt(), SRCCOPY); //贴红色方块 m_redDmd.BitBlt(bufferDC, m_redDmd.GetDmdPt(), SRCCOPY); //贴时间 CString time = m_timer.ToString(); CSize sTime = bufferDC.GetTextExtent(time); bufferDC.SetBkMode(TRANSPARENT); bufferDC.TextOutW((m_sClient.cx - sTime.cx) / 2, 0, time); //将缓冲DC中的图贴到客户区 pDC->BitBlt(0, 0, m_sClient.cx, m_sClient.cy, &bufferDC, 0, 0, SRCCOPY); //回收内存资源 bufferBmp.DeleteObject(); bufferDC.DeleteDC(); }
释放内存资源
void CHoldOnView::OnDestroy() { CView::OnDestroy(); //关闭计时器 KillTimer(ID_MOVE); KillTimer(ID_INTERSECT); //释放 m_bk.ReleaseGDIPlus(); m_leftUp.ReleaseGDIPlus(); m_leftDown.ReleaseGDIPlus(); m_rightUp.ReleaseGDIPlus(); m_rightDown.ReleaseGDIPlus(); }
五、零积分源码下载
点击下载源代码