netlib.narod.ru | < Назад | Оглавление | Далее > |
Вы помните многослойные блоки, о которых рассказывалось в главе 5? Если нет, вам лучше сейчас вернуться назад и повторить изложенный там материал. Слои позволяют вам отображать несколько блоков один поверх другого. Например, вы можете вывести блок с изображением травы, а затем добавить поверх него блок с изображением деревьев. Вы можете даже добавить поверх блока с изображением деревьев блок с изображением огня, чтобы показать лесной пожар. Открывающиеся возможности безграничны. В связи с этим возникает вопрос: как реализовать редактирование нескольких слоев в редакторе карт? Подумайте об этом, поскольку я собираюсь показать вам подобную возможность! Взгляните на рис. 10.16, где изображен редактор карт с поддержкой слоев.
Рис. 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. Здесь нет ничего специального — лишь обычный для оконного графического интерфейса код.
Поскольку на панели инструментов появилось четыре новых окна, необходимо сделать соответствующие изменения в функции создания панели инструментов. Вот как выглядят необходимые изменения в коде:
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(). Она уничтожает все кнопки слоев, создает их заново, устанавливая каждую в состояние по умолчанию, и затем создает кнопку активного слоя с черной рамкой вокруг. Вот как выглядит код, выполняющий эти действия:
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(). К счастью, объем вносимых изменений незначителен и все они сконцентрированы в небольшом фрагменте кода. Вот как выглядит измененный код:
// Слои 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. Объединение слоев карты
Поскольку вам необходима возможность редактировать различные слои карты, требуется внести изменения в функцию vCheckMouse(). Вот как выглядит измененный код:
g_iTileMap[iTileX+g_iXPos+ ((iTileY + g_iYPos) * g_iMapWidth)][g_iCurLayer] = g_iCurTile;
В коде видно, что теперь номер блока помещается в многомерный массив g_iTileMap. Поскольку карта теперь состоит из нескольких слоев, для того, чтобы определить, в какой именно слой должен быть помещен блок, я использую переменную g_iCurLayer.
В программе редактора карт есть и еще несколько изменений, но здесь я показал самые важные. Если вы этого еще не сделали, запустите программу и поиграйте с редактированием слоев, чтобы лучше разобраться с ним.
netlib.narod.ru | < Назад | Оглавление | Далее > |