用框架方式检测碰撞需要的计算量小,并且不需要制作模板,但是存在误差。
可以通过颜色检测精确的判断出两个非规则形体是否发生了碰撞。 用颜色碰撞常常用于地图的碰撞检测,原理首先将地图用图像软件进行处理,制作出对应的地图蒙版,地图蒙版由黑白两种颜色组成,发生碰撞的部分用黑色绘出。
算法思想:
首先得到被碰撞物体的具体位置,然后在蒙版中对应的位置获取与被碰物体同样大小的图像,将从蒙版中得到的图像与被捧物体图像在映射过程中执行与操作,最后检测合成后的图像,如果发现其包含蒙版中碰撞部分的颜色,则碰撞发生。
运行结果:
源代码:
#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);
}