Михаил Краснов - Графика DirectX в Delphi
Опытным путем я определил, что пикселы фона для установленной палитры имеют значение 191, поэтому такие пикселы пропускаем. Как только встречается пиксел, по адресу которого в обеих поверхностях записывается значение, отличное от 191, перебор прекращается:
function TfrmDD.SpritesCollidePixel(Spritel, Sprite2 : TSprite) : BOOL;
var
Rectl : TRect;
Rect2 : TRect;
IRect : TRect;
rltarget : TRect;
r2target : TRect;
locWidth : Integer;
locHeight : Integer;
Descl, Desc2 : TDDSURFACEDESC2;
Ret : BOOL;
Surfptrl : POINTER; // Указатели на начало области памяти поверхности
Surfptr2 : POINTER;
Pixel1 : PBYTE; // Пикселы поверхностей
Pixel2 : PBYTE;
XX, YY : Integer;
label
Done ;
begin
// Прямоугольники, ограничивающие спрайты
Rectl := Spritel.GetRect;
Rect2 := Sprite2.GetRect;
// Вычисляем точку пересечения прямоугольников
IntersectRect (IRect, Rectl, Rect2);
// Если нет пересечения прямоугольников, спрайты сталкиваться не могут
if (IRect.Left = 0) and (IRect.Top = 0) and
(IRect.Right = 0) and (IRect.Bottom = 0) then begin
Result := FALSE;
Exit;
end;
// Находим положение области пересечения для каждого спрайта
IntersectRect (rltarget, Rectl, IRect);
OffsetRect(rltarget, -Rectl.Left, -Rectl.Top);
IntersectRect (r2target, Rect2, IRect);
OffsetRect(r2target, -Rect2.Left, -Rect2.Top);
r2target.Right := r2target.Right - 1;
r2target.Bottom := r2target.Bottom - 1;
// Предыдущие две строки обеспечивают корректное нахождение
// размеров области пересечения
locWidth := IRect.Right - IRect.Left;
locHeight := IRect.Bottom - IRect.Top;
// Подготавливаем структуры для работы с памятью поверхностей
ZeroMemory (gdescl, SizeOf(descl));
descl.dwSize := SizeOf(descl);
ZeroMemory (@desc2, SizeOf(desc2));
desc2.dwSize := SizeOf(desc2);
Ret := False;
// Запираем поверхности спрайтов
Spritel.FSpriteSurface.Lock(nil, descl, DDLOCK_WAIT, 0) ;
Surfptrl := descl.IpSurface;
Sprite2.FSpriteSurface.Lock(nil, desc2, DDLOCK_WAIT, 0) ;
Surfptr2 := desc2.IpSurface;
// Просмотр содержимого пикселов для каждого спрайта
//в пределах области пересечения
for YY := 0 to locHeight - 1 do
for XX := 0 to locWidth - 1 do begin
// Для оптимизации эти действия можно свернуть в одну строку
Pixell := PByte (Integer (Surfptrl) + (yy+rltarget.Top) *descl. IPitcht (xx+rltarget.Left));
Pixel2 := PByte (Integer (Surfptr2) + (yy+retarget. Top) Mesc2 . IPiccr,. (xx+r2target.Left));
if (Р1хе11Л о 191) and (Pixel2A <> 191) then begin
Ret := True; // Найдено пересечение, выходим
goto Done;
end;
end;
Done:
Sprite2.FSpriteSurface.Unlock(nil);
Spritel.FSpriteSurface.Unlock(nil);
Result := Ret;
end;
Спрайты и оконный режим
Взгляните на рис. 4.6, на котором запечатлен момент работы проекта из каталога Ех10, единственного примера этого раздела.
На экране выводится фантастическая картинка с тигром, бегущим на фоне леса. На заднем плане отображается звездное небо, в небесах - вращающаяся планета. Все образы, кроме звездного неба, меняются со временем.
Для подготовки примера я взял, с любезного разрешения корпорации Intel, образы, поставляемые в составе RDX COM SDK.
Все используемые образы реализованы в 256-цветной палитре, а при оконном режиме нельзя явно задавать формат первичной поверхности. Поэтому в данном примере, подобно предыдущему, при создании поверхностей образов явно задается формат пиксела для каждой из них.
Разница хорошо заметна, если установить 256-цветную палитру рабочего стола, вариант с явным указанием формата пиксела выводит неискаженную картинку. Удалите в коде все, связанное с форматом пиксела, и запустите перекомпилированное приложение в 8-битной палитре. Вы должны увидеть, как сильны потери в качестве передачи цветов изображения.
В остальном, рассмотренный пример не сильно отличается от предыдущих. Ограничусь лишь небольшими замечаниями.
Образ леса по размерам совпадает с размерами окна и для создания иллюзии движения отображается в два этапа. На первом этапе выводится последовательно сужающийся фрагмент, примыкающий к правой границе образа, а впритык к ней - расширяющийся фрагмент, примыкающий к левой нице образа леса.
Должен напомнить, что при использовании отсечения итоговый вывод на первичную поверхность должен осуществляться с помощью метода Bit. Отображение на "самодельном" заднем буфере выполняется с помощью метода BitFast. Таким же может быть и финальный вывод, только если не выполнять отсечение.
Неспешно поработайте с этим примером. Определите по коду, с помощью каких клавиш можно менять скорость бега тифа, вращения планеты и смещения фона.
Не пропустите также, что этот пример совершенно безболезненно переживает моменты "засыпания" компьютера: функция восстановления поверхностей вызывается до тех пора, пока не возвратит успешный результат.
Что вы узнали в этой главе
Вы познакомились со вспомогательной библиотекой разработчика DDUtilS, предоставляющей объектно-ориентированный подход к применению DirectDraw и появившейся с восьмой версией DirectX; изучили принципы работы с меняющимися во времени образами; научились определять моменты столкновения спрайтов.
Главный вывод, который мы можем сделать из примеров этой главы, состоит в том, что Delphi вполне можно использовать для разработки больших проектов, требующих высокой скорости воспроизведения.
Глава 5 Пишем игру
Оригинальный сплэш
Космический истребитель
Игра "Меткий стрелок"
Работа с клавиатурой
Работа с мышью
Вывод текста
Создание консоли
Диалоговые окна
Использование отсечения в полноэкранном приложении
Библиотека CDX
Что вы узнали в этой главе
В данной главе освещаются вопросы использования знаний, полученных в предыдущих главах, для разработки проекта-каркаса полноценной игры. Такие проекты обычно называются "движками".
Примеры располагаются в каталоге ExamplesChapter05.
Оригинальный сплэш
Так в обиходе программисты называют заставки, появляющиеся в начале работы приложения (splash - красочное пятно, всплеск). Выводят их на время долгой инициализации основного модуля, как правило, для того, чтобы скрасить секунды (или минуты) ожидания пользователя. Иногда их использованием авторы преследуют цель поразить воображение зрителя, заявить на весь мир о своих выдающихся художественных и профессиональных способностях.
В этом разделе я приведу пример, который может стать основой для создания вашей собственной оригинальной заставки. Это проект каталога Ex01. Во время его работы посередине рабочего стола выводится изображение земного шара, на фоне которого вращается фраза "DirectX". На неискушенных пользователей окна непрямоугольной формы обычно производят сильное впечатление. Подобные окна можно создавать разными способами, например, с помощью регионов. Мы же решим задачу обычным для DirectDraw способом. Совсем необязательно должно получаться именно круглое окно, как в моем примере.
Приемы, используемые в проекте, во многом вам знакомы по примерам предыдущих глав, однако добавилось и кое-что новое.
Заставка должна появляться всегда посередине экрана, при любых установленных разрешениях, поэтому в начале работы нам необходимо определить текущие размеры рабочего стола, относительно середины которого выверить координаты вывода нашего образа размером 256x256 пикселов:
HalfWidth := (GetSystemMetrics (SM_CXSCREEN) -256) div2;
HalfHeight := (GetSystemMetrics(SM_CYSCREEN) - 256) div 2;
Конечно, если по ходу работы заставки пользователь поменяет настройки рабочего стола, значения установок, полученные нами в начале работы, станут неактуальны. Но нет смысла вычислять их значения беспрерывно, ведь в ситуации смены режима дальнейший вывод будет невозможен, точно так же, как и для любого другого приложения, использующего DirectDraw.
Уровень кооперации устанавливается нормальным, а очередной кадр не выходит за границу предыдущего. Поэтому наша заставка эффектно располагается поверх всех окон, и нет необходимости производить манипуляций с ее фоном (запоминать и восстанавливать для каждого кадра).
Но для того, чтобы заставка не исчезла при изменениях на рабочем столе, ее необходимо постоянно перерисовывать. Чтобы перерисовка протекала с большим эффектом, работают с двумя образами: земного шара и вращающейся надписи.
Мы уже использовали прием с вращением изображения, основанный на непосредственном доступе к 8-битной поверхности. Пример этой главы рассчитан на, минимум, 16-разрядную глубину поверхности, а вызываемая нами тогда функция вращения для такого режима требует корректировки.
Я переписал эту функцию. Теперь поворачивается содержимое передаваемого объекта класса TBitmap, и возвращается объект такого же класса:
function TfrmDD.RotateBmp (const BitmapOriginal: TBitmap;
const iRotationAxis, jRotationAxis: Integer;
const AngleOfRotation: Single): TBitmap;
const
MaxPixelCount = 32768;
type
TRGBTripleArray = Array [0..MaxPixelCount-1] of TRGBTriple;
pRGBTripleArray = ATRGBTripleArray;
var
cosTheta Single;
i : Integer;
iOriginal : Integer;
iPrime : Integer;
j Integer;
jOriginal : Integer;
jPrime : Integer;
RowOriginal : pRGBTripleArray;
RowRotated : pRGBTRipieArray;
sinTheta : Single;
begin
Result := TBitmap.Create; // Создание результирующего растра
Result.Width := BitmapOriginal.Widths
Result .Height := BitmapOriginal.Height;
Result.PixelFormat := pf24bit; // Очень важно задать явно режим пиксела