рефераты конспекты курсовые дипломные лекции шпоры

Реферат Курсовая Конспект

E_string.pas

E_string.pas - раздел Образование, Состоит из следующих компонентов - Две Функции - Ltos() И Ltoc() Для Преобразования Типа Longint К Строке Stri...

- две функции - ltos() и ltoc() для преобразования типа longint к строке string или pchar соответственно. Базируются на процедуре str() из модуля system.pas. Это здорово сокращает объём исполняемого файла по сравнению с тем, что включает в себя ссылку на sysutils.pas.

Префикс "e_" в названии модулей происходит от "engine" и предназначен для обозначения принадлежности к движку. Все модули базируются только на вызове API-функций Windows и методов интерфейсов DirectX. Это обеспечивает миниатюрность получаемого кода - динамическая библиотека (DLL), содержащая в себе весь код, после компиляции имеет размер около 50 кб (для IDE Delphi версии 5). Это значительное преимущество перед другими подобными программами, написанными на Delphi с использованием VCL (я видел exe-файлы размером 1,5 Мб).

Одно из правил классического программирования - это написание программ с наименьшим количеством глобальных переменных, т. е. их сокрытие, инкапсулирование. Я как мог, внимал этому правилу, но всё же иногда разным модулям нужно иметь доступ к одной и той же переменной или массиву. Например, интерфейс IDirectDraw7 требуется для многих функций, и он сделан видимым для всех модулей движка. В принципе, можно и сокрыть переменную внтри одного модуля, а доступ к ней обеспечить через функцию GetXXX(), но это нагружает код излишними конструкциями и в данном случае необязательно.

Рассмотрим общий механизм работы движка на основе модулей e_bmp.pas и e_sprite.pas. Так как классы не используются, обмен данными происходит через т. н. декрипторы, т. е. идентификаторы чего-либо (это напоминает механизм, на котором базируется API Windows).

Например, вот так выглядит прототип функции для загрузки BMP-файла:

function sdiLoadBmp( strFileName: string ): DWORD;

Как видно, функция возвращает как результат целое беззнаковое число. Его необходимо запомнить при вызове функции. По сути, эта функция аналогична (по принципу работы) функции GDI LoadImage(). Рузультатом работы обеих являтся идентификатор загруженного ресурса в списке уже существующих ресурсов, который можно использовать в дальнейшей работе. В нашем случае возвращается номер элемента вот этого динамического массива:

var g_pBmp: array of SDIBMP_STRUCT = nil; где type SDIBMP_STRUCT = record pPixels: IDirectDrawSurface7; dwWidth: DWORD; dwHeight: DWORD; end;

В случае ошибки функция возвращает 0, иначе любое положительное число в пределах типа longword. На самом деле возвращаемое значение всегда не 1 больше реального номера элемента в массиве, например, значение 1 будет соответствовать номеру 0 элемента массива, 2 - 1 и т.д. Это связано именно с тем, что 0 уже занят под код ошибки.

Все операции по работе с массивом g_pBmp берёт на себя функция

function FindBmp(): DWORD;var i: integer;begin if g_pBmp <> nil then for i := 0 to high( g_pBmp ) do if g_pBmp[ i ].pPixels = nil then begin result := i; exit; end; // первое обращение к массиву if g_pBmp = nil then begin setlength( g_pBmp, 30 ); result := 0; end else // свободный элемент в массиве не найден, в этом случае расширяем массив begin result := high( g_pBmp ) + 1; setlength( g_pBmp, length( g_pBmp ) + 30 ); end;end;

Результатом работы функции является реальный номер свободного элемента массива g_pBmp. Если массив существует в памяти, идёт поиск свободного элемента. Если массив не инициализирован, то функцией setlength() выделяется память для него и возвращается первый элемент (0). Иначе, если массив существует и свободные ячейки не найдены, необходимо расширить массив. К сожалению, при изменении длины уже существующего динамического массива сначала резервируется нужная для размещения нового массива память, затем элементы старого массива переносятся в новый, после чего освобождается память, выделенная прежнему массиву. Такие перезаёмы памяти при каждом новом изменении размера массива могут притормаживать работу программы. В данном случае это некритично, т. к. размеры одного элемента массива (и, следовательно, всего массива из этих элементов), невелики - указатель и четыре слова. Однако в более сложных случаях постоянный перезаём памяти может серьёзно "тормознуть" старт программы. Решением может служить изменение размера массива не на один элемент, а скачкообразно. Например, в моём случае - на 30 элементов сразу. Думаю, можно пожертвовать тем, что некоторая память будет постоянно заниматься напрасно, ради увеличения скорости работы.

Итак, мы получили идентификатор загруженного растра. Куда его девать? Функция

function sdiCreateSprite( bmp: DWORD; pr: Prect ): DWORD;

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

// узнаём характеристики битовой картыif not GetBmp( bmp, @bmps ) then exit;

Кстати, обратите внимание, что второй параметр передаётся через указатель. Это означает, что в структуру bmps будут записаны какие-то данные. Я советую поступать именно так и не использовать служебное слово var - в этом случае с первого взглада на программу непонятно, что происходит с таким параметром - в него что-то записывается, или наоборот, он предоставляет информацию кому-то?

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

id_bmp := sdiLoadBmp( 'picture.bmp' );id_sprite := sdiCreateSprite( id_bmp, nil );sdiDraw( id_sprite );

Правда, просто?

Затрону немного тему инициализации и удаления. Во многих программах существуют всякие функции вроде InitEngine(), DeleteEngine() и т.п. Оказалось, что вполне можно обойтись без отдельной функции инициализации, а разместить её внутри тех функций, которые могут быть вызваны первыми и требовать какие-то объекты для себя. Например, функция

function sdiEnumVideoModes( pvma: PSDIVIDEOMODEARRAY ): boolean;

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

if (not g_bInitDirectDraw) and (not InitDirectDraw()) then exit;

В данном случае g_bInitDirectDraw - глобальная переменная-флаг, сообщающая, инициализирован объект DirectDraw или нет.

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

Единственный вызов, который должен присутствовать - это sdiCloseEngine(). Этим вызовом мы удаляем все занятые движком ресурсы. Впрочем, кое-что можно удалить и явно, например

procedure sdiDestroyWindow();

Отдельный вопрос - это реализация механизма вывода сообщений об ошибках. Фантазии авторов программ здесь простираются от банального "Ошибка в программе" до "Тут длинная и интересная история об ошибке в файле таком-то, строка такая-то, код ошибки DDERR_ТАКАЯ_ТО_АББРЕВИАТУРА. Application will now exit.". Авторы программ, похоже, всерьёз задумываются над тем, как бы приукрасить окошко с ошибкой самой детальной информацией. Надо молиться, чтобы такое окошко никогда не всплыло вообще!

Я решил ограничиться простым текстовым сообщением об ошибке. Функция sle() предназначена для её установки единственный раз. Если же она будет вызвана повторно (например, на более высоком уровне), запись не произойдёт:

procedure sle( str: string );begin if bBuildLog then WriteErrorToLogFile( str ); if not bAlreadySetLastError then begin strError := str; bAlreadySetLastError := true; end;end;

Это гарантирует, что мы получим описание настоящей ошибки, а не ёё последствия. А вот лог-файл программы желательно должен содержать все сообщения об ошибках (для простоты "охоты" за ними). Также лог-файл обычно содержит описание всех произошедших действий, но я пока не реализовал это.

Для правильного контроля в идеале необходимо проверять КАЖДУЮ вызываемую функцию на возвращаемый результат, будь то метод DirectX или функция GDI. Это повышает гарантию того, что программа, например, не "вылетит" тихо в Windows или не допустит ошибок вроде AV. Я пытался следовать этому правилу как мог, но всё же не стоит усердствовать над IDirectDrawSurface7.Unlock() или DeleteDC(). Заметьте, что я совсем не использую популярные у некоторых программистов блоки try..except.

По-моему, легче проверить делитель на ноль, чем делить вслепую и потом смотреть, что получилось. Из личного опыта замечено, что с помощью try..except не всегда можно избежать краха программы, в частности иногда ошибка AV неминуема.

На первом этапе работы можно контролировать только наиболее критичные участки кода. Когда движок будет практически завершён, можно "навесить" таких обработчиков побольше. Тут, как говорится, можно дать волю рукам - жёсткий контроль на каждом этапе только уменьшает шансы на принудительную остановку программы операционной системой. Вообще, контроль ошибок может быть серьёзно расширен вплоть до определения характеристик оборудования - например, DirectDraw позволяет провести опрос характеристик видеокарты. Впрочем, выделка должна стоить овчинки.

Причина, по которой я так долго не выставлял материалы в Королевство - это попытка реализовать собственные эффекты. Например, изначально движок мог выводить полупрозрачные спрайты, уже описанные мною в предыдущий раз, а также масштабировать изображение и осуществлять поворот спрайта (путём "прямого" доступа к поверхноти DirectDraw). Однако скорость вывода оказалось настолько мала, что я в конце-концов вырезал всё это из кода. Получилась смешная ситуация - достаточно быстрый акселератор вроде GeForce 2MX 400 выдавал просто неприличный fps при повороте спрайта размером 256*256 пикселей. Могу посоветовать только одно - не пытайтесь сделать с помощью DirectDraw какие-либо эффекты. Аппаратно они попросту не поддерживаются ни одной видеокартой (например, поворот на произвольный угол), а если сделать всё вручную, то скорость вывода попросту очень низкая.

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

uses windows, messages, // файлы движка e_win, e_drawc, e_draw, e_drawu, e_bmp, e_sprite, e_movie, e_color, e_pscrn, e_fps, e_dxver, e_error, e_close, e_string;

Как видите, немаленький. Размещение всего кода в динамической библиотеке и подключение единственного заголовочного файла для работы с ней решает проблему, но это не очень красиво. Обычно поступают таким образом - весь код на последнем этапе разработки "спихивается" в один или несколько модулей, и список uses уменьшается. Например, описание API DirectX 6 от Хироюки Хори располагается в одном модуле DirectX.pas (в то время как SDK от Microsoft содержит в папке include десятки отдельных файлов). После такого "решения проблемы" программа становится трудно модифицируемой.

В языках C и C++ такая ситуация не возникает - для этого можно создать отдельный модуль, например, sdi.h, и подключить в нём все необходимые файлы:

#include "e_win.h"#include "e_drawc.h"#include "e_drawu.h"...#include "e_string.h"

Теперь программа станет "видеть" весь код в этих файлах после подключения единственного файла sdi.h. К сожалению, язык Object Pascal до сих пор не поддерживает такое "неявное" подключение модулей, поэтому разработка действительно больших Проектов на этом языке всегда будет сопровождаться огромным списком uses или, наоборот, огромными модулями. Если кто-то знает, как решить эту проблему, автор будет очень благодарен за совет. Возможно, единственным приемлемым решением являются всё же DLL.

– Конец работы –

Эта тема принадлежит разделу:

Состоит из следующих компонентов

Введение... Фанаты игр часто встречаются с аббревиатурой quot DirectX quot На упаковках... Компоненты DirectX обеспечивают не только прямой доступ к устройствам компьютера они избавляют программиста от...

Если Вам нужно дополнительный материал на эту тему, или Вы не нашли то, что искали, рекомендуем воспользоваться поиском по нашей базе работ: E_string.pas

Что будем делать с полученным материалом:

Если этот материал оказался полезным ля Вас, Вы можете сохранить его на свою страничку в социальных сетях:

Все темы данного раздела:

DirectShow
используется в мультимедиа-технологиях - можно выполнить высококачественое воспроизведение или захват видео и звука. Распространённость использования компоненты не слишком велика Все компо

Виктор Кода
Смотрите по теме : DirectX для начинающих.Часть вторая DirectX для начинающих. Часть третья. Считывание и запись Цикл лекция JINX'а Direc

Небольшое отступление
Прошёл месяц с тех пор как я написал первую часть ( http://www.delphikingdom.com/helloworld/directx.htm ) статьи по использованию DirectX в среде Delphi. У меня накопилось ещё несколько примеров, к

Почему я не рекомендую использовать DelphiX
Хочется поделиться с новичками своим мнением по поводу компонетов DelphiX и почему я не рекомендую их использовать. С одной стороны, DelphiX - это удобно – нет необходимости выполнять утом

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

DDLOCK_READONLY и DDLOCK_WRITEONLY
указываются, если из памяти будет производиться только чтение или наоборот, будет идти только запись. Обычно в использовании этих флагах нет необходимости, но их можно применять для контроля содерж

Несколько слов о недоработках.
Первое: пример MainExample в окне в видеорежимах HighColor или TrueColor на всех компьютерах с видеокартами GeForce2 MX 400, где я его тестировал, почему-то работает некорректно. Н

Хотите получать на электронную почту самые свежие новости?
Education Insider Sample
Подпишитесь на Нашу рассылку
Наша политика приватности обеспечивает 100% безопасность и анонимность Ваших E-Mail
Реклама
Соответствующий теме материал
  • Похожее
  • Популярное
  • Облако тегов
  • Здесь
  • Временно
  • Пусто
Теги