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
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.
На этапе 1 мы инициализируем два объекта CRectфункцией Sprite::GetRect(). Функция GetRect()возвращает прямоугольник CRect, представляющий положение и размеры спрайта. Затем оператор &(оператор пересечения класса CRect) определяет область пересечения двух прямоугольников. Ниже снова приведен соответствующий фрагмент листинга 9.1:
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));
Как мы узнали из функции SpritesCollideRect(), оператор пересечения класса CRectобнуляет все четыре поля CRect, если операнды не пересекаются. В этом случае функцию SpritesCollidePixel()вызывать не следует, поэтому о такой ситуации сообщает макрос ASSERT().
На этапе 2 мы вычисляем область каждого спрайта, для которой должна осуществляться проверка пикселей. Для этого снова используется оператор пересечения:
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--;
В прямоугольниках r1targetи r2targetхранятся области спрайтов, для которых потребуется проверка на уровне пикселей. После того как пересечение будет найдено, оба прямоугольника сдвигаются функцией CRect::OffsetRect()так, чтобы левый верхний угол имел координаты (0, 0). Это объясняется тем, что поля rightи bottomобъектов CRectбудут использоваться для обращений к поверхностям обоих спрайтов, а это требует перехода к локальным системам координат этих поверхностей.
Также обратите внимание на то, что правый и нижний края каждого прямоугольника обрезаются на один пиксель. Это связано с особенностями реализации CRect.
СОВЕТ
Кое-что о классе CRect
Класс MFC CRectреализован так, чтобы при вычитании поля leftиз поля rightполучалась ширина прямоугольника. Такой подход удобен, но смысл поля rightнесколько изменяется. Например, рассмотрим прямоугольник, у которого поле leftравно 0, а полю rightприсвоено значение 4. В соответствии с реализацией класса CRectтакой прямоугольник имеет ширину в 4 пикселя, но если использовать эти же значения для обращений к пикселям, ширина прямоугольника окажется равной 5 пикселям (поскольку в нее будут включены пиксели с номерами от 0 до 4). Такие же расхождения возникают и для полей topи bottom. Следовательно, чтобы использовать поля CRectдля работы с пикселями, необходимо уменьшить на 1 значения полей rightи bottom.
Настоящая проверка столкновений происходит на этапе 3. Способ ее выполнения зависит от того, используют ли оба спрайта одну и ту же поверхность или нет. Сначала мы получаем поверхности обоих спрайтов функцией Sprite::GetSurf():
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);
Читать дальше