Программирование игр для Windows. Советы профессионала

       

Функциональное описание программы WarEdit


Программа WarEdit исключительно проста для понимания. В основном, бопьшинство инструментов не слишком сложно создать и их описание только отнимает время. Единственная убийственно сложная вещь — это графический интерфейс Поскольку мы используем DOS, а не Windows, то должны создать все сами. Например, если мы хотим увидеть в программе кнопки, придется написать функции для их изображения и обслуживания. То же самое относится и ко всем прочим элементам интерфейса. Тут уж ничего не поделаешь, с этим нужно считаться.

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

Попробуем подойти к процессу дизайна с другой стороны. Посмотрим, что необходимо для воплощения конкретно WarEdit'a. Затем мы охватим несколько наиболее интересных функций.

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

§

Для загрузки PCX-файлов;

§          Для рисования спрайтов и масштабирования объектов;

§          Библиотека функций мыши;

§          Диалоговые окна;

§          Процедура определяющая положение курсора мыши в момент щелчка;

§          Функция для детализации изображения;






§          Некоторые функции ввода/вывода для загрузки и-сохранения данных;

§          Интерфейс пользователя.

Мы уже написали первые три функции. Остальные я собираюсь создавать от нуля или делая добавления к уже имеющейся программе.

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

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

При выборе цвета, представляющего собой определенную текстуру или объект, вы видите изображение выбранного объекта в окне предварительного просмотра. Это выполняется посредством взятия битовой карты текстуры или объекта и изменения ее масштаба до необходимого размера. Теперь цветом можно рисовать изображения. Однако когда вы рисуете объекты игрового пространства, в базу данных передается не само значение цвета. С использованием справочной таблицы цвет переводится в значение, распознаваемое программным кодом игры, как обычные данные текстуры или объекта. Листинг 15.1 содержит определения типов этих данных.



Листинг 15.1. Секция определений WarEdit.

#define walls START        64

#define NUM_WALLS         24

#define DOORS START       128

#define NUM_DOORS          4

#define SCROLLS_START      144

#define NUM_SCROLLS        4

#define potions_START      160

#define NUM_POTIONS        4

#define foods_start        176

#define num_foods          2

#define MONSTERS_START     192

#define num_monsters       2

#define wall_STONE_1      (WALLS_START+0)   // Пока

только 6

#define WALL_STONE_2      (WALLS_START+1)

#define WALL_STONE_3      (WALLS_START+2)

#define WALL_STONE_4      (WALLS_START+3)

#define WALL_STONE_5      (WALLS_START+4)

#define WALL_STONE_6.      (WALLS_START+5)

#define NUM_STONE_WALLS   6

#define WALL_MELT_1     (WALLS_START+6) // Пока только 6

#define WALL_MELT_2     (WALL3_START+7)

#define WALL_MELT_3     (WALLS_START+8)

#define WALL_MELT_4     (WALLS_START+9)

#define WALL_MELT__5    (WALLS_START+10)

#define WALL_MELT_6     (WALLS__START+11)

#define NUM_MELT_WALLS 6

#define WALL_OOZ_1      (WALLS_START+12) // Пока только 6

#define WALL_OOZ_2      (WALLS_START+13)

#define WALL_OOZ_3      (WALLS_START+14)

#define WALL_OOZ_4      (WALLS_START+15)

#define WALL_OOZ_5      (WALLS_START+16)

#define WALL_OOZ_6      (WALLS_START+17)

#define NUM_OOZ_WALLS   6

#define WALL_ICE_1      (WALLS_START+18) // Пока только 6

#define WALL_ICE_2      (WALLS_START+19)

#define WALL_ICE_3      (WALLS_START+20)

#define WALL_ICE_4      (WALLS_START+21)

#define WALL_ICE_5      (WALLS_START+22)

#define WALL_ICE_6      (WALLS_START+23)

#define NUM_ICE_WALLS   б

#define DOORS_1         (DOORS_START+0) // Пока только 4

#define DOORS_2         (DOORS_START+1)

#define DOORS_3         (DOORS_START+2)

#define DOORS_4         (DOORS_START+3)

#define SCROLLS_1       (SCROLLS_START+0) // Пока только 4

#define SCROLLS_2       (SCROLLS_START+1)

#define SCROLLS_3       (SCROLLS_START+2)

#define SCROLLS_4       (SCROLLS_START+3)

#define РОТIONS_1       (POTIONS_START+0) // Пока только 4



#define POTIONS_2       (POTIONS_START+1)

#define POTIONS_3       (POTIONS_START+2)

#define POTIONS_4       (POTIONS_START+3)

#define FOODS_1         (FOODS_START+0) // Пока только 2

#define FOODS_2         (FOODS_START+1)

#define MONSTERS_1      (MONSTERS_START+0) // Пока только 2

#define MONSTERS_2      (MONSTERS_START+1)

#define GAME_START      255 // точка, из которой игрок

                        // начинает свой путь

Как можно видеть из Листинга 15.1, для каждой текстуры или объекта игрового пространства выделен некоторый диапазон значений. К примеру, все стены будут кодироваться числами от 64 до 127. В данный момент существует только 64 стены, но я оставил в программе место еще для 64 стен. Такой же подход применен и для определения всех остальных игровых объектов, так что структуру данных можно улучшить без особого труда. Когда вы рисуете карту игрового поля, то в действительности заполняете базу данных. Она представляет собой длинный массив, по способу доступа напоминающий двухмерную матрицу. Когда массив построен и уровень закончен, мы должны как-то сохранить данные. Для этого предусмотрены кнопки LOAD и SAVE. При нажатии одной из них, появляется диалоговое окно, запрашивающее подтверждение команды. Если вы выбрали YES, с помощью стандартной функции fopen() открывается файл, после чего данные сохраняются в файле или загружаются из файла. Но предварительно нужно ввести имя файла, используя строчный редактор. Этот редактор мне пришлось сделать самостоятельно. Он очень прост и понимает только алфавитно-цифровые клавиши.

Примечание

Мне пришлось написать такой редактор самостоятельно, поскольку единственный способ ввода строки, предлагаемый языком Си - это функция scanf (). Однако использование этой функции чревато неприятностями: при наборе возможны ошибки, текст может выйти за границы диалогового окна и т. д. 

Давайте поговорим о диалоговом окне, о котором я только что упоминал. Оно представляет собой PCX-файл с двумя нарисованными кнопками.


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

§          Размер каждой кнопки;

§          Количество кнопок в столбце и строке;

§          Местоположение первой кнопки;

§          Расстояние между кнопками.

Эта функция будет определять, какая из кнопок нажата. (Вспомните, не существует легкого пути для получения подобных вещей. Мы все должны сделать собственноручно. У нас нет приличного менеджера окон и механизма посылающего нам сообщения при нажатии кнопок. Хотя вы можете попробовать написать его сами, поскольку в мои планы это не входит.) В Листинге 15.2 показана функция, которая обнаруживает нажатие кнопок.

Листинг 15.2. Обнаружение нажатия кнопки.

int Icon_Hit(int хо, int yo, int dx, int dy,

int width, int height,

int num_columns, int num_rows,

int mx, int my)

{

// получая геометрические установки кнопок, эта функция вычисляет,

// по которой из них щелкнули мышью

int row, column, xs,ys,xe,ye;

for (row=0; row<num_rows; row++)

{

// вычислить начало и конец Y-координаты колонки

ys = row*dy + yo;

ye = ys+height;

for(column=0; column<num_columns; column++)

{   

xs = column*dx + xo;

xe = xs+width;

//Проверить, находится ли курсор мыши в области кнопки

if (mx>xs && mx<xe && my>ys && my<ye)

{

return(column + row*num_columns);

} // конец if

} // конец внутреннего цикла

} // конец внешнего цикла                                   

return(-1); // не нажата

} // конец функции

Это все относительно редактора WarEdit. Я предлагаю вам поиграть с программой, чтобы получить представление о ней, прежде чем мы начнем разговор об улучшениях, которые могут быть внесены в редактор.

Наконец, для создания работающей версии WarEdit желательно использовать два объектных файла, называемых GRAPH0.OBJ и MOUSELIB.OBJ. Я предлагаю объединить их в библиотеку и затем связать с WEDIT.C. (Хотя, конечно, вы можете поступать, как вам больше нравится). Как вы понимаете, файл GRAPH0.OBJ получается после трансляции исходников GRAPH0.C и GRAPH0.H, a MOUSELIB.OBJ - из MOUSELIB.C, о котором уже упоминалось ранее.


Содержание раздела