(三)MFC学习之动画

版权声明:本文为博主原创文章,转载请注明原文链接 https://blog.csdn.net/qq_36922927/article/details/83178930

环境:vs2017+windows10+mfc

动画实现原理:

游戏中动画实现的方式主要有两种,

1,直接播放视频文件

2,连续显示稍有差比的图片序列来产生动画效果,称为程序动画,是用代码来控制动画

逐帧动画实现原理:

使用定时器创建游戏刷新循环,一般不使用多个定时器,多个定时器可能会出现某一时刻需要刷新多个动画,会造成混乱

需要用到得到类:

CMyApp(继承自CWinApp)和CMyWnd(继承自CFrameWnd)这在上一篇博客已经说到了

其余的是需要定时器:

Timer:

CWnd(CFrameWnd的父类)有创建和销毁Timer的方法

CWnd  定时器的两个方法:

使用 SetTimer()方法来启动定时器

先搜索 

在CWnd的members页面ctrl+f搜索SeetTimer

看看这两个方法的用法:

SetTimer

 
UINT_PTR SetTimer(
   UINT_PTR nIDEvent,//无符号整数,去非0的值,作为定时器的唯一标识
   UINT nElapse,//毫秒,定时器执行的周期
   void (CALLBACK* lpfnTimer//回调函数,暂时不管,一般传递NULL即可
)(HWND,
   UINT,
   UINT_PTR,
   DWORD
) 
);
 

KillTimer:

BOOL KillTimer(
   UINT_PTR nIDEvent //对应SetTimer那里指定的id
);

涉及到贴图,在上一篇博客已经说明,这里就默认你会了·

开始代码编写:

#include<afxwin.h>
class MyWnd :public CFrameWnd {

private :
	CDC * m_Dc;
	CBitmap * m_Bitmap;//小偷
	CBitmap * m_BackG;//背景
	int m_nFrameWndNo;//定时器计数,用于判断贴哪一张小偷位图
	int x;//改变绘制小偷的x方向位置,让其移动

public :
	MyWnd() {
		Create(NULL, "WINDOW");
		MoveWindow(0, 0, 800, 600);//指定窗口位置和大小

		CClientDC dc(this);
		m_Dc = new CDC;
		m_Dc->CreateCompatibleDC(&dc);
		//背景
		m_BackG = new CBitmap;
		m_BackG->m_hObject = LoadImage(NULL, "BackGround960x640.bmp", IMAGE_BITMAP, 0, 0,LR_LOADFROMFILE);

		//小偷
		m_Bitmap = new CBitmap;

		m_Bitmap->m_hObject = LoadImage(NULL, "crimer2.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
		m_nFrameWndNo = 0;
		SetTimer(1,100,NULL);
		x = 400;
	}
	DECLARE_MESSAGE_MAP()
	afx_msg void OnPaint();
	afx_msg void OnTimer(UINT_PTR nIDEvent);
};


class MyApp :public CWinApp {
	BOOL InitInstance();
};

BOOL MyApp::InitInstance() {
	//创建窗口
	MyWnd * pf = new MyWnd;
	  

	//pf->Create(NULL, "贴图测试window");
	pf->ShowWindow(m_nCmdShow);
	pf->UpdateWindow();
	this->m_pMainWnd = pf;
	return TRUE;
}

MyApp TheApp; BEGIN_MESSAGE_MAP(MyWnd, CFrameWnd)
ON_WM_PAINT()
ON_WM_TIMER()
END_MESSAGE_MAP()


void MyWnd::OnPaint()
{
	CPaintDC dc(this); // device context for painting
					   // TODO: 在此处添加消息处理程序代码
					  //如何贴换掉上次的画面呢?
//简单高效的处理,再贴一次背景,上一次的就被掩盖了
	//贴背景
m_Dc->SelectObject(m_BackG);
	dc.BitBlt(0, 0, 800, 600, m_Dc, 0, 0, SRCCOPY);
//选取小偷
	m_Dc->SelectObject(m_Bitmap);

	switch (m_nFrameWndNo) {
	case 1:
      
        //贴小偷动作1
		dc.BitBlt(x, 300, 63, 154, m_Dc, 63, 0, SRCAND);
		dc.BitBlt(x, 300, 63, 154, m_Dc, 0, 0, SRCPAINT);
		break;
	case 0:
          //贴小偷动作2
		dc.BitBlt(x, 300, 63, 154, m_Dc, 63, 154, SRCAND);
		dc.BitBlt(x, 300, 63, 154, m_Dc, 0, 154, SRCPAINT);
		break;

	default:
		break;
	}
//  处理小偷x方向移动
	if (x <= 0) {
		x = 400;
	}
	else {
		x -= 10;
	}
	m_nFrameWndNo = ((0 == m_nFrameWndNo) ? 1 : 0);


}


void MyWnd::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	
	
	//让窗口重绘
	Invalidate();
	
	
	CFrameWnd::OnTimer(nIDEvent);
}

代码中的两个方法:

MoveWindow:给变窗口的位置和大小,具体文档说明可见参考msdn文档

Invalidate():这是CWnd的一个方法:

传递参数,默认值是TRUE,

The client area is marked for painting when the next WM_PAINT message occurs

当下一个绘制窗口消息出现之前,客户区域标记为 绘制,

 
void Invalidate(
   BOOL bErase = TRUE 
);
 

思路总结:

1,CMyWnd的构造方法中,初始化成员变量,主要是加载图片到内存

2,CMyApp的InitInstance方法中实例化一个CMyWnd对象,将其交给CMyApp的一个成员指针m_pMainWnd

3,处理绘图逻辑,在OnPaint方法中处理贴图逻辑

另一种方法:

绘图逻辑可以不使用OnPaint函数,迁移到OnTimer方法中

需要注意的是:在InitInstance方法之外的地方需要使用CClientDc来创建dc


void MyWnd::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	
	CClientDC dc(this);//在InitInstance方法之外的地方需要使用CClientDc来创建dc
	m_Dc->SelectObject(m_BackG);
	dc.BitBlt(0, 0, 800, 600, m_Dc, 0, 0, SRCCOPY);
	m_Dc->SelectObject(m_Bitmap);
	switch (m_nFrameWndNo) {
	case 1:
		
		
		dc.BitBlt(x, 300,  63,154, m_Dc,63,0, SRCAND);
		dc.BitBlt(x, 300, 63, 154, m_Dc, 0, 0, SRCPAINT);
		break;
	case 0:
		
		dc.BitBlt(x, 300, 63, 154, m_Dc, 63, 154, SRCAND);
		dc.BitBlt(x, 300, 63, 154, m_Dc, 0,154, SRCPAINT);
		break;

	default:
		break;
	}
	if (x <= 0) {
		x = 400;
	}
	else {
		x -= 10;
	}
	m_nFrameWndNo = ((0 == m_nFrameWndNo) ? 1 : 0);

	

	

	
	
	CFrameWnd::OnTimer(nIDEvent);
}

效果:

给出几张截图吧:

至此,一个踏着小碎步的小偷就跑起来了!

猜你喜欢

转载自blog.csdn.net/qq_36922927/article/details/83178930