Изменения в заголовочном файле
Программа 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, он просто не отображается.
Изменения в функции vCheckMouse()
Поскольку вам необходима возможность редактировать различные слои карты, требуется внести изменения в функцию vCheckMouse(). Вот как выглядит измененный код:
g_iTileMap[iTileX+g_iXPos+
((iTileY + g_iYPos)
* g_iMapWidth)][g_iCurLayer]
= g_iCurTile;
В коде видно, что теперь номер блока помещается в многомерный массив g_iTileMap. Поскольку карта теперь состоит из нескольких слоев, для того, чтобы определить, в какой именно слой должен быть помещен блок, я использую переменную g_iCurLayer.
В программе редактора карт есть и еще несколько изменений, но здесь я показал самые важные. Если вы этого еще не сделали, запустите программу и поиграйте с редактированием слоев, чтобы лучше разобраться с ним.