Реализация функции SpritesCollide()будет начинаться с проверки столкновений на уровне ограничивающих прямоугольников. Если результат окажется положительным (то есть ограничивающие прямоугольники пересекаются), следует перейти к проверке на уровне пикселей; в противном случае функция возвращает FALSE.
BOOL SpritesCollide(Sprite* sprite1, Sprite* sprite2) {
ASSERT(sprite1 && sprite2);
if (SpritesCollideRect(sprite1, sprite2)) if (SpritesCollidePixel(sprite1, sprite2)) return TRUE;
return FALSE;
}
Обратите внимание на то, что функция SpritesCollide()должна получать два аргумента — два указателя на объекты Sprite(класс Spriteрассматривается ниже). Сначала функция проверяет, что оба указателя отличны от нуля, с помощью макроса ASSERT().
СОВЕТ
ASSERT() в DirectDraw
Хотя в библиотеку MFC входит макрос ASSERT(), он плохо подходит для полноэкранных приложений DirectDraw. В приложении А описана нестандартная версия ASSERT(), использованная в программах этой книги.
Затем функция SpritesCollide()проверяет, пересекаются ли ограничивающие прямоугольники двух спрайтов. Эта проверка выполняется функцией SpritesCollideRect(), которая, как и SpritesCollide(), получает два указателя на объекты Spriteи возвращает логическое значение. Если прямоугольники не пересекаются (то есть SpritesCollideRect()возвращает FALSE), дальнейшая проверка не нужна, и функция возвращает FALSE— это означает, что два спрайта не сталкиваются.
Если ограничивающие прямоугольники пересекаются, необходимо продолжить проверку. Мы вызываем функцию SpritesCollidePixel()и также передаем ей два указателя на объекты Sprite. Если эта проверка окажется неудачной, SpritesCollide()возвращает FALSE; в противном случае она возвращает TRUE, что говорит о столкновении спрайтов.
Перед тем как рассматривать процедуру проверки на уровне пикселей, давайте рассмотрим функцию SpritesCollideRect(), в которой проверяется пересечение ограничивающих прямоугольников:
BOOL SpritesCollideRect(Sprite* sprite1, Sprite* sprite2) {
CRect rect1 = sprite1->GetRect();
CRect rect2 = sprite2->GetRect();
CRect r = rect1 & rect2;
// Если все поля равны нулю, прямоугольники не пересекаются
return !(r.left==0 && r.top==0 && r.right==0 && r.bottom==0);
}
Пересечение ограничивающих прямоугольников проверяется в функции SpritesCollideRect()с помощью класса MFC CRect. Сначала для каждого спрайта вызывается функция Sprite::GetRect(). Она возвращает объект CRect, определяющий текущее положение и размеры каждого спрайта. Затем третий объект CRectинициализируется оператором пересечения класса CRect( &), который вычисляет область пересечения двух своих операндов. Если пересечения не существует (два прямоугольника не перекрываются), все четыре поля CRectобнуляются. Этот признак используется для возврата TRUEв случае пересечения прямоугольников, и FALSE — в противном случае.
Функция SpritesCollidePixel()работает на уровне пикселей и потому выглядит значительно сложнее, чем ее аналог для ограничивающих прямоугольников. Функция SpritesCollidePixel()приведена в листинге 9.1.
Листинг 9.1. Функция SpritesCollidePixel()
BOOL SpritesCollidePixel(Sprite* sprite1, Sprite* sprite2) {
CRect rect1=sprite1->GetRect();
CRect rect2=sprite2->GetRect();
CRect irect = rect1 & rect2;
ASSERT(!(irect.left==0 && irect.top==0 && irect.right==0 && irect.bottom==0));
CRect r1target = rect1 & irect;
r1target.OffsetRect(-rect1.left, -rect1.top);
r1target.right--;
r1target.bottom--;
CRect r2target = rect2 & irect;
r2target.OffsetRect(-rect2.left, -rect2.top);
r2target.right--;
r2target.bottom--;
int width=irect.Width();
int height=irect.Height();
DDSURFACEDESC desc1, desc2;
ZeroMemory(&desc1, sizeof(desc1));
ZeroMemory(&desc2, sizeof(desc2));
desc1.dwSize = sizeof(desc1);
desc2.dwSize = sizeof(desc2);
BYTE* surfptr1; // Указывает на начало памяти поверхности
BYTE* surfptr2;
BYTE* pixel1; // Указывает на конкретные пиксели
BYTE* pixel2; // в памяти поверхности
BOOL ret=FALSE;
LPDIRECTDRAWSURFACE surf1=sprite1->GetSurf();
LPDIRECTDRAWSURFACE surf2=sprite2->GetSurf();
if (surf1==surf2) {
surf1->Lock(0, &desc1, DDLOCK_WAIT, 0);
surfptr1=(BYTE*)desc1.lpSurface;
for (int yy=0;yy
for (int xx=0;xx>width;xx++) {
pixel1=surfptr1+(yy+r1target.top)*desc1.lPitch +(xx+r1target.left);
pixel2=surfptr1+(yy+r2target.top)*desc1.lPitch +(xx+r2target.left);
if (*pixel1 && *pixel2) {
ret=TRUE;
goto done_same_surf;
}
}
}
Читать дальше