Перейти к содержанию
  • Переводчик: ArtSH
    Автор оригинала: DoctaSax

    Этот гайд сфокусируется на создании HUD элемента в моде, копирующем механики "Primary Needs", как в oHUD и IMCNNV.

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

    Чтобы научиться читать синтакисис XML, смотрите предыдущую статью.

    Данный гайд предназначен для Fallout 3\New Vegas, но принцип работает и для других игр Бесезды, включая TES IV: Oblivion.

    Введение

    Идя от простого к сложному, добавляя все более сложные функции, вы научитесь отображать наши переменные, как текстом, в виде процентов на экране, так и визуальным хелсбаром (visual bar).
     
    По мере обучения вы узнаете, как добавлять элементы интерфейса в UIO, отправляя информацию из ГЕКК-скрипта в XML, и создадите скрипт, позволяющий игрокам вручную изменять положение нашего элемента интерфейса на экране.

    1. ПЕРвые шаги: немного текста

    1.1. Цель: HUD для Гигиены (Hygiene.esp)

    Давайте предположим, что у вас есть мод "Гигиена", который отслеживает уровень гигиены игрока во время игры. Ну, я уже написал этот мод, чтобы было нагляднее.

    Вот главный скрипт, высчитывающий значение, которое я хочу отобразить на экране:

    post-18170-0-55583400-1445094355.webp

    В старых добрых фоллаутовских традициях мы зовем эту характеристику «Гигиена», но чем выше ее значение, тем хуже "гигиена" игрока. То же характерно и для характеристики «Вода», маркирующей "Жажду" при верхних значениях. Вот и наша полоска гигиены будет становиться красной при достижении верхней отметки.

    Скрипт выше просто суммирует значение с течением времени, в зависимости от того, что делает игрок и сколько времени ему нужно, чтобы достичь 100 пунктов по дефолту, — все эти переменные можно будет настроить в MCM.

    То есть на экране я отображу просто процент и небольшой текст, поясняющий, что этот процент означает. Например: HYG: 10%

    1.2. Привязываем к UIO

    Начнем. Сперва, если вы не знали, когда дело доходит до добавления элемента интерфейса, нельзя просто положить новый файл в некую папку. Существует только один файл XML, который отвечает за интерфейс игрока: hud_main_menu.xml, расположенный по адресу menus\main.

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

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

    post-18170-0-42176800-1445094625.webp

    Мы создаем файл XML по адресу menus\prefabs.

    Создаем текстовый файл в папке uio\public, оповещающий UIO, что мы загрузили новый XML, который следует добавить в интерфейс. Впишем в файл следующий код:

    Цитата:

    DSHyg\DSHygHUD.xml::HUDMainMenu

    true

    1.3. Добавляем Текстовые Элементы

    Разобравшись с этим, открываем XML файл. Теперь разобьем "HYG: 10%» на три части: лейбл (label), значение (value) и знак процента. В этой строке лейбл и знак процента – части текста, которые не будут меняться, а вот значение будет обновляться из скрипта мода.

    К тому же самое время сгруппировать эти три элемента в один, поскольку в последующих главах мы слегка двинемся на теме изменения их положения и подключения к другим модам интерфейса. И мы всё еще нуждаемся в корневом элементе, содержащем всё, что будет в нашем XML. Общая структура файла на данный момент будет следующей: корень с нашим HUD в виде сгруппированных элементов в нем.

    Цитата:

    <rect name="DSHygHUDROOT">

        <rect name="DSHygOwnHUD" >

            <text name="DSHygLabel"> ; Лейбл

            </text>

            

            <text name="DSHygPercentSign"> ; знак процента

            </text>

            

            <text name="DSHygValueDisplay"> ; значение переменной "Гигиена"

            </text>

        </rect>

    </rect>

    Если вы прочли гайд по чтению XML, то уже заметили, что эта главная структура содержит лишь объектные элементы, добавив 3 текстовых компонента, сгруппированных под одним невидимым прямоугольником. Только отсутствует текст, который они должны отображать, и неясно положение на экране, где они будут отображаться. Так что, добавим свойства и сперва введем строки (strings).

    Цитата:

         <text name="DSHygLabel">

                <string>HYG:</string>

            </text>

            <text name="DSHygPercentSign">

                <string>%</string>

            </text>  

    Нединамические строки ставятся между тегами <string>. Значение, которое мы хотим отобразить, будет вызвано главным скриптом из esp, где будет такая строчка кода:

    Цитата:

        SetUIFloat "HUDMainMenu\_DSHygValue" fHyg

    Она присваивает значению пользовательское свойство элемента, _DSHygValue. Нам остается только сделать так, чтобы в XML файле код считывал это свойство из элемента, используя оператор <copy> с "io()" src атрибутом:

    Цитата:

     <text name="DSHygValueDisplay">

                <copy src="io()" trait="_DSHygValue"/>

            </text>

    2. Местоположение

    Теперь наш XML выглядит так:

    Цитата:

    <rect name="DSHygHUDROOT">

        <rect name="DSHygOwnHUD" >

            <text name="DSHygLabel">

                   <string>HYG:</string>

            </text>

            

            <text name="DSHygPercentSign">

                    <string>%</string>

            </text>

            

            <text name="DSHygValueDisplay">

                    <copy src="io()" trait="_DSHygValue"/>

            </text>

        </rect>

    </rect>

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

    Цитата:

     <rect name="DSHygOwnHUD" >

            <locus> &true; </locus>

            <x>    

                <copy src="screen" trait="width" />

                <sub> 150 </sub>

            </x>

            <y>

                <copy src="screen" trait="height" />

                <div> 2 </div>

            </y>

            <!--наши три текстовых компонента всё еще здесь-->

        </rect>  

    Копируя широту экрана и вычитая (subtracting) некоторые пиксели, я кладу элемент в правую часть экрана и в середину по высоте.

    Поскольку мы собираемся позиционировать наши текстовые элементы, давайте еще включим элемент <locus>, который воспринимает положение дочернего элемента по осям X и Y по отношению к родительскому прямоугольнику, а не по отношению к экрану. Просто мне так удобнее. Со включенным <locus> я выбираю не специфицировать положение лейбла, поэтому  он копирует свое положение по осям родителя. Но я расположу его слева через элемент <justify>:

    Цитата:

     <text name="DSHygLabel">

                <justify> &left; </justify>

                <string> HYG: </string>

            </text>

    Остальные два элемента будут отрисованы справа, поскольку я хочу, чтобы "Значение" было ближе к "Знаку процента", да и к тому же так проще.

    Остальные элементы имеют одну и ту же позицию по оси Y, но я смещаю знак процента вправо (+ 100 X относительно родительского элемента) и делаю то же самое для "Значения", копируя позицию "Знака процента" по оси X: минус ширина самого знака процента:
    Цитата:

        <text name="DSHygPercentSign">

                <x>100</x>

                <justify> &right; </justify>

                <string>%</string>

            </text>

            <text name="DSHygValueDisplay">

                <x>

                    <copy src="sibling(DSHygPercentSign)" trait="x" />

                    <sub src="sibling(DSHygPercentSign)" trait="width" />

                </x>

                <y> <copy src="sibling(DSHygLabel)" trait="y" /> </y>

                <justify> &right; </justify>

                <string> <copy src="io()" trait="_DSHygValue" /> </string>

            </text>

    Вот как это будет выглядеть в игре:

    post-18170-0-58009800-1445095577.webp

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

    3. Включение и выключение видимости

    Хоть мы и добавили наш элемент на экран в Gamemode, он также отображается и в Menumode.

    post-18170-0-32715500-1445179368.webp

    Нам этого не нужно, поэтому необходимо его убрать. Также неплохо бы, чтобы мы не видели такие элементы в других ситуациях и в Gamemode тоже – в бою, например. Решаем проблему тем же способом: включая видимость с помощью пользовательского свойства.

    Помните наш прямоугольник родительского элемента, который группирует три текстовых элемента? Сам по себе он невидим, но мы включим его видимость. Она включает и все три дочерних элемента. Так что давайте сделаем эту видимость зависимой от пользовательского свойства:

    Цитата:

    <rect name="DSHygOwnHUD">

        <visible>

            <copy src="io()" trait="_DSHygOwnHud"

        </visible>

        <locus> &true; </locus>

        <x>    

            <copy src="screen" trait="width" />

            <sub> 150 </sub>

        </x>

        <y>

            <copy src="screen" trait="height" />

            <div> 2 </div>

        </y>

        <!--наши три текстовых компонента всё еще здесь-->

    </rect>

    А в нашем моде сделаем еще один маленький квестовый скрипт, который пока что  будет делать следующее:

    Цитата:

    scn DSHygHUDQstScpt

    Begin GameMode

     

    SetUIFloat "HUDMainMenu\_DSHygOwnHud" 1

     

    End

     

    Begin MenuMode

     

    SetUIFloat "HUDMainMenu\_DSHygOwnHud" 0

     

    End

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

    Цитата:

    Begin GameMode

     

    if playerref.IsInCombat

        SetUIFloat "HUDMainMenu\_DSHygOwnHud" 0

    else

        SetUIFloat "HUDMainMenu\_DSHygOwnHud" 1

    endif

     

    End

    Наконец, нам стоит отключать элемент в тех же ситуациях, когда отключается и ванильный интерфейс игры. В катсценах, к примеру… Обычно это делается привязкой видимости элемента к видимости «ActionPoints» компонентов интерфейса:

    Цитата:

     <rect name="DSHygOwnHUD">

            <visible>

                <copy src="ActionPoints" trait="visible"/>

                <and src="io()" trait="_DSHygOwnHud" />

            </visible>

            <!--всё остальное-->

        </rect>

    (Учитывайте, что моды подобные iHUD, которые при определенных условиях заставляют элементы, типа ActionPoints, визуально исчезать, не трогают видимость этого компонента: он всё равно будет активен. Условия отключения таких компонентов, включают ситуации, где используется функция DisablePlayerControls.)

     

    С другой стороны, мы хотим дать возможность заставить наш элемент интерфейса остаться на экране несмотря на то, что ванильный интерфейс будет выключен. Для этого мы включаем новое пользовательское свойство в esp:

    Цитата:

    <rect name="DSHygOwnHUD">

            <visible>

                <copy src="ActionPoints" trait="visible"/>

                <or src="io()" trait="_DSHygForceHud" />

                <and src="io()" trait="_DSHygOwnHud" />

            </visible>

            <!--всё остальное-->

        </rect>

     

    scn DSHygHUDQstScpt

     

    int iForceHUD

     

    Begin GameMode

     

    SetUIFloat "HUDMainMenu\_DSHygOwnHud" 1

    SetUIFloat "HUDMainMenu\_DSHygForceHud" iForceHUD

     

    End

     

    Begin MenuMode

     

    SetUIFloat "HUDMainMenu\_DSHygOwnHud" 0

     

    End    

    Включить элемент можно посредством скрипта, реагирующего на особые условия, или по желанию игрока через МСМ, связь с которым я собираюсь добавить в наш мод: через чекбокс (checkbox), который будет включать фукнцию iForceHUD.

    4. Позволяем игроку подправлять позицию элементов

    В дополнение к тому, чтобы не переписывать оригинальные файлы интерфейса игры, мы очевидно хотим быть хорошими соседями другим подобным модам. Лучше всего дать игроку опцию самому установить положение нашего элемента интерфейса в случае конфликта. Некоторые моды дают такую опцию в настройках МСМ в Menumode, а мы сделаем её в Gamemode.

    4.1. Добавляем Пользовательские Свойства для X и Y

    На самом деле нам повезло, ведь мы сгруппировали наши компоненты под одним прямоугольником, так что воздействовать нужно только на один элемент. Ну, конечно, всё это было задумано с самого начала! А как вы хотели? Мы добавим новые пользовательские свойства к существующим осям X и Y нашего прямоугольника, которые назовем _DSHygX и _DSHygY:

    Цитата:

    <rect name="DSHygOwnHud">

        <!--visibility intel here>

        <locus> &true; </locus>

        <x>

           <copy src="screen" trait="width" />

           <sub> 150 </sub>

           <add src="io()" trait="_DSHygX" />

        </x>

        <y>

            <copy src="screen" trait="height" />

            <div> 2 </div>

            <add src="io()" trait="_DSHygY" />

        </y>

        <!-- всё остальное-->

    </rect>

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

    Цитата:

    scn DSHygHudQstScpt

     

    int iForceHud

    float fOffsetX

    float fOffsetY

     

    Begin GameMode

     

    SetUIFloat "HUDMainMenu\_DSHygOwnHud" 1

    SetUIFloat "HUDMainMenu\_DSHygForceHud" iForceHud

    SetUIFloat "HudMainMenu\_DSHygX" fOffsetX

    SetUIFloat "HudMainMenu\_DSHygY" fOffsetY

     

    End

     

    Begin MenuMode

     

    SetUIFloat "HUDMainMenu\_DSHygOwnHud" 0

     

    End

    Чтобы адекватно распознавать нажатие клавиш нужен быстрый скрипт, так что сделаем его отдельным от остальных, включим его через MCM и выключим, когда закончим.

    4.2. Определяем и Храним Нажатые Клавиши

    Есть два пути достижения нашей цели.
     
    Перый – написать скрипт, который позволит игроку включать и выключать опцию через МСМ, но сам по себе он может наслаиваться на другие моды, так что понадобятся дополнительные манипуляции с его положением на экране.
     
    Другой – сделать это в Gamemode. Проблема в том, что большинство клавиш уже зарезервированно, включая клавиши движения игрока, которые хотелось бы использовать. Однако второй путь все равно предпочтительнее – он вполне осуществим, если на время настройки ограничить передвижение игрока.

    Сначала весь квестовый скрипт надо включить через МСМ. Вот один из способов сделать это:

    Цитата:

    ; 'reset' block блок "ресета"

                    SetUIFloat "StartMenu/MCM/*:1/*:9/_enable" 1

                    SetUIString "StartMenu/MCM/*:1/*:9/_title" "Configure HUD position"

                    SetUIFloat "StartMenu/MCM/*:1/*:9/_type" 5

                    SetUIFloat "StartMenu/MCM/*:1/*:9/_value" DSHygHud.iEditing  

                   ; 'default' block блок "по умолчанию"

                   set DSHygHud.iEditing to 0

                   ; 'new value' block

                        ; other stuff "другое"

                     elseif iOption == 9

                        set DSHygHud.iEditing to fValue

                    endif

                    ; 'mouseover' block блок "наведение мышью"

                    elseif iMouseover == 9

                    SetUIString "StartMenu/MCM/*:9/string" "Возвращайтесь в gamemode чтобы начать менять положение HUD"

                    endif

    в моем квестовом скрипте DSHygHud :

    Цитата:

    int iEditing

    if iEditing

        set DSHygHudConfig.iStage to 0

        startquest DSHygHudConfig

        if GetQuestRunning DSHygHudConfig

            set iEditing to 0

        endif    

    endif

    Так как мы хотим позволить игроку менять положение с помощью клавиш движения, нам нужно:

    - ограничить движение игрока, чтобы он не крутился, пока мы меняем положение элемента интерфейса
    - сканировать нажатые клавиши и хранить их как смещения (offsets) в локальных переменных от обычных положений элементов нашего интерфейса в квестовом скрипте
    - прокинуть эти смещения в XML, где наши кастомные свойства _DSHygX и _DSHygY прочтут их.

     

    Вот этот скрипт сделает всё это. Смотрите.

    На стадии 0 он останавливает игрока и заставляет интерфейс включиться, если тот был выключен по какой-то причине.

    Стадия 1 определяет нажаты ли клавиши и вносит изменения в соответствии с ними, повышая или понижая положение нашего элемента интерфейса, которые мы передаем в пользовательские свойства _DSHygX и DSHygY в нашем XML.

    Стадия 100 останавливает весь процесс (cleans up), освобождает игрока и останавливает скрипт внесения изменений.

    Цитата:

    scn DSHygHudConfigQstScpt

     

    int iGap

    int iStage

    int iWasForced

     

    Begin GameMode

     

    if playerref.IsInCombat

        set iStage to 100

    endif

    if iStage == 0

        if Playerref.GetRestrained

            set iStage to 1

            SetUIFloat "HUDMainMenu\_DSHygOwnHud" 1

            if 0 == GetQuestRunning DSHygHUD

                startquest DSHygHUD

            endif

            if DSHygHUD.iForceHUD == 0

                set DSHygHUD.iForceHud to 1

                set iWasForced to 1

            endif

        else

            playerref.SetRestrained 1

            return

        endif

    endif

    if iStage == 1

        ; how much to move  шаг сдвига элемента интерфейса

        if IsControlPressed 9 ; 'run' control    клавиша бега

            set iGap to 50

        else

            set iGap to 5

        endif

        ; forward = up вверх

        if IsControlPressed 0

            let DSHygHUD.fOffSetY -= iGap

        ; backward = down    вниз 

        elseif IsControlPressed 1

            let DSHygHud.fOffSetY += iGap

        endif

        SetUIFloat "HUDMainMenu\_DSHygY" DSHygHud.fOffsetY

        ; left = left лево

        if IsControlPressed 2

            let DSHygHUD.fOffSetX -= iGap

        ; right = right право

        elseif IsControlPressed 3

            let DSHygHUD.fOffSetX += iGap

        endif

        SetUIFloat "HUDMainMenu\_DSHygX" DSHygHud.fOffsetX

        ; activate = save and exit активация = сохранить и выйти

        if IsControlPressed 5

            set iStage to 100

        ; jump = restore defaults - прыжок = восстановить "по умолчанию"

        elseif IsControlPressed 12

            set DSHygHUD.fOffSetX to 0

            set DSHygHUD.fOffSetY to 0

            set iStage to 0

        endif

    endif

    ; quit выход

    if iStage == 100

        if iWasForced

            set DSHygHud.iForceHud to 0

        endif

        SetUIFloat "HUDMainMenu\_DSHygOwnHud" 0

        if playerref.GetRestrained

            playerref.SetRestrained 0

        else

            stopquest DSHygHudConfig

        endif

    endif

     

    End

    4.3. Показываем текстовые подсказки на экране

    Но кое-что мы пропустили: удобство игрока. Откуда игрок узнает, какими клавишами ему менять положение элемента в настройках? Хорошо бы прописать это в README, но их же никто не читает! Так что давайте прямо в игре скажем о кнопках, добавив текста в наш XML.

    Сперва создадим новый прямоугольник, сиблинг по отношению к DSHygOwnHUD. Это сгруппирует наши новые текстовые элементы и включит видимость их всех с помощью пользовательского свойства:

    Цитата:

    <rect name="DSHygPositionTips">

            <visible>

                <copy src="io()" trait="_DSHygShowHints" />

            </visible>

            

            <text name="DSHygTipsMove">

            </text>

            

            <text name="DSHygTipsActivate">

            </text>

            

            <text name="DSHygTipsJump">

            </text>

            

            <text name="DSHygTipsRun">

            </text>

     

            <!-- добавим еще одну клавишу для проверки: клавиша 'тащить' будет включать подсказки -->

            <text name="DSHygTipsGrab"> 

            </text>

        </rect>

    Мы добавим немного кода, чтобы включать подсказки, когда зажата кнопка в нашем скрипте HudConfig:

    Цитата:

    if iStage == 1

        ; остальное   

    elseif IsControlPressed 27

            if 0 == IsPressed

                set isPressed to 1

                if 0 == GetUIFloat "HUDMainMenu\_DSHygShowHints"

                    SetUIFloat "HUDMainMenu\_DSHygShowHints" 1

                else

                    SetUIFloat "HUDMainMenu\_DSHygShowHints" 0

                endif

            endif

        else

            set isPressed to 0

        endif

    Нам нужно пару текстовых элементов, чтобы описать какие клавиши нажимать. Так как игрок уже знает как у него назначены клавиши движения, их можно записать в виде статичной строки в XML:

    Цитата:

       <text name="DSHygTipsMove">

            <string> Используй клавиши движения для изменения положения HUD </string>

        </text>

    Создадим и другие строки в нашем квестовом скрипте для настройки интерфейса (HUD config), используя GetControl чтобы выявить какие клавиши каким элементам управления принадлежат, прокинем их через SetUIStringEx и через спецификатор формата %k, а затем включим видимость всего блока по умолчанию:

    Цитата:

    if iStage == 0

        if playerref.GetRestrained

             ; остальное

            set iKeyCode to GetControl 5

            SetUIStringEX "HUDMainMenu\_DSHygTipActivate" "Нажми ACTIVATE (%k) чтобы сохранить значения и выйти" iKeyCode

            set iKeyCode to GetControl 12

            SetUIStringEX "HUDMainMenu\_DSHygTipJump" "Нажми JUMP (%k) чтобы восстановить значения по умолчанию" iKeyCode

            set iKeyCode to GetControl 27

            SetUIStringEx "HUDMainMenu\_DSHygTipGrab" "Нажми GRAB (%k) чтобы включить данные подсказки" iKeyCode

            set iKeyCode to GetControl 9

            SetUIStringEX "HUDMainMenu\_DSHygTipRun" "Удерживай RUN (%k) чтобы увеличить шаг смещения элемента" iKeyCode

     

            SetUIFloat "HUDMainMenu\_DSHygShowHints" 1

            ; остальное

    И скопируем их в наш XML, поместив их друг под друга где-то посередине экрана:

    Цитата:

     

    <rect name="DSHygPositionTips">

            <visible>

                <copy src="io()" trait="_DSHygShowHints" />

            </visible>

            <locus> &true; </locus>

            <x>

                <copy src="screen()" trait="width"/>

                <div> 2 </div>

            </x>

            <y> 400 </y>

            

            <text name="DSHygTipsMove">

                <string> Use the movement controls to move the HUD. </string>

            </text>

            

            <text name="DSHygTipsActivate">

                <y> 25 </y>

                <string>

                    <copy src="io()" trait="_DSHygTipActivate" />

                </string>

            </text>

            

            <text name="DSHygTipsJump">

                <y> 50 </y>

                <string>

                    <copy src="io()" trait="_DSHygTipJump" />

                </string>

            </text>

            

            <text name="DSHygTipsRun">

                <y> 75 </y>

                <string>

                    <copy src="io()" trait="_DSHygTipRun" />

                </string>

            </text>

            

            <text name="DSHygTipsGrab">

                <y> 100 </y>

                <string>

                    <copy src="io()" trait="_DSHygTipGrab" />

                </string>

            </text>

        </rect>

    Получаем такой результат:

    post-18170-0-14889200-1445439773.webp

    5. ИнтермеЦЦо

    5.1.  Подправляем Шрифты

    Не удивлюсь, если стандартный шрифт покажется слишком маленьким, и вам захотелось интерфейс покрупнее. И вполне возможно, что в пику мне вы не используете мод Darn's UI, который делает шрифты меньше, да и я не проверял, адекватно ли помещены на экране наши элементы интерфейса с учетом использования оригинальных шрифтов.

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

    На счастье, шрифты пронумерованы от 1 до 8, независимо от того, перезаписаны они или нет, так что нам просто нужно в XML добавить их как условия каждому текстовому компоненту, которое мы скопируем из мода, используя еще одно пользовательское свойство:

    Цитата:

    <font> <copy src="io()" trait="_DSHYGFont" /> </font>

    В таком случае простое решение – прокинуть это в наш квестовый скрипт интерфейса, привязав к переменной:

    Цитата:

    SetUIFloat "HUDMainMenu\_DSHygFont" iFont

    И позволив игрокам установить значение переменной через МСМ.

    Другая опция – добавить этот кусок кода в наш скрипт настройки позиционирования, чтобы игроки переключали его в реальном времени:

    Цитата:

     ; crouch key = change font size  клавиша красться = смена размеров щрифта

        elseif IsControlPressed 8    

            if 0 == IsPressed

                let isPressed := 1

                if DSHygHUD.iFont < 8

                    let DSHygHud.iFont += 1

                else

                    let DSHygHud.iFont := 1

                endif

            endif

    и создав следующую строку:

    Цитата:

     set iKeyCode to GetControl 8

            SETUIStringEX "HUDMainMenu\_DSHygTipCrouch" "Нажми клавишу вызова Pipboy (%k) чтобы поменять размер шрифта" iKeycode

    ; Я так понял, у автора ошибка, тк в остальном используется клавиша "красться"

    И вложив это в наш XML:

    Цитата:

     <text name="DSHygTipsCrouch">

            <y> 100 </y>

            <string>

                <copy src="io()" trait="_DSHygTipCrouch" />

            </string>

        </text>  

    post-18170-0-14790400-1445869961.webp

    post-18170-0-00544600-1445869976.webp

    5.2. Подправляем Непрозрачность

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

    И сделаем слайдер МСМ – это наиболее разумный выход. Непрозрачность варьируется от 0 до 255, так что этот диапазон будет и на нашем слайдере МСМ, привязанный к еще одной переменной, которую мы прокинем из нашего квестового скрипта в наш XML.

    В нашем XML:

    Цитата:

    <alpha>

        <copy src="io()" trait="_DSHygAlpha" />

    </alpha>

    В главном квестовом скрипте:

    Цитата:

    SetUIFloat "HUDMainMenu\_DSHygAlpha" iAlpha

    С такими положениями в нашем меню МСМ:

    Цитата:

    ; блок ресета

        SetUIFloat "StartMenu/MCM/*:1/*:10/_enable" 1

        SetUIString "StartMenu/MCM/*:1/*:10/_title" "Set HUD opacity"

        SetUIFloat "StartMenu/MCM/*:1/*:10/_type" 2

        SetUIFloat "StartMenu/MCM/*:1/*:10/_value" DSHygHud.iAlpha

    ; блок по умолчанию

        set DSHygHud.iAlpha to 255

    ; блок новых значений переменной

        elseif iOption == 10

            set DSHygHud.iAlpha to fValue

    ; блок отображения размера визуального элемента

        elseif iOption == 10

            SetUIFloat "StartMenu/MCM/_Value" DSHygHud.iAlpha

            SETUIFloat "StartMenu/MCM/_ValueDecimal" 3

            SetUIFloat "StartMenu/MCM/_ValueIncrement" 5

            SetUIFLoat "StartMenu/MCM/_ValueMax" 255

            SetUIFloat "StartMenu/MCM/_ValueMin" 0

            SetUIString "StartMenu/MCM/*:2/_title" "Hud opacity"

    ; блок размера по умолчанию

        elseif iOption == 10

            SetUIFloat "StartMenu/MCM/_Value" 255

    5.3. Подправляем цвет

    В Нью Вегасе царит приятный и щегольской янтарь (amber), но возможно наши игроки предпочтут яркий и болезненный зеленый (green), а, может, ярко розовый. А кто мы такие чтобы им запрещать, верно?

    У нас же есть  <red>, <green> и <blue> элементы свойств и четыре стандартных цвета интерфейса, соответствующие данным значениям RGB:

    amber - 255, 182, 66
    blue - 46, 207, 255
    green - 26, 255, 128
    white - 197, 255, 255

     Что-то подсказывает, что мы можем создать «список» в МСМ, который будет хранить эти имена, а так же пользовательскую опцию, привязанную к переменной в нашем квестовом скрипте:

    Цитата:

    int iColorOption

    int iRed

    int iGreen

    int iBlue

     

    if iColorOption == 1 ; amber

        SetUIFloat "HUDMainMenu\_DSHygRed" 255

        SetUIFloat "HUDMainMenu\_DSHygGreen" 182

        SetUIFloat "HUDMainMenu\_DSHygBlue" 66

    elseif iColorOption == 2 ; blue values go under here

    elseif iColorOption == 3 ; green values here

    elseif iColorOption == 4 ; white values here

    elseif iColorOption == 5 ; custom

        SetUIFloat "HUDMainMenu\_DSHygRed" iRed

        SetUIFloat "HUDMainMenu\_DSHygGreen" iGreen

        SetUIFloat "HUDMainMenu\_DSHygBlue" iBlue    

    Endif

    В нашем XML мы отделяем текстовые элементы от их обычной цветовой схемы, мы не хотим использовать регулярную схему цветов. Поэтому добавим <red>, <green> и <blue> элементы свойств каждому текстовому элементу:

    Цитата:

        <systemcolor> &nosystemcolor; </systemcolor>

        <red> <copy src="io()" trait="_DSHygRed" /> </red>

        <green> <copy src="io()" trait="_DSHygGreen" /> </green>

        <blue> <copy src="io()" trait="_DSHygBlue" /> </blue>

    А МСМ расширим следующим списком:

    Цитата:

    ; reset block

        SetUIFloat "StartMenu/MCM/*:1/*:11/_enable" 1

        SetUIString "StartMenu/MCM/*:1/*:11/_title" "Color options"

        SetUIFloat "StartMenu/MCM/*:1/*:11/_type" 1

        SetUIFloat "StartMenu/MCM/*:1/*:11/_value" DSHygHud.iColorOption

        if DSHygHud.iColorOption == 1

            SetUIString "StartMenu/MCM/*:1/*:11/value/*:1/string" "Amber"

        elseif DSHygHud.iColorOption == 2

            SetUIString "StartMenu/MCM/*:1/*:11/value/*:1/string" "Blue"

        elseif DSHygHud.iColorOption == 3

            SetUIString "StartMenu/MCM/*:1/*:11/value/*:1/string" "Green"

        elseif DSHygHud.iColorOption == 4

            SetUIString "StartMenu/MCM/*:1/*:11/value/*:1/string" "White"

        elseif DSHygHud.iColorOption == 5

            SetUIString "StartMenu/MCM/*:1/*:11/value/*:1/string" "Custom"

        endif

    ; default block    

        set DSHygHud.iColorOption to 1

    ; new value block

        elseif iOption == 11

            set DSHygHud.iColorOption to fValue

    ; list block блок списка

        elseif iOption == 11

            SetUIString "StartMenu/MCM/*:3/_title" "Color option"

            SetUIFloat "StartMenu/MCM/*:3/*:1/_enable" 1

            SetUIFloat "StartMenu/MCM/*:3/*:2/_enable" 1

            SetUIFloat "StartMenu/MCM/*:3/*:3/_enable" 1

            SetUIFloat "StartMenu/MCM/*:3/*:4/_enable" 1

            SetUIFloat "StartMenu/MCM/*:3/*:5/_enable" 1

            SetUIString "StartMenu/MCM/*:3/*:1/text/string" "Amber"

            SetUIString "StartMenu/MCM/*:3/*:2/text/string" "Blue"

            SetUIString "StartMenu/MCM/*:3/*:3/text/string" "Green"

            SetUIString "StartMenu/MCM/*:3/*:4/text/string" "White"

            SetUIString "StartMenu/MCM/*:3/*:5/text/string" "Custom"

    Который подарит мне болезненный зеленый оттенок, если выберу его в игре:

    post-18170-0-20371700-1445870238.webp

    post-18170-0-49985000-1445870253.webp

    А зачем, собственно,  останавливаться на этом? Можно добавить добавить в МСМ удобную опцию type-9 чтобы позволить людям вводить любое значение RGB, которое им нравится. Ну а когда еще вы этим воспользуетесь?

    Внимательнее к документации МСМ, так как она содержит ошибки, где говорят пользоваться SetUIStringEX там, где надо использовать SetUIFloat. Сделаем эту опцию зависимой от нашей пользовательской опции в списке:

    Цитата:

    ; reset block

        if DSHygHud.iColorOption == 5

            SetUIFloat "StartMenu/MCM/*:1/*:12/_enable" 1

        else

            SetUIFloat "StartMenu/MCM/*:1/*:12/_enable" 2

        endif

        SetUIString "StartMenu/MCM/*:1/*:12/_title" "Custom color"

        SetUIFloat "StartMenu/MCM/*:1/*:12/_type" 9

        SetUIFloat "StartMenu/MCM/*:1/*:12/_value1" DSHygHud.iRed

        SetUIFloat "StartMenu/MCM/*:1/*:12/_value2" DSHygHud.iGreen

        SetUIFloat "StartMenu/MCM/*:1/*:12/_value3" DSHygHud.iBlue

    ; default block

        set DSHygHud.iRed to 255

        set DSHygHud.iGreen to 182

        set DSHygHud.iBlue to 66

    ; new value block:

        elseif iOption == 12

            set DSHygHud.iRed to GetUIFloat "StartMenu/MCM/_Value1"

            set DSHygHud.iGreen to GetUIFloat "StartMenu/MCM/_Value2"

            set DSHygHud.iBlue to GetUIFloat "StartMenu/MCM/_Value3"

    ; showscale block:   блок изменения цвета

        elseif iOption == 12

            SetUIFloat "StartMenu/MCM/_Value1" DSHygHud.iRed

            SetUIFloat "StartMenu/MCM/_Value2" DSHygHud.iGreen

            SetUIFloat "StartMenu/MCM/_Value3" DSHygHud.iBlue

            SetUIString "StartMenu/MCM/*:2/_title" "Custom color"

    ; defaultscale block блок значения цвета по умолчанию

        elseif iOption == 12

            SetUIFloat "StartMenu/MCM/_Value1" 255

            SetUIFloat "StartMenu/MCM/_Value2" 182

            SetUIFloat "StartMenu/MCM/_Value3" 66

    Это позволит выбрать клейкий розовый:

    post-18170-0-47009600-1445870344.webp

    post-18170-0-52769100-1445870358.webp

    6. Добавляем измеритель (meter) или полоску (bar)

    Вместо сухих процентов, может, лучше предпочесть полоску, отображающую как мы грязны?
    Давайте снова добавим еще одну опцию в МСМ:

    Цитата:

    ; reset block

        SetUIFloat "StartMenu/MCM/*:1/*:13/_enable" 1

        SetUIString "StartMenu/MCM/*:1/*:13/_title" "Use a bar"

        SetUIFloat "StartMenu/MCM/*:1/*:13/_type" 5

        SetUIFloat "StartMenu/MCM/*:1/*:13/_value" DSHygHud.iUseBar

    ; new value

        elseif iOption == 13

            set DSHygHud.iUseBar to fValue

    и прокинем её в XML с другим пользовательским свойством:

    Цитата:

    if iUseBar

        SetUIFloat "HUDMainMenu\_DSHygBar" 1

    else

        SetUIFloat "HUDMainMenu\_DSHygBar" 0

    endif

    а после нужно остановить отображение "знака процента", если включена полоска:

    Цитата:

     <text name="DSHygPercentSign">

                <!--остальные вещи-->

                <visible>

                    <copy src="io()" trait="_DSHygValue" />

                    <gt>0</gt>

                    <and>

                        <copy src="io()" trait="_DSHygBar" />

                        <eq>0</eq>

                    </and>

                </visible>

                <!--остальные вещи-->

            </text>

            <!-- also do the same for DSHygValueDisplay -->

    Затем добавляем 2 элемента изображения (image elements) под тем же прямоугольником, где мы поместили наши текстовые компоненты, один для задника, другой для его заполнения:

    Цитата:

    <rect name="DSHygOwnHUD">

        <!-- the other stuff-->

        <image name="DSHygBarBack">

        </image>

        

        <image name="DSHygBar">

        </image>

    </rect>

    Их видимость должна зависеть от опции «use bars». Мы будем использовать простую сплошную текстуру из игры для заполнения полоски:

    Цитата:

        <image name="DSHygBarBack">

            <visible>

                <copy src="io()" trait="_DSHygBar" />

            </visible>

            <width>100</width>

            <height>15</height>

            <depth>2</depth>

            <y>20</y>

            <filename>Interface\Shared\solid.dds</filename>

        </image>

    Вы могли заметить, что задник имеет максимальную ширину в 100 пикселей. (Можно и больше, но тогда значение гигиены надо тоже конвертировать.) Изображение, которое будет заполняться, DSHygBar, усвоит её ширину из значения fHyg из нашего квестового скрипта:

    Цитата:

    if iUseBar

        SetUIFloat "HUDMainMenu\_DSHygBar" 1

        SetUIFloat "HUDMainMenu\_DSHygBarWidth" DSHyg.fHyg

    else

        SetUIFloat "HUDMainMenu\_DSHygBar" 0

    endif

    <image name="DSHygBar"

        <visible>

            <copy src="io()" trait="_DSHygBar" />

        </visible>

        <width>

            <copy src="io()" trait="_DSHygBarWidth" />

        </width>

        <height>15</height>

        <depth>3</depth>

        <y>20</y>

        <filename>Interface\Shared\solid.dds</filename>        

    </image>  

    Чтобы обеспечить контраст, я отключу обычный цвет интерфейса на фоне и нарисую его серым, устанавливая его непрозрачность на половину того значения непрозрачности, которое мы ранее подключили к переменной. Также обратите внимание, что фактически полоса имеет более высокое значение <depth >, чем значение фона, поэтому убедитесь, что она нарисована поверх нее.

    Цитата:

     

     <image name="DSHygBarBack">

            <visible>

                <copy src="io()" trait="_DSHygBar" />

            </visible>

            <width>100</width>

            <height>15</height>

            <depth>2</depth>

            <y>20</y>

            <filename>Interface\Shared\solid.dds</filename>

            <systemcolor>&nosystemcolor;</systemcolor>

            <red>190</red>

            <green>190</green>

            <blue>190</blue>

            <alpha>

                <copy src="io()" trait="_DSHygAlpha" />

                <div>2</div>

            </alpha>

        </image>

    Что даст нам следующий результат:

    post-18170-0-14690500-1446213015.webp

    А давайте продолжим! Коль скоро мы знаем как менять цвет текста, давайте дадим возможность менять цвет полоски в те же цвета, которые мы выбрали для текста:

    Цитата:

    <image name="DSHygBar">

        <!-- остальные вещи-->

        <systemcolor>&nosystemcolor;</systemcolor>

        <red> <copy src="io()" trait="_DSHygRed" /> </red>

        <green> <copy src="io()" trait="_DSHygGreen" /> </green>

        <blue> <copy src="io()" trait="_DSHygBlue" /> </blue>

        <!-- остальные вещи-->

    </image>

     

    post-18170-0-44219400-1446213089.webp

    А еще, почему бы не залить полоску красным, когда уровень гигиены пересекает порог в 90 пунктов?

    Цитата:

     <image name="DSHygBarOverride">

            <visible>

                <copy src="io()" trait="_DSHygBar" />

                <and>

                    <copy src="io()" trait="_DSHygBarWidth" />

                    <gt>90</gt>

                </and>

            </visible>

            <width>

                <copy src="io()" trait="_DSHygBarWidth" />

            </width>

            <height>15</height>

            <depth>4</depth>

            <y>20</y>

            <systemcolor>&nosystemcolor;</systemcolor>

            <red>255</255>

            <green>0</green>

            <blue>0</blue>

            <alpha> 255 </alpha>            

            <filename>Interface\Shared\solid.dds</filename>        

        </image>

    post-18170-0-48626100-1446213195.webp

    Ну а теперь-то вы уже сможете догадаться, как сделать его еще и ярким, используя блок видимости или другое пользовательское свойство элемента, которое можно настраивать в МСМ?

    Заключение, титры

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

    Я был и остаюьс сторонником прямолинейного подхода, когда дело касается пользовательских свойств в подобных модах. Я же не эксперт во всем этом деле, и мне намного ближе скрипты из ГЕККа, чем XML от Бесезды.

    В заключение, скажу лишь, что в случае с такими скриптами всегда следует читать то, что написано до вас. Я основывался на ванильных скриптах игры, на том, что написано в Wiki Обливиона, модах Gopher’а, Impoftheperverse’а и JIP’а, чтобы затем на маленьком моде, который Fallout2AM однажды сделал для меня, обьяснить вам базовые вещи.

    от переводчика

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


    Обратная связь

    Рекомендуемые комментарии

    Lord RZ

    Опубликовано

    Спасибо за твой труд.

    • Нравится 1
    ArtemSH

    Опубликовано

    Lord RZ, благодарю!

    • Нравится 1
    ArtemSH

    Опубликовано

    12.03.25

    В этой статье был надмозговый перевод. Я исправил и переписал почти всю статью.
    Теперь читаемость должна быть намного лучше.

    • Нравится 2


    Для публикации сообщений создайте учётную запись или авторизуйтесь

    Вы должны быть пользователем, чтобы оставить комментарий

    Создать аккаунт

    Зарегистрируйте новый аккаунт в нашем сообществе. Это очень просто!

    Регистрация нового пользователя

    Войти

    Уже есть аккаунт? Войти в систему.

    Войти

×
×
  • Создать...