Графика DirectX в Delphi
Шрифт:
end;
Пользователь может перемещать курсор, попытаться выполнить привычные действия, видя знакомое содержимое экрана, но реакции на его действия, конечно, не последует. Чтобы у него не возникало паники, закрываем приложение по нажатию любой клавиши, иначе у пользователя может возникнуть ощущение того, что система зависла.
Выбор объектов
В этом разделе мы познакомимся с простейшим способом организации выбора и выяснением, какой объект находится в определенной точке экрана, например под курсором. В простейших проектах, конечно, можно всего-навсего анализировать координаты нужной точки и перебирать все
В таких случаях используется выбор по цвету, заключающийся в том, что объекты раскрашиваются в различные цвета, анализ цвета нужной точки дает ответ на вопрос: "Что в настоящий момент находится под курсором".
Рассмотрим пример из проекта каталога Ех20. На экране перемещаются три одинаковых образа, при этом образ, находящийся под курсором, перекрашивается (рис. 3.9).
Поскольку образы выводятся совершенно одинаковые, мы не можем напрямую различать их по цвету. Действуем так: на вспомогательной поверхности DDSDoubie отображаем образы такой же формы, что и на экране, но разные по цвету (в моем примере это три круга чистых цветов: красного, зеленого i синего). Выводятся они с теми же координатами, что и на экране. Перед тем, как отобразить сферы на экране, анализируем цвет нужного пиксела на вспомогательной поверхности:
function TfrmDD.UpdateFrame : HRESULT;
var
ddbltfx : TDDBLTFX; // Для очистки экрана
wrkl : Integer; // Рабочая переменная
begin
Result := DD_FALSE;
ZeroMemory (@ddbltfx, SizeOf(ddbltfx));
ddbltfx.dwSize := SizeOf(ddbltfx); ddbltfx.dwFillColor := 0;
// Закрашиваем, очищаем обе поверхности
FDDSBack.Blt(nil, nil, nil, DDBLT_COLORFILL or DDBLT_WAIT, @ddbltfx);
FDDSDouble.'Blt(nil, nil, nil, DDBLT_COLORFILL or DDBLT_WAIT, Sddbltfx);
ThisTickCount := GetTickCount;
// Пауза для смены положения сфер
if ThisTickCount - LastTickCount > 10 then begin
Angle := Angle + 0.02;
if Angle > 2 * Pi then Angle := Angle - 2 * Pi; LastTickCount := GetTickCount;
end;
// Выводим три сферы на вспомогательную поверхность
FDDSDouble.BltFast (0, 140 - trunc (sin (Angle) * 100),
FDDSImageRed, nil, DDBLTFAST_WAIT);
// Красная, соответствует первому образу
FDDSDouble.BltFast (230, 140 - trunc (sin (Angle + Pi / 4) * 100),
FDDSImageGreen, nil, DDBLTFAST_WAIT);
// Зеленая, для второго образа
FDDSDouble.BltFast (440, 140 - trunc (sin (Angle + Pi / 2) * 100),
FDDSImageBlue, nil, DDBLTFAST_WAIT);
// Синяя для третьего
wrkl := Select (mouseX, mouseY); // Выбор элемента под курсором
if wrkl = -1 then begin // Произошла авария
Result := RestoreAll;
Exit;
end;
if wrkl =1 // Под курсором первая сфера, ее выводим помеченной
then FDDSBack.BltFast (0, 140 - trunc (sin (Angle) * 100),
FDDSImageSelect, nil, DDBLTFAST_WAIT)
// Под курсором не первая сфера, ее выводим обычной
else FDDSBack.BltFast (0, 140 - trunc (sin (Angle) * 100),
FDDSImageSphere, nil, DDBLTFAST_WAIT);
//
Аналогично с двумя оставшимися сферамиif wrkl = 2
then FDDSBack.BltFast (220, 140 - trunc (sin (Angle + Pi / 4) * 100),
FDDSImageSelect, nil, DDBLTFAST_WAIT)
else FDDSBack.BltFast (220, 140 - trunc (sin (Angle + Pi / 4) * 100),
FDDSImageSphere, nil, DDBLTFAST_WAIT);
if wrkl = 3
then FDDSBack.BltFast (440, 140 - trunc (sin (Angle + Pi / 2) * 100),
FDDSImageSelect, nil, DDBLTFAST_WAIT)
else FDDSBack.BltFast (440, 140 - trunc (sin (Angle + Pi / 2) * 100),
FDDSImageSphere, nil, DDBLTFAST_WAIT);
// Вывод указателя курсора
FDDSBack.BltFast (mouseX, mouseY, FDDSMouse, nil,
DDBLTFAST_WAIT or DDBLTFAST_SRCCOLORKEY);
if Failed (FlipPages)
then Result := RestoreAll
else Result := DD_OK;
end;
Теперь посмотрим функцию выбора:
function TfrmDD.Select (const X, Y : Integer) : Integer;
var
desc : TDDSURFACEDESC2;
Red, Green, Blue : Byte;
Pixel : Word;
begin
Result := -1;
ZeroMemory (@desc, SizeOf(desc));
desc.dwSize := SizeOf(desc) ;
if Failed (FDDSDouble.Lock (nil, desc, DDLOCK_WAIT, 0))
then Exit; // Закрыть не удается, выходим
Pixel := PWord (Integer (desc.IpSurface) + У * desc.lPitch + X * 2)^;
Blue := Pixel and $1F; // Цветовые компоненты пиксела
Green := (Pixel shr 5) and $3F; Red := (Pixel shr 11) and $1F; FDDSDouble.Unlock (nil);
if Blue <> 0 then Result := 3 else // Анализируем результат if Green <> 0 then Result := 2 else
if Red <> 0 then Result := 1 else Result := 0;
end;
Конечно, для этого конкретного примера можно делать выбор просто по координате, но я надеюсь, что сумел достичь данной иллюстрацией понимания, как поступать в случаях, когда подобный анализ получается слишком длинным.
В рассмотренном примере фон не используется. Но если он потребуется, то учтите, что на вспомогательную поверхность его выводить совершенно не нужно.
В некоторых стратегических играх вы можете заметить, что на экране можно выбирать "спрятавшиеся" объекты, закрытые каким-то элементом пейзажа, деревом или горой. На вспомогательной поверхности они не рисуются, поэтому так и происходит.
Также часто возникают ситуации, когда выбор осуществляется неточно, в некотором районе объекта. Это происходит потому, что для повышения скорости работы на вспомогательную поверхность выводятся окрашенные прямоугольники, а не силуэт объекта.
Лупа
В данном разделе мы рассмотрим два любопытных примера, посвященных организации лупы. Задача сама по себе занимательна, но вдобавок мы узнаем кое-что интересное и загадочное.
Запустите проект, располагающийся в каталоге Ех21. По экрану перемещается "лупа", кружок, в пределах которого выводится увеличенный участок фона (рис. 3.10).
В качестве фона в примерах этого раздела я использую, с любезного разрешения автора, работы художника, имя которого присутствует в левом нижнем углу растрового изображения. Псевдоним автора - Beardo, а адрес ею страницы http://home5.swipnet.se/~w-57902/images/art/.