VC++游戏编程----基础动画显示1

来源:互联网 发布:模拟定位软件 编辑:程序博客网 时间:2024/05/16 09:31

一、定时器

Timer定时器对象可以再每隔一段时间发出一个时间消息,程序收到消息后,就可以执行一些操作。比如,可以设置定时器来播放静态的连续图片,就可以产生动画效果。这也是通常显示动画的一种方式。

Windows API中有这样的函数:SetTimer()为我们定义一个定时器。函数原型:

UINT_PTR SetTimer(  HWND hWnd,              // 窗口句柄  UINT_PTR nIDEvent,      // 定时器代号  UINT uElapse,           // 时间设定的值,单位为毫秒  TIMERPROC lpTimerFunc   // 定时器响应函数);


这里举一个小例子:

SetTimer(hwnd,                  // 窗口句柄          IDT_TIMER1,            // 代号         10000,                 // 10秒        (TIMERPROC) NULL);      // 没有响应函数 SetTimer(hwnd,                      IDT_TIMER2,                     5000,                        (TIMERPROC) NULL);     case WM_TIMER:     switch (wParam)     {         case IDT_TIMER1:             // 执行10秒的操作             return 0;          case IDT_TIMER2:             // 执行5秒的操作            return 0; }

创建后自然需要删除定时器。KillTimer()就是用来终止某个定时器的

 

BOOL KillTimer(  HWND hWnd,          // 窗口句柄  UINT_PTR uIDEvent   // 定时器代号);


 

运用定时器使预先做好的连续的静态图片播放,形成动画的效果。
新建Win32程序,在VS2008中默认使用默认的生成窗口的代码。

这里顺便提一提,在VS中使用多字节字符集设置可以在解决方案资源管理器中右击方案,选择属性,打开的属性页里可以设置。

如图:


 


接下来,在程序头部添加全局变量:

HBITMAP girl[7]; // 用于7张人物的位图数组HDC mdc,hmdc;int num; // 用于计数循环

 

InitInstance()函数中添加代码:

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow){HWND hWnd;char filename[20] = "";int i;    hInst = hInstance; // 将实例句柄存储在全局变量中hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,  CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);if (!hWnd){  return FALSE;}MoveWindow(hWnd, 10, 10, 600, 450, true);ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);hmdc = GetDC(hWnd);mdc = CreateCompatibleDC(hmdc);//将7张位图载入for (i = 0; i < 7; i++){        sprintf(filename, "girl%d.bmp", i);girl[i] = (HBITMAP)LoadImage(NULL,filename,IMAGE_BITMAP,640,480,LR_LOADFROMFILE);}num = 0;SetTimer(hWnd, 1, 500, NULL); // 设定0.5秒的定时器MyPaint(hmdc);return TRUE;}

 

WndProc()中添加WM_TIMER消息:

case WM_TIMER:MyPaint(hmdc);break;

程序退出是需要析构所有创建的对象:

case WM_DESTROY://删除和释放工作DeleteDC(mdc);ReleaseDC(hWnd, hmdc);for (i = 0; i < 7; i++){DeleteObject(girl[i]);}KillTimer(hWnd, 1);PostQuitMessage(0);break;

MyPaint()函数的实现如下:

void MyPaint(HDC hdc){if(num == 7)num = 0;SelectObject(mdc,girl[num]);BitBlt(hdc,0,0,600,450,mdc,0,0,SRCCOPY);num++;}


这样,程序就算完成了。运行,会看到小女孩在摆动的动画。


我们可以加快定时器的速率,使动画看起来更加连贯。

这里是源代码文件:点击

 

二、游戏循环

 

在上例中,通过Timer来设定动画的帧来显示游戏,这事实上市仅仅使用在小型游戏中的方式。一般来说,我们为了是游戏显示得更加顺畅,每秒钟必须更新画面25次以上。在此之中,我们还需要处理其他大量的游戏操作。而用定时器来驱动,往往得不到我们想要的画面效果。这里提出了一种叫做“游戏循环”的概念。

游戏循环是将原先程序中的消息循环加以修改,方法是判断其中的内容目前是否要处理的消息,如果有则进行,否则按照设定的时间间隔来重绘画面。

 

这里借助一个例子讲解:

在WinMain函数中

//游戏循环while (msg.message != WM_QUIT){if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);}else{tNow = GetTickCount(); // 获得系统开启时间if (tNow - tPre >= 100)MyPaint(hdc); // 绘图函数}}

msg.message收到不是WM_QUIT消息时,进行运行循环。PeekMessage检测是否有处理消息(包括WM_QUIT)。没有消息就返回0,否则返回非0。值得注意的是,不能用GetMessage()取代PeekMessage()

 

此例子和上面的例子是同样的原理,显示一个动画。

运行后,显示的也是一个动画。本动画由七副静态图循环粘贴形成。即,BITMAP man[7]

整个程序如下:

#include <windows.h>#include <stdio.h>// 全局变量HINSTANCE hInst;HBITMAP man[7];HDC hdc,mdc;HWND hWnd;// 分别记录上次绘图时间,本次准备绘图时间,记录每秒开始的时间DWORD tPre, tNow, tCheck;int num, frame, fps;// 函数声明ATOM MyRegisterClass(HINSTANCE hInstance);BOOL InitInstance(HINSTANCE, int);LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);void MyPaint(HDC hdc);// 入口函数int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){MSG msg;ZeroMemory(&msg, sizeof(MSG));MyRegisterClass(hInstance);if( !InitInstance(hInstance, nCmdShow) ){return FALSE;}//游戏循环while (msg.message != WM_QUIT){if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);}else{tNow = GetTickCount(); // 获得系统开启时间if (tNow - tPre >= 100)MyPaint(hdc); // 绘图函数}}return msg.wParam;}//注册窗口类函数ATOM MyRegisterClass(HINSTANCE hInstance){WNDCLASSEX wcex;wcex.cbSize = sizeof(WNDCLASSEX); wcex.style= CS_HREDRAW | CS_VREDRAW;wcex.lpfnWndProc= (WNDPROC)WndProc;wcex.cbClsExtra= 0;wcex.cbWndExtra= 0;wcex.hInstance= hInstance;wcex.hIcon= NULL;wcex.hCursor= NULL;wcex.hCursor= LoadCursor(NULL, IDC_ARROW);wcex.hbrBackground= (HBRUSH)(COLOR_WINDOW+1);wcex.lpszMenuName= NULL;wcex.lpszClassName= "canvas";wcex.hIconSm= NULL;return RegisterClassEx(&wcex);}// 初始化窗口BOOL InitInstance(HINSTANCE hInstance, int nCmdShow){char filename[20] = "";int i;hInst = hInstance;hWnd = CreateWindow("canvas", "游戏循环" , WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);if (!hWnd){return FALSE;}MoveWindow(hWnd,10,10,600,450,true);ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);hdc = GetDC(hWnd);mdc = CreateCompatibleDC(hdc);// 循环载入各个静态图for(i=0;i<7;i++){sprintf(filename,"man%d.bmp",i);man[i] = (HBITMAP)LoadImage(NULL,filename,IMAGE_BITMAP,640,480,LR_LOADFROMFILE);}num = 0;frame = 0;MyPaint(hdc);return TRUE;}// 绘制图形void MyPaint(HDC hdc){char str[40] = "";if(num == 7)num = 0;frame++; // 更新画面次数加1if(tNow - tCheck >= 1000){fps = frame;frame = 0;tCheck = tNow;}SelectObject(mdc,man[num]);sprintf(str,"每秒钟显示 %d 个画面",fps);TextOut(mdc,0,0,str,strlen(str));BitBlt(hdc,0,0,600,450,mdc,0,0,SRCCOPY);tPre = GetTickCount(); // 记录此次绘图时间num++;}// 消息循环LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){int i;switch (message){case WM_DESTROY: // 清理回收DeleteDC(mdc);for(i=0;i<7;i++)DeleteObject(man[i]);ReleaseDC(hWnd,hdc);PostQuitMessage(0);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}return 0;}

相信有了第一部分的例子,对于这部分的例子理解并不难。



原创粉丝点击