本系列文章由七十一雾央编写,转载请注明出处。

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游戏开发》笔记五 定时器和简单动画的更多相关文章

  1. 《MFC游戏开发》笔记四 键盘响应和鼠标响应:让人物动起来

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9327377 作者:七十一雾央 新浪微博:http:// ...

  2. 《MFC游戏开发》笔记九 游戏中的碰撞判定初步&怪物运动简单AI

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9374935 作者:七十一雾央 新浪微博:http:// ...

  3. 《MFC游戏开发》笔记八 游戏特效的实现(二):粒子系统

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9360993 作者:七十一雾央 新浪微博:http:// ...

  4. 《MFC游戏开发》笔记三 游戏贴图与透明特效的实现

    本系列文章由七十一雾央编写,转载请注明出处. 313239 作者:七十一雾央 新浪微博:http://weibo.com/1689160943/profile?rightmod=1&wvr=5 ...

  5. 《MFC游戏开发》笔记十 游戏中的碰撞检测进阶:地图类型&障碍物判定

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9394465 作者:七十一雾央 新浪微博:http:// ...

  6. 《MFC游戏开发》笔记七 游戏特效的实现(一):背景滚动

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9344721 作者:七十一雾央 新浪微博:http:// ...

  7. 《MFC游戏开发》笔记六 图像双缓冲技术:实现一个流畅的动画

    本系列文章由七十一雾央编写,转载请注明出处.  http://blog.csdn.net/u011371356/article/details/9334121 作者:七十一雾央 新浪微博:http:/ ...

  8. 《MFC游戏开发》笔记二 建立工程、调整窗口

    本系列文章由七十一雾央编写,转载请注明出处.  http://blog.csdn.net/u011371356/article/details/9300383 作者:七十一雾央 新浪微博:http:/ ...

  9. [置顶] 《MFC游戏开发》笔记一 系列简介

    本系列文章由七十一雾央编写,转载请注明出处.  http://blog.csdn.net/u011371356/article/details/9299121 作者:七十一雾央 新浪微博:http:/ ...

随机推荐

  1. oracle学习 八 分页(使用rownumber)(持续更)

    rownumber是查询的数据集之后加入一个伪列(连续的)使用它可以去制作以oracle数据库为基础的分页,语句类似于公式直接套用如下: select * from (select rownum r, ...

  2. hdoj 5335 Walk Out

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5335 #include<stdio.h> #include<cstring> ...

  3. Excel数据通过plsql导入到Oracle

    Excel数据通过plsql导入到Oracle 1 准备Excel导入数据 2 把Excel文件另存为(文本文件(制表符分隔)(*.txt)) 或者将Excel文件另存为(Unicode文本) 之后唯 ...

  4. ASP.NET MVC- Upload File的例子

    上传文件是一项基本功能,一定要了解的.先来看一下使用ASP.NET MVC实现简单的上传. 一.简单的例子 Controller的例子 public ActionResult UploadDemo() ...

  5. C:数组

    数组.排序 关于排序 :参考 关于数组: 参考 求a[i][j]行与列的和然后求平均值 参考 二维数组使用指针的表示方法  参考 字符串数组:char  name [5][20] ={ {} , {} ...

  6. 面试过程中javascript原型链与作用域的问题

    现在校招也基本结束了,所以有时间把这段时间遇到的问题做个总结.在很多的笔试题目中,发现有很多对JS作用域方面的考察,所以查阅资料总结一下. 众所周知,js不像其他OOP语言那样,他是一种弱类型的语言, ...

  7. (剑指Offer)面试题17:合并两个排序的链表

    题目: 输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然时按照递增排序的. 链表结点定义如下: struct ListNode{ int val; ListNode* next; }; 思 ...

  8. android手机ping不通linux的ip地址

    我的linux是装载虚拟机里的,修改虚拟机的网络连接方式为桥接模式即可.

  9. hdu5071 2014 Asia AnShan Regional Contest B Chat

    模拟题: add的时候出现过的则不再添加 close的时候会影响到top rotate(Prior.Choose)的时候会影响到top /*============================== ...

  10. 对.NET中Hashtable和ArryList的理解

    1.HashTabel 在.NET Framework中,Hashtable是System.Collections命名空间提供的集合对象,同时它也是一个可变长的数组,用于处理和表现类似key/valu ...