本系列文章由七十一雾央编写,转载请注明出处。
http://blog.csdn.net/u011371356/article/details/9332377
作者:七十一雾央 新浪微博:http://weibo.com/1689160943/profile?rightmod=1&wvr=5&mod=personinfo
上一节笔记中,我们讲解了键盘响应和鼠标响应,实现了对于玩家的操作,程序做出正确的响应。但是大家在玩游戏的过程中,应该会注意到,在大家没有操作的时候,程序的画面仍然不是静止的,比如NPC会四处走动,怪物仍然会跑过来攻击玩家等,也就是说,画面仍然在随着时间不断的更新。这一点在程序中对应的就是无时无刻不在更新着游戏信息和绘制画面,以便及时的反映出游戏的状态。
在WIN 32程序中,一般大家会把绘制程序放到消息循环之中,但是在MFC中由于对WIN 32高度的封装和消息印射机制,我们很难找到消息循环的位置,所以我们就需要采用别的办法。大家现在已经知道,我们绘图都是在OnPaint里进行的,那么我们不断的执行OnPaint函数不就行了吗?实现的方法就是今天要讲解的定时器了。
定时器(Timer)对象可以每隔一段时间发出一个时间消息,程序一旦接收到此消息之后,便可以决定接下来要做哪些事情。雾央先说一下定时器大概会有5毫秒左右的误差,精度不够,在实际游戏开发中,很少使用到,但是对于我们初学者来说,这个对于游戏性几乎没有任何影响,还很方便大家的开发,所以我们仍然使用了定时器。
下面来介绍如何建立与删除定时器。
1.建立定时器
Windows API 的SetTimer()函数可为窗口建立一个定时器,并每隔一段时间就发出WM_TIMER消息,此函数的定义是
UINT_PTRSetTimer(
UINT_PTRnIDEvent, //定时器代号
UINTuElapse, //时间间隔
TIMERPROClpTimerFunc //处理函数
);
SetTimer()函数的第1个参数是定时器的代号,这个代号在同一个窗口中必须是唯一的,且值不为0,第2个参数则是定时器发出WM_TIMER消息的时间间隔:第3个参则用于设定由系统调用处理WM_TIMER消息的相应函数,如果不用响应函数处理WM_TIMER消息,则此参数应设为NULL。
一句话概括,就是SetTimer函数会创建一个ID为第一个参数的定时器,它每隔第二个参数的时间就会执行一次第三个参数指向的函数。
如果不需要自己定义处理函数,第三个参数设置为NULL,我们可以使用默认的消息处理函数。
下面是设定一个每隔100毫秒发出WM_TIMER消息的定时器的程序代码。
SetTimer(1,100,NULL);
2.删除定时器
定时器建立后,就会一直自动地按照定义设定的时间间隔发出WM_TIMER消息,如果要停用某个定时器,必须使用下面的这个函数:
BOOL KillTimer(int 定时器代号);
在MFC中,大家要使用定时器,需要先通过类向导添加“WM_TIMER”消息,添加的具体过程如果有不会的同学请阅读上一节笔记:鼠标响应和键盘响应。
在添加完定时器消息后,CChildView.cpp中会出现
void CChildView::OnTimer(UINT_PTR nIDEvent)
这个函数,这就是定时器消息处理函数了,它的参数nIDEvent就是表示执行OnTimer函数的定时器的ID了。
雾央要强调一下关于创建定时器的位置,大家基本可以在任何地方创建,比如在OnPaint中等,但是千万不要在PreCreateWindow函数中创建定时器,否则大家就会发现程序一运行就会弹出来一个出错框了。
如果大家希望在窗口一创建的时候就创建定时器,比如驱动我们窗口绘制的定时器等,那么我们可以添加“WM_CREATE”消息,在这里面进行创建。
在示例程序中我们要实现的是按下T键人物自动向右移动,按下I键定值移动。大家如果自己运行一下程序,就会感觉这有几分动画的影子了。事实上,如果让人物移动的时候,变化一下图片,比如几张跑动的图片不断的切换,那么就是一个真正意义上的动画了。
另外,雾央有一个感到非常抱歉的事情要和大家说明一下,在之前的代码中,雾央漏掉了一句很重要的代码,在OnPaint函数中释放DC即ReleaseDC之前要加上ValidateRect(&m_client);这个函数的作用是使绘图区变得有效。在windows中,如果我们的窗口被遮挡了什么的,窗口那部分就变得无效,就会产生WM_PAINT消息,当绘制完毕后,必须要使窗口变得有效,否则系统将周而复始的产生WM_PAINT消息,使得CPU占用率非常高,而且还会出现很多莫名其妙的问题,比如使用MessageBox会导致程序失去响应等。
下面贴代码
头文件
// ChildView.h : CChildView 类的接口
//
#pragma once
// CChildView 窗口
class CChildView : public CWnd
{
// 构造
public:
CChildView();
// 特性
public:
CRect m_client; //保存客户区大小
CRect m_heroPos; //保存英雄的位置
CImage m_hero; //英雄
CImage m_bg; //背景图片
// 操作
public:
// 重写
protected:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
// 实现
public:
virtual ~CChildView();
// 生成的消息映射函数
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnTimer(UINT_PTR nIDEvent);
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
};
CPP文件
// ChildView.cpp : CChildView 类的实现
//
#include "stdafx.h"
#include "GameMFC.h"
#include "ChildView.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
//定时器的名称用宏比较清楚
#define TIMER_PAINT 1
#define TIMER_HEROMOVE 2
// CChildView
CChildView::CChildView()
{
}
CChildView::~CChildView()
{
}
BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
ON_WM_KEYDOWN()
ON_WM_LBUTTONDOWN()
ON_WM_TIMER()
ON_WM_CREATE()
END_MESSAGE_MAP()
//将png贴图透明
void TransparentPNG(CImage *png)
{
for(int i = 0; i <png->GetWidth(); i++)
{
for(int j = 0; j <png->GetHeight(); j++)
{
unsigned char* pucColor = reinterpret_cast<unsigned char *>(png->GetPixelAddress(i , j));
pucColor[0] = pucColor[0] * pucColor[3] / 255;
pucColor[1] = pucColor[1] * pucColor[3] / 255;
pucColor[2] = pucColor[2] * pucColor[3] / 255;
}
}
}
// CChildView 消息处理程序
BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CWnd::PreCreateWindow(cs))
return FALSE;
cs.dwExStyle |= WS_EX_CLIENTEDGE;
cs.style &= ~WS_BORDER;
cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
//加载背景
m_bg.Load("bg.png");
//加载英雄图片
m_hero.Load("hero.png");
TransparentPNG(&m_hero);
//设置英雄初始位置
m_heroPos.left=100; //人物左边贴在100的位置
m_heroPos.right=100+60; //人物的右边等于左边加上人物的宽度
m_heroPos.top=400;
m_heroPos.bottom=400+60;
return TRUE;
}
void CChildView::OnPaint()
{
//获取窗口DC指针
CDC *cDC=this->GetDC();
//获取窗口大小
GetClientRect(&m_client);
//贴背景
m_bg.Draw(*cDC,m_client);
//贴英雄
m_hero.Draw(*cDC,m_heroPos);
//在绘制完图后,使窗口区有效
ValidateRect(&m_client);
//释放DC
ReleaseDC(cDC);
}
//按键响应函数
void CChildView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
//nChar表示按下的键值
switch(nChar)
{
case 'd': //游戏中按下的键当然应该不区分大小写了
case 'D':
m_heroPos.left+=10; //向右移动10个像素的单位
m_heroPos.right+=10; //左边和右边都要移动哦
break;
case 'a':
case 'A':
m_heroPos.left-=10;
m_heroPos.right-=10;
break;
case 'w':
case 'W':
m_heroPos.top-=10;
m_heroPos.bottom-=10;
break;
case 's':
case 'S':
m_heroPos.top+=10;
m_heroPos.bottom+=10;
break;
case 't':
case 'T': //创建定时器
SetTimer(TIMER_HEROMOVE,100,NULL);
break;
case 'i':
case 'I': //撤销定时器
KillTimer(TIMER_HEROMOVE);
}
}
//鼠标左键单击响应函数
void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
m_heroPos.left=point.x;
m_heroPos.right=m_heroPos.left+60;
m_heroPos.top=point.y;
m_heroPos.bottom=m_heroPos.top+60;
}
//定时器响应函数
void CChildView::OnTimer(UINT_PTR nIDEvent)
{
switch(nIDEvent)
{
case TIMER_PAINT:OnPaint();break; //若是重绘定时器,就执行OnPaint函数
case TIMER_HEROMOVE: //控制人物移动的定时器
{
m_heroPos.left+=10;
m_heroPos.right+=10;
}
break;
}
}
int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
//创建一个10毫秒产生一次消息的定时器
SetTimer(TIMER_PAINT,10,NULL);
return 0;
}
《MFC游戏开发》笔记五到这里就结束了,更多精彩请关注下一篇。如果您觉得文章对您有帮助的话,请留下您的评论,点个赞,能看到你们的留言是我最高兴的事情,因为这让我知道我正在帮助曾和我一样迷茫的少年,你们的支持就是我继续写下去的动力,愿我们一起学习,共同努力,复兴国产游戏。
对于文章的疏漏或错误,欢迎大家的指出。
分享到:
相关推荐
MFC定时器的使用MFC定时器的使用MFC定时器的使用MFC定时器的使用MFC定时器的使用MFC定时器的使用MFC定时器的使用
MFC笔记 MFC笔记 MFC笔记 MFC笔记 MFC笔记 MFC笔记
vs2019 MFC 多媒体定时器 ms级定时器
雾央的《MFC游戏开发》系列教程 这是笔记七 场景滚动的实现源代码 欢迎大家阅读配套博文 http://blog.csdn.net/u011371356/article/details/9344721
MFC中利用多线程实现定时器,对于学习多线程和 ontimer的同学有用
C++默认的定时器,好像精度不大,如果精确到毫秒(ms),使用媒体定时器,multimedia timer
《MFC游戏开发》笔记十 碰撞检测进阶的源代码 欢迎大家阅读博文 http://blog.csdn.net/u011371356/article/details/9394465
本人开发此工具目的有两个,一、熟悉MFC空间操作,熟悉MFC定时器的操作和使用。二、由于本人整天对着电脑,经常容易忘记时间,如果自己做一个定时器,能够在设定时间提醒自己该做什么事。那可是一举多得的事情。最后...
较为详细地介绍了MFC定时器的使用方法,希望能对就定时器不是很清楚的朋友有帮助
mfc 定时器简单用法的小例子
讲述图像双缓冲技术,并实现一个游戏动画demo 《MFC游戏开发》源代码 http://blog.csdn.net/u011371356 欢迎大家关注
《MFC游戏开发》笔记三 透明贴图 配套源代码 欢迎大家关注配套博文:http://blog.csdn.net/u011371356/article/details/9313239#reply
《MFC游戏开发》笔记八 游戏特效的实现(二):粒子系统 配套源代码 欢迎大家关注 配套博文地址http://blog.csdn.net/u011371356/article/details/9360993
MFC 通过定时器实现space键的暂停和开始 , 该方法原理能够实现MFC图片幻灯片暂停/开始播放.
Visual Studio 2008 开发MFC dlgTimer定时器
来自于浅谈 QT 中的 QTimer、QTimerEvent 与 MFC 中的 OnTimer。QT于MFC的定时器的Demo,可以继续扩展。更多的详细部分,请看个人的博客 http://blog.csdn.net/qq_18286031
MFC定时器的用法,包含中文注释以及简单的用法介绍。VC6直接编译运行,其他VS双击打开自动完成转换。
达内MFC课程完整笔记
深入浅出MFC读书笔记3 深入浅出MFC 读书笔记
用MFC写的简易定时器,开始时设置倒计时秒数,提醒时间。双击开始倒计时,单击暂停,右键复位。 希望可以有些用处。