XML – Расширяемый Язык Разметки, мета-язык, описывающий информацию, разбивая её на части в маленькие элементы и используя теги <>. Сам по себе этот язык ничего не делает. Он лишь помогает организовать данные.
1. Основы XML
XML часто противопоставляется HTML, в конце концов у них много общего в применении. В то время как HTML показывает данные, фокусируясь на том, как это должно выглядеть, XML описывает информацию, говоря нам из чего она состоит.
…
Что важно, так это то, что, будучи разбитой на базовые элементы информация легко распределяется среди разных программ, и различные их версии по-разному подходят к отображению этой информации, без того, чтобы необработанная информация становилась нестабильной, даже если некоторые программы созданы не использовать всю информацию или если к уже существующей добавляется новая. Поэтому язык и назван «расширяемым».
2. Правила XML
В XML мы можем использовать теги (tags) чтобыв значительной степени разработать свой собственный язык. Значение тега определяется вами (или в нашем случае Бесездой). Лишь немного элементов уже пред-определены заранее, к которым необходимо пользоваться особо, поскольку некоторые элементы (certain characters), так как <and > уже зарезервированы языком, то есть они уже используются для обозначения тегов. Вместо этого они бы означали < (меньше чем), > (больше чем), "e; (цитата) и & (амперсанд). Такие вещи называются сущностями (entities) или ярлыками (shortcuts), и Бесезда использует разработанные ей сущности чтобы ссылаться на элементы, которые, судя по всему, зашиты в код (hardcoded). Напр. булевы элементы имеют значения &true; или &false.
Поскольку это не язык сам по себе, здесь не много того, что необходимо запомнить, когда читаешь код, несозданный Бесездой. Поэтому…
А) все элементы должны закрывать закрывающим тегом
<tag>content</tag>
Или быть само-закрывающим пустым-элементом тегом:
<tag/>
Последнее обычно используется в комбинации с атрибутами, смотри ниже.
Б) теги чувствительны к регистру (case-sensitive) и должны быть правильно вложены (properly nested):
<Tag> content </tag> : неверно
<tag1><tag2> content </tag1></tag2> : неверно
<tag1><Tag2> content </Tag2></tag1> : блестяще
В) все документы должны иметь корневой тег (root tag), структура документа начинается с одного тега, который держит все остальные:
<A>
<B>
</B>
</A>
<C>
</C>
Неверно, потому что два элемента А и С оба являются корневыми.
<A>
<B>
<D>
</D>
</B>
<C>
</C>
</A>
Верно.
Отношения между разными элементами XML документа часто описывается как семейное древо (family tree). Элементы на одном и том же уровне иерархии названы сиблингами\брат-сестра (siblings). Элемент, создержащий другой, является «родителем» (parent) ему, а тот – ребенком (child) или дочерним элементом. Внуки и деды прилагаются.
Г) некоторая информация об элементах, может содержаться в самом теге (parked inside the tag), а не в под-элементе. Такой способ размещения информации называется атрибутом (attribute). К примеру, элемент, обозначенный ниже, атрибуирован именем через атрибут:
<text name="NameOfTextElement">
</text>
Информация, содержащаяся в атибруте, представляет собой мета-данные такого типа, которым благодаря своей уникальности нет смысла содержаться в под-элементах.
Д) комментарии в коде добавляются следующим образом:
<!-- mycomment -->
3. XML в играх Бесезды: схема
До-скайримовские игры Бесезды на Геймбрио свои меню и компоненты интерфейса (HUD components) организуют в элементы внутри документа XML. Элементы и их иерархия переводятся в то, что игрок видит на экране с помощью собственного XML-парсера игры. Для этого парсер придает значение элементам, тегам и тому, что между ними. XML поддерживает обширный синтаксис, а Бесезда наполняет его словарем. Но словарь этот она особо не публиковала, так что для моддинга это может стать проблемой.
Такой тип словаря, говорящий нам какие теги в XML структуре валидны и значимы, называется схемой XML. К счастью для нас некоторые умные люди на КС Вики (CS wiki) выяснили большую часть этой схемы в отношении Обливиона. Конечно, надо с осторожностью подходить к тому, чтобы автоматически полагать, будто работающие вещи в Обливионе по определению будут работать в Ф3 или Нью Вегасе, но тем не менее я опишу именно схему Обливиона.
У Бесезды элементы XML в целом делятся на три больших типа: обьектные элементы (object elements), элементы свойств (property elements), оперантные элементы (operator elements). Вот все они в эксцерпции из stats_menu.xml, линии 1415-1420:
<image name="stats_hunger_meter">
<locus> &true; </locus>
<filename> Interface\Stats\rad_meter_bracket.dds </filename>
<zoom>
<copy src="parent()" trait="zoom"/>
</zoom>
...
</image>
В этом коде главный элемент декларирует появление нового компонента ихображения, которое добавлено в меню статов. Такие вещи называются обьектными элементами, которые вводят новые обьекты в меню или интерфейс. Само собой разумеется, они немедленно получают имя с помощью именного атрибута (name attribute), чтобы оперантные элементы и функции расширителя скриптов могли сылаться на них.
На уровень ниже мы можем видеть, что несколько вещей об этом новом компоненте должны быть разьяснены, такие как, какую текстуру он использует (тег <filename>) и его процент масштабирования. Другие свойства включали размеры и положение элемента объекта. Элементы, обозначающие свойства обьектных элементов, именуются элементами свойств (property elements). Также они известны как характеристики (traits).
Во множестве случаев содержимое элементов свойств может быть абсолютной величиной, либо нумерической, либо строкой (string), такой как путь файла (filepath). Однако эти значения могут также калькулироваться в течение игровой сессии, скопированные из свойств других обьектных элементов и\или манипулируются математическими и обуславливающими (conditional) элементами. В приведенном выше примере процент масштабирования изображения копируется из процента масштабирования его собственного родительского элемента. Элементы, которые создают такие «живые» операции на элементах свойств, называются оперантными элементами (operator elements). Они часто включают использование src и trait атрибтов, чтобы ссылаться на другие свойства. Src будет в большинстве случает ссылаться на обьектный элемент или нечто глобальное, когда как trait ссылается на свойства.
Вот примеры элементов, которые выпадают из описанной выше типизации. К примеру, элемент <include>, который вы можете обнаружить в ванильных файлах повсюду, включает код из других документов, очевидное преимущество которого в том, что один и тот же код с ним можно использовать везде, а не печатать каждый раз заново:
<image name="stats_headline_h1">
<include src="line.xml"/>
</image>
Хоть мы сейчас и сделаем краткий пересказ самых общераспространенных обьектных, оперантных элементов и элементов свойств, важно отметить, что многие документы могут содержать кастомные элементы свойств. Они маркированы через то, что начинаются с подчеркивания. Для парсера они – загадка, но на них можно ссылаться по имени с помощью операторов и функционала расширителя скриптов, а так же они могут обладать операторами как дочерними элементами, поэтому они хорошо умеют хранить значения и выполнять промежуточные вычисления, чтобы избежать длинных формул, а так же хороши во взаимодействии со скриптами вашего мода.
Снова пример из stats_menu:
<_meter_height> 38 </_meter_height> <!-- линия 26 -->
<height> <copy src="StatsMenu" trait="_meter_height"/> </height> <!-- различные линии -->
3.1 Обьектные Элементы
<rect> определяет прямоугольную область на экране. Сам по себе элемент невидим, но его свойства влияют на любые дочерние элементы,, которые он имеет. В примере ниже текстовый элемент, являющийся ребенком к прямоугльному элементу, наследует свойство <systemcolor>.
<rect name="SneakMeter">
<id> &noglow_branch; </id>
<systemcolor>&hudmain;</systemcolor>
<width> 356 </width>
<height> 34 </height>
<text name="sneak_nif">
<width>128</width>
<height>128</height>
<justify>¢er;</justify>
</text>
</rect>
<text> как вы можете представить, определяет текстовую компоненту.
<image> определяет прямоугольную область, которая заполняется текстурой.
<image name="filled_checkbox">
<visible>&false;</visible>
<alpha>0</alpha>
<filename>Interface\Shared\Marker\glow_square_filled_small.dds</filename>
<texatlas> Interface\InterfaceShared.tai </texatlas>
<width>26</width>
<height>26</height>
<x>-20</x>
<y>3</y>
</image>
<nif> определяет анимацию; используется в loading_menu.xml, а так же в полоске дыхания в интерфейсе:
<nif name="BreathMeter">
<filename>Interface\HUD\AirTimer01.NIF</filename>
<animation>Forward</animation>
<visible>&false;</visible>
<alpha>0</alpha>
<width>64</width>
<height>64</height>
<systemcolor>&hudmain;</systemcolor>
</nif>
<menu> - корневой элемент файлов xml, связанных с меню.
<menu name="HUDMainMenu">
<class> &HUDMainMenu; </class>
<stackingtype> &does_not_stack; </stackingtype>
<visible>&false;</visible>
... и т.д.
</menu>
Другие обьектные элементы, встречающиеся в ваниле, включают <hotrect> и <template>. Как обьектный элемент, <template> скорее всего служит той же функции, которую выполняет кастомный элемент свойства: хранит информацию, которая может быть использована снова в любом другом месте. Это имеет смысл. Но в различиях между <rect> и <hotrect> я не уверен (см. stats_menu.xml).
<template name="stats_list_template">
<hotrect name="stats_list_template_item">
<include src="list_box_template.xml"/>
3.2 Элементы Свойств
Элементы свойств или характеристики определяют свойства их родительских элементов. Они могут иметь буквальные значения (числа, строки, сущности в духе &true; или &false;) или состоять из формул, которые используют оперантные элементы.
<width> 450 </width> vs <width> <copy src="screen()" trait="width" />
Если элемент содержит 2 или более свойства того же типа, последний побеждает. Если никакое свойство не присвоено в контексте, где оно должно быть описано, будет использовано стандартное значение, обычно 0 или пустая строка. В игре свойства обновляются, если любое из их свойств меняется в результате калькуляций движка или по команде скрипта. К примеру, изменение свойства с помощью SetUIFloat или SetUIString сразу же обновит все свойства, использующие его.
А) размер и позиция
<x> и <y> определяют позицию родительского элемента на оси x & y на экране
<width> и <height> самоочевидны: широта и высота соответственно.
<locus> булев элемент. Если &true; позиционирование x/y дочернего элемента будет зависеть относительно родительского элемента, если же значение локуса ложно, то позиция дочернего элемента будет абсолютной.
Б) отрисовка (rendering)
<visible> булев элемент, включает или выключает отображение элемента, а также его дочерние элементы. На скрытый элемент нельзя нажать мышью, но можно активировать через скрипт.
<alpha> числовой элемент, определяющий прозрачность родительского элемента, значение установлено в периоде от 0 до 255 (нормальная, полная видимость)
<depth> числовой элемент, определяющий приоритет перекрывающих элементов. Элемент с более высоким значением будет отрисован поверх остальных. Изначально, дочерние элементы имеют более высокую глубину, чем родительские.
<clips> булев элемент, который гарантирует, что если родительское свойство <clipwindow> истино, дочерний элемент будет скрыт, если он находится за пределами размеров родительского элемента.
<systemcolor> берет сущности, обычно &hudmain;, которые определяют цвет главного интерфейса, например пипбоя.
В) клики и наведение мышью
<target> булев элемент, определяющий будет ли элемент (rect или изображение) реагировать на ввод игроком (player input). Если истино, то свойство наведения мышью будет соответствующе установленно движком, и если элемент имеет свойство <id>, клик по нему установит его свойство на «кликнуто» и воспроизведет специфицированный звук нажатия мышью (clicksound).
<clicked> числовой элемент, чье значение меняется на 1 на один фрейм после того, как на элемент кликнули мышью.
<clicksound> числовой элемент, определяющий какой именно звук будет сыгран, когда на элемент кликнули мышью.
<mouseover> другой числовой элемент, который автоматически меняет значение на 1 игровым движком, когда на элемент навели мышью. В противном случае приобретает значение 0.
Г) уникальные свойства элементов-изображений
<filename> определяет путь файла для текстуры, которую будет отображть элемент, находящийся в папке Data\Textures.
<zoom> числовой элемент, определяющий процент масштабирования. Если больше 100, увеличит источник текстуры, если 0, то спрячет изображение. Если -1, то изображение будет изменено таким образом, чтобы втиснуться в ширину и высоту элемента вместо того, чтобы быть обрезанным.
<filewidth>, <fileheight> это элементы только для чтения. Их значение устанавливается игровым движком под актуальные ширину и высоту загруженной текстуры (в пикселях), после того как было применено масштабирование. Если масштабирование равно -1, значение этого элемента равно 0.
<tile> булев элемент, если истинно и широта или высота элемента больше текстуры, 2 или более текстур будут собраны в одном изображении.
<cropx>, <cropy> определяет точку текстуры, которая будет помещена в верхний левый угол изображения. Пример смотрите здесь.
<texatlas> судя по всему ссылается на файл с расширением .tai, который, очевидно, содержит атласную текстуру, файл, хранящий несколько текстур.
Д) уникальные свойства текстовых элементов
<string> содержит текст, который должен быть отображен.
<red>, <green> и <blue> числовые элементы, которые ты можешь исопльзовать чтобы определить цвет текста. Ранжируется от 0 до 255 (также работает на изображениях!)
<font> числовой элемент, определяющий, какой шрифт следует использовать в соответствии со шрифтами, перечисленными в ini-файле игры. Если он отсутствует или недействителен, по умолчанию будет использоваться шрифт 1.
<justify> элемент, определяющий, где будет начинаться текст относительно х-кординаты элемента. Значения "&left;", "¢er;" и "&right;".
<wrapwidth> обозначает максимальную широту элемента в пикселях. Если текст превышает эту широту, он раздваивается в несколько множественных линий.
<wraplines> максимальное число отображаемых линий.
Вот здесь пример более продвинутого использования элементов <IsHTML>, <PageNum> и <PageCount>, которые должны позволить отобразить текст, разделенный на страницы.
Е) уникальные свойства элементов меню
<class> определяет какой класс внутренного меню использовать для обработки ввода и вывода в меню. Значения могут быть числовыми и соответствовать кодам, использованным в MenuMode, но обычно они определены как сущности, соответствующие названиям меню, такие как HUDMainMenu; или &StatsMenu; . Если введенное значение не соответствует классу меню, меню откроется, но не будет обновляться или принимать вводные значения.
<stackingtype> определяет блокирует ли меню использование других меню или элементов управления игровым процессом, когда оно открыто. Большая часть блокирует такие взаимодействия и маркирована как "&no_click_past;".
<menufade> определяет длительность эффекта постепенного появления/исчезновения (fade in/out) в секундах, когда меню открывается и закрывается. Меню не обновляет и не принимает ввод, когда активен эффект.
Ж) общие и разные свойства
<id> по идее связано с меню тегом <class>, в котором определяет, какой части собственной классовой системы этого меню соответствует элемент с целью обновления и приема ввода игрока. Примеры схемы Обливиона ограничены типами меню, характерными для этой игры, так что не очень ясно как этот элемент работает в контексте меню Фоллаутов.
<user#> : типы элементов, начинающиеся с пользователя, обычно не являются частью схемы XML, но могут распознаваться определенными классами меню. Как и <id>, кажется, что их значение во многом зависит от меню, в котором они находятся. Согласно странице обсуждения, они, похоже, предназначены для использования в качестве своего рода заполнителей (placeholders), а не как ссылки на структуру меню на несколько уровней ниже, что может быть полезным при моддинге.
Другие незатронутые элементы свойств можно найти здесь.
3.3 Оперантные элементы
А) как они работают
Операторы производят конкретные операции на элементах свойств, используя либо буквенные значения...
<rect> <!--обьект-->
<x> <!--свойство-->
<copy> 10 </copy> <!-- оператор: «скопируй» - необходимо, если свойство необходимо в других операциях -->
<add> 5 </add> <!-- оператор -->
</x>
</rect>
...Либо другие свойства как их второй операнд, с атрибутами 'src' и 'trait' в само-закрывающемся теге:
<rect name="thiselement">
<x>
<copy> 10 </copy>
<add src="anotherelement" trait="y" />
</x>
</rect>
Множественные операции возможны на одном и том же свойстве, и они осуществляются сверху вниз. Если необходим другой порядок их активации, их можно вложить друг в друга:
<rect name="thiselement">
<x>
<copy> 10 </copy>
<add src="anotherelement" trait="y" />
<mul> 2 </mul>
<add>
<copy> 10 </copy>
<div src="anotherelement" trait="x" />
</add>
</x>
</rect>
Б) возможные значения 'src'
Наиболее прямой способ сослаться на другое свойство, с которым будет работать оператор, заключается во всего навсего создании ему имени.
<rect name="firstrect">
<x> 10 </x>
</rect>
<image name="firstimage">
<x>
<copy src="firstrect" trait="x" />
</x>
</image>
Однако присутствуют и «относительные» способы ссылаться на другие свойства, используя значения 'src', которые включают me, parent, sibling(name), и child(name):
<y> <copy src="me()" trait="x" /> </y>
Устанавливает позицию у (игрек) у элемента такой же как его позиция х (икс).
<x>
<copy src="parent()" trait="x" />
</x>
Устанавливает позицию х (икс) у элемента такой же как у его родителя.
<width>
<copy src="sibling(siblingsname)" trait="width" />
</width>
Устанавливает ширину элемента такой же как у конкретного сиблинга. Если имя сиблинга опущено, то в таком случае по дефолту значение берется у сиблинга, созданного прямо перед оператором.
Дочерний элемент следует тому же синтаксису: вам нужно указать имя, но он также найдет еще более нижние элементы и далее.
Если множество дочерних элементов, «внучатых» элементов и тд. существуют с тем же именем, будет выбрано последнее записанное в документ xml.
И еще кое-что. Src так же может ссылаться на конкретные глобальные свойства, в большинстве случаев используя "screen()" и "io()". В примере ниже элемент интерфейса позиционируется в крайне правой части экрана, устанавливая его позицию х (икс) к ширине «screen()", вычитая из неё элемент своей собственной ширины:
<image name="thisimage">
<width> 300 </width>
<x>
<copy src="screen()" trait="width" />
<sub src="me()" trait="width" />
</x>
</image>
io() возможно аббревиатура для ввода\вывода (input/output), так как, судя по всему, служит как средство коммуникации между xml-парсером и скриптами игры. Свойства, скопированные из io, могут быть установлены через скрипт с помощью SetUIFloat и SetUIString. Довольно важный элемент, если вы планируется включать элементы или позиционировать их с помощью скрипта и, возможно, настроек МСМ (для Фоллаута).
<visible>
<copy src="io()" trait="_MyModVisible" />
</visible>
Этот скрипт означает, что элемент интерфейса, описываемый этим элементом, будет виден, если кастомное свойство "_MyModVisible"будет включено с помощью функции SetUIFloat в скрипте:
SetUIFloat "HudMainMenu\_MyModVisible" 1
Г) общие операторы
Математические операторы:
<add>, <sub>, <mul> and <div> должны быть самоочевидными. Также доступны <mod> (модуль) и <rand> (случайное число между 0 и содержимым <rand> </rand>).
Логические операторы, все возвращающие сущности со значением &true; (то есть число 2), или &false; (1, или любое другое число):
<onlyif> и <onlyifnot> элементы условия перед ними.
<systemcolor>
<copy> &hudmain; </copy>
<onlyifnot>
<copy src="io()" trait="_MyModAltColorTrait" />
<eq> 1 </eq> <!-- superfluous, really -->
</onlyif>
</systemcolor>
<and>, <or>, и <not> довольно очевидны.
Когда доходит до сравнения чисел и значений, у нас есть <lt> (меньше чем), <lte> (меньше чем или равно), <gt> (больше чем), <gte>, <eq> (равно), and <neq> (не равно).
<min> и <max> устанавливает значение элемента свойста, соответственно минимуму или максимуму самого себя и тому, что находится между тегом.
<x>
<copy src="parent()" trait="x" />
<min> 200 </min> <!—будет 200 или меньше -->
</x>
4. Выводы
Всё это должно было быть гайдом о том, как читать связанные с интерфейсом xml файлы. Даже понимая синтаксис xml и большую часть вокабуляра Бесезды, все равно требуется стальное упорство для того, чтобы понять более сложные меню, но по крайней мере этот гайд даст базу. Чтение – очевидное условие для любого желающего написать своё, а написание станет уже темой другого туториала.
Пс. Замечания переводчика
Я придерживался стиля и разметки оригинала, а в скобках оставлял оригинальные термины на случай, если мой перевод не отображает общепринятых в IT переводов данных понятий или пассажей.
ППС. Вообще слово Bethesda обозначает...Вифезду, город в Иудее, который посещал Господь Иисус Христос в эпизоде с исцелением расслабленного. Возникает оторопь: зачем надо было брать это название, если суть всех игр компании ничего общего не имеет с иерофаническим смыслом, закрепившемся за этим названием города?
Комментарии