Думаю многим известно, что такое файл bind_stalker.script. В этом файле прописан класс actor_binder обрабатывающий действия актора. Всего в классе семнадцать методов. Коротко расскажу о назначении каждого:
__init - вызывается единожды при создании экземпляра класса, т.е. в случае актора - один раз при начале новой игры; net_spawn - вызывается при спавне актора, т.е. при загрузке (загрузка сохранения, переход между локациями); net_destroy - противоположный предыдущему метод. Вызывается при удалении актора. В игре это единственный момент - момент перед загрузкой другой локации во время перехода; reinit - вызывается после net_spawn и представляет из себя инициализацию объекта актора. Примечателен метод тем, что в нём устанавливаются колбеки; take_item_from_box - вызывается, когда ГГ берёт вещи из объекта inventory_box; level_border_enter - вызывается, когда ГГ входит в разрешенную границу уровня; level_border_exit - вызывается, когда ГГ покидает разрешенную границу уровня;
Как именно определяются границы уровня я не знаю. Но эти колбеки нужны для схемы xr_detector.script, которая в свою очередь реагирует на данные действия повышением уровня радиации, когда мы заходим в запрещённую зону на границе уровня.
info_callback - вызывается при выдачи ГГ любой инфопорции; on_trade - вызывается при нажатии кнопки "Торговать" в акне торговли; article_callback - вызывается во время выдачи статьи в энциклопедии; on_item_take - вызывается в момент появления в инвентаре ГГ любого предмета; on_item_drop - противоположный предыдущему метод. Вызывается, когда из инвентаря удаляется любой предмет; task_callback - вызывается для обработки заданий в ПДА. В какие именно моменты это происходит сказать сложно, скорее всего с некоторой периодичностью; map_location_added_callback - вызывается во время установки какой-либо метки на карте ПДА; update - знаменитый "апдейт". Вызывается движком постоянно, довольно часто (несколько десятков раз в секунду); save - вызывается при сохранении; load - соответственно при загрузке.
Самым частым в использовании является метод update. Именно в него неосведомлённый модостроитель порою "заталкивает" такое, что волосы становятся дыбом, и невероятно часто данный метод используют для одноразовых "заданий", прописывая проверку инфопорции, чтобы результат не проверялся несколько раз, но тем не менее проверка продолжает выполняться. А что если использовать update по требованию и когда необходимость в нём отпадает - убирать проверку? Как? Оказывается не очень то и сложно... Таковым функционалом обладает файл xr_s.script из папки скриптов ЧН или ЗП. Знающий скрипты человек, уделив некоторое внимание данному файлу, сам поймёт как его использовать, не знающий - будет долго всматриваться в код в поисках знакомых слов. Ничего страшного в этом нет - самообразование всегда похвально, я же помогу Вам разобраться в коде окончательно. Идея скрипта как раз в том и состоит, чтобы в нужный момент подключить любую функцию к одному из колбеков, включая update, и когда необходимость в функции отпадёт - убрать её от туда, тем самым избавив колбек от лишней работы. При этом список колбеков можно свободно расширить, и подключать функцию можно вообще по сути куда угодно. Итак, давайте разберём файл более подробно.
Сразу скажу, что в файле присутствуют функции, которые не относятся к "подключению" функций, поэтому речь пойдёт только о тех функциях, которые нам понадобятся. Таблица callbacks. Таблица состоящая из пары ключ - значение, где ключом выступает имя колбека в который подключается функция, значение - это таблица содержащая параметры для нашей функции, если она их требует. Звучит сложно, но Вы поймёте на примере, который будет ниже.
Далее идут две функции: function register_callback (name, func, userobj) Функция "регистрирует" нашу функцию в указанном колбеке. Принимает следующие параметры: name - называние колбека из таблицы callbacks в который мы подключаем нашу функцию; func - ссылка на нашу функцию. Указывается её имя без кавычек! Если функция находится в другом файле, ты в ссылке через точку указывается и модуль в котором расположена функцию; userobj - параметры, которые принимает наша функцию. Если параметров несколько, то нужно передавать параметры в таблице. function unregister_callback (name, func) Функция удаляет нашу функцию из указанного колбека. Принимает следующие параметры: name - называние колбека из таблицы callbacks из которого мы удаляем нашу функцию; func - ссылка на нашу функцию. Указывается её имя без кавычек! Если функция находится в другом файле, ты в ссылке через точку указывается и модуль в котором расположена функцию.
Далее идут одиннадцать однотипных функций. Их нужно вписывать по сути в одноимённые функции в скриптах. Т.е. к примеру функция on_actor_update нужно вписывать в метод update биндера актора (файл bind_stalker.script).
Вот некоторые соответствия того, какую функцию из xr_s.script куда прописывать и какой "колбек" из таблицы callbacks использовать в последствии:
Имя функции из xr_s.script -> Имя функции куда вписывать -> Какой "колбек" из таблицы использовать для подключения/отключения on_actor_update -> actor_binder:update (bind_stalker.script) -> update on_game_load -> actor_binder:net_spawn (bind_stalker.script) -> game_load on_actor_destroy -> actor_binder:net_destroy (bind_stalker.script) -> actor_destroy on_npc_hit -> motivator_binder:hit_callback (xr_motivator.script) -> npc_hit on_monster_hit -> generic_object_binder:hit_callback (bind_monster.script) -> monster_hit on_main_menu_on -> main_menu:__init (ui_main_menu.script) -> main_menu_on on_main_menu_off -> main_menu:OnButton_return_game (ui_main_menu.script) -> main_menu_off on_item_drop -> actor_binder:on_item_drop (bind_stalker.script) -> item_drop
Подозреваю, что вышеизложенный текст, не для всех является простым, но изложить доступно словами всю идею, не отклоняясь от основной мысли (динамическое "включение" функций) достаточно сложно. Я стараюсь, на сколько это возможно, придерживаться этого компромисса, поэтому, если Вам не понятен текст выше, прочитайте его ещё раз, чтобы у Вас была теория "на уме", а не бегать в последствии глазами по тексту - так Вам же будет проще.
Перейдём, так сказать, к делу... В качестве примера приведу решение того, как реализовать убирание оружия во время приёма аптечки. Вопрос достаточно часто задают в соседней теме, поэтому это будет актуально, интересно, а самое главное наглядно отобразит основную идею динамического "включения" функций.
Сначала, расскажу алгоритм, т.е. как это реализуется "на словах". Нужно отследить момент использования аптечки; затем запустить таймер, на время которого убирать оружие; и по окончании работы таймера, дать возможность вновь им пользоваться. А теперь реализация: Момент использования аптечки можно отследить в колбеке актора on_item_drop, но это будет не совсем правильно, так как убираться оружие будет и тогда, когда мы просто выбросим/переложим/продадим аптечку, а нам этого не нужно. Поэтому мы используем колбек именно на использование предмета use_object. В ЗП он есть, в ТЧ/ЧН также есть, т.е. предусмотрен движком, но не используется. Если Вы делаете это для ТЧ или ЧН, то нужно добавить метод в биндер актора, который будет отрабатывать во время использования предмета. Делается это так: В методе reinit того же биндера установим колбек на "юз", прописав вот это в тело метода:
Теперь нам нужно добавить сам метод. В любое место файла (но НЕ во внутрь какой-либо функции!!!), например перед строкой:
Код
function actor_binder:update(delta)
добавьте следующий код:
Код
function actor_binder:use_inventory_item(obj) end
Именно эта функция, а точнее метод биндера, будет вызываться, когда вы будете использовать какой-то предмет. Нас интересует конкретно аптечка, при этом не важно какая: простая, армейская или научная. Чтобы не прописывать три условия, проверяющие секции аптечек на соответствие, мы используем одну, в которой проверим, что в секции присутствует слово "medkit". Это будет чуть быстрее и универсальнее. Универсальность заключается в том, что Вы можете добавить новые медпрепараты и если Вы хотите того же эффекта и от них, Вам достаточно задать название секции со словом "medkit", например "medkit_special" или "special_medkit". Проверка выглядит так:
Код
if string.find(obj:section(), "medkit") then end
Эту проверку нужно добавить в ново созданный (в случае ТЧ/ЧН) метод use_inventory_item. Момент использования аптечки мы отследили. Теперь нужно запустить таймер на время работы которого убирать оружие. Вот здесь и выходит на передний план функции файла xr_s.script. Сама функция реализующая таймер не сложна и выглядит так (предположим, что она находится в файле use_med.script):
Код
function MedkitUsing(userObj) --# Прячем оружие. bind_stalker.hide_weapon() --# Проверим работает ли ещё таймер? --# userObj[1] - время начала работы таймера; --# userObj[2] - время работы таймера (мсек) if time_global() > userObj[1] + userObj[2] then --# Работа таймера закончена - покажем оружие. bind_stalker.restore_weapon() end end
Принимает в качестве параметра таблицу с двумя элементами: первый - это время запуска, второй - время работы таймера в миллисекундах. Эту функцию нужно "вешать" на апдейт, т.к. нужно постоянно проверять значение таймера. Если просто вставить это как есть, то, во-первых - нужно прописывать дополнительную проверку, что использована аптечка, во-вторых - проверяться условие будет всегда. Поэтому подключим её динамически. В проверку использования аптечки в колбеке use_object нужно добавить вот такой вызов:
Что это всё означает? Я описывал эту функцию, теперь для ясности давайте сопоставим переданные параметры с описанием выше: "update" - это name, т.е. имя "колбека" из таблицы callbacks; use_med.MedkitUsing - это ссылка на функцию, которую мы "включаем". Обратите внимание на способ подключения, указывается в каком файле находится функция и не пишется никаких скобок, не говоря уже о параметрах; {time_global(), 4000} - помните я говорил, что функция принимает таблицу из двух элементов? Так вот это они и есть. Таблица передаётся в качестве параметра в нашу функцию. Добавив такой вызов, функция начнёт работать на ближайшем апдейте. Всё хорошо - функция отрабатывает, когда мы используем аптечку, но как остановить работу, чтобы разгрузить апдейт? Здесь нам поможет функция unregister_callback из файла xr_s.script. Нужно вписать вызов этой функции в том месте, где работа таймера закончена. После строки:
Всё тоже самое, что и в предыдущей только лишь отсутствуют параметры, т.к. в них нет надобности. По окончании работы таймера, появиться возможность пользоваться оружием и функция "выключится" разгрузив тем самым апдейт. Как видите - всё просто, даже слишком. На самом деле это не всё. В апдейт всё же придётся навсегда вписать вызов одной функции, это функция on_actor_update из файла xr_s.script. Именно эта функция будет проверять есть ли у нас другие функции которые требуется подключить к апдейту. Здесь возможно расхождение в понимании того, что же мы делаем и вообще для чего, для чего делать проверку, если задача стоит в том, чтобы избавиться от неё? В данном примере это сопоставимо с обычной проверкой использовали ли мы аптечку или нет на самом апдейте. А что если таких "примеров" несколько - три, пять, десять? Вот именно здесь данный способ вне конкуренции. Одна проверка, которая контролирует десяток-другой вызовов, при этом те функции, которые не нуждаются в работе находятся "в спящем режиме", если так можно сказать и не используют ресурсы требуемые на постоянную проверку - "а нужно ли нам работать?".
Пример вышел тривиальным, но более чем наглядным. Естественно его можно развить, добавить медленное восстановление здоровья, ограничить возможность использования нескольких аптечек подряд, пока работает таймер и т.д. Развитие функционала уже выходит за рамки "урока", поэтому это остаётся на Ваших плечах. Всё работает без вылетов, лично преверял на ТЧ 1.0006. Хотя это и предполагается на ТЧ, но работать будет и на ЧН и на ЗП.
Ссылка на xr_s.script (ЗП 1.6.0.2), если у кого нет: >>ClicK Me<<
"Без лишних слов"
Урок, хотя скорее это даже не "урок", а готовый мод, который добавляет убирание оружия при использовании аптечек. В качестве бонуса: медленное восстановление здоровья, невозможность пользоваться другой аптечкой, пока мы используем первую и прерывание восстановления здоровья, в случае какого либо воздействия приведшее здоровья, иными словами, если мы получили хит, восстановление жизни прекращается. Кроме того, "бонусы" можно отключать по желанию. Реализоваться это будет, понятное дело, на схеме предоставленной в шапке темы. Список файлов, который нам понадобиться: xr_s.script - основной файл, без него никак (ссылка есть чуть выше по тексту); bind_stalker.script - куда же без него? Будем проверять используется ли аптечка или нет; medicine.script - файл в котором находится весь функционал; item.ltx - придётся подправить параметры аптечек, выключив им движковое лечение.
Приступим! Скачиваем вот этот файл: >>ClicK Me<< и располагаем его в папку gamedata/scripts. Открываем файл bind_stalker.script и перед методом use_inventory_item (как его добавить описано выше под спойлером "Realization") вписываем вот такую таблицу:
if item_[obj:section()] then medicine.Rehabilitation(obj) end
В метод update, в этом же файле, впишите вызов функции on_actor_update из xr_s.script:
Код
xr_s.on_actor_update(delta)
В файле item.ltx уберите движковое восстановление здоровья у аптечек, прописав значение 0 для параметра eat_health. В противном случае от медленного лечения не будет толка. Собственно всё! Все "бонусы", в том числе и само убирание оружия, можно выключить в файле medicine.script. Да и вообще в нём прокомментирована каждая строчка, так что проблем возникнуть не должно. Единственный момент - не сохраняйтесь пока работает восстановление! К вылету это не приведёт, но при загрузке возможен момент, когда первая аптечка не даст желаемого эффекта, для последующих всё будет так, как нужно. Не критично, но неприятно. Не смог поправить в силу данной реализации. Вылеты отловлены. Логические ошибки поправлены. Всё работает - проверено.
P.S. Если возникли какие-то вопросы в плане использования функционала xr_s.script не стесняйтесь - задавайте вопросы. Только, ПОЖАЛУЙСТА, задавайте их так, чтобы было понятно не только Вам. Если всё же у Вас возникли проблемы с реализацией примера (напомню - он на 100% рабочий) и происходит вылет - под спойлер в порядке действий: что-куда прописывали, что сделали в игре (в какой момент), лог вылета; дабы ускорить процессию разбирания Ваших кодов - пользуйтесь тегом [cоde][/cоde].
Любые возмущения, не довольствия - останутся без ответа, во всяком случае с моей стороны.
Сообщение отредактировал ColR_iT - Вторник, 19.02.2013, 12:50
статья полезная. Хочу кое-что выделить добавить(не по скриптам):
Цитата (ColR_iT)
невероятно часто данный метод используют для одноразовых "заданий", прописывая проверку инфопорции, чтобы результат не проверялся несколько раз
У меня самого было около 15-ти(!) функций проверки инфопоршней и прочего хлама, висящего на апдейте - из-за этого игра начинала постепенно тормозить. Поэтому я не придумал ничего лучше, как вешать проверку на рестрикторы, ставил их в нужные места, создавая определенную логику. Может, кому и пригодиться. Лучше пожертвовать размерами олспавна, чем тормозами игры.
Uyman358, можно и так, но иногда какое-то действие нужно выполнить не зависимо от местоположения, при этом другой возможности запустить функцию нет (имею ввиду схему логики или диалог), поэтому чуть ли не единственным вариантом становиться апдейт. Опытный модстроитель всегда подгонит скрипты под ситуацию , а вот новички, к сожалению, из-за отсутствия знаний/опыта/смекалки подгоняют ситуацию под скрипты, как следствие - горы хлама повсюду (как правило больше всего достаётся апдейту) и вполне ожидаемые вылеты. Данный способ поможет "расставить всё по полочкам" параллельно оптимизируя игру.
Функции вроде info_callback это просто функции, не методы. Эти функции вызывают коллбэки. Не мешало бы разделить коллбэки и методы. И еще, ты забыл про коллбэк на использование предмета.
Функции вроде info_callback это просто функции, не методы.
, я перечислил исключительно методы класса actor_binder, всего их семнадцать, во всяком случае для ТЧ 1.0006.
Цитата (SkyLoader)
Эти функции вызывают коллбэки.
Вообще-то колбеки вызывают функции, в данном случае некоторые методы, если быть точным.
Цитата (SkyLoader)
Не мешало бы разделить коллбэки и методы.
Их то собственно ничего и не объединяет. Все методы данного класса вызываются движком при определённых действиях, если считать, что колбек - это "функция" вызываемая при определённых условиях, то все методы биндера и есть колбеки.
Цитата (SkyLoader)
И еще, ты забыл про коллбэк на использование предмета.
В ТЧ, например, он не используется, ровно как и в ЧН. Но даже так я о нём не забыл, так как использую его в примере.
Сообщение отредактировал ColR_iT - Вторник, 05.02.2013, 21:07