Форумы Blizzard.GG

Вернуться   Форумы Blizzard.GG > Warcraft III > Творцы миров > Библиотека "Творцов" > Jass - язык программирования Warcraft 3

Закрытая тема
 
Опции темы
Старый 16.07.2007, 17:01   #1
Boom, headshot!
 
Аватар для ANDRЕW II
 
Регистрация: 22.04.2004
Сообщений: 1,918
ANDRЕW II - Известный
Exclamation Все что вы хотели знать про мультиборд, но боялись спросить

Автор: Alexey_BH (aka Cacodemon) (c) 2004 - alexey_bh[webdog]mail.ru

В этой статье размещена вся необходимая информация по устройству, принципам и алгоритмам работы с мультибордами.

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

1. Что такое мультиборд и как он работает?


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


На этой картинке показаны несколько примеров мультибордов. Левый мультиборд - пример из проекта Graveyard. При помощи мультибордов можно делать даже графики (пример слева), миникарты лабиринтов и даже "тетрис"!


Принцип организации мультиборда.


Мультиборды доступны только в Warcraft - The Frozen Throne начиная с самой ранней версии (1.07). Однако в редакторе триггеров эти объекты доступны только в версиях редактора начиная с 1.13. Для более ранних версий необходимо установить патч - UMSWE или программировать эти объекты на языке JASS. Кроме мультибордов, он вносит в редактор множество новых полезных возможностей. Далее все названия триггерных функций для работы с мультибордом будут взяты именно из UMSWE, поэтому возможно незначительное отличие от названия этих функций в WorldEditor'е (далее WE) 1.13 и выше.

Итак, как устроен мультиборд? Этот объект представляет из себя матрицу, каждый элемент которой состоит из двух частей - картинки (Multiboard Icon) и текста (Multiboard Item Value). Пользователь может менять картинки отдельных элементами, расстояние по горизонтали между ними, текст, цвет текста и так далее. Однако в мультиборде (в отличие от Leaderboard'а) нет автоматической сортировки строк - функции сортировки (если они нужны), вам прийдется писать самим. Вообще для написания любого сложного мультиборда необходимы скрипты на языке JASS, но мы пока будем рассматривать довольно простые варианты, в которых можно обойтись только стандартным редактором триггеров (GUI).


На этой картинке изображена структура мультиборда. Рассмотрим подробнее из чего она состоит:
  1. Заголовок (Title) - У каждого мультиборда обязательно есть заголовок, помещенный в отдельную область. Пользователь может его менять, однако необходимо помнить о том, что если длина заголовка будет больше длины основной области, текст "вылезет" за рамку и это будет не очень красиво выглядеть.
  2. Кнопка "Свернуть" (Minimize Multiboard) - Аналог кнопки "свернуть" в Windows. При ее нажатии матрица исчезает и видны только заголовок мультиборда и кнопка "развернуть", при нажатии которой матрица снова появляется.
  3. Границы (Borders) - Все три области выделены границей. Вы можете выбирать картинку, которая будет границей мультиборда. Делается это в меню Advanced (Расширенные настройки) -> Game Interface (Интерфейс Игры) -> Image - Multiboard Border Картинка - Граница Мультиборда. В этом же разделе WE вы найдете другие важные данные - например, пути к изображениям кнопки "свернуть" / "развернуть" и так далее.
  4. Элементы матрицы(Items) - Иногда я буду называть их "ячейками". Матрица мультиборда характеризуется двумя параметрами - числом элементов по горизонтали (число столбцов - Nx) и по вертикали (число строк - Ny). Номера строк / столбцов начинаются с нуля, отсчет идет из левого верхнего угла (смотрите зеленые стрелки на рисунке). В дальнейшем адрес элемента будем обозначать как (Y,X), то есть для элемента, расположенного во второй строке и третьем столбце, адрес будет равен (1,2).
У каждого элемента есть четыре параметра, которые может задавать пользователь:
  1. Картинка (Item Icon) - Картинки всегда имеют размер 16x16 пикселей, другие размеры будут в игре подогнаны под этот - поэтому всегда рисуйте картинки для мультиборда размером только 16x16. Очень часто разрешения 16x16 не хватает для точного вывода информации, особенно в полосках - счетчиках жизни (см. пример мультиборда в самом начале статьи). Пока единственный способ увелисить разрешение - сделать несколько вариаций одного элемента и в случае необходимости использовать нужный. Например, для простой полоски, состоящей из одного однородного цвета возможно сделать 3 варианта - 0%, 50%, 100% залито цветом. В этом случае изменение полоски станет более плавным, хотя разрешения в 8 пикселей недостаточно для точного и плавного отображения информации (например, мультиборд в проекте Graveyard использует для своей работы 126 разных картинок).
  2. Значение (Item Value) - Текстовая строка. Текст, его прозрачность и цвет пользователь может задавать сам. Изменение цвета происходит как с помощью стандартных цветовых тэгов Варкрафта (|c и |r), так и c использованием специальной функции Multiboard - Change The Color Of Item (Мультиборд - Сменить цвет элемента). Однако тэги форматирования (например, перенос строки - \n) будут проигнорированы и выведены как обычный текст.
  3. Стиль (Item Style) - Определяет какие из предыдущих двух параметров будут видны. Можно показывать / прятать как изображение, так и текстовую составляющую каждой ячейки. Задавая разные стили разным ячейкам, можно использовать часть ячеек только для вывода графики, часть - только текста и т.д.
  4. Ширина (Item Width) - Это значение измеряется в размерах экрана (1 - целый экран) и отсчитывается между двумя соседними картинками (см. рисунок со структурой мультиборда), текст при этом игнорируетя. Минимальное значение этого параметра - 0.01 (при 0 для всех ячеек мультиборд будет пустым), в этом случае картинки расположены непрерывно. Такое значение для всей матрицы при отображении только картинок обычно используется в полностью графических мультибордах. Задавая разное значение этого параметра для разных ячеек, можно добиваться последующего сдвига всех следующих ячеек вправо. Расстояние между ячейками по вертикали менять нельзя! На следующем рисунке показан тестовый мультиборд при разных значениях параметра Width:


Функции для работы с мультибордом


Теперь рассмотрим список функций и типов переменных, которые предоставляет UMSWE для работы с этими объектами. Существует два типа переменных для работы с мультибордами - Multoboard (Мультиборд) и Multiboard Item (Элемент Мультиборда). Если вам прийдется использовать как минимум одну переменную типа Multiboard, то без использования Multiboard Items можно легко обойтись за счет функции Multiboard - Get Multiboard Item, о которой я расскажу дальше. Итак, вот полный список всех функций UMSWE для работы с мультибордами (так же привожу JASS-код этих функций):
  • New Multiboard (Новый мультиборд): Нет аргументов - Это значение присваивается по умолчанию всем новым переменным типа Multiboard. Создает в памяти новый объект данного типа, об использовании этой функции надо задумываться только тем, кто пишет на JASS. Остальные пусть просто оставляют у новых мультибордов это значение при создании переменной.
    JASS-функция: function CreateMultiboard takes nothing returns multiboard
  • Multiboard - Destroy (Уничтожить): Destroy (multiboard) - Уничтожает мультиборд, он исчезает с экрана и восстановить его невозможно, разве что создать новый.
    JASS-функция: native DestroyMultiboard takes multiboard Mb returns nothing
  • Multiboard - Show/Hide (Показать / Спрятать): Change the display style of (multiboard) to (show) - Прячет или показывает мультиборд, не уничтожая его при этом. Кстати, при создании мультиборда не забудьте его показать, иначе он будет существовать невидимым.
    JASS-функция: native MultiboardDisplay takes multiboard Mb, boolean Show returns nothing
  • Multiboard - Minimize/Maximize (Свернуть / Развернуть): Perform for (multiboard) following action: (minimize) - Принудительно сворачивает или разворачивает мультиборд. Это действие аналогично нажатию кнопки "свернуть / развернуть" и не уничтожает сам мультиборд.
    JASS-функция: native MultiboardMinimize takes multiboard Mb, boolean Minimize returns nothing
  • Multiboard - Clear (Очистить): Clear (multiboard) - Очищает мультиборд, он становится полностью пустым как при создании. Это действие должно выполняться только после того, как мультиборд будет спрятан при помощи функйии Multiboard - Show/Hide, иначе игра вылетит с ошибкой!
    JASS-функция: native MultiboardClear takes multiboard Mb returns nothing
  • Multiboard - Change Title (Сменить Заголовок): Change the title of (multiboard) to (title) - Изменяет заголовок мультиборда. Главное - не задавать слишком длинный текст, иначе текст будет "вылезать".
    JASS-функция: native MultiboardSetTitleText takes multiboard Mb, string Label returns nothing
  • Multiboard - Set Number Of Columns (Задать число столбцов): Set the number of columns for (multiboard) to (0) - Задает число столбцов в мультиборде. Столбцы считаются слева направо.
    JASS-функция: native MultiboardSetColumnCount takes multiboard Mb, integer Count returns nothing
  • Multiboard - Set Number Of Rows (Задать число строк): Set the number of rows for (multiboard) to (0) - Задает число строк в мультиборде. Строки считаются сверху вниз.
    JASS-функция: native MultiboardSetRowCount takes multiboard Mb, integer Count returns nothing
  • Multiboard - Release Multiboard Item (Удалить элемент): Release (multiboard item) - Удаляет заданный элемент.
    JASS-функция: native MultiboardReleaseItem takes multiboarditem Mbi returns nothing
Приведенные ниже функции существуют в двух вариантах - для одной ячейки и для всей матрицы. Поэтому я буду писать вариант только для одной ячейки, т.к. синтаксис у каждой пары функций практически одинаковый. Отличие состоит лишь в том, что для функций, работающих со всей матрицей, вместо аргумента (Multiboard Item) выбирается (Multiboard). Однако JASS-объявления этих функций cbkmyj отличаются, я буду писать два варианта - сначала вариант для одной ячейки, потом вариант для всей матрицы.
  • Multiboard - Change Style of Item (Изменить стиль): Change the display style of (multiboard item) to (Show) the value and (Show) the icon - Задает стиль каждого элемента (как я уже говорил, есть вторая функция Multiboard - Change Style of Items, которая задает одинаковый стиль для всех элементов). Вы можете выбрать несколько вариантов стиля элемента - как с картинками и текстом, так и без.
    JASS-функции:
    native MultiboardSetItemStyle takes multiboarditem Mbi, boolean ShowValue, boolean ShowIcon returns nothing
    native MultiboardSetItemsStyle takes multiboard Mb, boolean ShowValues, boolean ShowIcons returns nothing
  • Multiboard - Change Value of Item (Изменить значение элемента): Change the value of (multiboard item) to (value) - Меняет текстовое значение элемента. (Value) - устанавливаемое значение в виде строки. Если вам надо отобразить на мультиборде число, предварительно переведите его в строку при помощи функции Conversion - Integer To String. Так же убедитесь в том, что параметр Width этого элемента позволяет записать строку целиком, иначе получится "обрезанная" строка.
    JASS-функции:
    native MultiboardSetItemValue takes multiboarditem Mbi, string Val returns nothing
    native MultiboardSetItemsValue takes multiboard Mb, string Value returns nothing
  • Multiboard - Change Color of Item (Изменить цвет элемента): Change the color of (multiboard item) to ((255) red, (255) green, (255) blue, (255) alpha) - Меняет цвет элемента, который задается 4-мя RGBA составляющими. Здесь используется инверсный альфа-канал, поэтому для полностью непрозрачного шрифта значение (alpha) равно 255. Кроме этого способа, цвет текста можно установить в самом тексте при помощи цветовых тегов.
    JASS-функции:
    native MultiboardSetItemValueColor takes multiboarditem Mbi, integer Red, integer Green, integer Blue, integer Alpha returns nothing
    native MultiboardSetItemsValueColor takes multiboar Mb, integer Red, integer Green, integer Blue, integer Alpha returns nothing
  • Multiboard - Change Width of Item (Изменить ширину элемента): Change the width of (multiboard item) to (0.00) - Меняет ширину элемента элемента. Подробнее о влиянии ширины на отображение информации написано в начале статьи. Как я уже говорил, ширина измеряется в долях экрана (1 - целый экран). При значении 0.01 соседние картинки плотно прилегают друг к другу без промежутков.
    JASS-функции:
    native MultiboardSetItemWidth takes multiboarditem Mbi, real Width returns nothing
    native MultiboardSetItemsWidth takes multiboard Mb, real Width returns nothing
  • Multiboard - Change Icon of Item (Изменить картинку элемента): Change the icon of (multiboard item) to (icon) - Задает картинку для элемента. Картинка может быть как импортированной в TGA или BLP, так и одной из картинок Варкрафта. Единственное, что надо учитывать - ее размер всегда будет отмасштабирован до 16x16. Поэтому импортируйте картинки такого размера. Насколько я знаю, альфа канал для картинок элементов не поддерживается даже при использовании BLP-формата.
    JASS-функции:
    native MultiboardSetItemIcon takes multiboarditem Mbi, string IconFileName returns nothing
    native MultiboardSetItemsIcon takes multiboard Mb, string IconFileName returns nothing

Теперь я перечислю функции, которые возвращают какое-либо значение и используются в условиях или как аргументы других функций. Перед функцией указан тип возвращаемого значения, эту функцию надо искать в соответствующем ей разделе условий или аргументов.
  • Multiboard Item -> Get Multiboard Item (Взять элемент): From (multiboard item) get item in row (0), column (0) - Возвращает элемент матрицы с заданными координатами. Нумерация элементов идет из верхнего левого угла, номера начинаются с нуля. Эта функция почти во всех случаях заменяет использование переменных типа Multiboard Item.
    JASS-функция: native MultiboardGetItem takes multiboard Mb, integer Row, integer Column returns multiboarditem
  • Boolean -> Multiboard is Displayed (Мультиборд виден на экране): (Multiboard) is displayed - Возвращает true, если данный мультиборд виден на экране (не спрятан).
    JASS-функция: native IsMultiboardDisplayed takes multiboard Mb returns boolean
  • Boolean -> Multiboard is Minimized (Мультиборд Свернут): (Multiboard) is minimized - Возвращает true, если данный мультиборд свернут.
    JASS-функция: native IsMultiboardMinimized takes multiboard Mb returns boolean
  • Integer -> Row Count (Число строк в матрице): Row count of (Multiboard) - Возвращает число строк в данном мультиборде. Максимальный номер строки в адресе равен этому числу минус единица.
    JASS-функция: native MultiboardGetRowCount takes multiboard Mb returns integer
  • Integer -> Column Count (Число столбцов в матрице): Column count of (Multiboard) - Возвращает число столбцов в данном мультиборде. Максимальный номер столбца в адресе равен этому числу минус единица.
    JASS-функция: native MultiboardGetColumnCount takes multiboard Mb returns integer
  • String -> Multiboard Title (Заголовок мультиборда): Title of (Multiboard) - Возвращает заголовок мультиборда в виде строки.
    JASS-функция: native MultiboardGetTitleText takes multiboard Mb returns string

    Кроме этого, есть еще функция native MultiboardSuppressDisplay takes boolean flag returns nothing, которой нет даже в действиях UMSWE, доступна только в JASS-скриптах. Она запрещает или разрешает показ текущего и всех последующих мультибордов.

Итак, в этой статье я подробно рассказал о устройстве мультиборда и тригерных функциях, обеспечивающих его работу. Теперь осталось рассказать о том, как делать разные виды мультибордов на примерах. Это будет в следующих статьях. Пока присылаю скриншот триггеров самого простого мультиборда, который изображен на скриншотах (с надписями "test") .


Что важно знать при работе с мультибордом?


При создании мультиборда необходимо помнить, что он будет показан лишь в том случае, если создан в начале игры (а не при инициализации карты) и был показан после создания. Поэтому не работайте с мультибордом в действиях триггера, запускающегося при Map Initialization. Вместо этого используйте событие Elapsed Game Time is XXX seconds. И не забудьте включить показ мультиборда после его создания соответствующим действием.

Можно ли сделать разные мультиборды для разных игроков?


Да, можно. Мультиборд является графическим элементом и локальные действия с ним не приводят к десинхронизации игры (т.н. десинку). Однако реализация локальной работы с мультибордом требует знаний языка jass. Поэтому читать этот пункт дальше имеет смысл тем, кто уже неплохо разбирается в этом языке и имеет понятие о том, как выполняется скрипт карты в мультиплеере. Всю необходимую информацию про jass вы можете найти на нашем сайте www.blizzplanet.ru в этом же разделе.

Итак, существует два способа локальной работы с мультибордом. Кстати, обращаю внимание новичков, что понятие 'локальная переменная' и 'локальная запись / работа' - вещи, не имеющие ничего общего. Локальные сегменты кода получаются путем использования jass-функции GetLocalPlayer():
  • Создание массива мультибордов и локальный показ: Идея заключается в том, что создается массив мультибордов, номер в массиве соответствует номеру игрока. Затем каждый мультиборд локально показывается игроку с соответствующим номером. То есть игроку 0 показывается мультиборд 0, игроку 1 - мультиборд 1 и т.д. А вся дальнейшая работа с мультибордами ведется как обычно. Данные по всем мультибордам обновляются глобально, он за счет того что каждый из мультибордов видим только на одном компьютере, будет создаваться впечатление, что у каждого игрока свой мультиборд. Это самый простой способ (так как локальный сегмент кода только один - показ мультибордов после их создания), однако его имеет смысл использовать, когда мультиборды у игроков сильно отличаются друг от друга. В противном случае обновлять одну и ту же информацию в случае 12 игроков для 12 мультибордов - это как минимум неграмотно.

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

    Код:
    // udg_Multiboards - массив мультибордов, считаем что они уже созданы.
    
    function MbShowLoc takes nothing returns nothing
     // Вызов этой функции покажет каждый мультиборд соответствующему игроку.
     local integer Id = 11 // Номер последнего игрока в игре.
     // Игроки имеют номера от 0 до 11.
     loop
      if (udg_Multiboards[Id] != null) and (GetLocalPlayer() == Player(Id)) then
       call MultiboardDisplay(udg_Multiboards[Id], true)
      endif
      exitwhen Id == 0
      set Id = Id - 1
     endloop
    endfunction
    Кстати, эту функцию можно поместить в jass-заголовок карты и вызывать после создания всех мультибордов из GUI-триггера действием Custom Script: call MbShowLoc(). Тогда можно получить разные мультиборды для разных игроков в 'обычных триггерах'. Только надо учитывать, что мультиборд в ячейке 0 (а не в ячейке 1) соответствует красному игроку и т.д.

    .
  • Создание одного мультиборда и локальная запись данных: Способ впервые был предложен и проверен True.Rus'ом. Создается один мультиборд, но данные в нем меляются локально для каждого игрока. Этот способ нужно использовать, когда мультиборды у игроков практически одинаковы и различие, например, в заголовке или нескольких строках. Я не проверял, приводит ли разные локальные количества строк и колонок к десинхронизации. Но проверено, что разные локальные значения ячеек не приводят к десинку.

    Пример кода, локально обновляющего заголовок мультиборда (как в мультиборде Майдоты, например)

    Код:
    // udg_Strings - здесь хранятся заголовки мультибордов для каждого игрока
    // udg_Multiboard - уже созданный мультиборд
    
    function UpdateMbCaptions takes nothing returns nothing
     // Вызов этой функции обновит заголовок мультиборда разными значениями для разных игроков.
     local integer Id = 11 // Номер последнего игрока в игре.
    
     loop
      exitwhen Id == 0
      if GetLocalPlayer() == Player(Id) then
       call MultiboardSetTitleText(udg_Multiboard, udg_Strings[Id])
      endif
      set Id = Id - 1
     endloop
    endfunction

2. Как сделать мультиборд, отображающий жизни у группы юнитов?


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



Что нам для этого потребуется?
  1. Базовое знание триггеров, в частности циклов. На сайте blizzplanet.ru вы можете найти статьи, подробно рассказывающие о них, например, статьи Сергея и Raz'а.
  2. Патч UMSWE для редактора. Конечно, для игры в такую карту никакой патч не будет нужен.
  3. Обязательное знание теории работы мультибордов.
  4. Любой графический редактор и хотя бы базовое владение им. К этой статье прилагаются примеры графических файлов, поэтому редактор вам необходим если только вы хотите нарисовать что-то свое. В конце статьи вы сможете скачать RAR-архив с уже готовыми графичекими файлами для мультиборда и карту-пример с расставленными юнитами. Если вы не планируете вставить этот мультиборд в вашу карту, этот архив будет хорошим подспорьем - вам остается только написать триггеры.

Как это будет работать?

Вот задача, стоящая перед нами: нам надо сделать счетчики жизней для каждого юнита в каждой четной строке мультиборда (включая нулевую строку - как я уже говорил, номера строк и колонок в мультиборде начинаются с нуля) и название юнита в каждой нечетной. Число юнитов может быть любым - 1, 5, 10... Как сделать мультиборд, показывающий жизни и название всех юнитов?
Для начала рассмотрим, как будет работать счетчик жизней хотя бы для одного юнита. Очевидно, что строку счетчика надо делать из плотно прилегающих друг к другу картинок без текстового комментария. Для простоты рассмотрим самый простой случай. На этом рисунке изображено устройство строки мультиборда, выполняющей роль счетчика:


Это самый простой вариант - каждая ячейка или закрашена зеленым или является пустой. Красной полоской обознычены реальные жизни юнита в процентах. Наша шкала состоит из 10 ячеек, соответственно каждая ячейка соответствует каждым 10% жизни юнита. Мы округляем жизни юнита по ячейкам: если красная полоска хотя бы чуть-чуть "заходит" в ячейку, то мы закрашиваем ячейку зеленым. Понятно, что алгоритм для такого закрашивания получается очень простой: если жизнь в процентах больше или равно (9 - номер_ячейки)*10, то мы закрашиваем ячейку зеленым, если нет - то закрашиваем черным. В мультиборде ячейки считаются слева направо, а жизнь растет справа налево, поэтому и потребовалось использовать (9 - номер_ячейки)*10 вместо обычного номера ячейки. Действительно, при этом закрашиванию нулевой ячейки будет соответствовать условие, когда у юнита больше 90% жизней, а закрашиванию 9-й - когда у юнита больше 10% жизней.

Однако такой счетчик получится очень неточным - если жизнь юнита в процентах меняется в пределах от i * 10 до (i + 1) * 10 {i - целое число от 0 до 9}, то на мультиборде не будет никаких изменений. Это нас, конечно, не устраивает! Но каждая картинка мультиборда имеет строгий размер - 16x16. Поэтому остается один выход - рисовать несколько вариаций одной ячейки с разной степенью заполненности - например, ячейка заполнена на 0, 50, 100%. В этом случае разрешение счетчика увеличится в два раза - он сможет показывать изменения жизни юнита в пределах 5%, а не 10% как в предыдущем примере. Если сделать еще больше комбинаций - 0%, 25%, 50%, 75%, 100%, то разрешение увеличится до 2.5% (разрешение тем больше, чем точнее мультиборд показывает жизни юнита). И так далее - чем больше комбинаций для ячеек, тем выше разрешение. Размер одной ячейки 16x16 пикселей, поэтому максимально возможное число комбинаций - 16. Однако такая точность никому не нужна - изменение полоски на один пиксель слишком плавное, а картинок получается слишком много. Я нашел оптимальным разрешение счетчика в 2.5% - для этого надо 5 картинок для элемента. Именно так и сделано в мультиборде проекта Graveyard, а для примера мы обойдемся разрешением в 5% - то есть, нам понадобятся картинки, соответствующие заполнению в 0, 50 и 100% каждой ячейки.

Еще раз посмотрите на структуру счетчика - мы делаем полоску не "просто так", а в отдельной рамке. Эта рамка повторяет внешнюю рамку мультиборда - мне кажется, так смотрится гораздо эстетичнее. Однако тут же появляется сложность - крайние ячейки отличаются от всех остальных. Поэтому разделим наш мультиборд на три части (см. рисунок):
  1. Left - Ячейка под номером 0. Все вариации этой ячейки имеют закругление слева.
  2. Middle - Все ячейки с номерами 1 - 8. У них закруглений нет.
  3. Right - Ячейка под номером 9. Все вариации этой ячейки имеют закругление справа.
Итак, у нас есть 3 типа ячеек, у каждой 3 варианта картинок (0, 50 и 100% заполненности). В результате получаем 9 картинок, которые нам и нужно нарисовать, а затем импортировать в наш мультиборд. На приведенном ниже рисунке я изобразил все варианты ячеек (ячейки увеличены в 2 раза для наглядности) и их обозначение. Если вы владеете каким-либо графическим редактором, то можете нарисовать свои варианты этих картинок, если же нет - берите эти картинки из RAR-архива, который вы можете скачать в конце статьи и просто импортируйте эти файлы в карту с рельефом и юнитами, которая находится в том же архиве. Не забудьте указать путь к файлам - Multiboard\(Имя_Файла)


Нам надо выбрать какую-то схему для обращения к этим картинкам из триггеров. В нашем примере мы будем использовать такую схему (см. рисунок) - все рисунки будут в формате TGA для простоты:

Multiboard\(Часть Мультиборда)_(Заполненность Ячейки).tga

Например, полностью заполненная средняя ячейка будет называться Multiboard\Middle_High.tga, крайняя левая пустая ячейка будет называться Multiboard\Left_Low.tga и так далее. Все обозначения приведены в предыдущем рисунке.

Итак, с ячейками разобрались. В нашей карте уже есть 9 импортированных файлов с нужными названиями. Как я уже говорил в статье о теории мультибордов, для того, чтобы картинки в строке плотно друг к другу, параметр Width для всех ячеек этой строки должен быть равен 0.01, а стиль отображения должен быть (hide) value, (show) icon. По умолчанию Warcraft Import Manager задает файлам путь War3Imported\(Имя_Файла). Не забудьте прописать всем картинкам пути War3Imported\(Имя_Картинки). Делается это очень просто - щелкаете на каждом файле правой кнопкой мыши, выбираете в контекстном меню соответствующий пункт и затем пишите новый путь к файлу.Еще раз проверьте все названия - ошибка в них приведет к "зеленым квадратам" в мультиборде:
  • Multiboard\Left_Low.tga
  • Multiboard\Left_Med.tga
  • Multiboard\Left_High.tga
  • Multiboard\Middle_Low.tga
  • Multiboard\Middle_Med.tga
  • Multiboard\Middle_High.tga
  • Multiboard\Right_Low.tga
  • Multiboard\Right_Med.tga
  • Multiboard\Right_High.tga
Теперь рассмотрим текстовую состовляющую. Как видно из самого первого скриншота - это строка, занимающия весь (или почти весь мультиборд). Для того, чтобы заданная строка мультиборда выглядела именно так, надо первой ячейке в этой строке задать стиль - (show) value, (hide) icon и параметр Width такой, чтобы под строку Value отводилось место, равное ширине мультиборда (если непонятно, откуда взялись именно такие значения и стили - смотрите статью с теорией). Остальные ячейки (с номерами колонок 1 - 9) в этой строке должны быть спрятанными, то есть иметь Width равным 0,01 и Style соответствующим (hide) value, (hide) icon. Кстати, параметр Width задается в долях экрана, поэтому его прийдется определять "на глаз". Для разрешения 1024x768 установим Width нулевой ячейки равным 0.2.

В итоге мы рассмотрели алгоритмы формирования разных строк мультиборда:
  • Четные строки (включая нулевую) служат для отображения счетчиков жизни и являются чисто графическими. Все ячейки таких строк имеют стиль (hide) value, (show) icon и Width, равный 0.01.
  • Нечетные строки служат для вывода текста. Все ячейки таких строк, кроме нулевой, скрыты, т.е. имеют стиль (hide) value, (hide) icon и Width, равный 0.01. Самая первая ячейка (с номером 0) имеет стиль (show) value, (hide) icon и Width, равный 0.2. Такая ячейка позволит выводить достаточно длинный текст с выравниванием по левому краю.

Создаем триггер инициализации мультиборда.

Для начала создадим два массива:
  1. Units (Unit Array) - В этом массиве мы будем хранить всех юнитов, жизнь которых мы будем показывать в мультиборде.
  2. Names (String Array) - А в этом массиве будут хранится имена соответствующих юнитов. Например, строка в первой ячейке этого массива будет соответствовать юниту, хранящемуся в первой ячейке массива Units и так далее...
Данные в эти массивы мы занесем специальным триггером при инициализации карты. В карте-примере этот триггер называется Variables. Он срабатывает при старте карты (Map Initialization) и заносит в эти два массива как самих юнитов, так и соответствующие им имена. Кроме этого в карте есть и второй "тестовый триггер" Respawn, который воскрешает героя после смерти. Это сделано на случай, если вашего героя убьют крипы.


Кроме этого, необходимо создать саму переменную типа multiboard - назовем ее MBoard. Убедитесь, что значением по умолчанию у нее стоит New Multiboard. Теперь создадим триггер, который будет инициализировать мультиборд - сделайте пустой триггер с названием Init. Событием ему ставим Game - Elapsed Game Time is 0,01 second. Почему именно 0,01 секунда после начала игры, а не Map Initialization? Во-первых для того, чтобы раньше сработал триггер Variables и занес все данные в оба массива. А во-вторых, мультиборд так же как и обычный лидерборд не надо инициализировать при Game Initialization, иначе не будет работать. Условия этого триггера следует оставить пустыми.

Теперь рассмотрим как будет работать этот триггер. В начале этой статьи было сказано, что число полосок в мультиборде будет зависеть от числа юнитов, записанных в массив Units. Каждому юниту будут соответствовать две строки в мультиборде - строка с полоской жизней и информационная (текстовая) строка. Однако тут возникает одна проблема - как узнать, сколько юнитов было записано в массив при инициализации? Функций определения размера массива и поиска по массиву в стандартных библиотеках Варкрафта не существует - поэтому функцию определения массива прийдется писать самим. Это совсем не сложно - при инициализации карты мы вносили юнитов последовательно в ячейки массива Units, начиная с нулевой. Поэтому после занесения данных в какое-то число первых ячеек последовательно записаны юниты, остальные ячейки остаются пустыми. Определение размера массива организуем очень просто - будем последовательно перебирать ячейки массива и смотреть, что в них находится. Если ячейка пустая - значит мы нашли конец массива, а номер этой ячейки - число юнитов, записанных в этот массив (так как при инициализации мы последовательно записывали юнитов в ячейки начиная с нулевой). Итак, поехали:
Код:
 For each (Integer A) from (0) to (15) do actions:
   // Последовательно перебираем все ячейки с номерами от 0 до 15 
   // (больше не имеет смысла так как даже 32 строки мультиборда (16 * 2)
   // с трудом уместятся на экране)
   If Units(Integer A) is equal to (no unit) then
     // Смотрим, является ли ячейка массива под номером 
     // (Integer A) пустой или нет
    Then - Actions:
      // Случай когда ячейка является пустой т.е. содержит значение (no unit)
      Custom Code - "exitwhen true"
      // Без JASS все-таки не обойтись. Я не буду подробно рассказывать 
      // о JASS, ведь мы делаем мультиборд в обычном редакторе триггеров 
      // (GUI). Скажу только, что код "exitwhen true" обозначает 
      // мгновенный выход из цикла, но действия триггера, расположенные
      // после цикла, все равно будут выполнены. В общем, как только нам
      // встретилась пустая ячейка, мы тут же выходим из цикла.
    Else - Actions:
      // Ячейка не пустая - ничего не делаем.
      Do Nothing

 // Итак, мы последовательно перебираем в цикле ячейки массива и как 
 // только нам попадается первая пустая ячейка, тут же выходим из цикла.
 // А переменная-счетчик этого цикла (т.е. For Loop Integer A)
 // содержит номер первой пустой ячейки (т.к. в этом месте мы вышли) т.е. 
 // просто число юнитов, которых мы записали в этот массив. Если же все
 // ячейки содержат в себе юнитов, то цикл все равно закончится когда
 // переменная For Loop Integer A достигнет значения 15.
 // В результате выполнения такого цикла мы знаем, сколько юнитов у нас
 // записано в массив, соответственно можем задать нужное число строк
 // в мультиборде. Это число будет равно (Integer A) * 2 так
 // как под каждого юнита мы отводим две строки.

 Multiboard - Set Number of Rows in (MBoard) to 2 * (Integer A)
   // Устанавливаем число строк в мультиборде.
 Multiboard - Set Number of Columns in (MBoard) to 10
   // А колонок у нас будет всегда 10 - об этом мы уже говорили.
 Multiboard - Set Style of (MBoard): (hide) values, (show) icons
 Multiboard - Set Width of Every Item in (MBoard) to (0.01).
   // Устанавливаем всему мультиборду "графический" стиль, т.е.
   // показываем только каринки, промежуток ставим минимальный.
 Multiboard - Set Title of (MBoard) to "Health Bar".
   // Устанавливаем заголовок мультиборда.

 // У нас есть "графический" мультиборд нужного размера. Однако кроме
 // полосок жизней нам в каждой нечетной строке мультиборда надо 
 // отображать текстовую информацию. Соответственно стиль ячеек в 
 // каждой нечетной строке будет "текстовым". Как я уже говорил 
 // раньше, для таких строк нужен следующий стиль - у первой ячейки
 // в строке отображается только текст, а ее Width = 0,20.
 // Остальные ячейки в этой строке должны быть скрыты. То есть нам нужен
 // еще один цикл, который будет перебирать все 10 колонок в каждой
 // нечетной строке, для первой устанавливать текстовый стиль и 
 // нужный Width, остальные - прятать.

 For each (Integer A) from 0 to ((Number of Rows in MBoard) / 2) - 1 do action:
   // Во внешнем цикле мы перебираем все номера от 0 до числа юнитов в массиве.
   // Так как Число_Строк = (2 * Число_юнитов), то номер последнего юнита 
   // будет равен ((Число_Строк / 2) - 1).
   For each (Integer B) from 0 to 9 do action:
     // А во внутреннем цикле мы последовательно перебираем 10 колонок -
     // от нулевой до девятой. 
     If (Integer B) equal to 0
       // А тут проверяем - если у нас нулевой столбец (т.е. самая 
       // левая ячейка), то устанавливаем ей текстовый стиль, иначе
       // (для столбцов с номерами 1 - 9) прячем ячейки)
       Multiboard - Set Style of Item in MBoard in Position:
         ((2 * (Integer A)) + 1, (Integer B)): Show value, Hide icon
       Multiboard - Set Width of Item in MBoard in Position:
         ((2 * (Integer A)) + 1, (Integer B)) to 0.2
         // Integer A меняется от 0 до (числа юнитов в массиве - 1),
         // соответственно 2 * (Integer A) + 1 будет нечетными числами:
         //
         //  (Integer A)    (2 * (Integer A) + 1)
         //      0                            1
         //      1                            3
         //      2                            5
         //      3                            7
         //     ...                          ...
         // (число юнитов)            (последняя строка)
         //
         // Таким образом выражение (2 * (Integer A) + 1) позволяет
         // получить номера нечетных текстовых строк для всех юнитов,
         // записанных в массив. Таким образом, для всех ячеек, расположенных
         // на пересечении нулевых столбцов и нечетных строк, мы задаем
         // "текстовый" стиль.
     Else - Actions:
       // В противном случае (если (Integer B) > 0 и у нас не первая колонка)
       // мы прячем остальные ячейки в нечетной строке, таким образом видна
       // будет только первая с "текстовым" стилем.
       Multiboard - Set Style of Item in MBoard in Position:
         ((2 * (Integer A)) + 1, (Integer B)): Hide value, Hide icon
       Multiboard - Set Width of Item in MBoard in Position:
         ((2 * (Integer A)) + 1, (Integer B)) to 0.01

   Multiboard - Set Value of Item in MBoard in Position:
     ((2 * (Integer A)) + 1, (Integer B)) to Names(Integer A)
     // А когда внутренний цикл закончен (т.е. мы обработали все колонки)
     // в нечетной строке, мы задаем текстовое значение нулевой ячейки -
     // присваиваем ей имя юнита, записанного в массиве Names.
   Wait (1.00) seconds
     // Зачем нужен этот Wait, я расскажу позже. Хотя можете его убрать
     // и посмотреть что будет. 
   Multiboard - (MBoard): Show
     // И, наконец, показываем мультиборд. Пока на нем не отображается
     // графика, но имена юнитов в нечетных строках уже видны.
     // Теперь остается сделать триггер, обновляющий графическую информацию.
В результате у вас должно получится примерно то, что изображено на рисунке. Предыдущий код предназначен именно как комментарий всех действий, поэтому для проверки своего кода лучше ориентируйтесь на рисунок или же откройте триггер инициализации в тестовой карте - так будет гораздо нагляднее.



Создаем триггер обновления графики.

Пока у нас есть размеченный мудьтиборд нужного нам размера, в нечетных строках которого находятся названия юнитов. Число строк в мультиборде равно числу юнитов, занесенных в массив и умноженному на два. Однако счетчиков жизней пока нет - вместо них вы видите стандартные зеленые квадраты, которыми Варкрафт замещает отсутствующие графические элементы в мультиборде. Тепеьр нам надо написать второй триггер, который будет "обновлять" графическую информацию в соответствие с жизнями каждого из юнитов в массиве. Я думаю, очевидно, почему этот триггер должен быть периодическим (то есть его событие - Time - Periodic Event) с довольно коротким периодом для того, чтобы информация на мультиборде была достоверной. Я считаю, что для нашего случая надо использовать периодическое событие с периодом 0.5 секунды. За один период мы в цикле пройдем все четные строки мультиборда (а именно в четных строках у нас находятся счетчики жизней), за каждый шаг цикла будем перебирать все колонки в строке и устанавливать для них графику в зависимости от жизней юнита. То есть нам прийдется опять использовать цикл в цикле ("вложенные циклы"), аналогичный тому, что мы использовали в триггере инициализации мультиборда. Сейчас мы подробно рассмотрим действия такого триггера, но для начала создайте его (Название в тестовой карте - Refresh) и добавьте событие - Time - Periodic Event: every 0.5 second of game time. Как и в прошлом триггере, никаких условий добавлять не надо.

Вам потребуется еще одна переменная - Prefix (тип - String). С ее помощью мы будем последовательно формировать название нужной картинки на каждом шаге цикла. Действия триггера подробно рассмотрены ниже:
Код:
 For each (Integer A) from 0 to ((Number of Rows in MBoard) / 2) - 1 do action:
   // Такой же цикл использовался и в предыдущем триггере - мы делаем столько шагов в
   // цикле, сколько юнитов у нас записано в массиве. Размер массива мы определили еще
   // в прошлом триггере, однако в этот массив значение без использования дополнительных
   // переменных никак не передать. Проще определить число юнитов из числа строк в
   // мультиборде делением на 2. А так как нумерация идет с нуля, то от этого числа 
   // надо отнять еще единицу. 
   For each (Integer B) from 0 to 9 do action:
     // А в этом внутреннем цикле мы последовательно перебираем все 10 
     // колонок каждой строки.
     Set Prefix = "Multiboard\Middle_"
       // Начинаем формировать имя картинки. Это имя начинается с Middle_ для
       // всех колонок, кроме крайних (нулевой и девятой).
     If (Integer B) equal to 9 then: 
       Set Prefix = "Multiboard\Left_"
     else
       Do Nothing
     If (Integer B) equal to 0 then: 
       Set Prefix = "Multiboard\Right_"
     else
       Do Nothing
       // Для крайних колонок используем картинки с закруглением - 
       // их названия начинаются с "Left_" и "Right_"

     // А теперь будем определять, какую приставку приписать для картинки -
     // Low, Med или High. Это зависит от того, сколько жизней есть у юнита.
     // У нас 10 колонок, следовательно каждая соответствует каждым 10% жизни.
     // Единственное отличие - номера колонок увеличиваются слева направо, а 
     // жизни юнита наоборот - поэтому каждая колонка соответствует (9 - i) * 10
     // процентам жизни (самая левая - 90%, самая правая - 0%), i - номер колонки
     // Поэтому на каждом шаге мы будем считать такое выражение:
     // (Жизнь_В_Процентах - 10*(9 - Номер_Колонки)).
     //  - Если это выражение 0 или меньше - значит данная колонка полностью пуста,
     //    т.е. жизни просто "не доходят" до этой колонки. В этом случае выбираем
     //    заполнение ячейки на 0%.
     //  - Если это выражение больше нуля, но меньше 5 - значит значение жизни
     //    юнита находится где-то в районе "середины" интервала, соответствующего
     //    данной ячейке. В этом случае устанавливаем заполнени на 50%
     //  - Если значение больше 5, то можно считать ячейку 100% заполненной.
     // Пример (L - Low (0%), M - Med (50%), H - High (100%)):
     //    72% - LLMHHHHHHH, 10% - LLLLLLLLLH, 11% - LLLLLLLLMH, 100% - HHHHHHHHHH

     If Percent Life Of (Units(Integer A)) - Real(10 * (Integer B)) less or equal to 0
        // Случай когда это выражение меньше или равно нулю.
        Set Prefix = Prefix + Low
          // Добавляем к названию ячейки слово Low 
          // (так мы обозначили пустые ячейки)
     Else If Percent Life Of [i](Units(Integer A)) - Real(10 * (Integer B))[i] greater than [i]5[i]
        // Случай когда это выражение больше пяти.
        Set Prefix = Prefix + High
     Else
       // Последний вариант - частичное заполнение.
       Set Prefix = Prefix + Med
         // Делаем аналогично для остальных типов заполненности.
     Multiboard - Set icon of Item in position: 
       (2 * (Integer A), (9 - (Integer B))) to Prefix + ".tga"
       // Устанавливаем картинку для ячейки. 2 * (Integer A) дает 
       // все четные строки мультиборда, а обратный номер колонки
       // используется из-за того, что жизнь и номера колонок
       // отсчитываются в разных направлениях.
Если что, то вы можете проверить свой триггер по скриншоту или тестовой карте. У вас должно получится следующее:


В результате у нас готовы триггеры мультиборда. Еще раз проверьте, что на вашей карте есть:
  • Несколько любых юнитов (мы их будем заносить в массив).
  • Все импортированные картинки (9 штук) с правильными названиями и путями.
  • Триггер Variables, который при старте карты заполняет массивы Units и Names.
  • Два рабочих триггера - Init и Refresh.
Если все сделано правильно, можете запускать карту. Через секунду после начала игры вы увидите мультиборд, точно такой же, как и на самом первом скриншоте (размер, графика и названия юнитов, конечно же, будут отличаться). Зеленые полоски будут заполняться в зависимости от числа жизней у каждого юнита, записанного вами в массив Units. Можете провести эксперимент - поменяйте числа регистрированных юнитов в триггере Variables при следующем запуске - вы увидите, что размер мультиборда автоматически меняется в зависимости от числа записанных юнитов в массив.

Если вы хотите использовать мультиборд, подобный этому, в вашей карте.

ВНИМАНИЕ! Для того, чтобы такой мультиборд без ошибок работал в вашей карте, не используйте в циклах стандартные переменные (For Loop Integer A) и (For Loop Integer B) - дело в том, что скорее всего, все цикл в вашей карте построены при помощи этих же переменных. Поэтому возможны ошибки при параллельной работе триггера Refresh и одного из циклов в ваших триггерах из-за одновременного обращения к одной переменной. Так как Refresh запускается каждые 0,5 секунды - вероятность ошибки очень велика. Поэтому сделайте 2 integer-переменные и используйте их в качестве счетчиков в циклах вместо (For Loop Integer A) и (For Loop Integer B) - UMSWE это позволяет.

Для разнообразия и лучшего изучения материала попробуйте реализовать одну или несколько из этих идей в мультиборде. Задания расположены в порядке нарастания их сложности:
  1. Сделайте так, что когда у юнита остается меньше 30% жизней, полоска жизней становится красной.
  2. Для каждого юнита в трех строках отображается жизнь, мана и его имя. Для маны нарисуйте аналогичные картинки, но синего цвета.
  3. Массив юнитов и имен возможно менять во время игры, удалять / добавлять / заменять юнитов и их имена. При этом мультиборд будет автоматически адаптироваться ко всем изменениям - размер / счетчики / имена будут меняться в зависимости от этого.
  4. Если вы хорошо разобрались в том, как устроен и работает этот мультиборд, попробуйте сделать все три предыдущих пункта на одной карте Это не так уж и сложно...

(c) Alexey_BH (aka Cacodemon) - alexey_bh[webdog]mail.ru
ANDRЕW II вне форума  
Закрытая тема


Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.



Текущее время: 03:42. Часовой пояс GMT +3.


Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
(c) Blizzard.GG