走在游戏开发道路上的青年 —— BlueCoder(黎小华)的博客

湛是清澈的水底 , 蓝是美丽的天空 , 这也是我所喜欢的代码风格

【VC++游戏开发#五】2D篇 —— 游戏之二:看看你能坚持多少秒

本文由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();
}


 

五、零积分源码下载

点击下载源代码

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/crocodile_/article/details/14556113
所属专栏: VC++游戏开发
想对作者说点什么? 我来说一句

vc++经典游戏编程

2009年12月15日 12.63MB 下载

VC++游戏开发实例教程

2010年05月18日 17.71MB 下载

没有更多推荐了,返回首页

不良信息举报

【VC++游戏开发#五】2D篇 —— 游戏之二:看看你能坚持多少秒

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭