Содержание 1 Введение 2 Скрипт 3 XML-описатель 4 Погружение 4.1 Видео 4.2 Бэкграунд 4.3 Кнопки 5 Продолжение 6 Внимание 7 Ссылки
[править] Введение
Итак, нам вдруг захотелось оформить красиво ранее описанный способ запуска скриптов. Порывшись в текстурах можно подобрать понравившиеся элементы интерфейса. Сейчас попробуем сделать свое окно с рамкой, проигрывающимся видеороликом на заднем плане и несколькими кнопками.
Способ вызова нашего скрипта отстанется прежним, Esc - F1, но вызывать окно можно и из диалогов, и по некоторому событию, вобщем простор для мододелов огромный. Я использовал уже имеющиеся текстуры и элементы управления, но никто не запрещает использовать свои. Для рамки я выбрал текстуру ui\ui_dg_inventory, а кнопки брал из ui\ui_common. [править] Скрипт
Будем использовать тот же скрипт ui_cheat.script, но немного видоизмененный. Я приведу его содержание полностью, а потом начнем его дополнять.
Начало: class "cheat" (CUIScriptWnd) -- инициализируем элементы интерфейса и каллбаки function cheat:__init(owner) super() self.owner = owner self:InitControls() self:InitCallBacks() end function cheat:__finalize() end function cheat:InitControls() -- здесь определяем координаты левого верхнего угла и ширину-высоту self:Init(50,50,550,450) -- файл-описатель наших элементов local xml = CScriptXmlInit() xml:ParseFile("ui_cheat_wnd.xml") -- видео на заднем плане xml:InitStatic("back_video", self) -- рамка xml:InitStatic("background", self) -- кнопки self:Register(xml:Init3tButton("btn_1", self),"btn_1") self:Register(xml:Init3tButton("btn_2", self),"btn_2") self:Register(xml:Init3tButton("btn_3", self),"btn_3") -- кнопка выхода self:Register(xml:Init3tButton("btn_quit", self),"btn_quit") end function cheat:InitCallBacks() -- тут интерактивные элементы, при определенном действии выполняется заданная ф-ия self:AddCallback("btn_1", ui_events.BUTTON_CLICKED, self.cheat1, self) self:AddCallback("btn_2", ui_events.BUTTON_CLICKED, self.cheat2, self) self:AddCallback("btn_3", ui_events.BUTTON_CLICKED, self.cheat3, self) self:AddCallback("btn_quit", ui_events.BUTTON_CLICKED, self.on_quit, self) end
Теперь на имеющиеся кнопки прикрутим ф-ии, которые будут что-нибудь делать. function cheat:cheat1() -- колдуем СВД self:spawn_item("wpn_svd_m1", 3) end function cheat:cheat2() -- патроны к ней self:spawn_item("ammo_7.62x54_ap", 3) end function cheat:cheat3() -- и того, кого будем из СВД мучать self:spawn_item("dog_weak", 30) end function cheat:check_game() -- возвращает true если запущена игра local check = false if level.present() and (db.actor ~= nil) and db.actor:alive() then check = true end return check end function cheat:on_quit() -- убираем наше окно, показываем предыдущее (или возвращаемся в игру) local console = get_console() self:GetHolder():start_stop_menu (self.owner, true) self:GetHolder():start_stop_menu (self,true) self.owner:Show (true) -- скрытие главного меню только если запущена игра if self:check_game() then console:execute("main_menu off") end end function cheat:spawn_item(spawn_item, dist) -- спавним только в игре if self:check_game() then self:on_quit() local pos = db.actor:position() local dir = db.actor:direction() pos = pos:add(dir:mul(dist)) alife():create(spawn_item, pos, 1, db.actor:game_vertex_id()) end end
Скриптовую часть закончили. Теперь надо "нарисовать" интерфейс. Инструмет для его создания пока отсутствует (по крайней мере у нас), поэтому будем делать ручками, методом проб и ошибок. [править] XML-описатель
Так как мы уже определили файл, который будет содержать расположение элементов (xml:ParseFile("ui_cheat_wnd.xml")), то создадим его. Полный путь до файла: config\ui\ui_cheat_wnd.xml.
Разберем построчно. Так как ф-ия cheat:InitControls() сначала отображает видеоролик (чтобы он оказался под всеми элементами, с него и начнем. <back_video x="10" y="10" width="380" height="320" stretch="1">
Название элемента должно совпадать с именем, которое присутствует в xml:InitStatic("back_video", self)
x,y - координаты левого верхнего угла относительно объявленного в self:Init
width, height - ширина-высота
stretch="1" - указывает на необходимость растягивания элемента <texture>ui\1_hyuv_sq</texture>
Используется видеоролик находящися по адресу textures\ui\1_hyuv_sq.ogm. Его исходные размеры - 512х512, поэтому нам и понадобилось растягивание, вернее так называемый стретч. </back_video>
конец элемента. [править] Бэкграунд <background x="0" y="0" width="400" height="340" stretch="1">
координаты, размеры, растягивание, все как и выше <texture x="0" y="0" width="350" height="460">ui\ui_dg_inventory</texture>
а вот здесь ссылка на используемую текстуру. Мы не стали рисовать свою, а взяли уже имеющуюся, поэтому задаваемые здесь координаты и размеры означают, что нужно взять текстуру ui\ui_dg_inventory.dds и вырезать из нее нужный нам кусок. Обратите внимание, что размеры вырезанной части и определенные нами не совпадают, маловата текстура оказалась, поэтому пришлось ее растягивать ;) </background>
конец элемента. [править] Кнопки <btn_1 x="20" y="30" width="117" height="29">
координаты, размеры, без растягивания, все как и выше. Но, здесь это означает размеры интерактивной области. Если указать иные размеры, то кнопка будет реагировать только в указанной зоне. Если указать нулевые размеры, то кнопка вообще перестанет быть интерактивной. <texture_e>ui_button_ordinary_e</texture_e>
а вот тут уже интереснее. не пытайтесь искать текстуру с именем ui_button_ordinary_e. Она описана в другом файле, config\ui\ui_common.xml. Ссылка на этот файл входит в секцию [texture_desc] в system.ltx. То есть, если вы создали свой файл-описатель интерфейса, то нужно проставить ссылку на этот файл в вышеописанной секции. <text>wpn-svd</text>
текст на кнопке. Здесь тоже не все так просто. Сначала игра ищет выражение wpn-svd в файлах заданных в секции [string_table] файла localization.ltx, то есть зависит от установленного языка. В данном случае ссылка на wpn-svd есть в файле config\text\rus\string_table_enc_weapons.xml, и равно <string id="wpn-svd"><text>СВДм 2</text></string>
При таком способе установки текстового значения мы не будем зависеть от языка игры и сможем создавать не привязанные к языку моды.
Если посмотреть на вторую кнопку, то ее текстовое значение равно <text>Патроны</text>
Такого значения игра не найдет и просто отобразит так как есть. Но англоязычные пользователи врядли поймут написанное. Поэтому я лично рекомендую использовать уже имеющиеся ресурсы, и минимально вмешиваться в ресурсы и добавлять новые. Но, например, файл с названиями машин (string_table_enc_vehicles.xml) не входит в файл localization.ltx, поэтому придется его туда подключить, либо указать конечному пользователю на необходимость добавления одной строчки в этот файл.
Ну кнопочка "Выход" сделана вот так: <text>ui_inv_exit</text> [править] Продолжение
Все. Сохраняем, запускаем игру, жмем Esc - F1, и видим наше окошко. Можно понажимать на кнопки и увидеть что они какие-то неинтерактивные. То есть никак не реагируют на наведение мыши и нажатие. Но это легко поправимо. В игре все для этого есть. Достаточно оформить кнопку вот так (пример для одной, остальные аналогично): <btn_quit x="270" y="300" width="117" height="29"> <texture_e>ui_button_ordinary_e</texture_e> <texture_t>ui_button_ordinary_t</texture_t> <texture_h>ui_button_ordinary_h</texture_h> <text>ui_inv_exit</text> </btn_quit>
Обозначения: texture_e - вид кнопки по умолчанию texture_t - вид кнопки при нажатии texture_h - вид кнопки при наведении мыши
есть еще одно состояние, texture_d, оно означает текстуру выключенной кнопки. Можно скриптами задавать состояние кнопки (включена-выключена), но об этом в другой раз.
Поизучайте файлик ui_common.xml на предмет других элементов, там еще много интересного. [править] Внимание
Так как скрипт спавна изменен по сравнению с первой статьей (добавлен вызов self:on_quit() перед самим спавном), то функцию привязки к клавишам следует оформить следующим образом: function cheat:OnKeyboard(dik, keyboard_action) CUIScriptWnd.OnKeyboard(self,dik,keyboard_action) if keyboard_action == ui_events.WINDOW_KEY_PRESSED then -- отсюда убран вызов self:on_quit() if dik == DIK_keys.DIK_ESCAPE then self:on_quit() elseif dik == DIK_keys.DIK_NUMPAD1 then self:cheat1() elseif dik == DIK_keys.DIK_NUMPAD2 then self:cheat2() elseif dik == DIK_keys.DIK_NUMPAD3 then self:cheat3() elseif dik == DIK_keys.DIK_NUMPAD4 then self:cheat4() elseif dik == DIK_keys.DIK_NUMPAD5 then self:cheat5() elseif dik == DIK_keys.DIK_NUMPAD6 then self:cheat6() elseif dik == DIK_keys.DIK_NUMPAD7 then self:cheat7() elseif dik == DIK_keys.DIK_NUMPAD8 then self:cheat8() elseif dik == DIK_keys.DIK_NUMPAD9 then self:cheat9() end end return true end