您的位置:首页 > 编程语言 > C语言/C++

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

2012-04-09 20:47 363 查看

一、定时器

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++; // 更新画面次数加1
if(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;
}


相信有了第一部分的例子,对于这部分的例子理解并不难。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐