В прошлой части мы установили IDE и создали шаблон проекта. Теперь поучимся немного программировать. Скрипты в данной игре пишутся на языке C# - он достаточно прост для изучения, даже если вы программирование знаете только по школьной информатике.
Суть всех скриптов заключается в том, что мы получаем параметры с одних блоков корабля и управляем по некоторому алгоритму другими блоками. Функционал C# в скриптах довольно сильно ограничен, поэтому глубоких знаний не потребуется.
1. Взаимодействие с кораблём
GridTerminalSystem
GridTerminalSystem - это основной и единственный способ взаимодействия с блоками корабля. Предоставляет возможность получить доступ к любому блоку по имени, типу или принадлежности к группе (рис. 1).
IDE очень умная и подсказывает различные варианты реализации, также к каждой функции есть описание на английском.
Что GridTerminalSystem, что блоки в скрипте представлены интерфейсами ("IMy...."). Интерфейсы в C# - это ссылки на методы, но без реализации. Проще говоря заготовка. В скриптах это используется для описания все возможных блоков, к примеру все двери имеют интерфейс "IMyDoor", а все экраны, даже встроенные в кабину - "IMyTextSurface". Через один интерфейс мы можем взаимодействовать со всеми блоками одного типа, что удобно и универсально.
Runtime
Runtime - это среда выполнения скрипта. Раньше, в очень старые времена единственным способом запуска скрипта был пинок с кнопки, либо таймером, который помимо программируемого блока пинал и себя. Потом разработчики одумались и добавили возможность зациклить скрипт через Runtime.
Классическое использование данной функции следующее:
Runtime.UpdateFrequency = UpdateFrequency.Update10; // Запуск скрипта раз в 10 тиков
Также можно получить время с предыдущего запуска скрипта:
int seconds = Runtime.TimeSinceLastRun.Seconds; // Время в секундах с предыдущего запуска скрипта
Вообщем штука полезная, но в данный момент сильно зацикливаться на ней не будем.
2. Структура скрипта
Если очистить стандартный пример от лишнего кода, то получим следующую заготовку (рис. 2).
Директивы #region нужны для сворачивания кода и если свернуть код из примеров, то получим прям шаблон из программируемого блока (рис. 3 и рис. 4).
Ну а теперь пришло время разобраться, что за что отвечает:
- public Program() - конструктор класса скрипта (наш скрипт это класс, который выполняется внутри кода игры). Он вызывается один раз при первом запуске. В нём обычно инициализируют блоки, настраивают всякое. Проще говоря - данная функция служит для инициализации начальных параметров и поиска блоков.
- public void Main(string args, UpdateType updateSource) - основная функция скрипта, она выполняется каждый раз при запуске скрипта. В ней и происходит выполнение основного алгоритма. string args - аргументы запуска, представляет собой строку, которую можно указать при выполнении его с кнопки, из кабины или таймера. Часто используется для управления скриптом.
- public void Save() - эта функция служит для сохранения параметров программы. Для этого используется поле Storage.
3. Основы C#
В целом когда стало более менее понятно, что к чему, то пришло время освоить основы основ C#.
C# крайне схож по синтаксису на язык C++, а вот структурно косит под Java, так что для программистов Ардуинок здесь многое будет понятно, да и Jav-истам тоже.
Типы данных
Базовые типы данных тут вполне стандартны:
- int - целые числа со знаком;
- uint - целые числа без знака;
- float - числа с плавающей точкой одинарной точности;
- double - числа с плавающей точкой двойной точности;
- string - строки;
- char - символ;
- bool - логический тип (ture или false).
Также есть всевозможные варианты int и uint: 16-битные, 32-битные и 64-битные.
Ветвления - if else (Если иначе)
Тут как и везде существует конструкция if else (рис. 5).
Операторы сравнения тут такие же как и в С++:
- Больше ">";
- Меньше "<";
- Равно "==";
- Неравно "!=";
- Больше либо равно ">=";
- Меньше либо равно "<=".
Булевы операторы также имеют схожий вид:
- Логическое "И" - "&&";
- Логическое "ИЛИ" - "||";
- Логическое "НЕ" - "!".
Ветвления - switch case
Если нужен перебор аргумента (тот же вариант с запуском по аргументу) используется конструкция switch case (рис. 6).
- switch (<аргумент>) - это объявление конструкции и указание переменной, от значения которой и будет меняться выполнения кода.
- case <значение>: - это ветвление, если аргумент будет равен значению, то код после двоеточия будет выполняться до break; Если код длиннее одной команды, то используются скобочки "{}".
- break; - точка останова, ещё используется в циклах для преждевременного выхода по условию.
- default: - действие по умолчанию. Если ни одно условие не сработало, то будет выполнен блок default. Этот блок может отсутствовать, если действие по умолчанию в программе не требуется.
Цикл со счётчиком - for
Классический школьный цикл "for". В нём явно указывается итератор, условие по которому цикл закончится, а также действие с итератором (рис. 7).
Самый частый вариант использования: int i = 0; i < какой-либо константы или переменной; и i++ - инкремент итератора (i = i + 1).
Чуть реже встречающийся обратный вариант: int i = чему-то; i > 0; и i-- - декремент итератора (i = i - 1).
Сам итератор может быть вполне и типом float или double, а инкремент или декремент может быть на дробные числа. Всё зависит от конкретного сценария использования.
Сам цикл используется для повторения чего-либо N-раз, либо для перебора массивов, списков и прочих структур данных.
Цикл со счётчиком - foreach
Этот тип уже поинтереснее. Представляет собой специальный цикл для перебора массивов, списков и прочих структур данных. В скриптах поголовно используется для перебора блоков (рис. 8).
В данном случае переменная door - это одна из дверей списка doors. В целом это аналогично конструкции: IMyDoor door = doors[i], где i - итератор в обычном цикле for.
Цикл с условием - while
В скриптах редкая вещь, так как может положить скрипт, если зациклится. Но пару слов всё равно скажу. Синтаксис показан на рисунке 9.
Условие понятно, что может быть любым, но есть опасность, что при сложных условиях данная конструкция может зациклиться. Да и в целом сценарий использования редкий, так как сам скрипт является бесконечным циклом, в котором можно делать всякое.
Конструкции по работе с интерфейсами - as is
Часто приходится проверять или менять интерфейс объекта. К примеру, некоторые функции GridTerminalSystem возвращают общий интерфейс IMyTerminalBlock, а нам надо конкретный, к примеру дверь IMyDoor. Что делать? Ответ есть - функция as (как). Синтаксис на рис. 10.
Для компактности можно избавиться от переменной doorBlock и делать преобразование сразу (рис. 11).
Если преобразование интерфейсов не удастся, то скрипт упадёт.
Функция is позволяет определить принадлежность объекта к типу. Часто используется для сравнения чего либо с null. Пример на рис. 12.
4. Немного практики
По классике жанра напишем скрипт, который выведет на экран сообщение "Привет мир!" (рис. 13).
Далее компилируем скрипт командой "BuildCommandBlockFile" (рис. 14).
Копируем содержимое и вставляем в программируемый блок (рис. 15).
Далее называем экран "LCD" и выставляем в нём текстовый режим (рис. 16).
И запускаем программу (рис. 17).
Работает!
Заключение
Сегодня мы изучили синтаксис C# и общую структуру скриптов. В следующем занятии мы рассмотрим математику, векторную алгебру и начнём писать автопилот на колёсиках, который я когда-то уже делал.
Пишите комментарии, чтобы вы хотели ещё добавить в это небольшое руководство.
Часть 3: https://dzen.ru/a/aC4raVs303tvJdrP