用框架方式检测碰撞需要的计算量小,并且不需要制作模板,但是存在误差。

    可以通过颜色检测精确的判断出两个非规则形体是否发生了碰撞。 用颜色碰撞常常用于地图的碰撞检测,原理首先将地图用图像软件进行处理,制作出对应的地图蒙版,地图蒙版由黑白两种颜色组成,发生碰撞的部分用黑色绘出。

【游戏程序设计】颜色碰撞检测

【游戏程序设计】颜色碰撞检测

算法思想:

首先得到被碰撞物体的具体位置,然后在蒙版中对应的位置获取与被碰物体同样大小的图像,将从蒙版中得到的图像与被捧物体图像在映射过程中执行与操作,最后检测合成后的图像,如果发现其包含蒙版中碰撞部分的颜色,则碰撞发生。

运行结果:

【游戏程序设计】颜色碰撞检测

【游戏程序设计】颜色碰撞检测

源代码:

#include <windows.h>
#pragma comment(lib, "winmm.lib")									//调用PlaySound函数所需库文件t

#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 300
#define WINDOW_TITLE L"【游戏程序设计】颜色碰撞检测"

HINSTANCE hInst;
HDC hdc, mdc, bufdc, mdc2;
HBITMAP hcar, hforest, hmask, hdark;								//dark为检测图像句柄
unsigned char *px;													//保存原图像像素信息
int x, vx;

int  MyWindowClass(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void MyDraw(HWND);

/*****************************************************************************************************************
在不同的应用程序中,在此处添加相关的全局变量
******************************************************************************************************************/
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPreInstace,
									LPSTR lpCmdLine, int nCmdShow) 
{
	MyWindowClass(hInstance);
	PlaySound(L"sound.wav", NULL, SND_FILENAME| SND_ASYNC | SND_LOOP);	//循环播放背景音乐
	if(!InitInstance(hInstance, nCmdShow))
		return FALSE;
	MSG msg;
	while(GetMessage(&msg, NULL, 0, 0)) 
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}

int MyWindowClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = NULL;
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName = NULL;
	wcex.lpszClassName = L"gamebase";
	wcex.hIconSm = NULL;

	return RegisterClassEx(&wcex); 
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
	hInst = hInstance;
	HWND hwnd = CreateWindow(L"gamebase", WINDOW_TITLE, WS_OVERLAPPEDWINDOW, 
		CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);
	if(!hwnd)
		return FALSE;
	MoveWindow(hwnd, 10, 10, WINDOW_WIDTH, WINDOW_HEIGHT, true);
	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	hdc = GetDC(hwnd);
	mdc = CreateCompatibleDC(hdc);
	bufdc = CreateCompatibleDC(hdc);
	mdc2 = CreateCompatibleDC(hdc);											//存放检测图像的内存dc

	hcar = (HBITMAP)LoadImage(NULL, L"car.bmp", IMAGE_BITMAP, 112, 64, LR_LOADFROMFILE);
	hforest = (HBITMAP)LoadImage(NULL, L"forest.bmp", IMAGE_BITMAP, 800, 300, LR_LOADFROMFILE);	
	hmask = (HBITMAP)LoadImage(NULL, L"mask.bmp", IMAGE_BITMAP, 800, 300, LR_LOADFROMFILE);	
	hdark = CreateCompatibleBitmap(hdc, 112, 64);
	HBITMAP temp = CreateCompatibleBitmap(hdc, 800, 300);
	
	x = 650;
	vx = -5;
	
	BITMAP bm;
	GetObject(hcar, sizeof(BITMAP), &bm);									//获取car图像的相关信息
	px = new unsigned char[bm.bmHeight*bm.bmWidthBytes];					//分配car图像数据大小空间,并将该地址赋给px
	GetBitmapBits(hcar, bm.bmHeight*bm.bmWidthBytes, px);					//将car图像数据读到px中

	SelectObject(mdc, temp);												//首先将temp放入g_mdc中,相当于在mdc中放入底层模板
	SelectObject(mdc2, hdark);												//将dark放入mdc2中

	SetTimer(hwnd, 1, 100, NULL);											//建立定时器
	return TRUE;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
	case WM_TIMER:															// 定时器消息
		MyDraw(hwnd);
		break;
/**************************************************************************************************************
在退出程序前,往往在此处删除创建的相关资源
***************************************************************************************************************/
	case WM_DESTROY:
		DeleteObject(hcar);
		DeleteObject(hforest);
		DeleteObject(hmask);
		DeleteObject(hdark);
		DeleteDC(mdc);
		DeleteDC(bufdc);
		DeleteDC(mdc2);
		ReleaseDC(hwnd, hdc);
		delete []px;
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hwnd, message, wParam, lParam);
	}
	return 0;
}
/***************************************************************************************************************
在函数MyDraw()中进行相关绘制工作
****************************************************************************************************************/
void MyDraw(HWND hwnd)
{
	//首先刷新背景
	BitBlt(mdc, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, bufdc, 0, 0, WHITENESS);
	//贴上小车
	SelectObject(bufdc, hcar);
	BitBlt(mdc, x, 130, 112, 64, bufdc, 0, 0, SRCCOPY);
	//在mdc2中绘制检测图像。
	SelectObject(bufdc, hmask);
	BitBlt(mdc2, 0, 0, 112, 64, bufdc, x, 130, SRCCOPY);
	//贴上森林
	BitBlt(mdc, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, bufdc, 0, 0, SRCAND);
	SelectObject(bufdc, hforest);
	BitBlt(mdc, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, bufdc, 0, 0, SRCPAINT);
	
	BITMAP bm;
	unsigned char *px1 = NULL;
	//在对应的森林模板中选择与car大小相同的图像,内容放入dark中
	GetObject(hdark, sizeof(BITMAP), &bm);
	px1 = new unsigned char[bm.bmHeight * bm.bmWidthBytes];
	GetBitmapBits(hdark, bm.bmHeight * bm.bmWidthBytes, px1);
	
	//内存中没有三个字节的数据结构存储 
	if(bm.bmBitsPixel != 32)
		return;
	//每个像素的字节数
	int PixelBytes = bm.bmBitsPixel / 8;

	for(int ty = 0; ty < bm.bmHeight; ++ty)
		for(int tx = 0; tx < bm.bmWidth; ++tx)
		{
			int rgb_b = ty*bm.bmWidthBytes + tx*PixelBytes;
			if(px[rgb_b] != 255 || px[rgb_b+1] != 255 || px[rgb_b+2] != 255)		//不是纯白色,即不是背景,而是小车
			{
				//检测图像与源图像与操作,如果发现包含碰撞部分的颜色,即黑色,就是发生了碰撞
				int b = px[rgb_b] & px1[rgb_b];
				int g = px[rgb_b+1] & px1[rgb_b+1];
				int r = px[rgb_b+2] & px1[rgb_b+2];
				if(!b && !g && !r)													
				{
					//unicode字符集,汉字由一个WORD表示
					TextOut(mdc, 300, 50, L"在森林中", 4);
					break;
				}
			}
		}
	delete []px1;
	
	BitBlt(hdc, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, mdc, 0, 0, SRCCOPY);

	x += vx;
	if(x < 0)
		KillTimer(hwnd, 1);
}

 

相关文章: