【VC++游戏开发#七】2D篇 —— 物理建模(二) 重力模拟:让愤怒的小鸟来感受一次自由落体运动

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

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

我的邮箱:[email protected]    欢迎大家和我交流编程心得

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

 

临近期末,各种考试和课设都随之来临,所以博文更新进度也就慢了许多,不过今天总算抽出来一天的时间来继续聊聊2D游戏效果的那些事儿

 

今天做的效果就是生活中很常见的一个现象:自由落体运动——重力作用的测试,下面进入正题^_^……

 

一、效果演示

还是来一个gif

【VC++游戏开发#七】2D篇 —— 物理建模(二) 重力模拟:让愤怒的小鸟来感受一次自由落体运动_第1张图片

 

 

二、准备工作

(1)准备素材:本次是采用愤怒的小鸟游戏中的素材

【VC++游戏开发#七】2D篇 —— 物理建模(二) 重力模拟:让愤怒的小鸟来感受一次自由落体运动_第2张图片

【VC++游戏开发#七】2D篇 —— 物理建模(二) 重力模拟:让愤怒的小鸟来感受一次自由落体运动_第3张图片

 

(2)再来一首配套的背景音乐(还是取自愤怒的小鸟游戏)

(3)VS2010创建的MFC工程的类视图

【VC++游戏开发#七】2D篇 —— 物理建模(二) 重力模拟:让愤怒的小鸟来感受一次自由落体运动_第4张图片

 

 

 

三、实现细节

        由于今天实现的效果是重力的测试,那么要想模拟出重力作用的效果,还是要点儿物理知识(不过这很简单),就是加速度有关的重要推论:(初速度为0的匀加速运动的一个重要推论)

第1s内,第2s内,第3s内……第ns内的位移之比为1:3:5:……:(2n-1)

       可见这是一个等差数列,换句话说就是线性关系——即第ns内走过的位移比第(n-1)s内走过的位移始终多一个定长,可以证明到就是加速度a的值:

初速度为0的匀加速直线运动的位移公式为:S(t) = 1/2 * a * t^2 (S位移, a加速度, t时间)

第ns内走过的位移:S<n> = S(n) - S(n-1) = 1/2 * a * (2n - 1) 

第(n-1)s内走过的位移:S<n-1> = S(n-1) - S(n-2) = 1/2 * a * (2n - 3)

S<n> - S<n-1> = a

       根据这个结论,我们就可以轻松模拟自由落体运动了,下面结合一段核心代码来理解一下:

	m_bird.yMove = m_bird.yMove + m_bird.gravity;
	m_bird.y += m_bird.yMove;


 

其中yMove是y轴上每1s内移动的位移,y就是纵坐标,gravity就是重力加速度(不变的值)

只要每一次计时器消息到来时,我们就可以此来更新小鸟的纵坐标y,就能到达自由落体的效果

 

 

四、代码剖析

(1).CChildView.h头文件中的重要内容:

//计时器ID
#define ID_TIMER 100

//愤怒的小鸟
typedef struct bird
{
	int x;//x坐标
	int y;//y坐标
	int xMove;//x轴上的位移分量
	int yMove;//y轴上的位移分量
	int gravity;//重力加速度
}BIRD;

 

// 特性
public:
	static const int DOWNBORDER = 42;
private:
	CImage m_bg;//背景
	CImage m_imgBird;//小鸟png
	BIRD   m_bird;//关联m_imgBird的参数结构体

	CSize  m_sClient;//客户区大小
	CSize  m_sBird;//小鸟png大小
	CString m_status;//小鸟运动状态
	CString m_state;//说明

// 操作
public:
	//初始化
	bool Initialize();
	//移动小鸟
	void MoveBird();
	//绘制小鸟状态以及说明
	void DrawString(CDC&);
	//绘制小鸟以及场景
	void PaintScene(CDC*);

 

(2). 初始化操作函数

//初始化
bool CChildView::Initialize()
{
	m_bg.Load(L"res\\bg.jpg");
	m_imgBird.Load(L"res\\bird.png");

	//如果初始化失败
	if(m_bg.IsNull() ||
		m_imgBird.IsNull())
	{
		return false;
	}

	m_sClient.SetSize(790, 568);
	m_sBird.SetSize(50, 50);

	//实例化小鸟结构体
	m_bird.x = (m_sClient.cx - m_sBird.cx) / 2;
	m_bird.y = - m_sBird.cy;
	m_bird.xMove = 0;
	m_bird.yMove = 0;
	m_bird.gravity = 4;

	//初始状态小鸟处于下降的运动状态以及说明
	m_status = "运动状态:下降";
	m_state = "说明:\n(1)鼠标左键重新测试\n(2)按下Esc键退出程序.";

	//打开并播放背景音乐
	mciSendString(L"open res\\愤怒的小鸟.mp3 alias bgm",
		NULL, 0, NULL);
	mciSendString(L"play bgm repeat", NULL, 0, NULL);

	return true;
}


 

(3). 移动小鸟

//移动小鸟
void CChildView::MoveBird()
{
	//更新y轴上的每单位时间内的位移分量和y坐标
	m_bird.yMove = m_bird.yMove + m_bird.gravity;
	m_bird.y += m_bird.yMove;

	//更改运动状态
	m_status = "运动状态:  ";
	m_status += m_bird.yMove > 0 ?
		"下降":"上升";

	//如果小鸟碰到下边界, 速度反向
	if(m_bird.y >= 
		(m_sClient.cy - m_sBird.cy - DOWNBORDER))
	{
		m_bird.y = m_sClient.cy - 
			m_sBird.cy - DOWNBORDER;

		//------------------------速度反向---------------------------
		//由于实际中,物体与地面碰撞反弹时会有动能损失(转换为了热能)
		//因此反向的速度应该小于原来的速度(这里是指绝对值), 故应该乘上4/5,
		//其中1/5就是转换为了热能
		//-----------------------------------------------------------
		m_bird.yMove = - m_bird.yMove * 4 / 5;

		//如果速度为0, 则小鸟停止运动了(关闭计时器)
		if(m_bird.yMove == 0)
		{
			m_status = "运动状态:  静止";
			KillTimer(ID_TIMER);
		}
	}
}

 

(4). 绘制小鸟的运动状态和程序说明

//绘制小鸟状态以及说明
void CChildView::DrawString(CDC &cdc)
{
	//更改字体和文本背景模式
	CFont font;
	font.CreatePointFont(150, L"微软雅黑");
	cdc.SelectObject(font);
	cdc.SetBkMode(TRANSPARENT);

	CSize size;
	size = cdc.GetTextExtent(m_state);
	size.cx /= 2;
	size.cy *= 3;
	CRect rect(CPoint(5,
		m_sClient.cy - size.cy * 5/3),
		size);

	//小鸟运动状态
	cdc.SetTextColor(RGB(100, 230, 0));
	cdc.TextOutW(5,
		(m_sClient.cy - size.cy * 2),
		m_status);

	//程序说明
	cdc.SetTextColor(RGB(250, 30, 20));
	cdc.DrawText(m_state, rect, 
		DT_WORDBREAK | DT_EDITCONTROL | DT_LEFT);
}



 

(5). 在响应WM_PAINT消息的消息映射函数OnPaint中绘制整个场景

//绘制场景
void CChildView::PaintScene(CDC *pDC)
{
	CDC bufferDC;
	CBitmap bufferBmp;

	//----------------双缓冲贴图-----------------------

	bufferDC.CreateCompatibleDC(NULL);
	bufferBmp.CreateCompatibleBitmap(pDC,
		m_sClient.cx, m_sClient.cy);
	bufferDC.SelectObject(bufferBmp);

	//绘制背景
	bufferDC.SetStretchBltMode(COLORONCOLOR);
	m_bg.StretchBlt(bufferDC, 0, 0, 
		m_sClient.cx, m_sClient.cy, SRCCOPY);

	//绘制小鸟状态以及说明
	DrawString(bufferDC);

	//绘制小鸟
	m_imgBird.TransparentBlt(bufferDC, m_bird.x, m_bird.y, 
		m_sBird.cx, m_sBird.cy, RGB(0, 162, 232));

	//将内存中的图统一绘制到客户区
	pDC->BitBlt(0, 0, m_sClient.cx, m_sClient.cy, 
		&bufferDC, 0, 0, SRCCOPY);

	//释放内存
	bufferBmp.DeleteObject();
	bufferDC.DeleteDC();
}


 

(6). 在响应WM_CREATE消息的消息映射函数中进行初始化操作

int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;

	//初始化
	if(!Initialize())
	{
		AfxMessageBox(L"图片加载失败");
		exit(0);
	}

	//设置计时器
	SetTimer(ID_TIMER, 40, NULL);

	return 0;
}


 

(7). 在响应WM_TIMER消息的消息映射函数中移动小鸟

void CChildView::OnTimer(UINT_PTR nIDEvent)
{
	//移动小鸟
	MoveBird();

	//重绘客户区
	InvalidateRect(NULL, false);

	CWnd::OnTimer(nIDEvent);
}

 

(8). 以下是一些一般消息的响应

void CChildView::OnLButtonDown(UINT nFlags, CPoint point)//鼠标左键消息
{

	//实例化小鸟结构体
	m_bird.x = m_bird.x = (m_sClient.cx - m_sBird.cx) / 2;
	m_bird.y = - m_sBird.cy;
	m_bird.xMove = 0;
	m_bird.yMove = 0;
	m_bird.gravity = 4;

	//先关闭计时器
	KillTimer(ID_TIMER);
	//设置计时器
	SetTimer(ID_TIMER, 40, NULL);

	CWnd::OnLButtonDown(nFlags, point);
}


void CChildView::OnDestroy()//窗口销毁消息
{
	CWnd::OnDestroy();

	//关闭计时器和音乐
	KillTimer(ID_TIMER);
	mciSendString(L"close bgm", NULL, 0, NULL);

	//回收内存资源
	m_bg.Destroy();
	m_imgBird.Destroy();
}


void CChildView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)//按键消息
{
	//按下Esc键退出程序
	if(nChar == VK_ESCAPE)
	{
		exit(0);
	}

	CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}

 


 

五、零积分资源下载

点击下载源代码

 

 

在结束之前,送大家一句话(和大家共勉):

人的一生可以不去拥有很多东西,但唯独不能失去的是希望

——摘自欧亨利《最后一片叶子》

       我们都曾因为实际生活不为自己所想而迷茫过、失落过,甚至放弃过。没关系,我们不能改变环境,就只能迫使自己去适应环境——只要你心中始终保持着那份纯真和坚定不移的希望,相信自己总有一天会成功

 

 

ok,本次游戏效果模拟到此结束,下次我们再会( ^_^ )/~~

————

先透露一下"剧情"哈:下次模拟一个跑酷游戏的Demo,敬请期待吧:)

你可能感兴趣的:(游戏,C++,mfc,愤怒的小鸟,自由落体)