Титульная страница DelphiGFX Сделать закладку Написать письмо автору сервера 

  Главная - Документация - 2D Графика

Мастера Delphi | Corba  

Учимся работать с DelphiX

Содержание
Краткий обзор DirectX
Терминология
Обзор DelphiX
Замечания по инсталляции
Начало программирования
Создание поверхности
Добавление в программу изображений
Изучаем таймер
Подготовительные действия
Самое главное - это время
Список ссылок
Краткий обзор DirectX

Говоря техническим языком, DirectX - набор объектов COM (Component Object Model), которые реализуют интерфейсы для облегчения работы с видеоаппаратурой, звуком, межкомпьютерными соединениями и некоторыми системными сервисами.

DirectX был создан для решения проблемы совместимости аппаратуры, пополняющейся все новыми образцами с новыми возможностями и функциями, и программ, этой аппаратурой управляющих. Также применение DirectX с аппаратурой, имеющей функции аппаратного ускорения (3Dfx, NVidia и подобные) позволяет разгрузить основной процессор.

DirectX состоит из 7 основных компонент:

  • DirectDraw - позволяет напрямую работать с видеопамятью и аппаратными функциями оборудования, при этом сохраняя совместимость с Windows-приложениями.
  • DirectInput - интерфейс для устройств ввода (мышь, клавиатура, джойстик и т.д.)
  • DirectPlay - интерфейс для многопользовательских приложений (TCP/IP, Direct Dial, локальное подключение)
  • DirectSound - интерфейс для звуковой аппаратуры (WAV, MIDI и др.)
  • DirectSound3D - позвляет позиционировать звуковые источники в любой точке трехмерного пространства, создавая таким образом реальный объемный звук.
  • Direct3D - интерфейс к 3D - аппаратуре

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

Терминология

Перед началом работы с DirectX необходимо остановиться на терминологии.

Поверхность (surface) - участок видеопамяти, который используется для хранения различных картинок. Все видеобуферы ссылаются на поверхности. Поверхность, которая отображается на экране в текущий момент называется основной (primary) поверхностью. Эта поверхность занимает столько памяти, сколько нужно для текущего разрешения и глубины цвета. Так, если установлен видеорежим 640 x 480 x 256 цветов (8 bpp), тогда основная поверхность будет занимать 307200 байт видеопамяти. Обычно вам нужна еще одна поверхность такого же размера, что и основная, используемая для флиппинга (что это такое будет объяснено чуть позже). Это значит, что нужно 614400 байт видеопамяти просто чтобы начать работать, не загружая никаких картинок. Если количества видеопамяти не хватает, поверхности будут создаваться в системной памяти, теряя преимущества аппаратного ускорения. В настоящий момент вам необходима видеокарта с 2MB видеопамяти - это абсолютный минимум для простых игр.

Двойная буферизация (double buffering) - техника, применяемая для получения быстрой, плавной анимации. Обычно используется дополнительный буфер, где создается сцена, которая затем отображается на экране.

Анимация с флиппингом страниц (page flipping animation) - для понимания этой техники вам достаточно нарисовать что-нибудь в блокноте и быстро пролистать страницы. При этом получится изменяющаяся картинка. В нашем случае сцена создается копированием картинок и спрайтов на поверхность (буфер), которая затем отображается на экране. Флиппинг - очень быстрая операция из-за того, что реально не происходит копирования больших объемов информации. При флиппинге происходит изменение одного регистра видеокарты, который содержит адрес участка памяти, отображающегося в текущий момент. В основном флиппинг состоит из 3-х шагов:
1. Создание сцены в буфере.
2. Переключение буфера для отображения, при этом поверхность, которая отображалась до этого становится буфером.
3. Повторяем с шага 1.

Кадры в секунду (frames per second) - обычно записывается как FPS, и обозначает количество кадров анимации, необходимое для получения плавного реального движения. Обычно для игр достаточно 24-25 FPS для получения приемлемых результатов (хотя для некоторых игр это значение может быть еще меньше).

Эффект разрывания экрана (tearing) - Большинство мониторов обновляют экран с частотой примерно 70 раз/сек (70 Гц) сверху вниз. Проблема возникает когда вы пытаетесь отобразить новую картинку где-то в середине процесса обновления экрана. При этом верхняя половина экрана отображает старую картинку, а нижняя - новую. Во время существования DOS для предотвращения этого эффекта вам нужно было бы синхронизироваться с вертикальной разверткой. DirectX освобождает вас от этой процедуры.

Картинки (битмапы) и спрайты - спрайты отличаются от битмапов тем, что они имеют прозрачные области. Термин "спрайт" также используется для анимированных изображений в играх. Обычно спрайт состоит из нескольких фреймов (кадров), как в ледующем примере:

Отметим, что каждый фрейм отображает определенный угол поворота объекта. Если показывать эти картинки одна за другой, то получится что-то тип этого:

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

Клиппинг (clipping) - эта техника применяется для того, чтобы приложение смогло рисовать только в отведенный ему прямоугольник на экране. Если клиппинг не применяется, приложение может рисовать на всем экране. Это прекрасно работает если ваше приложение полноэкранное, но если оно оконное - клиппинг должен применяться обязательно.

Теория цвета - цвет в Windows обычно представляется моделью RGB (красный, зеленый, синий). Используя эти три основных цвета позволяет получить все мыслимые оттенки комбинацией основных. Обычно цвет хранится в виде 3-х байтов - каждый байт представляет относительную интенсивность основного цвета (от 0 до 255 включительно).

В Delphi цвет представляется в виде класса TColor, объявленном в модуле Graphics. Вы можете определить цвет используя консттанты типа clBlue, clLime и т.п. или определяя TColor как 4-хбайтовое число, где 3 младших байта представляют RGB цвет. Так, 0x00FF0000 - синий, 0x0000FF00 - зеленый, 0х000000FF - красный, 0x00000000 - черный, 0х00FFFFFF - белый и т.д. По поводу старшего байта в помощи по VCL сказано: "Если старший байт равен $00, получаемый цвет - ближайший подходящий цвет системной палитры, если он равен $01 - берется ближайший цвет из текущей палитры, если $02 - подбирается ближайший цвет, соответствующий контексту устройства".

Обзор DelphiX

Для работы с DelphiX необходимы следующие компоненты:

  • DirectX Run-time
  • Delphi 3, 4, 5, 6
  • DelphiX

DelphiX - набор бесплатных компонент для Delphi для упрощения использования DirectX. Компоненты и их назначение представлены ниже:

Название
компоненты
Описание
компоненты
TDXDraw Дает доступ к поверхностям DirectDraw и включает весь код, необходимый для работы с DirectDraw и DirectDraw.
TDXDib Позволяет хранить DIB (Device Independent Bitmap) подробне
TDXImageList Позволяет хранить серии DIB-файлов, что очень удобно для программ, содержащих спрайты. Позволяет загружать серию с диска во время выполнения программы.
TDX3D Оставлен для совместимости с предыдущими версиями DelphiX, используйте TDXDraw.
TDXSound Позволяет легко проигрывать wav-файлы.
TDXWave "Хранилище" для wav-файла.
TDXWaveList "Хранилище" для серии wav-файлов.
TDXInput Позволяет получить доступ к объекту DirectInput и, соответственно, к мыши, клавиатуре и т.д.
TDXPlay Позволяет разработчику легко подсоединить данные, находящиеся на другом компьютере, в том числе через Internet или LAN.
TDXSpriteEngine Облегчает и автоматизирует работу со спрайтами. Поддержка методов Move, Kill и т.д.
TDXTimer Дает более высокую точность, чем при использовании обычного таймера (TTimer). Используются потоки, синхронизация.
TDXPaintBox DIB-версия стандартной компоненты TImage
Замечания по инсталляции

Предположим, вы уже распаковали DelphiX на жесткий диск и готовы инсталлировать ее на палитру компонент. Если библиотека пришла в виде исходников, желательно перекомпилировать ее. В любом случае для инсталляции выберите из меню File пункт Open и найдите каталог, в который вы распаковали библиотеку. Там должны быть файлы Delphi_for3.dpk, Delphi_for4.dpk и Delphi_for5.dpk, для соответствующей версии Delphi. Появится диалог подобный следующему:

DelphiX project

Нажмите кнопку Compile и после компиляции нажмите кнопку Install. Если все прошло успешно, появится диалог, сообщающий об этом. Все готово для экспериментов с DirectX.

Если у Вас имеется каталог DelphiX\Bin то для подключения компонент проще использовать утилитки Install_for3.exe, Install_for4.exe и Install_for5.exe - они не только установят DelphiX, но и подключат файл помощи.

Начало программирования

В качестве примера создадим следующий эффект:

Animated hole

Создание поверхности

Для использования DirectDraw нужно создать поверхность, на которой мы бдем рисовать. Просто перетащите компоненту TDXDraw на вашу форму. Дайте ей имя DXDraw. В инспекторе объектов вы увидите 4 свойства, которые нас интересуют. Это Align, Autoinitialize, Display и Options.

Установите свойство Align в alClient, т.к. мы хотим, чтобы весь экран стал поверхностью DirectDraw.

Autoinitialize всегда должно быть установлено в true, только если мы не хотим инициализировать поверхность вручную, для чего, наверное, нужно использовать метод DXDraw.Initialize в обработчике OnCreate формы.

Свойство Display поможет вам выбрать размер области рисования. Допустимые видеорежимы показаны в выпадающем списке. Для нашего примера установите свойство в 640x480x8.

Свойство Options дает доступ к 18 атрибутам. Таблица объясняет их назначение.

Атрибут
Описание
doFullScreen Запускает приложение в полноэкранном режиме. Видеорежим может быть указан в свойстве Display.
doNoWindowChange Если выбрана эта опция и doFullScreen, приложение сначала максимизирует свое окно, а затем устанавливает режим, указанный в свойстве Display.
doAllowReboot Определяет, можно ли в программе использовать комбинацию Alt+Ctrl+Del. Это полезно во время отладки.
doWaitForBlank Определяет, будет ли ожидаться вертикальная развертка при выполнении операции флиппинга. Опция немного уменьшает FPS.
doAllowPalette256 Будет ли использоваться 256-цветная палитра
doSystemMemory Определяет, использовать ли системную память вместо видеопамяти. Опция немного уменьшает FPS.
doStretch Если ваша игра использует область отображения большую (или меньшую), чем указано в свойстве Display, с помощью этой опции можно сжать (растянуть) изображение на весь экран.
doCenter Поверхность отобразится в центре экрана.
doFlip Применяется только для полноэкранных режимов. Если используется двойная буферизация и требуется отобразить буфер, то в случае установленной опции это происходит очень быстро (применяется операция флиппинга).
Замечание: размер буфера должен равняться размеру основной поверхности.
do3D Позволено ли использовать 3D акселерацию
doHadrware

Если видеоадаптер поддерживает аппаратное ускорение типа 3D или 2D, то полезно установить опцию в true.
Замечание: Если опция установлена в true, а видеокарта не поддерживает акселерацию, опция будет установлена в false. это можно использовать для определения поддержки аппаратного ускорения.

doRetainedMode Опция имеет эффект только если установлена опция do3D. Если опция равна true, используется режим Direct3D Retained, иначе - Immediate.
doSelectDriver В полноэкранном режиме определяет будет ли использоваться драйвер DirectDraw. Для Voodoo и подобных видеоадаптеров опция должна быть установлена в True.
doDrawPrimitive Использовать рисование примитивов.
doZBuffer Использовать ли Z-буфер. Эта опция может устранить некоторые проблемы с пропаданием объектов или наоборот, с появлением объектов, которые должны находиться на заднем плане. Требует часть процессорного времени. Некоторые карты поддерживают эту функцию аппаратно.
doTexture Будем ли мы использовать текстуры на 3D объектах?
doRGB Определяет, станет ли использоваться цветовая модель RGB. Может улучшить внешний вид 3D объектов, но отнимает процессорное время. Если карта аппаратно поддерживает эту функцию, опция не влияет на работу.
doMono Использовать ли черно-белую цветовую модель.
doDither Определяет будет ли подбираться ближайший цвет из палитры, если в ней не окажется запрашиваемого нами цвета. В основном используется с атрибутом doAllowPalette256.

Наши установки буду выглядеть следующим образом:

  • doFullScreen=False (программа будет стартовать в обычном окне)
  • doAllowReboot=True (Возможно, возникнет ситуация, когда нам нужно будет снять задачу из-за ошибок)
  • doWaitVBlank=True/False (Попробуйте оба значения. Возможно, вы получите приемлемое качество при установке False, при этом возрастет FPS)
  • do3D=False (Наше приложение будет использовать только 2D)
  • doHardware=True (Нам нужно определить, поддерживает ли аппаратура акселерацию.)

Остальные атрибуты оставлены как есть.

Добавление в программу изображений

Работа с битмапами в DelphiX обычно трудностей не вызывает. Все, что вам нужно сделать - создать картинку и затем добавить компоненту TDXDib или TDXImageList. Для нашего примера перетащим на форму TDXImageList (и назовем ее DXImageList). В инспекторе объектов вы увидите 2 свтойства: DXDraw и Items.

DXDraw определяет поверхность DirectDraw, на которой будет рисоваться эта картинка (или серия картинок). Возможно использование нескольких поверхностей. В нашем случае просто выберите уже созданную поверхность DXDraw.

Items содержит все изображения в серии. Для добавления новых изображений нажмите на кнопку "..." и добавьте свои картинки.

Изучаем таймер

Следующий шаг в нашем простом примере - добавление таймера (назовем его DXTimer). Как вы наверное знаете, SystemTimer, входящий в стандартный набор компонент Delphi, не очень точен для использования в играх. DXTimer имеет разрешение, близкое к миллисекунде, что вполне достаточно для наших (и более серьезных) целей. Установите его свойства следующим образом:

  • ActiveOnly = true (таймер всегда активен)
  • Enabled=false (мы сами будем управлять им - запукать и останавливать)
  • Interval=0 (максимальная частота срабатывания таймера)

Еще одна очень приятная фича DXTimer'а - свойство DXTimer.FrameRate. Оно позволяет получить значение FPS в любой момент времени. Событие таймера обычно используется для методов типа DXDraw.Flip, DXDraw.Render, DXDraw.Update и др. В нашем примере используется метод DXDraw.Flip.

Подготовительные действия

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

Во-первых, в закрытую секцию необъодимо добавить некоторые переменные и процедуры:

SineMove : array[0..255] of integer; { Таблица синусов для движения }
CosineMove : array[0..255] of integer; { Таблица косинусов }
SineTable : array[0..449] of integer; { Таблица синусов }
CenterX, CenterY : Integer; { Для координат центра черной дыры, которую мы будем рисовать }

procedure CalculateTables; { Заполнение таблиц синусов и косинусов }
procedure PlotPoint( XCenter, YCenter, Radius, Angle : Word); { Рисование точки на бэк-буфере }

Таблицы содержат заранее рассчитываемые значения синусов и косинусов, используемые для моделирования волн при движении. Зачем эти значения рассчитываются заранее? Вообще, в программировании игр всегда существует компромисс между скоростью и объемом. Если приложение занимает немного памяти, то оно все просчитывает само, следовательно, отнимается какое-то время. Либо можно просчитать все до цикла рисования, сохранить результаты и затем использовать их, выиграв в скорости. Процедура заполнения таблиц может выглядеть следующим образом (хотя ваша реализация, несомненно, будет работать намного лучше):

procedure TMainForm.CalculateTables;
var
   wCount : Word;
begin
{ Precalculted Values for movement }
   for wCount := 0 to 255 do
   begin
      SineMove[wCount] := round( sin( pi*wCount/128 ) * 45 );
      CosineMove[wCount] := round( cos( pi*wCount/128 ) * 60 );
   end;
{ Precalculated Sine table. Only One table because cos(i) = sin(i + 90) }
   for wCount := 0 to 449 do
   begin
      SineTable[wCount] := round( sin( pi*wCount/180 ) * 128);
   end;
end;

К процедуре построения точки мы вернемся позже. Следующее, что нужно сделать - это добавить кое-какой код в событие OnCreate формы:

procedure TMainForm.FormCreate(Sender: TObject);
begin
   CenterX := Width div 2;
   CenterY := Height div 2;
   CalculateTables;
end;

И для того, чтобы форма завершилась по нажатию на клавишу ESC, нужно добавить код в обработчик события OnKeyDown:

procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
   if Key=VK_ESCAPE then Close;
end;

Теперь выбираем компоненту DXDraw, которую мы поместили на форму и создаем обработчик события OnFinalize:

DXTimer.Enabled := False;

Этот кусок кода останавливает таймер. Создайте обработчик события OnInitialize и добавьте в него строчку:

DXTimer.Enabled := True;

что запускает таймер, т.е. начинает отображение картинки (конечно, если поверхность готова к этому)

Самое главное - это время

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

procedure TMainForm.DXTimerTimer(Sender: TObject; LagCount: Integer);
const
  x : Word = 0;
  y : Word = 0;
  IncAngle = 12;
  XMove = 7;
  YMove = 8;
var
  CountAngle : Word;
  CountLong : Word;
  IncLong :Word;
begin
  if not DXDraw.CanDraw then exit;
  IncLong := 2;
  CountLong := 20;
  DXDraw.Surface.Fill( 0 );
  repeat
    CountAngle := 0;
    repeat
      PlotPoint(CosineMove[( x + ( 200 - CountLong )) mod 255],
      SineMove[( y + ( 200 - CountLong )) mod 255], CountLong, CountAngle);
      inc(CountAngle, IncAngle);
    until CountAngle >= 360;
    inc(CountLong, IncLong);
    if ( CountLong mod 3 ) = 0 then inc(IncLong);
  until CountLong >= 270;
  x := XMove + x mod 255;
  y := YMove + y mod 255;   
  with DXDraw.Surface.Canvas do
  begin
    Brush.Style := bsClear;
    Font.Color := clWhite;
    Font.Size := 12;
    Textout( 0, 0, 'FPS: '+inttostr( DXTimer.FrameRate ) );
    Release;
  end;
DXDraw.Flip;
end;

Это основной код нашего приложения. Рассмотрим некторые важные моменты:

Вызов процедуры Fill поверхности DXDraw.Surface (DXDraw.Surface.Fill(0);) заполняет буфер цветом, который передается ей в качестве параметра (в нашем случае - черный).

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

procedure TMainForm.PlotPoint(XCenter, YCenter, Radius, Angle: Word);
var
   X, Y : Word;
begin
   X := ( Radius * SineTable[90 + Angle]);
   asm
      sar x,7
   end;
   X := CenterX + XCenter + X;
   Y := ( Radius * SineTable[Angle] );
   asm
      sar y,7
   end;
   Y := CenterY + YCenter + Y;
   if (X < Width ) and ( Y < Height ) then
   begin
      DXDraw.Surface.Canvas.Pixels[X, Y] := clBlue;
      DXImageList.Items[0].Draw( DXDraw.Surface, X, Y, 0 );
   end;
end;

Основная строка в этой процедуре:

DXImageList.Items[0].Draw( DXDraw.Surface, X, Y, 0 );

Как было сказано выше DXImageList содержит массив картинок, с которыми мы работаем. Доступ к элементам осуществляется через индекс, начинающийся с 0. Т.е. указывая DXImageList.Items[0], мы получаем первую картинку, и т.д. Свойство Items имеет метод Draw, в который нужно передать 4 параметра. Первый параметр определяет поверхность, на которой будет рисоваться эта картинка. Второй и третий параметры - X и Y - определяют позицию, в которую будет выведено изображение. Последний параметр - флаг, определяющий прозрачность выводимой картинки. Так что строка кода, приведенная выше может прочитаться как "Вывести картинку с индексом 0 на поверхность DXDraw.Surface в позицию X, Y со значением прозрачности 0".

Также можно использовать свойство Pixels объекта Canvas для указания цвета определенной точки на экране (эта строка закоментирована, так как в нашем случае используется картинка. Эксперименты со свойством Pixels даются вам в качестве домашнего задания).

После того, как мы нарисовали нашу картинку, мы выводим значение FPS используя свойство FrameRate таймера. Вывод производится с помощью свойства Canvas объекта DXDraw.Surface.

Наконец вызывается метод DXDraw.Flip для отображения картинки на экране. При этом основная поверхность становится буфером.

Все, компилируйте и запускайте ваше приложение. На P120 получается порядка 12-15FPS (в зависимости от полноэкранного/оконного режима).

Список ссылок

Скачать пример
Скачать библиотеку DelphiX
Официальный сайт DelphiX
Не официальный сайт поддержки DelphiX
DelphiX FAQ
Титульная страница DelphiGFX Сделать закладку Написать письмо автору сервера
Hosted by uCoz