Страница 10 из 10

Re: LuaScript - для RS

СообщениеДобавлено: 12.10.2016, 15:37
i2GR
Крутой костыль!
Только мне казалось, что в случаях если между локами вагон, то второй лок в середине надо делать IsDeadEngine в сценарии. Или он все равно "тянуть" будет?
З.Ы. и применять то не на чем, как всегда

Re: LuaScript - для RS

СообщениеДобавлено: 12.10.2016, 17:12
радиомастер
i2GR писал(а):Или он все равно "тянуть" будет?

тянуть не будет , но вот и звуков наката тоже не будет , косяк такой, только стыки (сами удары)

Re: LuaScript - для RS

СообщениеДобавлено: 18.10.2016, 08:59
Света
i2GR писал(а):...в случаях если между локами вагон, то второй лок в середине надо делать IsDeadEngine в сценарии.
Не обязательно, я помню, как через нашу станцию ходили составы лок+лок+вагоны+лок+лок+вагоны. Тогда ещё 2ТЭ10л использовались. В таком случае надо, чтобы тянули все 4 лока, а "сотрудничество" по переданных данных было только между парами.

Re: LuaScript - для RS

СообщениеДобавлено: 05.11.2016, 14:08
Света
Всем доброго дня :)

Очередная заготовка, облегчившая мне написание скрипта, которой я хочу поделиться. Это скрипт-модуль, предназначенный для управления яркостью источниками света (ИС).

Назначение
Управлять яркостью ИС можно, воздействуя на параметры каналов цветности RGB. Если установить значение всех каналов в 0, источник будет полностью погашен, если же в значения, заданные в конфиге ИС - будет светить максимально заданной яркостью и цветом.
Установкой значений RGB в промежуточные значения можно изменять яркость ИС от 0 до максимума.

Основные, ИМХО, трудности в этом методе управления:
1. Значения каналов одной модели практически всегда различные. Для того, чтобы при изменении яркости ИС не потерять баланс (и цвет), все значения каналов нужно изменять синхронно и пропорционально.
2. Необходимо иметь точные данные о установленных в конфиге значениях RGB - это определяет максимальные уровни яркости и цветовой баланс.
3. Управление ИС таким методом подразумевает отсчет временных интервалов. А это автоматически требует создания счетчиков-переменных, их инкрементирование/декрементирование, слежение за результатом, разрешение/запрет перезапуска и другие "прелести" ручной таймеризации в программе.
4. Нельзя обойтись без защитных механизмов, которые заблокируют обработку ИС в случае серии однотипных команд (если, например, будет сформировано 10 команд "выключить ИС", то должна обработаться только первая, остальные должны игнорироваться, причем, чем раньше, тем оптимальнее.
5. Все вышеуказанное требует создания достаточно запутанной схемы, которая для одного ИС ещё приемлема, но с увеличением количества ИС сложность и запутанность растет в разы.

Эти моменты побудили меня разработать логически завершенный блок, который взял бы всю нагрузку на себя. Возможность изменять яркость ИС вызвала желание сделать этот процесс плавным и управляемым по времени. Это получилось. К тому же, мне хотелось сделать простым добавление новых ИС под его "опеку". Это мне также удалось.


Принцип работы
При запуске программы в функции StartSetupLight () происходит начальная инициализация всех переменных. Также, используя данные из таблицы TabNames (где перечислены имена конфигов ИС как дочерних элементов), производится поиск и загрузка ключевых данных (в данном случае значения RGB).
Также создаются таблицы, используемые в процессе работы.
Одна из таблиц - LampSpeed. Здесь размещены имена ИС и их время на включение (ig) и выключение (qu) в нормальных условиях.

Для того, чтобы сделать управление каналами ИС пропорциональным и синхронным, логично использовать один множитель, который изменял бы свое значение от 0 до 1 и на который в процессе обработки следует умножать максимальное значение каждого канала.
Это переменная factor. По сути, её значение (от 0 до 1) определяет состояние ИС от полностью выключенного до включенного на полную яркость. Это значение постоянно хранится в таблице LampData.Factor_c[_имя ИС_] (c = current = "текущий").

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

Логически блок обработчика состоит из двух веток: function Lamp_Set (name, level, time) (эта функция запускает процесс изменения яркости) и function LampUpdate (time) (обработка изменения ИС во времени).

Lamp_Set (name, level, time)
При вызове этой функции первым делом подгружается текущее состояние ИС и сравнивается с заданным при вызове. Если эти значения равны (дубль команды), происходит завершение запуска обработки. В противном случае производится перезапуск ИС.
Для того, чтобы перезапустить ИС, сначала сравниваются текущее состояние и заданное. Это указывает, в какую сторону надо изменять factor - увеличивать или уменьшать яркость. Затем создаются контрольные точки - текущее состояние сохраняется как начальная точка (LampData.Factor_a[ch_n]), а требуемый уровень (level) - как конечная (LampData.Factor_b[ch_n]). Далее запускается таймер - на время, заданное в команде, или, если время не задано - на время по умолчанию, указанное в таблице LampSpeed. Дефолтное время также будет загружено в том случае, если значением time будет "none".
Также сбрасывается флаг LampSet - это будет свидетельствовать, что есть ИС, требующий обработку.

LampUpdate (time)
В первую очередь проверяется состояние флага LampSet. Если он установлен, это значит, что все ИС в статичном состоянии. В обработке нет необходимости, поэтому выход.
Если же флаг сброшен, значит есть как минимум 1 ИС, требующий просчета. В таком случае первым делом флаг LampSet устанавливается (авансом, чтобы если это ложный сбой, то при следующем входе в эту функцию не отрабатывать её снова) и запускается цикл, который последовательно переберет все ИС, прописанные в таблице TabNames.
В процессе перебора для каждого ИС считывается его состояние, если ИС статичен (status = "on" или status = "off"), этот ИС не обрабатывается. Иначе флаг LampSet сбрасывается (подтверждая, что есть необработанные ИС), и, в зависимости от статуса ИС ("ignition" или "quenching", то есть увеличение или снижение яркости) выполняется необходимая ветка, где на основании состояния таймера и ключевых точек производится расчет нового значения множителя factor. Если нужная точка яркости достигнута, это отмечается в статусе ИС, а канал таймера уничтожается за ненадобностью.
Дальше максимальные значения каналов цвета умножаются на множитель и передаются движку на управление ИС. Новое состояние ИС сохраняется.

Примечания
1. Количество подконтрольных ИС ограничено только здравым смыслом;
2. Яркость ИС можно "сдвинуть" в любую сторону в пределах от 0 до 1 (от минимума к максимуму) независимо от текущего;
3. Яркость может изменяться с любой скоростью, а если скорость не задана (или = "none"), подхватывается дефолтное значение;
4. Если все ИС статичны, обрабатывается всего 2 строки функции LampUpdate (time) за кадр;
5. Время изменения яркости стабильно, не зависит от fps. Но на очень слабых ПК не исключено ступенчатое изменение яркости;
6. Главный недостаток обработчика - время изменения яркости не зависит от начального состояния ИС. То есть, при заданном по умолчанию времени на выключение 0.2 сек, ИС будет выключатся именно 0.2 секунды, независимо от того, включен он на полную яркость или на 50%. Мне это не показалось критичным. В то же время, это исправимо;
7. Все ИС совершенно независимы друг от друга.


Подключение к скрипту и подготовка к использованию
Модуль можно разместить в любом удобном месте. В основном скрипте делается его подключение:
Код: Выделить всё
require "Assets/[...]/light_driver_script_v21.lua"
Если модуль скомпилирован, *.lua заменить на *.out.

В функцию инициализации основного скрипта необходимо вписать вызов функции инициализации:
Код: Выделить всё
StartSetupLight ()

А в функцию Update (time) вписать вызов функций таймера и обработчика:
Код: Выделить всё
   LampUpdate (time)
   Timer (time)


Дальше необходимо заполнить таблицу TabNames информацией о используемых ИС. Первое поле таблицы - это имя ИС, которое будет использоваться в командах. Любое удобное. Второе поле - это имя чайлда, задаётся в блоке child в поле "ChildName" основного конфига.
Затем для всех указанных в TabNames ИС нужно указать время на розжиг и на гашение в таблице LampSpeed.

В качестве примера я привожу 2 ИС, которые имеют имена в конфиге "Light_1" и "Light_2", я их буду называть в скрипте как Lampa_1 и Lampa_2, время розжига у обеих 0.2 сек, гашения - 0.3.


Использование
Для управления ИС используется вызов
Код: Выделить всё
   Lamp_Set (name, level, time),
где
   name - имя ИС, заданное Вами;
   level - требуемый уровень яркости, от 0 до 1;
   time - время, отведенное на это.


Время задается в секундах, допускаются дробные значения. Если время не задано или указан параметр "none", будет использовано время, заданное по умолчанию.


В модуле использована функция Timer (data_1, data_2, data_3), описанная мной раньше. Если Вы уже использовали её у себя, из модуля её нужно удалить. Это строки 21, 131 - 168.
Использование таймера не ограничивается только управлением ИС, его можно применять для контроля времени в других частях скрипта. Подробнее здесь.

Скрипт оттестирован.
За помощь в разработке формулы искренне благодарю пользователя maestro.
Также за поддержку и тестирование благодарю пользователя Cross из railworks2

Скрипт здесь.

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

Re: LuaScript - для RS

СообщениеДобавлено: 05.11.2016, 17:44
Skif
Есть подозрение, что синхронно с цветом источника надо менять и радиусы зоны светимости! Иначе эффект будет не полным.

Re: LuaScript - для RS

СообщениеДобавлено: 05.11.2016, 19:48
Света
Без проблем! Для этого надо добавить всего 3 - 4 строчки, но, чтобы в полной мере оценить эффект, нужна темная ночь в игровом мире, чего у меня, к сожалению, нет. А я подозреваю, что линейный закон для корректировки радиуса не подойдет. Если кто-нибудь захочет провести тестирования и имеет качественный ИС, реальную ночь в ТС, умеет работать со скриптами и доверяет своему зрению, пишите в ЛС, я покажу, как сделать необходимые правки.

Re: LuaScript - для RS

СообщениеДобавлено: 04.12.2016, 12:12
Света
Всем доброго дня :)

Представляю вниманию разработчиков функцию TIMER v2.0, предназначенную для вставки в скрипт.

С назначением и общим описанием первой версии функции можно ознакомиться здесь.
Отличие от первой версии: добавлен режим остановки счета (пауза).

Адаптация в скрипт
Функция вставляется в конец скрипта. Никакие подгонки/правки не нужны.

Для инициализации рабочей таблицы, вписываем её в функцию Initialise():
Код: Выделить всё
-- Data TIMER
TabTimerData = {}
TabTimerStatus = {}


Для обеспечения счёта периодически делаем вызов из функции Update ( time ):
Код: Выделить всё
 Timer (time)

Всё, таймер прописан и готов к работе.


Использование/вызов
Для того, чтобы создать канал таймер, надо придумать ему любое удобное имя и отправить запрос в его функцию (например, timer_1) вместе со значением нужной задержки в секундах (например, 15 сек):
Код: Выделить всё
Timer ("timer_1", 15)

Важно: пока таймер отсчитывает выдержку, его рестарт такой командой невозможен. Это сделано умышленно. Если нужно перезапустить отсчет сначала (а также снять таймер с паузы с обновлением задержки), применяется другая команда:
Код: Выделить всё
Timer ("timer_1", 15, "restart")

Контролировать окончание отсчета можно 2 способами. Первый способ - это контроль фактического окончания отсчета. Вариант использования:
Код: Выделить всё
   if Timer ( "timer_1" ) then
      -- блок операторов, если время вышло
   else
      -- блок операторов, если отсчет не завершен
   end

Обратите внимание - если опрашивается несуществующий канал таймера, функция также возвратит "false", как и при незавершенном отсчете.
Второй способ - контроль времени, которое осталось до окончания отсчета. Вариант использования:
Код: Выделить всё
   Pause = Timer ("timer_1", "ctrl")

В переменную Pause будет загружено оставшееся время (сек) в числовом формате. Если отсчет закончился, будет загружена строка "timeroff". В случае опроса несуществующего канала будет возвращено значение "nil".

Если необходимости в каком-либо канале таймера нет, его можно разрушить:
Код: Выделить всё
   Timer ("timer_1", "clear")

Такая команда уничтожит канал "timer_1". Это сэкономит время, затрачиваемое на обработку.
Автоматического удаления каналов с завершенным отсчетом нет.

Если надо остановить отсчет, используем команду
Код: Выделить всё
   Timer ("timer_1", "pause")

Для продолжения отсчета используем команду
Код: Выделить всё
   Timer ("timer_1", "run")
или 
   Timer ("timer_1", время, "restart")

Количество каналов ограничено объемом оперативной памяти.
Разрешающая способность (сек, грубо) - 1/fps.

Сам код:
Код: Выделить всё
function Timer (data_1, data_2, data_3)
   if type(data_1) == "number" then
      for k,v in pairs(TabTimerData) do
         if type(v) ~= "string" and TabTimerStatus[k] then
            v = v - data_1
            if v <= 0 then
               TabTimerData[k] = "timeroff"
            else
               TabTimerData[k] = v
            end
         end
      end
      return
   end

   if type(data_1) == "string" then
      if not data_2 then
         if TabTimerData[data_1] == "timeroff" then
            return true
         else
            return false
         end
      elseif data_2 == "ctrl" then
         return TabTimerData[data_1]
      elseif data_2 == "clear" then
         TabTimerData[data_1] = nil
      elseif data_2 == "pause" then
         TabTimerStatus[data_1] = false
      elseif data_2 == "run" then
         TabTimerStatus[data_1] = true
      elseif not TabTimerData[data_1] or TabTimerData[data_1] == "timeroff" or data_3 == "restart" then
         TabTimerData[data_1] = data_2
         TabTimerStatus[data_1] = true
      end
   end
end



Приветствуются пожелания по усовершенствованию. Также, несмотря на успешное тестирование, приветствуется любая информация по практическому использованию функции.

Эта версия успешно заменяет предыдущую без дополнительных исправлений кода.