netlib.narod.ru< Назад | Оглавление | Далее >

Многослойные карты

Вы помните многослойные блоки, о которых рассказывалось в главе 5? Если нет, вам лучше сейчас вернуться назад и повторить изложенный там материал. Слои позволяют вам отображать несколько блоков один поверх другого. Например, вы можете вывести блок с изображением травы, а затем добавить поверх него блок с изображением деревьев. Вы можете даже добавить поверх блока с изображением деревьев блок с изображением огня, чтобы показать лесной пожар. Открывающиеся возможности безграничны. В связи с этим возникает вопрос: как реализовать редактирование нескольких слоев в редакторе карт? Подумайте об этом, поскольку я собираюсь показать вам подобную возможность! Взгляните на рис. 10.16, где изображен редактор карт с поддержкой слоев.


Рис. 10.16. Окно программы D3D_MapEditorLayers

Рис. 10.16. Окно программы D3D_MapEditorLayers


На рис. 10.16 показано окно программы D3D_MapEditorLayers. На панели инструментов появились четыре новые кнопки, отмеченные цифрами от 1 до 4. Они позволяют установить активный слой, который будет редактироваться. Например, чтобы редактировать базовый слой, щелкните по кнопке 1. После того, как вы выбрали первый слой, все щелчки по области редактирования будут менять текстуры, выводящиеся в первом слое блоков. Кнопки, отвечающие за другие слои работают аналогично. В изображенном на иллюстрации окне редактирования вы видите маленький песчанный остров с травой в центре. Изображение песка находится в слое 1, а изображение травы — в слое 2. Благодаря этому достигается плавный переход от травы к песку без необходимости вводить специальные переходные блоки с изображением травы и песка. Хватит обсуждать иллюстрацию. Загрузите программу D3D_MapEditorLayers и следуйте вперед.

Изменения в заголовочном файле

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

int   g_iTileSize  = 32;
int   g_iTilesWide = 20;
int   g_iTilesHigh = 15;
int   g_iMapWidth  = 100;
int   g_iMapHeight = 100;
int   g_iXPos = 0;
int   g_iYPos = 0;
int   g_iTileMap[10000][4];
int   g_iCurTile    = 0;
int   g_iCurTileSet = 0;
int   g_iMaxTileSet = 3;
int   g_iTotalTiles = 18;
int   g_iCurLayer   = 0;

Многомерный массив

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

Следующее изменение — добавление переменной g_iCurLayer. Она отслеживает с каким именно слоем карты ведется работа. Это очень важно знать, когда вы размещаете новый блок в окне редактирования. Программа должна знать куда его поместить!

Функция смены слоя

Для поддержки переключения слоев я также добавил в редактор новую функцию. Вот ее прототип:

void vChangeLayer(int iLayer);

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

Переменные для новых кнопок

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

const int    ID_BUTTON_LAYER1 = 40006;
const int    ID_BUTTON_LAYER2 = 40007;
const int    ID_BUTTON_LAYER3 = 40008;
const int    ID_BUTTON_LAYER4 = 40009;
HWND         hBUTTON_LAYER1   = NULL;
HWND         hBUTTON_LAYER2   = NULL;
HWND         hBUTTON_LAYER3   = NULL;
HWND         hBUTTON_LAYER4   = NULL;

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

Изменения в функции vCreateToolbar()

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

hBUTTON_LAYER1 = CreateWindow(
   "BUTTON", "1",
   WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
   3, 275, 20, 20, hWndToolBar, (HMENU)ID_BUTTON_LAYER1,
   hinst, NULL);
hBUTTON_LAYER2 = CreateWindow(
   "BUTTON", "2",
   WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
   25, 275, 20, 20, hWndToolBar, (HMENU)ID_BUTTON_LAYER2,
   hinst, NULL);
hBUTTON_LAYER3 = CreateWindow(
   "BUTTON", "3",
   WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
   48, 275, 20, 20, hWndToolBar, (HMENU)ID_BUTTON_LAYER3,
   hinst, NULL);
hBUTTON_LAYER4 = CreateWindow(
   "BUTTON", "4",
   WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
   71, 275, 20, 20, hWndToolBar, (HMENU)ID_BUTTON_LAYER4,
   hinst, NULL);

В приведенном выше коде ясно видны четыре блока. Каждый из них отображает одну кнопку переключения слоя блоков. Одна из этих кнопок отличается от других. Внимательно посмотрите на код, и вы увидите, что тип первой кнопки — BS_DEFPUSHBUTTON. Это значение сообщает графическому интерфейсу о необходимости нарисовать вокруг кнопки черный прямоугольник. Я использую этот прямоугольник, чтобы показать, какой слой является активным. Поскольку по умолчанию программа работает с нулевым слоем, я делаю активной первую кнопку переключения слоев.

Функция vChangeLayer()

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

void vChangeLayer(int iLayer)
{
   // Уничтожение кнопок слоев
   DestroyWindow(hBUTTON_LAYER1);
   DestroyWindow(hBUTTON_LAYER2);
   DestroyWindow(hBUTTON_LAYER3);
   DestroyWindow(hBUTTON_LAYER4);

   // Установка кнопок в состояние по умолчанию
   hBUTTON_LAYER1 = CreateWindow(
      "BUTTON", "1",
      WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
      3, 275, 20, 20, hWndToolBar, (HMENU)ID_BUTTON_LAYER1,
      g_hInstance, NULL);
   hBUTTON_LAYER2 = CreateWindow(
      "BUTTON", "2",
      WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
      25, 275, 20, 20, hWndToolBar, (HMENU)ID_BUTTON_LAYER2,
      g_hInstance, NULL);
   hBUTTON_LAYER3 = CreateWindow(
      "BUTTON", "3",
      WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
      48, 275, 20, 20, hWndToolBar, (HMENU)ID_BUTTON_LAYER3,
      g_hInstance, NULL);
   hBUTTON_LAYER4 = CreateWindow(
      "BUTTON", "4",
      WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
      71, 275, 20, 20, hWndToolBar, (HMENU)ID_BUTTON_LAYER4,
      g_hInstance, NULL);

   // Активация требуемой кнопки
   if(iLayer == 1) {
      DestroyWindow(hBUTTON_LAYER1);
      hBUTTON_LAYER1 = CreateWindow(
         "BUTTON", "1",
         WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
         3, 275, 20, 20, hWndToolBar, (HMENU)ID_BUTTON_LAYER1,
         g_hInstance, NULL);
   }
   else if(iLayer == 2) {
      DestroyWindow(hBUTTON_LAYER2);
      hBUTTON_LAYER2 = CreateWindow(
         "BUTTON", "2",
         WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
         25, 275, 20, 20, hWndToolBar,
         (HMENU)ID_BUTTON_LAYER2,
         g_hInstance, NULL);
   }
   else if(iLayer == 3) {
      DestroyWindow(hBUTTON_LAYER3);
      hBUTTON_LAYER3 = CreateWindow(
         "BUTTON", "3",
         WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
         48, 275, 20, 20, hWndToolBar,
         (HMENU)ID_BUTTON_LAYER3,
         g_hInstance, NULL);
   }
   else if(iLayer == 4) {
      DestroyWindow(hBUTTON_LAYER4);
      hBUTTON_LAYER4 = CreateWindow(
         "BUTTON", "4",
         WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
         71, 275, 20, 20, hWndToolBar,
         (HMENU)ID_BUTTON_LAYER4,
         g_hInstance, NULL);
   }

   // Установка текущего слоя
   g_iCurLayer = (iLayer - 1);

   PlaySound("button.wav", NULL, SND_FILENAME|SND_ASYNC);
}

Возьмем к примеру кнопку слоя с номером 2. Когда вы щелкаете по ней, выполняется вызов функции и значение ее параметра iLayer равно 2. Функция уничтожает кнопки слоев, а затем создает снова без черной рамки вокруг. Затем функция проверяет, на какой слой указывает параметр iLayer. Она доходит до второй проверки и вновь уничтожает кнопку второго слоя. Затем кнопка создается вновь, но уже с черной рамкой вокруг, показывающей, что данный слой активен. В самом конце кода функции переменной g_iCurLayer также присваивается значение, соответствующее активному слою.

Изменение процедур сохранения и загрузки

Функции vSaveMap() и vLoadMap() модифицированы для включения в каждую карту информации о дополнительных слоях. Поскольку в примере поддерживается четыре слоя, будет сохраняться и записываться в четыре раза больше данных. Необходимые для этого изменения кода минимальны. Приведенная ниже строка показывает изменения, необходимые для функции vLoadMap():

fread(g_iTileMap, 40000, sizeof(int), fp);

Обратите внимание, что функция fread() считывает 40 000 целых чисел, а не 10 000, как раньше. Аналогичные изменения вносятся и в функцию vSaveMap():

fwrite(g_iTileMap, 40000, sizeof(int), fp);

В функции сохранения карты количество записываемых чисел также изменено с 10 000 на 40 000. Это единственное изменение, которое необходимо сделать в функции записи.

ВНИМАНИЕ!
Не пытайтесь загружать карты, созданные одной версией редактора в другую версию. Это может привести к краху программы.

Изменения в функции vRender()

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

// Слои
for(iLayer = 0; iLayer < 4; iLayer++) {
   // Вычисляем смещение в буфере
   iBufferPos = iX+g_iXPos+((iY+g_iYPos)*g_iMapWidth);
   // Получаем требуемый блок
   iCurTile = g_iTileMap[iBufferPos][iLayer];
   // Отображаем блок
   if(iCurTile != 0 || iLayer == 0) {
      vDrawInterfaceObject((iX * g_iTileSize),
                   (iY * g_iTileSize),
                   (float)g_iTileSize,
                   (float)g_iTileSize,
                   iCurTile);
   }
}

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

На рис. 10.17 показано совмещение слоев в действии. Там изображены четыре слоя с блоками. Первый слой заполнен блоками с номером 1. Большая часть второго слоя заполнена блоками с номером 0, но кроме этого там есть несколько блоков с номером 2. Большая часть третьего слоя также заполнена блоками с номером 0, но на нем есть и несколько блоков с номером 3. Аналогичным образом устроен и четвертый слой. Когда слои совмещаются вместе, блок с номером 0 работает как цветовой ключ для размещения второго, третьего и четвертого слоев поверх первого. Результат виден в нижней части иллюстрации. Рассмотрев изображенные в левой части рисунка отдельные блоки вы поймете, как он получен.


Рис. 10.17. Объединение слоев карты

Рис. 10.17. Объединение слоев карты


Изменения в функции vCheckMouse()

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

g_iTileMap[iTileX+g_iXPos+
   ((iTileY + g_iYPos)
   * g_iMapWidth)][g_iCurLayer]
   = g_iCurTile;

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

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


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz