Space Engineers

Space Engineers

Not enough ratings
Практическое руководство по Easy Automation 2.0
By Survival Ready
Программирование состояния игровых блоков и модулей с помощью скрипта Easy Automation 2.0. Способы отладки, диагностика ошибок и особенности русской локализации.
   
Award
Favorite
Favorited
Unfavorite
Что это такое и зачем оно надо
Originally posted by Survival Ready:
Больше не актуально по причине наличия модифицированного скрипта
https://steamhost.cn/steamcommunity_com/sharedfiles/filedetails/?id=2670971581

Сразу скажу, что это не перевод документации по Easy Automatition 2.0. Это руководство по организации процесса программирования состояния различных игровых блоков в среде, которую НЕ предоставляют разработчики игры и решение некоторых проблем отладки.

https://steamhost.cn/steamcommunity_com/sharedfiles/filedetails/?id=694296356
Итак, Easy Automatition 2.0 (далее просто ЕА2) это интерпретатор команд несложного языка, который придумал автор скрипта для упрощения автоматизации игрового процесса. Он занимает промежуточное положение между ванильной автоматизацией при помощи таймеров и игровыми скриптами на C# и позволяет существенно облегчить создание высоко функциональных крафтов различного назначения.

Как справедливо утверждает автор, с его помощью можно:
  • управлять задержками по таймеру с точность до милисекунд
  • поворачивать роторы на заданный угол
  • двигать поршни на нужную длину
  • выводить состояние любых блоков на LCD экран
  • создавать сложную логику автоматизации и т.п.
Воспользоваться EA2 я решил в процессе создания игровой реплики броневика APC M-577 из фильма "Чужие 2" после того как количество таймеров в нем приблизилось ко 2-му десятку.

https://steamhost.cn/steamcommunity_com/sharedfiles/filedetails/?id=2468325731
Основная "фишка" APC M-577 - складывающаяся назад турель, которая расположена на 3-м сабгриде - поршень-шарнир-ротор управление которыми и позволяют выставлять ее в боевое и походное положение. Автоматизация этого процесса потребовала установки 7 таймеров, но после добавления скрипта MART, все накрылось... (тут можете сами вставить чем).

Кроме того мне хотелось создать не просто реплику, а, по возможности, боевой крафт и было очевидно, что если во время боя пострадает хотя бы один из таймеров, то вся автоматизация накроется тем же. Один программный блок на корабле защитить гораздо проще, чем пару десятков таймеров и с этой точки зрения у ЕА2 альтернатив нет.

В итоге из APC M-577 получился вполне годная БРДМ, имеющая кроме складывающейся турели, 2-а предустановленных положения подвески, способной выполнять "полицейский разворот" в движении, MART-управление турелью по азимуту, дальномер для определения расстояния до цели и еще кое-что по мелочи.

Желающие могут посмотреть презентацию на Youtube и просмотреть содержимое поля "Свои данные" ЖК-панели LCD Code, где, собственно, и хранится вся там автоматизация, а мы приступим... https://www.youtube.com/watch?v=PL_XU1phuvE
Среда обитания
Для организации процесса вам понадобится один программный блок (ПБ), куда нужно будет загрузить сам скрипт, и один дисплей (LCD) для хранения кода в поле "Свои данные". Для отладки понадобится еще один дисплей с оригинальным именем Debug LCD, но он в принципе, необязателен. Для минимизации объема не нормативной лексики в адрес разработчиков лучше сразу:

1. Назвать ПБ и связанный с ним LCD так, чтобы их можно было быстро найти в панели инструментов фильтром по имени - моем случае используется ключевое слово "Code".

2. Имя ПБ и LCD лучше сделать короткими, поскольку LCD придется указывать каждый раз когда в цифровую панель кокпита или станции управления помещается программный блок запускающий тот или иной процесс. И то и другое участвует в выводе BuildInfo (см.ниже)

3. Переименовать блоки и группы, связанные с процессом автоматизации по-английски, чтобы минимизировать переключение раскладок клавиатуры. Во встроенном редакторе не работает откат по Ctrl+Z

4. Крайне желательно установить мод BuildInfo, который позволяет в том числе увидеть расширенную информацию в цифровой панели по аргументам, с которыми запускается программный блок. https://steamhost.cn/steamcommunity_com/sharedfiles/filedetails/?id=514062285
В итоге должно получиться примерно следующее

.
Сами программы и сообщения, выводимые в процессе, размещаются в 2-х полях настройки самой LCD-панели

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

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

Для автоматической регулировки подвески этот метод годится, а вот для автоматизации "полицейского разворота" - нет. Там программу приходится вызывается по цифровой кнопке из настроек кокпита, а не из контрольной панели. Здесь как раз и может пригодиться дополнительный отладочный LCD на который будет выводиться диагностика ошибок, которых, поверьте, будет немало особенно по началу.
Производственный процесс
Originally posted by Общее замечание:
Далее предполагается, что вы в, общих чертах, ознакомились с авторским руководством по Easy Automatiotion 2.0, т.к. ниже будут описаны особенности кодирования, на которых автор не обратил внимание, посчитав их очевидными, либо недостаточно расставил акценты. Руководство, как и сам интерпретатор - своеобразные, но в любом случае автору конкретный респект за +2000 строк кода и сопровождение скрипта.

Все программы автоматизации, а вернее, подпрограммы EA2 располагаются в блоке "Свои данные" LCD дисплея, имя которого указывается в качестве параметра при запуске ПБ в котором размещен скрипт. Поэтому для того чтобы разместить там код, необходимо:
  1. вызвать на экран общую панель управления по Ctrl+K
  2. найти в списке блоков нужный LCD и нажать "Свои данные"
В открывшемся окне, которое трудно назвать редактором кода, нужно разместить:
  1. блок @Variables (см. ниже) в котором, если необходимо, будут храниться общие настройки
  2. прочие подпрограммы или, как я их называю, @-блоки в которых размещается код автоматизации EA2
  3. комментарии - любая строка, начинающаяся с символа * (звездочка), комментарии не переносятся, каждая новая строка также начинается со *
В итоге должно получить примерно следующее

.
Originally posted by Важно:
Сразу обратите внимание на {} (фигурные скобки). В одной строке с именем @-блока открывающую скобку нужно располагать в конце строки, во всех остальных случаях каждая фигурная скобка должны быть оформлена отдельной строкой

Далее, все инструкции интерпретатора должны начинаться с заглавной буквы и написаны в точности так, как это указано в авторском руководстве. Т.е. @Variables, но не @variables или @VARIABLES, но, например, можно написать Show properties, а можно Show Properties и это сработает.

Названия @-блоков произвольные, но в них нельзя использовать спецсимволы, а обращаться к ним при запуске через в поле "аргумент" ПБ нужно в точности как написано: LCD Code(police_turn) - символ @ при обращении использовать не надо, но не LCD Code(POLICE_tutn) тоже и для названия блока LCD - именно LCD Code, но не LCD code(police_turn).

В общем, большинство ошибок по началу будет связано именно с написанием команд и имен блоков, к которым, как и в случае с LCD Code, нужно будет обращаться с учетом регистра. Поэтому если вы собираетесь заняться автоматизацией с помощью EA2, то следует пересмотреть свое отношение к именованию блоков.

Если колесо у вас названо по умолчанию в русской локализации, типа "Колесо с подвеской 3х3 (правое)" то вот так и придется писать в коде, если нужно будет узнать его установки. Мало того, что это длинно и придется переключаться между раскладками клавиатуры, но если вы собираетесь выкладывать свой крафт в Steam, то это еще и не понятно для большинства игроков. В списке блоков панели управления напротив такого колеса будет стоять соответствующая иконка, а вот в коде ее не будет.
Полицейский разворот
Поскольку руководство называется "практическим", то и дальнейшее описание пойдет на примере автоматизации "полицейского разворота" ровера при помощи гироскопов, точнее резкой смены направления движения на противоположное, которое называют еще "реверсным" или "ретро" разворотом.

Но в начале о двух самых важных командах, без которых автоматизация в SE была бы сильно ограниченной, поскольку данные Official Guide: Programmable block - официального руководства, где описаны свойства и действия большинства игровых блоков в SE, сильно устарели.
Originally posted by Важно!:
Show properties of <Имя блока> - вывод всех доступных настроек блока
Show actions of <Имя блока> - вывод всех доступных действий, которые можно выполнить
Обе эти команды выводят на экран, связанного с ПБ экрана, перечень свойств и действий, доступных для определенного блока. Например, чтобы узнать, что можно вытащить по гироскопам M-577, достаточно достаточно выполнить простую программу из одной строки:
@test { Show properties of Gyroscope }
В авторском руководстве показан вывод на ЖК-экран, но проще смотреть результат прямо в настройках LCD Code в поле "Редактировать" т.к. оттуда легко скопировать нужное свойство и вставить его в поле "Свои данные".

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

.

Собственно, Easy Automatition 2.0 и позволяет это сделать для любого блока - в этом суть автоматизации с его помощью.

Вернемся к задаче:

Для того, чтобы резко и безопасно развернуть ровер на 180 градусов потребуется:
  • уменьшить трение колес до 0
  • включить перехват управления гироскопами по рысканью на заданное время
  • восстановить сцепление колес с грунтом, чтобы можно было затормозить, нажав "P"
Все, кроме последнего - нажатия на тормоз, можно сделать при помощи EA2. Код полный автоматизации (выглядит несколько устрашающе, но это исключительно из-за требований к оформлению кода - "одна фигурная скобка на одной строке"):
* gyar - сила перехвата ускорения по рысканью * wfric - начальная установка трения колес -1 * wturn - время разворота в мс @Variables { gyaw = 10.0 wfric = 99.0 wturn = 3000 } @friction_down { DecreaseFriction (APC, Wheels) if Friction of Wheel 5x5 FL > 0.0 { @friction_down } } @friction_up { IncreaseFriction (APC, Wheels) if Friction of Wheel 5x5 FL < wfric { @friction_up } } @yaw_up { IncreaseYaw (APC, gyroscope) if Yaw of (APC, gyroscope) << gyaw { @yaw_up } } @yaw_down { DecreaseYaw (APC, gyroscope) if Yaw of (APC, gyroscope) > 0.0 { @yaw_down } } @police_turn { * если перехват не установлен руками If Override of Gyroscope = False { * увеличиваем перехват по крену @yaw_up * уменьшаем трение до 0 @friction_down * включаем перехват гироскопами Override (APC, gyroscope) * на заданное в переменной wturn время Delay wturn * выключаем перехват гироскопами Override (APC, gyroscope) * устанавливаем значение перехвата в 0 @yaw_down * восстанавливаем трение @friction_up } }
Боевое программирование
Название раздела несколько высокопарное, но между тем кто из вас задавался вопросом "а что будет с автоматизацией на таймерах, если один из них повредится во время боя или просто при столкновении с препятствием?" В случае с M-577 при попытке сложить турель, ее, как минимум, заклинит, а как максимум - поймаем clang.

Так что автоматизировать боевые крафты нужно с учетом возможных потерь и EA2 позволяет это сделать, хотя это и не описано явно у автора. Итак, обратите внимание на два @-блока кода:
@friction_up { IncreaseFriction (APC, Wheels) if Friction of Wheel 5x5 FL < wfric { @friction_up } } @yaw_up { IncreaseYaw (APC, gyroscope) if Yaw of (APC, gyroscope) << gyaw { @yaw_up } }
Первый реализует неявный цикл увеличения высоты подвески до заданного значения (это из другой автоматизации M-577), а второй увеличивает значение перехвата гироскопом по рысканью. Первый написан неправильно, а второй правильно и вот почему.

Поскольку настройка подвески всех колес M-577 одинаковая, то я изменяю ее, основываясь на текущем значении левого переднего колеса и если его оторвет, то скрипт вылетит с ошибкой и высота подвески остальных колес не изменится.

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

В этом и заключается "боевое программирование". Есть еще одна интересная особенность связанная с обращением по имени блока. Если вы посмотрите мой крафт, то сможете убедиться, что все гироскопы в нем названы одинаково - "Gyroscope" без традиционной нумерации после имени блока, а теперь вернемся к коду "полицейского разворота" и обратим внимание на следующую строку блока @police_turn:
If Override of Gyroscope = False
Здесь запрашивается текущее значение установки перехвата первого блока с именем Gyroscope, а поскольку все гироскопы названы одинаково, то до тех пор, пока будет цел хотя бы один гироскоп условие будет работать. Это, по сути, неявное обращение к группе однотипных блоков.
Кино и немцы
В смысле "Логика и циклы", дальше вы поймете, почему раздел назван именно так. :)

В EA2 присутствует традиционная логика if-else if-else, которая является основой любой автоматизации и которая достаточно подробно описано в руководстве. Чего там не написано, так это то, что она не всегда работает в для очевидных, казалось бы вещей.

Есть в EA2 полезная команда - развернуть ротор на заданный угол с заданной скоростью: Rotate <MyRotor> то <NN> at <SS>, где MyRotor - имя блока ротора, который нужно повернуть, NN - угол на который нужно повернуть и SS - скорость с которой поворачивать. Еще есть практически та же команда ShortRotate - повернуть по кратчайшему пути.

Все вроде просто и код типа
* повернуть ротор азимута турели по кратчайшему пути до нуля ShortRotate Rotor Azimuth to 0 at 5 * приостановить программу до тех пор пока угол 0 не достигнут When Current angle Rotor Azimuth = 0 * задвинуть поршень турели Retract Piston Gun
кажется, на первый взгляд, вполне нормальным и логичным, но только не для SE. Недаром у ротора есть такая установка, как "тормозной момент", которая влияет на скорость остановки.

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

Поэтому @-блок просто зависнет, а с ней и ПБ и вся завязанная на него автоматизация. Чтобы этого избежать, нужно использовать сравнение с 10-й точкой When Current angle Rotor Azimuth = 0.0, но еще лучше сравнивать либо с близким, заведомо превышающим точное значение, числом либо вообще, использовать неравенство: When Current angle Rotor Azimuth > 0.0, но как оказалось, в случае с ротором и это еще не все. Рабочее решение с анализом угла поворота есть в блоке @gun_trigger моего крафта. Здесь речь не об этом...

... а о том, что явных циклов, к сожалению, в EA2 на момент написания этого руководства нет. Ни условного while, ни итерационного for, а между тем они крайне нужны для установки "ползунковых" значений большинства блоков.

По непонятной мне причине, в SE для того же гироскопа нет команды "установить значение перехвата по рысканью = 100%", а есть только то, что можно увидеть в меню блока, в процессе перетаскивания его из панели инструментов, вызываемой по "G", на цифровую панель управления кокпита, а именно "уменьшить перехват" или "увеличить перехват" на неизвестную заранее величину.

Для гироскопа опытным путем это значение равно 5%, а для подвески - 6 см. Поэтому цикл пришлось изобретать на ходу и выглядит он просто как в таймере - замыкание выполнения на себя
@yaw_down { * уменьшить перехват по рысканью на 5% DecreaseYaw (APC, gyroscope) * до 0 if Yaw of (APC, gyroscope) > 0.0 { * замкнуть выполнение на себя @yaw_down } }
Конечно, подобный метод небезопасен и может вызвать зависание, если условие подобрано неверно, но другого пути организовать цикл я не нашел. When здесь не применить, потому что в отличии от запуска вращения ротора, уменьшение перехвата - разовая команда, которую нужно либо тупо повторить определенное число раз, либо выполнить в цикле. Поскольку начальные настройки зависят от игрока и могут быть любыми - повторение, хоть и безопаснее, но не является гарантией достижения результата. Вот такое кино...
Ошибки молодости
Судя по тому, как EA2 обрабатывает ошибки, у автора этого замечательного скрипта все же нет опыта в написании интерпретаторов и его диагностика хоть и облегчает процесс отладки, но не на столько, чтобы ее реализацию можно было назвать удовлетворительной.

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

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

Если же при построчном разборе кода ЕА2 не находит того, что относится к интерпретатору, то управление просто передается в игровой код SE, обрабатывающий ПБ и тот рушится с конкретной "портянкой" И "это фиаско, братан!" - после исправления ошибки придется перекомпилировать скрипт, чтобы ПБ снова заработал.

.
Часто диагностика просто "молчит", но при этом ничего не работает. Обычно это связано с расстановкой скобок "{}()", стоит по привычке указать открывающую скобку в конце If или заключить условие в "()", чтобы получить в ответ пустую диагностику и легкое недоумение - "все же только что работало!".

Добавьте сюда невозможность отката в редакторе и игнорирование закрытие окна по [ x ] в правом верхнем углу, в надежде предотвратить запись только что произведенных изменений и вы познаете дзен кодирования в среде SE, но третьего не дано - вы либо пишите код, либо его не пишите.

Чтобы узнать, работает ли блок вообще или безнадежно висит просто попробуйте обратиться к несуществующему @-блоку. Если вы увидите сообщение о том что блок не найден, значит ПБ работает, а если пустую диагностику, значит требуется перекомпиляция кода скрипта EA2.

.
И последнее. Внутренняя команда определения положения поршня "Current position" зависит от локализации
@variables { piston = Current position of MyPiston } @show { WriteNew to DBG Code = "\piston" }
Вызовет ошибку "Поршень does not have a "Current position" value, в русской версии игры будет работать
@variables { piston = Текущая позиция of MyPiston } @show { WriteNew to DBG Code = "\piston" }
При этом аналогичный запрос "Current angle of MyRotor" работает нормально.Возможно, автор скрипта поправит это в следующих версиях. Я оставил запрос в комментариях под авторским руководством, но пока придется каждый раз указывать полностью имя блока в If, что неудобно при необходимости переименовать поршень или при случайном изменении его имени.
Unofficial Patch
I use the unofficial patch of the script in my craft. It adds some new features and fixes some bugs related to the crash of the interpreter. The main differences from the official release:

  • The code of @-blocks is now stored in the Custom Data of the program block and does not require a separate LCD panel, therefore, when calling an @-block with a parameter via the system panel or assigning a button in the numeric keypad, you no longer need to specify the LCD name, only the name @-block (no leading @).

  • Echo() is displayed regardless of the setting of the debug value, if true then the contents of Storage and Runtime are also displayed

  • The Show command displays both Properties and Actions on the debug screen or, if it is not specified, on the program block display, for example, Show My Rotor, instead of Show proprties of My Rotor and Show actions of My Rotor

  • Properties previously associated with Cockpit only are overridden for any IMyShipController

  • Read added to block properties Custom Data
    if CustomData of LCD Solar = 0 { WriteNew to PBL ST = "True"; }

  • Added operation of entering the string "?" - case insensitive; "??" - case sensitive
    if CustomData of LCD Solar ?? "online" { WriteNew to PBL ST = "True"; }

  • Added output of the status of the landing gears (magnetic plates): LockMode = Unlocked / ReadyToLock / Locked (case-sensitive output)

  • All Write and Clear LCD commands are displayed on the LCD panel specified in "to" or on the 1-st cockpit screen (pilot's seat or control station), if a block of this type is specified in "to", for example WriteNew to My Cockpit = "Missile launch", otherwise on the display of the program block.

  • added Surface* commands to output to LCD panel. The syntax is the same as Write *, but the value does not need to be quoted: SurfaceFontSize - font size from 0 to 10; SurfaceAlign - left / right / center alignment
    SurfaceFontSize to LCD Cruise = 1.2 SurfaceAlign to LCD Cruise = left

  • if there is a symbol in front of the block name (does not apply to program blocks):

    "#", then the command (or property) is applied to all blocks whose name exactly matches the specified one

    "!", then the command (or property) is applied only to the first block with this name

    "*", then the command (or property) is applied to all blocks starting with this name
    otherwise, the command (or property) is applied to all blocks that have the specified name in their name
    Gyroscope Gyroscope Gyroscope 1 Main Gyroscope Override Gyroscope = for everyone Override !Gyroscope = first only Override #Gyroscope = for the first two Override *Gyroscope = for the first three

  • added the Override* command, which sets the slider values of some blocks:

    OverrideTrust - interception of control of thrusters 0/100%
    OverrideGyroPitch - pitch control of the gyroscope -30/30 [-60/60] rpm large [small] grid
    OverrideGyroYaw - yaw control of the gyroscope -30/30 [-60/60] rpm large [small] grid
    OverrideGyroRoll - roll control of the gyroscope -30/30 [-60/60] rpm large [small] grid

    OverrideThrust to (TDrone, Ion Up) = 100 OverrideGyroPitch to #Gyroscope = 55

  • added the directive MyGrid to the Rename command - renaming the grid with the ability to set a random number at the end of the name using "+ N", where N = the number of digits

    @Variables { DroneName = "My Drone" } * usual renaming Rename MyGrid = "New_Drone" * rename using a variable Rename MyGrid = "DroneName" * rename using a random 4-digit number Rename MyGrid = "Drone_ + 4"

See last version of patch in my craft: https://steamhost.cn/steamcommunity_com/sharedfiles/filedetails/?id=2607611791