Найти в Дзене

Space Engineers - Пишем скрипты. Часть 2. Изучаем C#

В прошлой части мы установили IDE и создали шаблон проекта. Теперь поучимся немного программировать. Скрипты в данной игре пишутся на языке C# - он достаточно прост для изучения, даже если вы программирование знаете только по школьной информатике. Суть всех скриптов заключается в том, что мы получаем параметры с одних блоков корабля и управляем по некоторому алгоритму другими блоками. Функционал C# в скриптах довольно сильно ограничен, поэтому глубоких знаний не потребуется. GridTerminalSystem - это основной и единственный способ взаимодействия с блоками корабля. Предоставляет возможность получить доступ к любому блоку по имени, типу или принадлежности к группе (рис. 1). IDE очень умная и подсказывает различные варианты реализации, также к каждой функции есть описание на английском. Что GridTerminalSystem, что блоки в скрипте представлены интерфейсами ("IMy...."). Интерфейсы в C# - это ссылки на методы, но без реализации. Проще говоря заготовка. В скриптах это используется для описания
Оглавление

В прошлой части мы установили IDE и создали шаблон проекта. Теперь поучимся немного программировать. Скрипты в данной игре пишутся на языке C# - он достаточно прост для изучения, даже если вы программирование знаете только по школьной информатике.

Суть всех скриптов заключается в том, что мы получаем параметры с одних блоков корабля и управляем по некоторому алгоритму другими блоками. Функционал C# в скриптах довольно сильно ограничен, поэтому глубоких знаний не потребуется.

1. Взаимодействие с кораблём

GridTerminalSystem

GridTerminalSystem - это основной и единственный способ взаимодействия с блоками корабля. Предоставляет возможность получить доступ к любому блоку по имени, типу или принадлежности к группе (рис. 1).

Рис. 1 - Всевозможные функции GridTerminalSystem
Рис. 1 - Всевозможные функции GridTerminalSystem

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

Что GridTerminalSystem, что блоки в скрипте представлены интерфейсами ("IMy...."). Интерфейсы в C# - это ссылки на методы, но без реализации. Проще говоря заготовка. В скриптах это используется для описания все возможных блоков, к примеру все двери имеют интерфейс "IMyDoor", а все экраны, даже встроенные в кабину - "IMyTextSurface". Через один интерфейс мы можем взаимодействовать со всеми блоками одного типа, что удобно и универсально.

Runtime

Runtime - это среда выполнения скрипта. Раньше, в очень старые времена единственным способом запуска скрипта был пинок с кнопки, либо таймером, который помимо программируемого блока пинал и себя. Потом разработчики одумались и добавили возможность зациклить скрипт через Runtime.

Классическое использование данной функции следующее:

Runtime.UpdateFrequency = UpdateFrequency.Update10; // Запуск скрипта раз в 10 тиков

Также можно получить время с предыдущего запуска скрипта:

int seconds = Runtime.TimeSinceLastRun.Seconds; // Время в секундах с предыдущего запуска скрипта

Вообщем штука полезная, но в данный момент сильно зацикливаться на ней не будем.

2. Структура скрипта

Если очистить стандартный пример от лишнего кода, то получим следующую заготовку (рис. 2).

Рис. 2 - Стандартный шаблон скрипта
Рис. 2 - Стандартный шаблон скрипта

Директивы #region нужны для сворачивания кода и если свернуть код из примеров, то получим прям шаблон из программируемого блока (рис. 3 и рис. 4).

Рис. 3 - Прям код из блока, только функции Save не хватает
Рис. 3 - Прям код из блока, только функции Save не хватает
Рис. 4 - А это из игры
Рис. 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).

Рис. 5 - Стандартное ветвление, синтаксис очень похож на С++
Рис. 5 - Стандартное ветвление, синтаксис очень похож на С++

Операторы сравнения тут такие же как и в С++:

  • Больше ">";
  • Меньше "<";
  • Равно "==";
  • Неравно "!=";
  • Больше либо равно ">=";
  • Меньше либо равно "<=".

Булевы операторы также имеют схожий вид:

  • Логическое "И" - "&&";
  • Логическое "ИЛИ" - "||";
  • Логическое "НЕ" - "!".

Ветвления - switch case

Если нужен перебор аргумента (тот же вариант с запуском по аргументу) используется конструкция switch case (рис. 6).

Рис. 6 - switch case со строковым аргументом
Рис. 6 - switch case со строковым аргументом
  • switch (<аргумент>) - это объявление конструкции и указание переменной, от значения которой и будет меняться выполнения кода.
  • case <значение>: - это ветвление, если аргумент будет равен значению, то код после двоеточия будет выполняться до break; Если код длиннее одной команды, то используются скобочки "{}".
  • break; - точка останова, ещё используется в циклах для преждевременного выхода по условию.
  • default: - действие по умолчанию. Если ни одно условие не сработало, то будет выполнен блок default. Этот блок может отсутствовать, если действие по умолчанию в программе не требуется.

Цикл со счётчиком - for

Классический школьный цикл "for". В нём явно указывается итератор, условие по которому цикл закончится, а также действие с итератором (рис. 7).

Рис. 7 - Пример цикла for. Опять же синтаксис очень похож на С++
Рис. 7 - Пример цикла for. Опять же синтаксис очень похож на С++

Самый частый вариант использования: int i = 0; i < какой-либо константы или переменной; и i++ - инкремент итератора (i = i + 1).

Чуть реже встречающийся обратный вариант: int i = чему-то; i > 0; и i-- - декремент итератора (i = i - 1).

Сам итератор может быть вполне и типом float или double, а инкремент или декремент может быть на дробные числа. Всё зависит от конкретного сценария использования.

Сам цикл используется для повторения чего-либо N-раз, либо для перебора массивов, списков и прочих структур данных.

Цикл со счётчиком - foreach

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

Рис. 8 - Перебор дверей в списке дверей
Рис. 8 - Перебор дверей в списке дверей

В данном случае переменная door - это одна из дверей списка doors. В целом это аналогично конструкции: IMyDoor door = doors[i], где i - итератор в обычном цикле for.

Цикл с условием - while

В скриптах редкая вещь, так как может положить скрипт, если зациклится. Но пару слов всё равно скажу. Синтаксис показан на рисунке 9.

Рис. 9 - Простой пример цикла while
Рис. 9 - Простой пример цикла while

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

Конструкции по работе с интерфейсами - as is

Часто приходится проверять или менять интерфейс объекта. К примеру, некоторые функции GridTerminalSystem возвращают общий интерфейс IMyTerminalBlock, а нам надо конкретный, к примеру дверь IMyDoor. Что делать? Ответ есть - функция as (как). Синтаксис на рис. 10.

Рис. 10 - Вполне типичное применение функции as
Рис. 10 - Вполне типичное применение функции as

Для компактности можно избавиться от переменной doorBlock и делать преобразование сразу (рис. 11).

Рис. 11 - Компактная запись
Рис. 11 - Компактная запись

Если преобразование интерфейсов не удастся, то скрипт упадёт.

Функция is позволяет определить принадлежность объекта к типу. Часто используется для сравнения чего либо с null. Пример на рис. 12.

Рис. 12 - Проверяем, действительно ли door найдена. Оператор "!" используется для инверсии сравнения
Рис. 12 - Проверяем, действительно ли door найдена. Оператор "!" используется для инверсии сравнения

4. Немного практики

По классике жанра напишем скрипт, который выведет на экран сообщение "Привет мир!" (рис. 13).

Рис. 13 - Вот такой простой скрипт
Рис. 13 - Вот такой простой скрипт

Далее компилируем скрипт командой "BuildCommandBlockFile" (рис. 14).

Рис. 14 - Вот такой файл получим на выходе
Рис. 14 - Вот такой файл получим на выходе

Копируем содержимое и вставляем в программируемый блок (рис. 15).

Рис. 15 - Скопировали скрипт
Рис. 15 - Скопировали скрипт

Далее называем экран "LCD" и выставляем в нём текстовый режим (рис. 16).

Рис. 16 - Настраиваем текстовую панель
Рис. 16 - Настраиваем текстовую панель

И запускаем программу (рис. 17).

Рис. 17 - Результат выполнения программы
Рис. 17 - Результат выполнения программы

Работает!

Заключение

Сегодня мы изучили синтаксис C# и общую структуру скриптов. В следующем занятии мы рассмотрим математику, векторную алгебру и начнём писать автопилот на колёсиках, который я когда-то уже делал.

Пишите комментарии, чтобы вы хотели ещё добавить в это небольшое руководство.

Часть 3: https://dzen.ru/a/aC4raVs303tvJdrP