Чтение онлайн

ЖАНРЫ

Графика DirectX в Delphi

Краснов Михаил

Шрифт:

function Restore : HRESULT;

procedure CalcVector;

procedure Hit(S : TSprite);

procedure Show; override; // Вычисление нового положения и вывод private

Xinc : Integer;

Yinc : Integer;

Collidelnfo : TCollidelnfo;

ThisTickCount : DWORD;

LastTickCount : DWORD;

end;

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

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

const

DelayMonsters = 1000;// Через сколько миллисекунд появится новый монстр

MaxSprites = 100; // Ограничение количества спрайтов

var

Monsters : Array [0..MaxSprites - 1] of TSprite; // Массив чудовищ

Bullets : Array [0..MaxSprites - 1] of TBullet; // Массив пуль

Warrior : TWarrior; // Объект бойца

GlobalThisTickCount : DWORD; // Глобальный таймер

GlobalLastTickCount : DWORD;

NumMonsters : Integer =0; // Текущее количество монстров

NumBullets : Integer =0; // Текущее количество пуль

Создание отдельного спрайта (имеющего собственную поверхность) происходит очень долго, поэтому массивы спрайтов заполняются в начале работы приложения. Если же поступать так, как подсказывает логика, и создавать объекты только непосредственно перед их появлением на экране, картинка в такие моменты будет замирать на долю секунды. Создание двух сотен объектов будет долгим. Чтобы скрасить время ожидания, перед началом этого процесса я вывожу на первичную поверхность картинку фона, но можно было бы использовать и специальную заставку:

FDDSBackGround := DDLoadBitmap(FDD, bkBitmap, 0, 0); // Загружаем фон

if FDDSBackGround = nil then ErrorOut(hRet, 'DDLoadBitmap');

// Палитра предварительно загружена,

// устанавливается для всех поверхностей программы

hRet := FDDSBackGround.SetPalette(FDDPal);

if Failed (hRet) then ErrorOut(hRet, 'SetPalette');

// Прямоугольник, охватывающий весь экран

SetRect(bkRect, 0, 0, ScreenWidth, ScreenHeight);

// Сразу же после загрузки на экран выводится фон

FDDSPrimary.BltFast(0, 0, FDDSBackGround, ObkRect, DDBLTFAST_WAIT;

Randomize;

// Создание объекта воина

Warrior := TWarrior.Create (ImgWarrior);

// Заполняем массив монстров

for wrkl := Low (Monsters) to High (Monsters) do

if random > 0.5

then Monsters [wrkl] := TSprite.Create (ImgMosterl,

40+ random (40), 4)

else Monsters [wrkl] := TSprite.Create (ImgMoster2, 40 + random (20), 6);

// Заполняем массив пуль

for wrkl := Low (Bullets) to High (Bullets) do

Bullets [wrkl] := TBullet.Create (ImgBullet);

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

чтобы не получилось, будто бы все чудовища синхронно меняются в своем обличий:

Constructor TSprite.Create (const Image : TImage; const SprDelay : DWORD;

const FrmCount : Integer);

var

DC : HOC;

ddsd : TDDSurfaceDesc2;

hRet : HResult;

begin

ZeroMemory (@ddsd, SizeOf (ddsd) ) ;

with ddsd do begin

dwSize := SizeOf (ddsd) ;

dwFlags := DDSD_CAPS or DDSD_HEIGHT or DDSD_WIDTH;

dwHeight := Image.Height;

dwWidth := Image.Width;

ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN;

end;

hRet := frmDD.FDD.CreateSurface(ddsd, FSpriteSurface, nil);

if Failed (hRet) then frrr.DD. ErrorOut (hRet, ' CreateSpriteSurface ' ) ;

if FSpriteSurface.GetDC(DC) = DD_OK then begin

BitBlt (DC, 0, 0, Image.Width, Image.Height, Image. Canvas .Handle,

0,0, SRCCOPY);

FSpriteSurface.ReleaseDC(DC) ;

end;

// Оба вида монстров нарисованы на зеленом фоне

DDSetColorKey (FSpriteSurface, RGB(0, 255, 0) ) ;

FSpriteSurface.SetPalette(frmDD.FDDPal);

SpriteHeight := Image.Height;

// Image содержит вcе кадры

SpriteWidth := Image.Width div FrmCount;

Collide := False;

PosX := random (640 - SpriteWidth);

PosY := random (426 - SpriteHeight);

CalcVector;

AnimFrame := random (FrmCount); // Текущий кадр - случайно

// Количество кадров для каждого вида монстров свое

FrameCount := FrmCount;

// Индивидуальная задержка смены кадров, передается случайное число

Delay := SprDelay;

// Прямоугольник кадра, фрагмент из ленты кадров

SetRect (rcRect, AnimFrame * SpriteWidth, 0,

AnimFrame * SpriteWidth + SpriteWidth, SpriteHeight);

Live := True;

LastTickCount := GetTickCount;

end;

Остальные методы классов спрайтов или схожи с предыдущими примерами, или тривиальны. Подробно разбирать их, думаю, не стоит, обращу внимание только на некоторые моменты.

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

В рассматриваемом примере блиттинг спрайтов на задний буфер осуществляется с флагом DDBLTFASTJDONOTWAIT, что редко для примеров этой книги.

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

Каждый спрайт снабжен методом, связанным с восстановлением потерянной поверхности, в котором по высоте спрайта определяем, с какой картинкой ассоциирован конкретный объект:

function TSprite.Restore : HRESULT;

var

DC : HOC;

hRet : HRESULT;

Image : ТImage;

begin

Поделиться с друзьями: