Графика DirectX в Delphi
Шрифт:
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
hRet := FSpriteSurface .__Restore;
if Failed (hRet) then begin
Result := hRet;
Exit;
end;
// Пользуемся тем, что высота трех образов различна
if SpriteHeight = 15 then Image := frmDD.ImgMoster2 else
if SpriteHeight = 22 then Image := frmDD.ImgMosterl
else Image := frmDD.ImgDead;
// Копируем нужный образ на восстанавливаемую поверхность
if FSpriteSurface.GetDC(DC) = DD__OK then begin
BitBltfDC, 0, 0, Image.Width, Image.Height, Image.Canvas.Handle,
0, 0, SRCCOPY);
FSpriteSurface.ReleaseDC(DC);
end;
Result := FSpriteSurface.SetPalette(frmDD.FDDPal);
end;
Пули, долетевшие до края окна, должны удаляться из списка воспроизводимых образов:
procedure UpdateBul;
var
wrkl, wrkJ : Integer;
begin
for wrkl := 0 to NumBullets - 2 do
if (Bullets [wrkI].PosX >= 632) or (Bullets [wrkI].PosX <= 0) or
(Bullets [wrklJ.PosY <= 0) then begin