Давай продвинемся дальше в наших познаниях DirectDraw. Сразу же приготовься и забери исходники сегодняшнего примера здесь. В этом примере я уже не использую контексты GDI, я рисую только средствами DirectDraw. А самое интересное, что я рисую картинкой. Принцип работы программы похож на ту, что мы рассматривали в прошлой статье, поэтому я расскажу только отличия.
Хватит болтовни, давай приступим к программированию. Посмотри на рисунок 1. Сегодня ты получишь вот такую прогу.
Рис.1 Пример работы проги
Для облегчения проги я использовал библиотеку DDUtils1 . Её ты можешь взять (если ещё не сделал этого) в разделе "Полезности".
С чего всё началось? А началось всё с того, что я объявил ещё одну поверхность. Теперь у меня в программе три поверхности: две переключающиеся и одна для хранения BMP файла.
После этого, в конце процедуры StartDX я вызываю процедурку GetPicture.
procedure TForm1.GetPicture;
begin
FSecondSurface := DDLoadBitmap(FDirectDraw, '1.bmp', FRect.Left, FRect.Top);
if(FSecondSurface = nil) then
begin
InitFail();
Exit;
end;
end;
DDLoadBitmap загружает картинку в поверхность FSecondSurface. Похожая процедура есть в DDUtils1, но я решил переписать её внеся небольшие изменения. Давай посмотрим, что в ней происходит:
function DDLoadBitmap(DirectDraw : IDirectDraw2;
const BitmapName: string;var Width, Height: integer):IDirectDrawSurface;
var
Bitmap : HBitmap;
BM : Windows.TBitmap;
SurfaceDesc: TDDSurfaceDesc;
Begin
// Пытаемся загрузить картинку
Bitmap:= LoadImage(0, PChar(BitmapName), IMAGE_BITMAP, Width, Height,
LR_LOADFROMFILE or LR_CREATEDIBSECTION);
// Проверяем, получилось ли это?
if Bitmap = 0 then
Raise Exception.CreateFmt('Unable to load bitmap %s', [ BitmapName ]);
//Вытаскиваем из картинки растровые данные
GetObject(Bitmap, SizeOf(BM), @BM);
//Подготавливаем временную поверхность
FillChar(SurfaceDesc, SizeOf(SurfaceDesc), 0);
with SurfaceDesc do
begin
dwSize:= SizeOf(SurfaceDesc);
dwFlags:= DDSD_CAPS or DDSD_HEIGHT or DDSD_WIDTH;
ddsCaps.dwCaps:= DDSCAPS_OFFSCREENPLAIN;
dwWidth:= BM.bmWidth;
dwHeight:= BM.bmHeight;
end;
// Создаём поверхность
if DirectDraw.CreateSurface(SurfaceDesc, Result, NIL) <> DD_OK then
Raise Exception.Create('CreateSurface failed');
//Копируем картинку на поверхность с помощью процедуры из DDUtils1
DDCopyBitmap(Result, Bitmap, 0, 0, 0, 0);
Width:=BM.bmWidth;
Height:=BM.bmHeight;
if Bitmap <> 0 then DeleteObject(Bitmap);
end;
Здесь ничего сложного нет, ты можешь сам разобраться. Если тебе лень разбираться, то просто используй эту функцию. В качестве параметров Width и Height возвращается ширина и высота загруженной картинки. Вот и всё. Картинка загружена, переходим к рисованию.
Для рисования я использую функцию BltFast. Это самая быстрая функция из серии "рисовальщиков".
function BltFast(
dwX, dwY: DWORD; // Левая и верхняя позиция на результирующей поверхности.
lpDDSrcSurface: IDirectDrawSurface;// Исходная поверхность из неё будет копироваться.
const lpSrcRect: TRect; // Область исходной поверхности, которую надо скопировать
dwTrans: DWORD): HResult; stdcall;
Эта функция относится к объекту поверхности, поэтому вызывать её надо как ResultSurfase.BltFast(…). Копирование происходит на поверхность ResultSurfase.
dwTrans - это флаги, которые определяют параметры копирования.
DDBLTFAST_DESTCOLORKEY - Прозрачное копирование. В качестве прозрачного цвета используется colorkey результирующей поверхности.
DDBLTFAST_NOCOLORKEY - Копирование без прозрачности.
DDBLTFAST_SRCCOLORKEY - Прозрачное копирование. В качестве прозрачного цвета используется colorkey источника.
DDBLTFAST_WAIT - Если поверхность занята, то функция не генерирует ошибку, а ждёт освобождения поверхности и потом рисует.
Ещё одна интересная функция в программе - InitFail, которая вызывается, если не удалось загрузить картинку:
procedure TForm1.InitFail;
begin
if FDirectDraw <> nil then
begin
FDirectDraw.FlipToGDISurface;
MessageBox(Handle, 'DirectDraw Init FAILED', '', MB_OK );
Close();
end;
end;
Всё в этой функции тебе знакомо, я просто хотел обратить твоё внимание на то, что функция MessageBox вызывается после FDirectDraw.FlipToGDISurface. Зачем это? Я уже говорил в прошлых статьях, что FDirectDraw.FlipToGDISurface передает управление выводом на экран GDI, и после этого я вывожу сообщение. Если не передать управление на GDI, то сообщение не будет видно, потому что DirectDraw не может отобразить функцию MessageBox.
Вот и всё. Сегодня ты научился рисовать растровые данные на поверхности. Это уже половина необходимой информации по DirectDraw для создания игр.