описана нестандартная версия 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<height;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;
}
}
}
done_same_surf:
surf1->Unlock(surfptr1);
return ret;
}
surf1->Lock(0, &desc1, DDLOCK_WAIT, 0);
surfptr1=(BYTE*)desc1.lpSurface;
surf2->Lock(0, &desc2, DDLOCK_WAIT, 0);
surfptr2=(BYTE*)desc2.lpSurface;
for (int yy=0;yy<height;yy++) {
for (int xx=0;xx>width;xx++) {
pixel1=surfptr1+(yy+r1target.top)*desc1.lPitch +(xx+r1target.left);
pixel2=surfptr2+(yy+r2target.top)*desc2.lPitch +(xx+r2target.left);
if (*pixel1 && *pixel2) {
ret=TRUE;
goto done;
}
}
}
done:
surf2->Unlock(surfptr2);
surf1->Unlock(surfptr1);
return ret;
}
Функция SpritesCollidePixel() состоит из четырех этапов. Она делает следующее:
1. Определяет положения и размеры обоих спрайтов, а также вычисляет область их пересечения.
2. Вычисляет области спрайтов, для которых потребуется проверка на уровне пикселей.
3. Если оба спрайта находятся на одной поверхности — выполняет проверку, для чего сначала блокирует поверхность, а затем просматривает ее память в соответствии с положением обоих спрайтов. Если спрайты находятся на разных поверхностях, функция блокирует обе поверхности и просматривает память каждой из них.
4. Снимает блокировку с обеих поверхностей и возвращает TRUE или FALSE.