KnigaRead.com/

Михаил Краснов - Графика DirectX в Delphi

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Михаил Краснов, "Графика DirectX в Delphi" бесплатно, без регистрации.
Перейти на страницу:

Образы рыбок для этой программы любезно предоставлены Kelly Villoch. Рыбки будут сновать в разные стороны с различной скоростью, а пузырьки подниматься плавно вверх и лопаться, достигнув верхнего края экрана.

Особенно интересным для этого примера является использование в качестве фона "тайлового", зацикленного образа. Фон окна состоит из нескольких повторяющихся одинаковых фрагментов, состыкованных друг с другом. Так получается бесконечный образ фона.

Чтобы получить простейший хранитель экрана, достаточно переименовать исполняемый модуль любого полноэкранного приложения в файл с расширением scr и поместить его в папку System системного каталога (как правило, это C:WindowsSystem).

Но, чтобы хранитель экрана не выбивался из ряда других "скринсэйверов", необходимо хорошенько потрудиться. Так, следует обеспечить его работу в небольшом окне при предварительном просмотре, когда пользователь выбирает вкладку Заставка при задании свойств экрана (рис. 4.3).





Итак, у хранителя экрана должны быть оконный и полноэкранный режимы работы. Причем, совсем не нужно делать приложение комбинированным,

поскольку переключения между режимами по ходу работы происходить не будет. Просто надо обеспечить работу в одном из этих режимов. Требуемый режим устанавливается один раз, в начале работы, и будет действительным до самого конца его выполнения.

Далее, необходимо снабдить приложение диалоговыми окнами настройки параметров работы и задания пароля для выхода. И еще, на тот случай, если указан пароль, следует предусмотреть режим просмотра, при выходе из которого пароль не вводится и комбинация клавиш <Ctrl>+<Alt>+<Del> при работе хранителя в этом режиме не блокируется.

Также нам надо обеспечить наличие единственного модуля. Все необходимые образы не должны загружаться из других файлов, что будет совершенно неудобно для пользователя.

В этом разделе мы только начнем работу над приложением, затем упростим нашу работу, немного ограничим функциональность хранителя экрана и не будем принимать в расчет ничего, что связанно с паролем.

Рассмотрение готовой работы, проекта каталога Ех08, начнем с разбора его сердцевины - механизма визуализации жизни подводного мира.

Для хранения образов использую компоненты класса Timage, располагающиеся на форме. Хранитель экрана разместится в единственном файле.

Мне потребовались четыре образа рыбок, один образ всплывающего пузырька воздуха и образ для построения фона (рис. 4.4).






Для загрузки на поверхность образа из компонента класса Timage введена пользовательская функция createFromimage. Рыбки и пузырьки на экране будут иметь случайные размеры, чтобы при использовании цветового ключа не появлялась окантовка. Для корректного масштабирования приходится применять длинную процедуру перекладывания образа на вспомогательные объекты класса TBitMap:


function TfrmDD.CreateFromimage (var FDDS : IDirectDrawSurface7;

const Image : Timage; const imgWidth, imgHeight : Integer) : HRESULT;

var

DC : HDC;

ddsd : TDDSurfaceDesc2;

hRet : HResult;

wrkBitmapl : TBitMap;

wrkBitmap2 : TBitMap;

begin

ZeroMemory (@ddsd, SizeOf(ddsd)); with ddsd do begin

dwSize := SizeOf(ddsd);

dwFlags := DDSD_CAPS or DDSD_HEIGHT or DDSD_WIDTH;

dwWidth := imgWidth;

dwHeight := imgHeight;

ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN;

end;


// Создаем поверхность нужных размеров

hRet := FDD.CreateSurfасе(ddsd, FDDS, nil);

if Failed(hRet) then ErrorOut(hRet, 'Create Surface');

// Первое изображение хранит растр,

// переложенный с компонента класса TImage

wrkBitmapl := TBitMap.Create;

wrkBitmapl.Width := Image.Width;

wrkBitmapl.Height := Image.Height;

// Копирование растра, StretchBlt исказит образ

BitBlt(wrkBitmapl.Canvas.Handle, 0, 0, wrkBitmapl.Width,

wrkBitmapl.Height, Image.Canvas.Handle, 0, 0, SRCCOPY);

// Второе изображение используется для корректного масштабирования

wrkBitmap2 := TBitMap.Create;

wrkBitmap2.Width := imgWidth;

wrkBitmap2.Height := imgHeight;

// Перекладываем растр во второй битмап

wrkBitmap2.Canvas.StretchDraw (Rect (0, 0, imgWidth, imgHeight),

wrkBitmapl);

// Воспроизводим масштабированный растр на сформированной поверхности

if FDDS.GetDC(DC) = DD_OK then begin

BitBlt(DC, 0, 0, imgWidth, imgHeight,

wrkBitmap2.Canvas.Handle, 0, 0, SRCCOPY);

FDDS.ReleaseDC(DC);

end;


wrkBitmapl.Free;

wrkBitmap2.Free;

// Задаем ключ, берем цвет первого пиксела

Result := DDSetColorKey (FDDS, Image.Canvas.Pixels [0, 0]);

end;


Класс TFish инкапсулирует свойства и методы наших рыбок:


TFish = class

XFish, YFish :Integer; // Позиция на экране

Direction :0..1; // Направление движения

WidthFish :Integer; // Ширина

HeightFish :Integer; // Высота

FDDSFish :IDirectDrawSurface7; // Поверхность с образом

SpeedFish :Integer; // Скорость движения

procedure Init; // Инициализация

procedure Render; // Воспроизведение

end;


При инициализации очередной рыбки ее размеры задаются случайно, чтобы создать иллюзию пространства, как будто некоторые рыбки удалены дальше от глаза наблюдателя. Но при указании размеров я соблюдаю пропорции первоначальной картинки. Чтобы рыбка могла плавать и слева направо, и справа налево, можно заготовить два образа на каждую рыбку. Но в этом случае размер файла хранителя экрана резко увеличится. Я выбрал другой путь: имеется одна картинка каждого вида рыбок, плывущих слева направо, а при необходимости содержимое поверхности зеркально переворачивается.

Размер исполнимого файла при таком подходе не увеличивается, но мы, конечно, при каждой инициализации рыбки теряем немного во времени:


procedure TFish.Init;

procedure Rotate; // Зеркальный поворот поверхности рыбки

var

desc : TDDSURFACEDESC2; i, j : Integer; wrkW : Word;

begin

ZeroMemory (@desc, SizeOf(desc));

desc.dwSize := SizeOf(desc);

if Failed (FDDSFish.Lock (nil, desc, DDLOCK_WAIT, 0)) then Exit;

for i := 0 to (WidthFish - 1) div 2 do // Цикл по столбцам растра

for j := 0 to HeightFish - 1 do begin // Цикл по строкам растра

wrkW := PWord (Integer (desc.IpSurface) + j * desc.lPitch +

i * 2)^; // Переставляем пикселы растра

PWord (Integer (desc.IpSurface) + j * desc.lPitch + i * 2) ^ :=

PWord (Integer (desc.IpSurface) + j * desc.lPitch +

(WidthFish - I - i) * 2)л; PWord (Integer (desc.IpSurface) + j * desc.lPitch +

(WidthFish - I - i) * 2)л := wrkW;

end;


FDDSFish.Unlock (nil);

end;


begin

case random (4) of // Случайный выбор одного из четырех видов рыбок

0 : begin

WidthFish := random (141) + 24;

HeightFish := WidthFish * 129 div 164; // Сохранение пропорций

if Failed (frmDD.CreateFromlmage (FDDSFish, frmDD.imgFishl,

WidthFish, HeightFish))

then frmDD.ErrorOut(DDJTALSE, 'CreateFish');

end;


1 : begin

WidthFish := random (161) + 22; HeightFish := WidthFish * 115 div 182;

if Failed (frmDD.CreateFromlmage (FDDSFish, frmDD.imgFish2,

WidthFish, HeightFish))

then frmDD.ErrorOut(DD_FALSE, 'CreateFish');

end;


2 : begin

WidthFish := random (161) +22;

HeightFish := WidthFish * 122 div 182;

if Failed (frmDD.CreateFromlmage (FDDSFish, frmDD.imgFish3,

WidthFish, HeightFish))

then f rmDD. ErrorOut (DD__FALSE, 'CreateFish');

end;


3 : begin

WidthFish := random (175) +22; HeightFish := WidthFish * 142 div 182;

if Failed (frmDD.CreateFromlmage (FDDSFish, frmDD.imgFish4,

WidthFish, HeightFish))

then frmDD.ErrorOut(DD_FALSE, 'CreateFish');

end;


end;


Direction := random (2); // Направление движения случайно

SpeedFish := random (6) +1; // Следим, чтобы скорость была ненулевой

if Direction =0 // Плывет слева направо, значит,

// должна появиться слева экрана

then XFish := -WidthFish

else begin

XFish := ScreenWidth; // Должна появиться справа экрана

Rotate;

// Требуется зеркальный поворот картинки

end;


YFish := random (360) +5; // Глубина, на которой поплывет рыбка

end;


При отображении проплывающей рыбки надо отдельно обрабатывать ситуацию вблизи границ, когда выводится только часть образа:


procedure TFish.Render;

var

wrkRect : TRect; begin

case Direction of

0 : begin

XFish := XFish + SpeedFish; // Рыбка плывет вправо

if XFish > ScreenWidth then Init; // Уплыла за границы экрана

end;


1 : begin

XFish := XFish - SpeedFish; // Рыбка плывет влево

if XFish < -WidthFish then Init;

end;


end;


if XFish <= 0 then begin

SetRect (wrkRect, -XFish, 0, WidthFish, HeightFish);

frmDD.FDDSBack.BltFast (0, YFish, FDDSFish,

SwrkRect, DDBLTFAST_WAIT or DDBLTFAST_SRCCOLORKEY);

end

else begin

//На экране помещается вся картинка целиком

if XFish <= ScreenWidth - WidthFish then begin

frmDD.FDDSBack.BltFast (XFish, YFish, FDDSFish,

nil, DDBLTFAST_WAIT or DDBLTFAST_SRCCOLORKEY);

end

else begin

SetRect (wrkRect, 0, 0, ScreenWidth - XFish, HeightFish);

frmDD.FDDSBack.BltFast (XFish, YFish, FDDSFish,

SwrkRect, DDBLTFAST_WAIT or DDBLTFAST_SRCCOLORKEY);

end;


end;


end;


Для описания пузырьков воздуха также используется концепция ООП:


TBubble = class

X, Y : Integer; // Позиция пузырька на экране

Length : Integer; // Образы квадратные, достаточно одной величины

FDDSBubble : IDirectDrawSurface"7;

SpeedBubble : Integer;

Pict : Array of Array of Word; // Массив образа, для полупрозрачности

Alpha : Integer; // Степень прозрачности пузырька

procedure Init; // Инициализация пузырька

procedure Render, // Воспроизведение

end;


Инициализацию пузырька можно упростить. Его поверхность используется только для заполнения массива pict:


procedure TBubble.Init;

var

desc : TDDSURFACEDESC2;

i, j : Integer;

begin

Length := random (30) + 20;

if Failed (frmDD.CreateFromlmage (FDDSBubble, frmDD.imgSphere,

Length, Length) )

then frmDD.ErrorOut(DD_FALSE, 'Create Bubble');

SetLength(Pict, Length); // Задаем размер динамического массива

for i := 0 to Length - 1 do

SetLength(Pict [i], Length);

ZeroMemory (Sdesc, SizeOf(desc));

desc.dwSize := SizeOf(desc);

if Failed (FDDSBubble.Lock (nil, desc, DDLOCK_WAIT, 0)) then Exit;

for i : = 0 to Length - 1 do // Заполняем массив

for j := 0 to Length - 1 do // масштабированным образом

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*