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

1、定时器

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

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

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中使用多字节字符集设置能够在解决方案资源管理器中右击方案,选择属性,打开的属性页里能够设置。ui

如图:spa


 


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

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消息:blog

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++;
}


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


咱们能够加快定时器的速率,使动画看起来更加连贯。

这里是源代码文件:点击

 

2、游戏循环

 

在上例中,经过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;
}

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