Темное искусство игродела: Разрабатываем высокопроизводительные графические движки с помощью библиотеки Dark GDKЮрий «yurembo» Язев (yazevsoft@gmail.com) Считается, что разработка компьютерных игр требует долгого изучения разных API, кучи знаний и привлечения труда множества программистов. Отчасти так и есть, но появившаяся недавно библиотека Dark GDK (Game Development Kit) позволит тебе сократить время и объем изучаемого материала – и сразу же перейти к созданию мощных 3D-движков. Речь пойдет о детище компании с говорящим названием «The Game Creators». Славится эта шарага, в первую очередь, своей модификацией бэйсика в лице Dark BASIC (ребята заточили язык специально под разработку игр). Широким массам компания также известна продуктами для разработки игр без программирования, как такового (например, FPS Creator, The 3D Gamemaker). Dark GDK – это библиотека с функциями, работающими поверх DirectX. Самостоятельной ценности она не представляет, нуждаясь в подключении к компилятору Visual C++ 2008 (работает только с 9-ой версией). Тут ты вполне можешь сказать: «Нафига мне нужна эта Dark GDK, когда есть несложный в освоении Dark Basic?». Не соглашусь. На вкус, цвет и любимый язык программирования товарищей нет – лично меня от Dark BASIC отталкивал именно тот факт, что он основан на Бейсике. Вдобавок, взгляни на таблицу релевантности частоты кадров, которая взята с сайта авторов. В этой таблице показана частота кадров, соответствующая тестам, проведенным для Dark GDK и Dark BASIC. GDK лидирует во всех! Из плюсов стоит отметить и время компиляции (благодаря Visual C++). А использование Си++ вместо бейсика позволило в полной мере применить концепцию ООП, структуры, а также механизмы управления памятью (ссылки, указатели, etc), присущие этому языку. ИнсталляцияНадеюсь, ты уже установил себе Visual C++ 2008? Сразу рассмотрим процесс инсталляции библиотеки. В нем есть неожиданные моменты. Во-первых, в твоей системе обязательно должен быть Microsoft DirectX 9.0 SDK. После распаковки и запуска инсталляции на первый увиденный вопрос ответь отрицательно, – иначе тебя отправят на сайт только что упомянутой организации. Следующий вопрос испрашивает нашего разрешения хотя бы один раз запустить систему Visual C++. Советую проделать это действие: ничего плохо не произойдет (хорошего тоже). Затем – смело отвечай утвердительно. Начнется инсталляция, которую ты успешно пройдешь и без меня. После ее завершения можешь запустить студию и убедиться, что в визардах отсутствуют мастера создания проектов с использованием библиотеки. Добавь их, проделав следующие действия. Открой папку, куда установлена студия, в ней – подпапку VC, затем – Express и, наконец, – VCProject. У меня получился такой путь: D:\Program Files\Microsoft Visual Studio 9.0\VC\Express\VCProjects. Находясь в этой папке, создай подпапку, название которой будет группой проектов в студии. После чего, уже в этой папке, создай еще одну, которая будет подгруппой для визардов. Я назвал их соответственно: Wizards и DarkGDK. Теперь эти самые визарды надо сюда скопировать. Они находятся в папке, куда установлена Dark GDK – в подпапках Wizards, Files (в моем случае путь такой: D:\Program Files\The Game Creators\Dark GDK\Wizards\Files). Просто скопируй все имеющиеся там файлы в ранее созданную директорию. Можешь проверить: запусти компилятор и создай проект нового типа. Ага, ошибка! Закрывай студию. Сейчас ты увидишь, как легко можно решить проблему (если у тебя не было ошибки, значит, у тебя – английская версия операционки; респект тебе как реальному англоязычному пацану, можешь смело пропустить этот абзац). Чтобы решить проблему, создай в папке пользователя (например, C:\Documents and Settings\имя юзера\) следующие каталоги и подкаталоги: My Documents\Visual Studio 2008\Dark GDK. Затем скопируй сюда содержимое подпапки Wizards\Projects той папки, куда установлена библиотека GDK. Все, теперь проекты создаются, убедись лично. Первое испытаниеПредлагаю сначала испытать полученный «аппарат». Затем вкратце рассмотрим не только функциональность созданной «аппаратом» программы, но и некоторые другие остро необходимые при разработке мало-мальски крутой игры функции и методы. Запусти Visual C++, если ранее этого не сделал. Создай новый проект. В окне выбора шаблона для проекта разверни пункт Wizards, в котором выбери подпункт DarkGDK – иерархия такая, какой мы ее создали. Ты увидишь три возможных заготовки: Dark GDK – Game, Dark GDK – 2D Game и Dark GDK – 3D Game. Каждая генерирует проект соответствующего типа. Так как мы хотим создать трехмерную игру, то в качестве шаблона для своей новой программы выбери Dark GDK – 3D Game. После того, как проект будет создан, открой его исходный код, дважды щелкнув на файле Main.cpp в обозревателе решения. Сейчас мы приступим к исследованию кода, сгенерированного мастером. Рассмотрим код, или Разбор полетовВ первой строчке подключается заголовочный файл, который используется во всех Dark GDK приложениях. В отдельных случаях вместе с ним подключаются другие заголовочные файлы, но этот – всегда. Можно сказать, он подобен windows .h в приложениях на Си++. После подключения заголовочного файла, как в обычных Си-программах, идет главная функция – точка входа в программу. В Dark-приложениях нет функции Main или WinMain, зато есть такая – DarkGDK. С вызовом следующей функции мы как бы сообщаем системе, что берем под свой контроль перерисовку экрана (функция dbSyncOn). Далее вызывается функция dbSyncRate с параметром 60. То есть, мы хотим, чтобы частота перерисовки экрана по возможности была равна этому значению. В параметре передается максимальная частота, но она не обязательно будет такой. Все зависит от мощности компьютера, на котором запущена программа. Затем идет функция dbRandomize – инициализатор механизма случайных значений. Она нужна, чтобы при каждом последующем запуске программы снова генерировались значения. Параметром для нее служит функция dbTimer, которая возвращает системное время, измеряемое в миллисекундах, начиная со старта операционной системы. В следующем цикле мы создаем и изменяем свойства нескольких сфер (в данном случае – 49). По именам вызываемых функций видно, какие свойства они изменяют (ниже мы рассмотрим их более подробно). После цикла вызывается функция dbPositionCamera. Она проводит инициализацию камеры, а именно – устанавливает ее позицию. В качестве параметров передаем ей координаты по всем трем осям. Затем начинается основной цикл программы. Условие цикла – функция LoopGDK. Она возвращает единицу, пока программа может функционировать, и ноль – когда произошел какой-то сбой или пользователь просто закрыл приложение. В теле цикла вызываются несколько функций. Нам надо их рассмотреть. Первая вызываемая здесь функция dbText выводит текст (переданный в третьем параметре) в позиции, заданной первыми двумя параметрами (соответственно, координата x – первый параметр, и y – второй). Далее идут два условия проверки нажатия клавиш «стрелок»: ВВЕРХ и ВНИЗ (функции dbUpKey и dbDownKey). В результате выполняется функция приближения и отдаления камеры – dbMoveCamera. В качестве параметра ей передается шаг перемещения – соответственно, положительное и отрицательное числа. Потом начинается цикл, который поворачивает все ранее созданные сферы с помощью функции dbRotateObject. Ей передаются: номер объекта и три параметра: углы поворота по соответствующим осям. И последняя вызываемая в цикле while функция (dbSync) перерисовывает окно приложения со всеми имеющимися объектами. После завершения цикла надо подчистить за собой ресурсы, удалив все объекты. Что мы и делаем в цикле for посредством специальной функции dbDeleteObject, которой в качестве параметра передается номер объекта. И последней строчкой – возвращаем управление операционной системе. Ну, теперь откомпиль и запусти программу. Работает? Конечно, работает! Впечатлен? Взгляни еще раз на код и представь, сколько надо бы было писать под «голый» DirectX, чтобы получить тот же результат (смотри скриншот «Запущенное демо-приложение»). Да, здорово, но это далеко не предел для Dark GDK! Она также позволяет легко загружать анимационные объекты вместе с анимацией, и, естественно, циклически проигрывать ее. Но обо всем по порядку. Думаю, при рассмотрении первой программы у тебя появились вопросы. Попробую ответить на них. Ответы на твои вопросыВ процессе создания сферы в функцию dbMakeObjectSphere (внимание на первый цикл!) передается два параметра. Первый – циклически увеличивающаяся переменная i и второй – константа 1. Со вторым параметром все ясно (задает размер создаваемой сферы). А на первом остановимся подробнее. Он задает имя для объекта – к данному объекту можно будет обращаться по этому имени на протяжении всей последующей программы. Получается такое вот числовое имя, и это удобно – например, в каждой итерации цикла можно присваивать имена объектам по порядку, используя для этого переменную цикла (как в рассматриваемой программе). Имена необязательно должны идти по порядку, но, следуя определенному ряду, ты избавишь себя от того, использовал несколько раз одно и то же имя (число). Если такой эпизод будет иметь место в программе, ты не досчитаешься объектов – из-за того, что произойдет перезапись старого объекта новым. Ситуацию может усложнить тот факт, что для разных типов объектов используются одинаковые числа. Так, ты можешь создать сферу с именем «1» и текстуру с таким же именем. Но заморачиваться с цифрами не всегда удобно и приятно. Поэтому можно просто объявить константу, которая будет именем для определенного объекта, и присвоить ей нужную цифру. Допустим, const int man = 1. Таким образом, объявлена константа man для объекта, соответствующего этому названию. И ты уже никогда не забудешь, к чему относится цифра «1». Затем, как я уже говорил, идут функции, настраивающие свойства сферы. Первая из них, dbPositionObject, естественно, задает позицию. Ей передаются такие параметры: имя объекта (цифра) и три рандомных числа, играющих роль координат по трем осям. Каждое из этих чисел возвращается с помощью функции dbRnd (аналог по функциональности объекта класса Random из C++). Функция принимает параметром значение – предел отрезка чисел от нуля до данного значения. Из этого отрезка, собственно, и выбирается случайное число. Следующая затем функция dbScaleObject изменяет размер сферы. Передаваемые ей параметры аналогичны параметрам для предыдущей функции (только здесь 2-ой, 3-ий и 4-ый параметры задают размер по каждой из осей). Функция dbColorObject устанавливает цвет. Параметры со 2-го по 4-ый влияют на количество составляющей цвета: красный, синий и зеленый, соответственно (режим RGB). Дальше нас ждут две крайне вкусные функции! Первая из них – для управления материалами, а вторая для управления освещением. Вызовом этих функций все дело и обходится – а вспомни-ка чистый Direct3D, с которым, чтобы включить материалы и свет, приходилось не по-детски извращаться. Итак, первая из функций – dbSetObjectSpecularPower – устанавливает отражающие свойства материалов. Ее параметры: это имя объекта, материал которого мы хотим установить, и еще значение – мощность отражения. Вторая функция – dbSetObjectAmbient – управляет окружающим цветом. Здесь он просто отключается (второй параметр – 0). Теперь код заготовки полностью рассмотрен, и, взяв ее за основу, ты можешь приступить к программированию. DarkRobotНаш разговор будет неполным без рассмотрения примера работающего игрового движка. Предлагаю тебе загрузить с нашего диска VC++ проект DarkRobot (или хотя бы запустить экзэшник) и посмотреть на реализованную на нем заготовку игры, которую я для тебя разработал. Обязательно посмотри и возвращайся к чтению. Так как журнал не резиновый, я не буду приводить здесь весь исходник, рассмотрев только самое важное. Сначала – поле установки частоты перерисовки. Идет инициализация графического режима функциями dbSetDisplayMod и dbMaximiseWindow. Первая из них устанавливает параметры экрана (ей передается ширина, высота и глубина). Вторая функция при запуске приложения разворачивает окно на весь экран (независимо от выставленных параметров). Затем идут функции, выводящие текст и перерисовывающие экран. Следующая функция (dbSetCameraRange) устанавливает свойства камеры: ближнюю и дальнюю плоскости отсечения, определяющие отображаемое содержимое сцены. Все, что выходит за пределы плоскостей, на экран выводиться не будет! После установки камеры загружаются текстуры. Они будут наложены на ландшафт, предварительно пройдя процедуру смешения. Попросту говоря, будут наложены друг на друга. Функцию загрузки текстуры мы рассмотрели выше. Затем идет блок кода, который инициализирует и строит ландшафт. Как я упоминал, в Dark GDK есть функция генерирования ландшафта dbSetTerrainHeightMap. Ей передаются: имя объекта – ландшафта – и имя файла. Здесь-то мы ей и пользуемся, загружая карту высот из файла bmp. Затем масштабируем, освещаем и текстурируем (загруженными ранее текстурами). За подробностями обращайся к исходнику. Самое пристальное внимание обрати на загрузку *.x-файлов. Этот тип является родным для DirectX. Функцией dbLoadObject («skybox.x», skybox), которой передаются имя х-файла и имя объекта (номер), загружается небесная оболочка (skybox – gamedev-термин). Она представляет собой окружающий куб с наложенными на внутренние стороны текстурами (это задний план). Последний загружаемый из файла объект – главный персонаж в виде робота. Он также загружается из x-файла, в котором, помимо информации об объекте и текстурах, хранится анимация. Следовательно, он уже анимирован, и нам надо только загрузить его и проиграть анимацию. О такой возможности я уже упоминал. После того, как объект будет загружен, вызывается функция dbSetObjectSpeed(robot, 120) с такими параметрами: имя объекта, куда загружены данные из файла, и скорость проигрывания анимации. Затем следуют две функции, первая из которых поворачивает, а вторая – перемещает объект. Последний этап инициализации – установка позиции и угла поворота камеры. Следующая функция – наиболее аппетитная. С ее помощью можно «привязать» камеру к определенному тобой месту. Вот мы ее и привязываем к роботу, передавая в функцию такие параметры: текущее положение робота по оси X, текущую высоту поверхности плюс 1 (чтобы поднять камеру от земли), текущее положении робота по оси Z, и далее – дистанцию, высоту, сглаживание траектории движения и обрабатывать или нет столкновения камеры. Подошла очередь функции dbUpdateTerrain, не имеющей параметров и просто обновляющей ландшафт. Замыкающая тело цикла функция dbSync обновляет содержимое и перерисовывает экран (эту функцию мы рассматривали). После цикла возвращаем управление операционной системе. Разобравшись с кодом, обязательно запусти и посмотри игру (смотри скриншот «игра DarkROBOT»). Удачного старта!Конечно, автору не удалось рассмотреть (или хотя бы описать) всех возможностей библиотеки Dark GDK. Однако такой грандиозной цели и не ставилось. Как и везде в программировании, решений одной задачи существует очень и очень много. Я показал лишь одно из них. С помощью библиотеки Dark GDK можно взять удачный старт на дорожке игростроения. Если ты раньше не разрабатывал игры, возможно, сейчас самое время начать. Наш код #include "DarkGDK.h" // заголовочный файл if ( dbDownKey ( ) )// нажата ли клавиша ВНИЗ for ( int i = 1; i < 50; i++ ) dbSync ( );// перерисовываем экран DVDНа диске лежит полный исходный код игры DarkRobot. Для компиляции нужны: Visual C++ 2008 Express Edition, DirectX 9.0 SDK, Dark GDK. WWWЕсли в лом вставлять диск, можешь скачать исходник с www.xakep.ru. INFOЕсли тема тебя заинтересовала, – сообщи об этом автору. Продолжим развитие хакерского игропрома! |