Виталий - 2011-07-04

Часть вторая

Введение

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

Список изображений

В Андорре все изображения хранятся в так называемом «Списке изображений». Этот список управляет всеми изображениями, загруженными в систему. За весь этот список отвечает переменная, которую мы назовем AdImageList и определим ее тип, как TAdImageList и инициализируем ее в FormCreate. Следует отметить, что инициализация должна проходить только после вызова AdDraw.Initialize, поэтому после строчки Application.OnIdle:=Idle мы продолжаем писать:

AdImageList:= TadImageList.Create(AdDraw);

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

with AdImageList.Add('bild') do // Для рисунка с названием 'bild', принадлежащему к списку изображений  AdImageList устанавливаются параметры
begin
Texture.LoadGraphicFromFile('textur.bmp') // Текстура загружается в изображение
end;
AdImageList.Restore; // Мы вернемся к этому позже

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

В Андорре текстура и изображение — не одно и то же, текстурой является то, что мы загружаем из файла, то есть видимая часть. Если в файле Tux.bmp нарисован пингвин, то при загрузке его пингвин будет является текстурой. Изображение же представляет собой холст, на который прорисовывается этот пингвин.

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

with AdImageList.Add ( 'bild' ) do //Для рисунка с названием 'bild', принадлежащему к списку изображений  AdImageList устанавливаются параметры
begin
Texture.LoadGraphicFromFile ( 'textur.bmp' , false ,clNone ) ; // текстура загружается в изображение без прозрачности
Restore;
end ;

Однако данный метод несколько трудоемкий, когда необходимо загрзить не одну текстуру.

Загрузка изображений

У текстуры имеются следующие методы для загрузки и сохранения изображений:

procedure LoadFromStream ( AStream:TStream ) ;

procedure SaveToStream ( AStream:TStream ) ;

procedure SaveToFile ( AFile: string ) ;

procedure LoadFromFile ( AFile: string ) ;

procedure LoadGraphicFromFile ( AFile: string ;Transparent: boolean ;TransparentColor:TColor ) ;

procedure LoadFromGraphic ( AGraphic:TGraphic ) ;

Но только последние две функции важны для загрузки текстур напрямую. Первые функции Загружают текстуры в форма файла Андорры и не предназначены для загрузки нормальный графических файлов.

procedure LoadFromGraphic ( AGraphic:TGraphic )  получает параметры прозрачности непосредственно из загруженного файла. По умолчанию, Андорра может загружать все стандартные форматы графики VCL - *.bmp (Bitmap), *.dib (Device Interpendent Bitmap) и *.wmf (Windows Meta File). Для загрузки файлов с другими расширениями необходимо подключать дополнительные библиотеки, такие как PngDelphi и другие. При использовании модуля  PngDelphi программист, то есть вы, должны в раздел units записать модуль AdPng. Дополнительные библиотеки, хочу заметить, подключаются отдельно и могут иметь другие лицензии.

Поднять сцену, или отрисовка текстур

А теперь мы отобразим загруженную текстуру в окне. В процедуре Idle между строчками BeginScene и EndScene  запишем

AdImageList.Find('bild').Draw(AdDraw,0,0,0);

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

Но кроме функции Draw существуют еще функции, добавляющие возможностей к существующей:

//Самый простой способ рисования
procedure Draw ( Dest:TAdDraw;X,Y,PatternIndex: integer ) ;

//Изображение растягивается
procedure StretchDraw ( Dest:TAdDraw;const DestRect:TAdRect;PatternIndex: integer ) ;

//Изображение с определенным значением прозрачности от 0 как полностью прозрачное до 255 как полностью непрозрачное.
procedure DrawAlpha ( Dest: TAdDraw; const DestRect: TAdRect; PatternIndex: Integer ; Alpha: Integer ) ;

//Изображение с аддитивной смесью цвета и определенным значением альфа
procedure DrawAdd ( Dest: TAdDraw; const DestRect: TAdRect;PatternIndex: Integer ; Alpha: Integer ) ;

//Изображение, растягивается и имеет прозрачность
procedure DrawMask ( Dest: TAdDraw; const DestRect: TAdRect; PatternIndex: Integer ; Alpha: Integer ) ;

//Изображение повернуто на угол Angle, имеющий значение от 0 до 360 градусов, с центрами вращения  CenterX и CenterY, принимающими значения от 0 до 1, соответственно, центр картины будет находиться в точках 0,5
procedure DrawRotate ( Dest: TAdDraw; X, Y, Width, Height: Integer ; PatternIndex: Integer ; CenterX, CenterY: Double ; Angle: Integer ) ;

procedure DrawRotateAdd ( Dest: TAdDraw; X, Y, Width, Height: Integer ; PatternIndex: Integer ; CenterX, CenterY: Double ; Angle: Integer ; Alpha: Integer ) ;

procedure DrawRotateAlpha ( Dest: TAdDraw; X, Y, Width, Height: Integer ; PatternIndex: Integer ; CenterX, CenterY: Double ; Angle: Integer ; Alpha: Integer ) ;

procedure DrawRotateMask ( Dest: TAdDraw; X, Y, Width, Height: Integer ; PatternIndex: Integer ; CenterX, CenterY: Double ; Angle: Integer ; Alpha: Integer ) ;

//А вот эта функция включает в себя все предыдущие функции, следовательно все описанное ранее можно выполнить данной функцией
procedure DrawEx ( Dest:TAdDraw; SourceRect,DestRect:TAdRect;CenterX,CenterY: integer ;Angle: Integer ;Alpha: Integer ;BlendMode:TAd2dBlendMode ) ;

http://andorra.sourceforge.net/docs/AdDraws.TAdCustomImage.html

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

В полный экран

Многие современные игры работают в полноэкражном режиме. В Андорре он устанавливается следующим способом:
В событии OnCreate до строчки AdDraw.Initialize добавьте следующие строчки:

with AdDraw. Display do

begin

Width := 800 ;

Height := 600 ;

BitDepth := ad32Bit; //Глубина цвета. Здесь разрешены значения  ad32Bit и  ad16Bit

DisplayMode := dmFullscreen;

end ;

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

BorderStyle := bsNone;

Top := 0 ;

Left := 0 ;

Width := Screen. Width ;

Height := Screen. Height ;

Но в этом случае можно не достигнуть необходимого значения FPS, поскольку процессор комьютера «отвлекается» на другие окна.

Движения и анимации

Лучше всего удалить все измененияч из программы, сделанные в этой главе.

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

Анимация строится как в киноленте. Для показа анимации последовательно показываются части текстуры. Использование отдельных файлов с кадрами было бы неэффективно: видеокарте гораздо проще управлять одной текстурой разметром 512*512, чем много маленьких размером 128*128.

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

AdImageList := TAdImageList. Create ( AdDraw ) ;

with AdImageList. Add ( 'figur' ) do

begin

Texture. LoadGraphicFromFile ( 'boy.bmp' , true ,clFuchsia ) ; // Загружает изображение прозрачным с цветом прозрачности, указанным в последнем параметре  clFuchsia

PatternWidth := 96 ;

PatternHeight := 96 ;

end ;

AdImageList. Restore ;

Параметрами PatternHeight и PatternWidth мы устанавливаем ширину и высоту кадра, в нашем случае до 96. При вызове функции  Restore Андорра создает карту, в которой хранится количество и расположение каждого кадра. Кадры Андорра считает сперва слева направо, а затем сверху вниз, нумерация начинается с нуля.

Мы прямо сейчас можем отобразить один из кадров, просто указав его номер, но мы не будем этим заниматься, поскольку нашей задачей является анимация. Поэтому нам понадобиться переменная типа single или double. Позже вы увидите, почему она не может быть integer. Мы добавим переменную Pattern в программу в раздел объявления глобальных переменных

var

Form1: TForm1;

Pattern: single ;

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

Pattern := Pattern + 1 ;

if Pattern >= AdImageList. Items  . PatternCount -1 then Pattern := 0 ;

AdImageList. Items  . Draw ( AdDraw, 0 , 0 , round ( Pattern ) ) ;

Однако анимация происходит слишком быстро, ведь цикл выполняется до 5000 раз в секунду, следовательно, картинка будет прорисовываться 5000 раз в секунду, изменяя каждый раз значение кадра. Если же прибавлять каждый раз не 1, а, к примеру, 0,005, то скорость анимации будет нестабильная и различаться на каждом компьютере. Чтобы этого избежать, в Андорре имеется еще один модуль, называемый AdPerfomanceCounter. Мы объявляем переменную типа  TAdPerfomanceCounter под именем  AdPerCounter в секции public, создаем экземпляр класса в FormCreate и освобождаем его в FormDestroy, как мы до этого делали с остальными компонентами.

Запишем теперь AdPerCounter.Calculate в начале idle процедуры. При выполнении этой функции объект вычисляет промежуток времени, а также FPS ( число кадров в секунду ). Благодаря этому анимация будет идти примерно на всех компьютерах с одинаковой скоростью. Прошедшее время записывается в переменной  AdPerCounter.TimeGap.
А теперь записывем между BeginScene и EndScene:

AdPerCounter. Calculate ;

Pattern := Pattern + 15 * ( AdPerCounter. TimeGap / 1000 ) ;

if Pattern<16 then Pattern:=0;

Запускаем, и ( о чудо! ) анимация плавно воспроизводится! Число 15 указывает на количество кадров в секунду.

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

var

Form1: TForm1;

Pattern: single ;

StartPt,EndPt: integer ; // анимация начала и конца картины

Y,X: single ; // цифры X и Y позиции

XSpeed: single ; // Скорость по оси X

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

AdImageList.Find ( 'figur' ) . Draw ( AdDraw, round ( X ) , round ( Y ) , round ( Pattern ) ) ;

Теперь мы можем программно изменять положение рисунка. Для этого запишем выше этой самой функции вывода строчку:

X := X + XSpeed * ( AdPerCounter. TimeGap / 1000 ) ;

Сразу хочу заметить, что переменная Xspeed может быть и отрицательной. Если она положительна, то рисунок движется слева направо, то есть каждый раз значение X будет увеличиваться. Если же Xspeed примет отрицательное значение, то значение X каждый раз будет уменьшаться.

Теперь про анимацию человечка. Как мы видим, он во время движения по оси X может бежать и нормальным образом и задом-наперед. Чтобы этого избежать, нам и потребуются описанные ранее переменные StartPt и EndPt. Они будут ограничивать показываемые кадры, то есть при беге направо будут прокручиваться восемь кадров анимации, когда он бежит направо, а при беге налево будут показываться другие восемь кадров.

if Pattern >= EndPt then Pattern := StartPt;

Теперь нам нужна процедура для обработки изменения направления бега человечка. Назовем эту процедуру SetLine, объявив ее в разделе public формы.

procedure TForm1. SetLine ;

begin

//Обратное направление, как уже писал ранее, от переменной Xspeed зависит направление движения

XSpeed := -XSpeed;

if XSpeed > 0 then

begin

StartPt := 0 ;

EndPt := 7 ;

X := -96 ;

endelse

begin

StartPt := 8 ;

EndPt := 15 ;

X := ClientWidth +96 ;

end ;

//Die Y-Position setzen

Y := Random ( ClientHeight -96 ) ;

end ;

Помимо этого, нам понадобятся следующие строки в процедуре Idle, проверяющие, достигла ли фигура края экрана. Если да, то изменяем направление движения:

if ( ( X > ClientWidth ) and ( XSpeed > 0 ) ) or

( ( X < -96 ) and ( XSpeed < 0 ) ) then SetLine;

А также мы должны установить значение Xspeed и вызвать процедуру SetLine в FormCreate

XSpeed := -150 ;

SetLine;

Готово!

Исходный код:

unit Main;

interface

uses

Windows, Dialogs, SysUtils, Graphics, Classes, Forms, AdDraws, AdClasses, AdTypes,

AdPerformanceCounter;

type

TForm1 = class ( TForm )

procedure FormCreate ( Sender: TObject ) ;

procedure FormDestroy ( Sender: TObject ) ;

procedure FormResize ( Sender: TObject ) ;

private

{ Private-Deklarationen }

public

AdDraw:TAdDraw;

AdPerCounter:TAdPerformanceCounter;

AdImageList:TAdImageList;

procedure Idle ( Sender: TObject ;var Done: boolean ) ;

procedure SetLine;

{ Public-Deklarationen }

end ;

var

Form1: TForm1;

Pattern: single ;

StartPt,EndPt: integer ;

Y,X: single ;

XSpeed: single ;

implementation

{$R *.dfm}

procedure TForm1. SetLine ;

begin

XSpeed := -XSpeed;

if XSpeed > 0 then

begin

StartPt := 0 ;

EndPt := 7 ;

X := -96 ;

end

else

begin

StartPt := 8 ;

EndPt := 15 ;

X := ClientWidth +96 ;

end ;

Y := Random ( ClientHeight -96 ) ;

end ;

procedure TForm1. FormCreate ( Sender: TObject ) ;

begin

ReportMemoryLeaksOnShutdown := true ;

AdPerCounter := TPerformanceCounter. Create ;

AdDraw := TAdDraw. Create ( self ) ;

AdDraw. DllName := 'AndorraDX93D.dll' ;

if AdDraw. Initialize then

begin

Application. OnIdle := Idle;

AdImageList := TAdImageList. Create ( AdDraw ) ;

with AdImageList. Add ( 'figur' ) do

begin

Texture. LoadGraphicFromFile ( 'boy.bmp' , true ,clFuchsia ) ;

PatternWidth := 96 ;

PatternHeight := 96 ;

end ;

AdImageList. Restore ;

XSpeed := -150 ;

Randomize ;

SetLine;

end

else

begin

ShowMessage ( 'Error while initializing Andorra 2D. Try to use another display ' +

'mode or another video adapter.' ) ;

Close ;

end ;

end ;

procedure TForm1. FormDestroy ( Sender: TObject ) ;

begin

AdImageList. Free ;

AdPerCounter. Free ;

AdDraw. Free ;

end ;

procedure TForm1. Idle ( Sender: TObject ; var Done: boolean ) ;

begin

if AdDraw. CanDraw then

begin

AdPerCounter. Calculate ;

Caption := 'FPS:' + inttostr ( AdPerCounter. FPS ) ;

Pattern := Pattern + 15 *AdPerCounter. TimeGap / 1000 ;

if Pattern >= EndPt then Pattern := StartPt;

X := X + XSpeed*AdPerCounter. TimeGap / 1000 ;

if ( ( X > ClientWidth ) and ( XSpeed > 0 ) ) or

( ( X < -96 ) and ( XSpeed < 0 ) ) then SetLine;

AdDraw. ClearSurface ( clBlack ) ;

AdDraw. BeginScene ;

AdImageList. Find ( 'figur' ) . Draw ( AdDraw, round ( X ) , round ( Y ) , round ( Pattern ) ) ;

AdDraw. EndScene ;

AdDraw. Flip ;

Done := false ;

end ;

end ;

end .