Space Engineers

Space Engineers

72 ratings
"Занимательная скриптомеханика" ИЛИ "Моя первая РЛС"
By ☭Android☭
Здесь будет описано устройство полноценной РЛС в игре, их виды, устройство, частые ошибки и применение.
Кроме того здесь автор вместе с вами создаст первую и самую простую РЛС, показав процесс и принцип написания подобных систем.

3
2
   
Award
Favorite
Favorited
Unfavorite
Введение
Вступление
Вступление

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

РЛС в игре является целой системой обнаружения, опознавания и сопровождения целей и при этом не является РЛС по принципу своей работы (в игре нет радиоволн).
Гораздо корректнее подобную систему называть лидаром или как-либо ещё, но, в качестве потакания привычному названию, примем за данность, что подобную поисковую систему мы называем РЛС или просто - радаром.
Подобные системы необходимы для различных задач. Их на самом деле неисчислимое множество, поэтому перечислим и разберём наиболее очевидные из них:
  • Автопилот
  • Ракетные системы
  • Поиск целей на запредельных дистанциях
  • Опознавание и целеуказание

Так или иначе приведённые выше задачи пересекаются друг с другом и поэтому рассматривать их в полном отрыве друг от друга принципиально невозможно (технические решения для радаров любой из этих целей практически одинаковы).
Так повелось, что в мастерской Steam (работы, публикуемые для этой игры) есть некоторый ассортимент радиолокационных систем. Относительно разнообразный и с разной степенью полезности. Причины того, что подобных систем не слишком много очевидны.
Перечислим наиболее распространенные из них:
  • Засекреченность подобных систем (военное оружие обычно делают для себя, а не для других)
  • Сложность (РЛС имеющая смысл не самый простой скрипт в описании, пусть и в то же время, при наличии знаний и опыта, простой)
  • Ограниченность кол-ва авторов выбившихся "в люди" (сложные скрипты пишут преимущественно те, чей труд хоть как-то оценивают).
  • "Специфика" задач РЛС (будет рассмотрено в конце)


Термины
Ниже приведён список терминов и сокращений использующихся в этом руководстве. Некоторые термины выдуманы и используются автором.
  • РЛС - Радиолокационная Система
  • УР - Управляемая Ракета
  • Лидар - поисковая система основанная на лазерах
  • Рейкаст - Raycast, функция пуска луча
  • ЗРК - Зенитно-Ракетный Комплекс
  • ЗУР - Зенитная Управляемая Ракета
Примеры РЛС в мастерской, рассмотрение задач РЛС
Продолжение темы РЛС в мастерской

Было бы неплохо разделить РЛС в мастерской на две категории: устаревшие и актуальные.
Смысл этих категорий максимально прост: устаревшие радары используют функцию удаленную по жалобам игроков - GetFreeDestination() (GFD). Определить такой радар по фотографии очень просто - он не имеет камер, турелей и использует только блок дистанционного управления (далее - ДУ). Подобные радары отличались эффективностью, но, на данный момент, их использование просто невозможно.
Актуальных же радаров немного - из наиболее популярных можно было бы выделить только два радара: Whip's Turret Based Radar и Lidar Mapping Script.
Отсеивая бесполезные радары не имеющие практического, а не декоративного функционала из этой двойки останется только один радар - Lidar Mapping Script.

Определить не по названию их так же просто: они используют турели и камеры. Пример подобной РЛС можно видеть наглядно на этом истребителе:


(РЛС "Сапфир")

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


Продолжение темы разновидности РЛС
Вспомним основные задачи, которые могут выполняться системой:
  • Автопилот
  • Ракетные системы
  • Поиск целей на запредельных дистанциях
  • Опознавание и целеуказание

Рассмотрим их по порядку.
1. Автопилот.


Подобные РЛС обычно подключены модулем или встроены в скрипт автопилота. Автопилоту подобные системы необходимы для определения помех передвижению и поиска поверхностей. Автопилот в данном случае может быть любым - речь может идти даже о роботах.
Подобные РЛС обязательно используют камеры (почему - будет рассмотрено ниже), иногда - сенсоры и сканируют ограниченные пространства, но - максимально часто.

2. Ракетные системы.


Выбранная в данном случае задача не совсем удачна - дело в том, что ракетная система может быть разной. Для краткости выделим два основных типа: автоматическая и ручная.
Автоматическая ракетная система это автономный ЗРК. К радарам подобных систем требования самые серьёзные - они должны максимально быстро обнаружить цель типа "истребитель" на расстоянии 2 км+, после чего взять цель на сопровождение и поразить её ракетой. По причине того, что добиться обнаружения целей с помощью рейкаста в режиме близком к реальному времени целей класса "истребитель" на такой дистанции невероятно сложно (но - можно), подобных систем дальности выше, чем 1 км в мастерской практически нет.
Ручная ракетная система это стандартная схема "Оператор - РЛС". Обнаружение целей в такой системе берёт на себя оператор, РЛС осуществляет лишь захват и сопровождение целей. Нередко реализуют и комбинированные системы - когда работа оператора дополняется поисковой системой самой РЛС.


3. Поиск целей на запредельных дистанциях.


РЛС, выполняющие подобные задачи, как правило, называют телескопами. Скриптов на такие системы обычно немного и назвать их в полной мере эффективными довольно затруднительно. Подобные системы, как правило, занимаются исключительно поиском целей, причем - разово. Сканирование проводится на дистанцию 20 км+ (бывают системы и на 1000 км) один раз. Координаты всех обнаруженных целей записываются.
Длительность сканирования в обычных условиях при условии отсутствия заряда у камер и сканирования на дистанции 100 км может идти часами (в зависимости от плотности лучей сканирования).

4. Опознавание и целеуказание.


Большая часть РЛС, за исключением РЛС автопилотов, способны так или иначе выполнить эти задачи. Но необходимо заметить, что если речь идёт о целеуказании с помощью статичных координат и опознавании цели, то для этих задач используется крохотный "огрызок" от РЛС - дальномер (или сканер).
Основы РЛС
Блоки используемые в РЛС, устройство, принцип работы
Начать данный раздел следует с описания блоков, которые могут использоваться в РЛС и уточнения тех функций, которые нам в них понадобятся.

Блоки используемые в РЛС

Рассмотрим по порядку перечень основных блоков:
  • Турели
  • Блок ДУ (или кабина)
  • Дисплеи (отображение информации)
  • Камеры

1. Турели


Турели необходимы для гарантированного обнаружения целей на дистанции до 600 м. (малая сетка) и на дистанции до 800 м. (большая сетка).
Для их работы в качестве "сенсора" необходимы следующие условия:
1. Наличие боекомплекта у турели
2. Турель может обнаружить цель

При соблюдении этих условий турель атакует обнаруженную цель и передает данные о цели в скрипт, если требуется.
Некоторые нужные функции:
- GetTargetedEntity();
- ResetTargetingToDefault();

2. Блок ДУ


Блок ДУ необходим для определения ориентации корабля (если принять одну из проекций ДУ за проекцию всего корабля) и для определения своей точной скорости.
Некоторые нужные функции:
- GetShipVelocities().LinearVelocity;
- GetShipSpeed();

3. Дисплей


Дисплеи необходимы для передачи информации оператору в удобном для него виде. Это может быть как текст, так и изображение.
Некоторые нужные функции:
- WriteText();

4. Камеры


Камеры

Камеры необходимы
Некоторые нужные функции:
- Raycast();


Принцип работы РЛС

Принцип работы любой РЛС заключается в использовании функций блоков доступных для скрипта и обработки и использования получаемой информации.
Процесс работы стандартной РЛС можно расписать следующим образом:
- РЛС получает информацию с турелей и камер
- РЛС обрабатывает информацию
- РЛС использует информацию (захват и сопровождение цели, поиск целей и т.д.)
- РЛС выводит заданную информацию пользователю
- РЛС запускается сначала

Простейшую РЛС, которая не имеет практического функционала, написать очень просто, написание такой РЛС будет рассмотрено позже.
Эффективное использование блоков РЛС
Здесь будут рассмотрены способы наиболее эффективно использовать блоки РЛС, их недостатки. Начать данный раздел стоит с описания недостатков блоков.

Слабые стороны блоков и их решение

Было бы неплохо рассмотреть их по установившемуся распорядку:

Слабые стороны турелей:

- Большой, но ограниченный угол обзора
- Зависимость от боекомплекта (отсутствует в творческом режиме)
- Малая дальность видимости
- Невозможность видеть цели за препятствиями (препятствием считается любой объект не имеющий функциональных блоков и двигающийся со скоростью менее 5 м\с (или стационарный))

Решения:
- Подача боекомплекта через конвейеры
- Размещение турелей на обширной площади, в том числе вдали от базы
- Уничтожение всех препятствий
- Дополнение турелей камерами

Слабые стороны использования дисплеев и ДУ:
- Вывод из строя РЛС при уничтожении единственного ориентира
- Отсутствие обратной связи с оператором при уничтожении дисплеев

Решения:
- Добавление поддержки РЛС сразу двух ориентиров: кабины оператора и блока ДУ, привязка к другим ориентирам
- Дублирование дисплеев, повышение ремонтопригодности, дополнение дисплеев другими способами обратной связи (антенны, звуковые сигналы, проекторы)

Наиболее эффективное использование блоков РЛС

Продолжая установленный распорядок:

Эффективное использование турелей:

К счастью, эффективное использование турелей в целях РЛС значительно проще, нежели их расстановка для эффективного военного применения.
Необходимо понимать, что одна турель может сопровождать и атаковать только одну цель. Таким образом выполняется формула одна турель = одна цель. Турель захватывает ближайшую к себе цель наводясь преимущественно на её ловушки и функциональные блоки.
Тем не менее, турель может обнаружить цель и находясь под бронёй корабля, не имея прямой видимости на цель (при условии, что линия обзора закрыта самим кораблём, а не чем-либо ещё). К сожалению, по непонятным причинам зачастую такое наведение непостоянно, а иногда и вовсе отсутствует (частота обновления цели недостаточная для целеуказания), но, зато, турель автоматически переключается на более близкую цель.
Кроме того, важно знать, что скрипт не может брать целеуказание с турелей, которые он сам наводит. И хотя вопроса наведения турелей через скрипт мы касаться не будем, это знание пригодится в будущем.
Поэтому турели можно назначить любые - главное обеспечить круговой обзор. Проще всего для поиска целей использовать турели расставленные для обороны. На эффективность турелей это никак не повлияет, но, зато, упростит конструкцию и сделает её рациональнее. Не забудьте выставить настройки для турелей.

Эффективное использование блока ДУ и дисплеев:
Пожалуй самое очевидное, что ДУ (наш ориентир) должен быть как можно ближе к прицелу, которым мы хотим захватить цель (в случае, если ручного захвата цели в РЛС нет, то это не понадобится).
Дисплеи же должны быть защищены (даже пара пуль выведет их из строя, учтите это!), желательно находиться под остеклением и не закрывать минимально необходимый для пилота обзор. Будет разумно распределить дисплеи между несколькими местами, куда
часто смотрит оператор: прицел и кабина. Расстановка дисплеев перед прицелом (камерой) и кабиной не составит труда, но не должна в итоге закрыть оператору обзор.

Эффективное использование камер:
Многие аспекты эффективного использования камер в целях рейкаста были подняты в предыдущих руководствах, поэтому стоит остановиться на основном тезисе: суммарный угол обзора всех камер на корабле должен быть максимален.
У одной камеры угол обзора равен 45'. Повысить угол обзора можно с помощью специального расположения оных.
Виды расположения:

"Пирамидка"


"Плоскость"


"Копьё"


Как видно по схемам наиболее эффективными схемами покрытия являются "Пирамидка" и "Копьё", следовательно, использовать необходимо именно их.
Однако таким образом можно решить лишь проблему угла видимости, а ведь у камер есть ещё одна серьёзная проблема: малая скорость заряда (2 км\с).
Это означает, что раз в секунду камера может просканировать только 2 км, однако до рестарта при условии включения функции рейкаста у камеры через скрипт этот заряд накапливается в камере, позволяя в будущем просканировать либо большее расстояние, либо тоже самое, но чаще.
Сгладить эту проблему можно четырьмя способами:
1. Увеличение числа камер (они будут сканировать поочерёдно)
2. Предварительная "накачка" камер
3. Третий способ автором рассматриваться наглейшим образом не будет.
4. Перенос отслеживания целей на допустимых для того дистанциях с камер на турели
Создаем свою РЛС
Строим аппаратуру для РЛС
Перед тем, как написать скрипт для РЛС необходимо "рабочее место" для этого скрипта - сама РЛС!

Строительство РЛС

Для РЛС нам необходимы только конкретные блоки, с которыми РЛС будет взаимодействовать. Где они будут находиться для нас значения не имеет, главное - чтобы на одном строении. Таким образом можно начать строительство нашего радара на любом корабле или станции, однако, для удобства тестирования, мы будем строить РЛС на корабле.





Для уточнения вот перечисление блоков на нашем корабле:
  • Ускорители
  • Гироскоп
  • ДУ (ориентир)
  • Кабина
  • Камера
  • Дисплеи
  • Программируемый блок ("рабочее тело" скрипта)
  • Динамик
  • Источник питания
  • Блоки брони
  • Турель

Для отсутствия путаницы условимся назвать требуемые нам блоки на корабле одинаково:

  • Программируемый блок - ПБ
  • Дистанционное управление - ДУ
  • Динамик - ДНМК
  • Камера - ПРИЦЕЛ
  • Дисплей 1x1 1 - Дисплей РЛС 1
  • Дисплей 1x1 2 - Дисплей РЛС 2
  • Дисплей 1x1 3 - Дисплей РЛС 3
  • Дисплей 1x1 4 - Дисплей РЛС 4
  • "название турели" - Турель

После проверки функционирования корабля, если всё хорошо, то можно приступить к написанию скрипта (не забудьте впихнуть куда-нибудь турель!).
Пишем скрипт для РЛС ч.1
Очевидно, что если вы не знаете C# на базовом уровне, то написать скрипт вы самостоятельно не сможете. Однако совершенно так же очевидно, что рядом с вами находится автор (не забудьте пихнуть его, пока он не уснул)

Список необходимых действий

Ещё раз напомню, что автор не является профессиональным программистом.
Само собой разумеется, что как и в любом другом языке программирования, перед началом работы нам необходимо обозначить переменные (типы данных), которые мы будем использовать. Без этого никакая дальнейшая работа невозможна. Пример объявления переменной типа <string>:
string Dda = "";
string - тип данных (string - строка, текстовый тип данных).
Dda - название переменной.
В данном примере переменной при объявлении сразу же присваивается значение, на самом деле присваивать ей значение при объявлении обычно необязательно.

Далее необходимо присвоить переменным значение для его использования (если это не было сделано)
Dda = "Хорошо живёт на свете Винни-Пух!";
Учтите, что переменная Dda должна быть объявлена перед этим, иначе компьютер не поймет что вы имеете ввиду.

Сразу же после этого можно начинать писать "рабочую часть" скрипта

Таким образом, выделим следующий порядок действий:
  • Объявление переменных
  • Присвоение значения переменным (до их использования)
  • Написание рабочего тела скрипта

Пишем скрипт

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

Первым, что мы напишем, разумеется, будет объявление переменных.
Не пропускайте ";"!

[НАПИСАТЬ]
string RC_name = "ДУ"; string LCD_name = "Дисплей РЛС"; string dyn_name = "ДНМК"; string cam_name = "ПРИЦЕЛ"; string tur_name = "Турель"; string s = ""; // List<IMyTextPanel> LCD = new List<IMyTextPanel>(); IMyRemoteControl RC; IMyLargeTurretBase turret; IMySoundBlock dyn; IMyCameraBlock cam; // MyDetectedEntityInfo target; Vector3D MyPos; MyDetectedEntityInfo camray;
[НЕ ПИСАТЬ]

Теперь по-видимому надо объяснить что сейчас произошло.

В ряду переменных string мы объявили переменные с названиями блоков (они должны совпадать с названиями на корабле, они точно такие же, какие мы условились сделать).
Очевидно, что они нам понадобятся для поиска блоков.

А вот здесь
List<IMyTextPanel> LCD = new List<IMyTextPanel>();
всё сложнее.
Здесь был объявлен список, этакий массив блоков. В нём будут храниться все дисплеи, что мы найдем. Обратиться к конкретному дисплею в списке можно будет по индексу.
Следующие переменные объявляют один блок, тип которого обозначен перед названием.
IMyRemoteControl RC; // Блок ДУ IMyLargeTurretBase turret; // Турель (любая) IMySoundBlock dyn; // Блок, издающий звук (любой) IMyCameraBlock cam; // Камера

Остальные переменные понадобятся позже.

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

[НАПИСАТЬ]
Program() { RC = GridTerminalSystem.GetBlockWithName(RC_name) as IMyRemoteControl; GridTerminalSystem.GetBlocksOfType(LCD); dyn = GridTerminalSystem.GetBlockWithName(dyn_name) as IMySoundBlock; turret = GridTerminalSystem.GetBlockWithName(tur_name) as IMyLargeTurretBase; cam = GridTerminalSystem.GetBlockWithName(cam_name) as IMyCameraBlock; Runtime.UpdateFrequency |= UpdateFrequency.Update10; }
[НЕ ПИСАТЬ]

Объяснение на примере отдельно взятых строчках:
RC = GridTerminalSystem.GetBlockWithName(RC_name) as IMyRemoteControl;

здесь переменной RC присваивается через функцию GridTerminalSystem.GetBlockWithName(RC_name). Эта функция ищет на корабле блок с заданным названием. В нашем случае в качестве названия мы взяли переменную RC_name (берётся её значение).
as IMyRemoteControl - подразумевается тип блока, который ищется. В нашем случае - блок ДУ.

GridTerminalSystem.GetBlocksOfType(LCD);
Здесь дается команда на поиск всех блоков типа "Дисплей". Все найденные блоки помещаются в список LCD.

Runtime.UpdateFrequency |= UpdateFrequency.Update10;
Здесь скрипт ставится на самозапуск. То есть скрипт будет запускаться каждые 10 тиков.
Можно выбрать другую частоту, всего их несколько:
Runtime.UpdateFrequency |= UpdateFrequency.None; Runtime.UpdateFrequency |= UpdateFrequency.Once; Runtime.UpdateFrequency |= UpdateFrequency.Update1; Runtime.UpdateFrequency |= UpdateFrequency.Update10; Runtime.UpdateFrequency |= UpdateFrequency.Update100;

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

[НАПИСАТЬ]
void Main(string args) { target = turret.GetTargetedEntity(); // Взятие информации о цели с турели if (LCD.Count > 0) { foreach (IMyTextPanel lcd in LCD) // Цикл перебора дисплеев в списке { if (lcd.CustomName == LCD_name + " 1") // Выбор дисплеев с названием и еденицей в конце { lcd.Enabled = true; // Включение дисплея lcd.WriteText("РАДАР :ВИННИ-ПУХ: \n"); // Пишем инфо о радаре в дисплей if (turret.HasTarget) lcd.WriteText("РАДАР :ВИННИ-ПУХ: \n" + "Название цели: " + target.Name + "\n Скорость цели: " + target.Velocity.Length() + "\n Расстояние до цели: " + (RC.GetPosition() - target.Position).Length()); // Ввод информации в дисплей вместо предыдущей если у турели есть цель lcd.ContentType = VRage.Game.GUI.TextPanel.ContentType.TEXT_AND_IMAGE; // Установление типа отображаемой на дисплее информации } if (turret.HasTarget && lcd.CustomName == LCD_name + " 2") // Выбор дисплеев с названием и //двойкой в конце если у турели есть цель { // Векторы v1 = Vector3D.Dot(RC.WorldMatrix.Down, Vector3D.Normalize(RC.GetPosition() - target.Position)); v2 = Vector3D.Dot(RC.WorldMatrix.Left, Vector3D.Normalize(RC.GetPosition() - target.Position)); // Указываем на цель lcd.Enabled = true; if (v2 < 0) s = "<"; // Если цель слева, то стрелку влево if (v2 > 0) s = ">"; if (v1 < 0) s += "\nv"; // Если цель ниже, то стрелку вниз if (v1 > 0) s += "\n^"; lcd.WriteText(s); lcd.ContentType = VRage.Game.GUI.TextPanel.ContentType.TEXT_AND_IMAGE; } } } }
[НЕ ПИСАТЬ]

Для краткости лучше объяснить что тут творится:
0. Код выполняется только если дисплеев больше одного
1. За "//" пишется комментарий, он не участвует в коде.
2. С турели берётся целеуказание.
3. Список дисплеев перебирается .
4. На первый дисплей передается информация о цели, на второй - направление на неё.
5. '\n' в строке это литера новой строки.
6. 'string args' в скобках void Main это переменная аргумента, который вводится при выполнении скрипта. Если аргументы не планируются скобки можно оставить пустыми.



Учтите, что LCD.Write() переписывает всю информацию с дисплея, поэтому для того, чтобы удобнее вписать туда что-либо стоит использовать какую-то переменную, которой будет присваиваться значение, например 'StringBuilder' или 'String', которая затем и будет писаться.
Таким образом получен код работающей РЛС на одной турели.
Теперь добавим сканер и сброс цели у турели (с помощью аргументов).


Пишем скрипт для РЛС ч.2
Дописываем в блок Main().

[НАПИСАТЬ]
cam.EnableRaycast = true; // Включаем рейкаст у камеры if (args == "СКАН") camray = cam.Raycast(1000, 0, 0); // При выполнении скрипта с аргументом "СКАН" происходит пуск луча из камеры по прямой длиной 1000 метров if (!camray.IsEmpty()) { dyn.Play(); // При обнаружении чего-либо лучом делаем звук динамиком! foreach (IMyTextPanel lcd in LCD) // Цикл перебора дисплеев в списке { if (lcd.CustomName == LCD_name + " 3") // Выбор дисплеев с названием и тройкой в конце { lcd.Enabled = true; // Включение дисплея lcd.WriteText("РАДАР :ВИННИ-ПУХ: \n" + "Название цели: " + camray.Name + "\n Скорость цели: " + camray.Velocity.Length() + "\n Расстояние до цели: " + (RC.GetPosition() -target.Position).Length()); // Ввод информации в дисплей вместо предыдущей если у турели есть цель lcd.ContentType = VRage.Game.GUI.TextPanel.ContentType.TEXT_AND_IMAGE; // Установление типа отображаемой на дисплее информации } if (lcd.CustomName == LCD_name + " 4") // Выбор дисплеев с названием и тройкой в конце { lcd.Enabled = true; // Включение дисплея lcd.WriteText("Координаты: \n" + "GPS:ЦЕЛЬ #1: " + camray.Position.X + ":" + camray.Position.Y + ":" + camray.Position.Z + ":"); // Ввод координат цели в дисплей lcd.ContentType = VRage.Game.GUI.TextPanel.ContentType.TEXT_AND_IMAGE; // Установление типа отображаемой на дисплее информации } } } Echo(cam.AvailableScanRange.ToString()); // Выводим в инфо программного блока доступный заряд камеры для себя if (args == "СБРОС") turret.ResetTargetingToDefault(); // Сбрасываем цель у турели по аргументу "СБРОС"
[НЕ ПИСАТЬ]

Как и прежде, проще объяснить, чем разобрать по косточкам:
1. Включаем заряд у камеры
2. По аргументу проводим сканирование на 1000 метров по прямой
3. При обнаружении цели проигрываем звук
4. Полученную информацию выводим на дисплеи
5. Координаты выводятся на панель 4, можно загрузить координаты в скафандр
6. Успех!



Таким образом, был написан код простейшего радара на турелях, пусть и с такими ограничениями, как использование только одной турели и камеры (несложно исправить при повторении опыта дисплеев).
К сожалению, полностью понять код без знания C# не получится, а потому значительные изменения в коде могут быть только при достижении понимания хотя бы основ этого языка.
Учебники по этому языку можно поискать в интернете, в том числе на официальном сайте microsoft.


Итоговый код:
string RC_name = "ДУ"; string LCD_name = "Дисплей РЛС"; string dyn_name = "ДНМК"; string cam_name = "ПРИЦЕЛ"; string tur_name = "Турель"; string s = ""; // List<IMyTextPanel> LCD = new List<IMyTextPanel>(); IMyRemoteControl RC; IMyLargeTurretBase turret; IMySoundBlock dyn; IMyCameraBlock cam; // MyDetectedEntityInfo target; MyDetectedEntityInfo camray; Vector3D MyPos; double v1; double v2; Program() { RC = GridTerminalSystem.GetBlockWithName(RC_name) as IMyRemoteControl; GridTerminalSystem.GetBlocksOfType(LCD); dyn = GridTerminalSystem.GetBlockWithName(dyn_name) as IMySoundBlock; turret = GridTerminalSystem.GetBlockWithName(tur_name) as IMyLargeTurretBase; cam = GridTerminalSystem.GetBlockWithName(cam_name) as IMyCameraBlock; Runtime.UpdateFrequency |= UpdateFrequency.Update10; } void Main(string args) { target = turret.GetTargetedEntity(); // Взятие информации о цели с турели if (LCD.Count > 0) { foreach (IMyTextPanel lcd in LCD) // Цикл перебора дисплеев в списке { if (lcd.CustomName == LCD_name + " 1") // Выбор дисплеев с названием и еденицей в конце { lcd.Enabled = true; // Включение дисплея lcd.WriteText("РАДАР :ВИННИ-ПУХ: \n"); // Пишем инфо о радаре в дисплей if (turret.HasTarget) lcd.WriteText("РАДАР :ВИННИ-ПУХ: \n" + "Название цели: " + target.Name + "\n Скорость цели: " + target.Velocity + "\n Расстояние до цели: " + (RC.GetPosition() - target.Position).Length()); // Ввод информации в дисплей вместо предыдущей если у турели есть цель lcd.ContentType = VRage.Game.GUI.TextPanel.ContentType.TEXT_AND_IMAGE; // Установление типа отображаемой на дисплее информации } if (turret.HasTarget && lcd.CustomName == LCD_name + " 2") // Выбор дисплеев с названием и двойкой в конце если у турели есть цель { // Векторы v1 = Vector3D.Dot(RC.WorldMatrix.Down, Vector3D.Normalize(RC.GetPosition() - target.Position)); v2 = Vector3D.Dot(RC.WorldMatrix.Left, Vector3D.Normalize(RC.GetPosition() - target.Position)); // Указываем на цель lcd.Enabled = true; if (v2 < 0) s = "<"; // Если цель слева, то стрелку влево if (v2 > 0) s = ">"; if (v1 < 0) s += "\nv"; // Если цель ниже, то стрелку вниз if (v1 > 0) s += "\n^"; lcd.WriteText(s); lcd.ContentType = VRage.Game.GUI.TextPanel.ContentType.TEXT_AND_IMAGE; } } cam.EnableRaycast = true; if (args == "СКАН") camray = cam.Raycast(1000, 0, 0); // При выполнении скрипта с аргументом "СКАН" происходит пуск луча из камеры по прямой длиной 1000 метров if (!camray.IsEmpty()) { dyn.Play(); // При обнаружении чего-либо лучом делаем звук динамиком! foreach (IMyTextPanel lcd in LCD) // Цикл перебора дисплеев в списке { if (lcd.CustomName == LCD_name + " 3") // Выбор дисплеев с названием и тройкой в конце { lcd.Enabled = true; // Включение дисплея lcd.WriteText("РАДАР :ВИННИ-ПУХ: \n" + "Название цели: " + camray.Name + "\n Скорость цели: " + camray.Velocity.Length() + "\n Расстояние до цели: " + (RC.GetPosition() - target.Position).Length()); // Ввод информации в дисплей вместо предыдущей если у турели есть цель lcd.ContentType = VRage.Game.GUI.TextPanel.ContentType.TEXT_AND_IMAGE; // Установление типа отображаемой на дисплее информации } if (lcd.CustomName == LCD_name + " 4") // Выбор дисплеев с названием и тройкой в конце { lcd.Enabled = true; // Включение дисплея lcd.WriteText("Координаты: \n" + "GPS:ЦЕЛЬ #1: " + camray.Position.X + ":" + camray.Position.Y + ":" + camray.Position.Z + ":"); // Ввод координат цели в дисплей lcd.ContentType = VRage.Game.GUI.TextPanel.ContentType.TEXT_AND_IMAGE; // Установление типа отображаемой на дисплее информации } } } Echo(cam.AvailableScanRange.ToString()); // Выводим в инфо программного блока доступный заряд камеры для себя if (args == "СБРОС") turret.ResetTargetingToDefault(); // Сбрасываем цель у турели по аргументу "СБРОС" } }

Наиболее вероятная ошибка, что можно получить:
Object reference not set.... - ссылка на несуществующий объект, скрипт не может найти блок, на который вы ссылаетесь (нет на корабле\ошибка в названии\блок повреждён).
Дальнейшие модернизации РЛС "Винни-Пух"
Пути модернизации
Дальнейшие модернизации этой РЛС после решения её очевидных проблем вроде использования только одной камеры и одной турели, довольно разные. Имеет смысл рассмотреть следующие шаги:
  • 1. Добавление захвата и сопровождения целей
  • 2. Добавление графики
  • 3. Передача целеуказания другим скриптам
  • 4. Расширение ассортимента команд
  • 5. Добавление компьютера упреждения

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

Добавление захвата и сопровождения целей:
Для подобного необходимо создать список целей, по которым каждый вызов камеры, проходящие по циклу, будут "пробегать" по целям из списка обновляя их.
Подробно это наиболее наглядно можно посмотреть в моей РЛС "Изумруд-1", она написана довольно просто и понятно (пусть и далека от идеала).

Добавление графики:
Графика может быть разной: спрайтовой, символьной и векторной.
Векторно-символьная графика пишется через функции и рисуется, таким образом, самим компьютером. Опять же, через символы.
При наличии практики рисуется довольно просто. Совмещает красоту и понятность, но может нагрузить компьютер.
Символьная графика это обычный массив, символы в котором выполняют роль этаких "пикселов", в которые вписываются символы в различные позиции. Наверное самая простая и наименее лагающая графика, подобная используется в РЛС "Изумруд-1" и в старых РЛС в мастерской. "Нарисована" заранее.
Спрайтовая графика использует спрайты из игры, её использование влечёт за собой ньюансы, о ней можно почитать на форумах игры (познания автора об этом довольно скромные). Кол-во спрайтов ограничено (без модов добавить спрайты вроде бы нельзя), однако размеры, месторасположение спрайтов регулируются двумерными векторами.

Передача целеуказания:
Опять же - сбор данных о целях в строку или хранилище Tuple и передача другим скриптам напрямую (через аргумент) или опосредованно (через антенну).
Можно найти в РЛС "Изумруд-1" или подсмотреть в других скриптах.

Остальное:
Четвёртый пункт зависит от функциональности РЛС и предпочтений оператора, а пятый пункт реализовать можно и на существующей РЛС, просто необходимо изменить вектор направления на цель таким образом, чтобы он давал вектор упреждения. Знаний средней школы должно хватать для написания подобной формулы.
Итоги
Итоги написания скрипта
Итоги

Совершенно невероятно, однако мы сумели написать радар на турелях. Какое чудо, что подобные простейшие радары, которые можно обвешать большим количеством "бонусов" в виде графики и тому подобного, которые можно написать почти не зная языка C#, иногда могут получить тонны лайков!

Однако теперь у нас есть свой радар и мы можем делать с ним что угодно. Например, захватить мир. Если повезёт.
А теперь необходимо подвести итоги:
  • Написание скриптов возможно при минимальных познаниях в языке.
  • Написание различных скриптов позволяет с интересом провести время и укрепить знания о языке.
  • Скрипты - единственный путь получения абсолютного преимущества над противником.
  • Своё - всегда лучше.
  • На изученном примере возможно написание других скриптов подобного же уровня.
  • Не всё то злато, что блестит. Даже если это умеет передавать координаты по радиосвязи.

Команды, функции и прочее, что может пригодиться, можно найти здесь:
https://spaceengineerswiki.com/Programming_Guide/Action_List

Проще всего писать скрипты установив Visual Studio и подключив проект на ней к библиотекам Space Engineers.
Как это сделать - можно найти на канале Renesco Rocketman
https://www.youtube.com/channel/UCBC9faYOxS0yBBSS3uOx7LQ

Там же - большое число примеров и уроков.


А на этом всё!
13 Comments
Maxel 13 Jun @ 10:43am 
Жаль. Спасибо. Давным давно играл на сервере Прометей ПВЕ, там этот НПС многим жизнь подпортил, может это была местная специфика сервера? Потом его оттуда вроде как убрали. А тут я это чудо случайно нашел в старом блюпринте. Хм.... Кастомные блоки.... А, если выпилить ПБ, блоки "генератора щита" и поставить на другой грид?)))) Попробую, отпишусь о результатах.
☭Android☭  [author] 12 Jun @ 11:06pm 
Я кстати не знаю почему у тебя с ним возникли проблемы, там даже по размерам дрона - на один выстрел из рельс.
Не думаю, что даже у корвета вроде этого https://sp.zhabite.com/sharedfiles/filedetails/?id=2816365995 (без импульсника) возникли бы проблемы с ним. А с импульсником можно вообще на табуретке нападать, была бы броня лазер пережить, да рельса.
☭Android☭  [author] 12 Jun @ 10:58pm 
Короче, 99% что это как я и говорил киновская технология, недоступная простым смертным. Но по правде говоря я и не знаю нахрена она тебе. При текущем балансе ничего мощнее табурета на импульснике с мгновенным разгоном и рельсотронами уже не будет. Ну лазер может быть был бы ещё круче, но тут и без него можно быть богом. Единственное - неуязвимость была бы хороша, да, но это уже совсем приговор разработчикам был бы. Летящие табуреты с рельсотронами, да ещё и неуязвимые - это уже излишне даже для кинов.
☭Android☭  [author] 12 Jun @ 10:55pm 
В случае с лазером урон наносится после того, как данные сканирования ПБ занесёт в свой CustomData, а потом в настройках блока включит CorruptionLaserDamage.
В обычном ПБ такой настройки в терминале управления мы не видим. Есть конечно вероятность, что она есть и доступна всем, но просто не видна (разработчики дегенераты), но по-моему это бред и по идее игрокам это недоступно.
В случае с щитами он ссылается на какой-то блок с названием Shield Generator Module и меняет настройку в терминале управления CorruptionShieldRegister.
☭Android☭  [author] 12 Jun @ 10:44pm 
Так что либо это недоступные для строительства блоки, либо сам дрон дополнительно как-то прописан внутри игры. Причем многое хранится в самом ПБ дрона, возможно он сам по себе необычный.
Как я и думал, для тебя этот скрипт бесполезен.
Более детальный анализ проводить - надо в игру заходить и дрон разбирать. Но на первый взгляд тут нет ничего интересного - даже пилотирование дрона - и то осуществляется через функционал блока удаленного управления, а наличие игрока определяет через GetNearestPlayer.
☭Android☭  [author] 12 Jun @ 10:44pm 
Бесстыжие разработчики, тырят куски скриптов из стима. В данном случае куски из двух скриптов Whip's.
Короче судя по скрипту, на дроне стоят какие-то блоки специально для него созданные и по взаимодействию через API не отличающиеся от обычных.
Камера здесь использует исключительно рейкаст и переменные, в которые записывает урон (очевидно, внешняя оболочка захардкоженного лазера), с щитами тоже самое.
Maxel 12 Jun @ 10:24pm 
НПС-босс называется NPC-CPC Shielded Vengeance Drone, есть в мастерской стима, а сам скрипт на этом сайте кто-то опубликовал, - https://pastebin.com/U8GwRHk7 . У-у-у, сколько раз я об этого босса убился, не сосчитать)).
☭Android☭  [author] 11 Jun @ 9:06pm 
Ну, я посмотрю, если скинешь, но 99% что эти функции, раз их добавили, могут использовать только NPC.
Maxel 11 Jun @ 1:26pm 
Нет, не моды. В том-то и дело, я сам удивился. По крайней мере лазеру точно, такого я еще не видел. А вот что касательно защитных экранов(шилда), я знаю, что можно сделать неуязвимый грид с помощью скриптов. Внимательно почитав ваш гайд, я понял, что лазер будет работать только на игрока со статусом "враг", там и задана эта функция relationship enemy, собственно, я могу скинуть вам эти скрипты, не вопрос. Может потом поделитесь научными изысканиями?
☭Android☭  [author] 11 Jun @ 8:13am 
Лазер? Шилда? Моды что ли?
Если ваниль и это какой-то NPC-босс, не факт, что это будет работать, если во владении блоков включая ПБ будет игрок, а не NPC. Так-то для пиратов-NPC есть функция GetNearestPlayer, но игроки её не могут использовать.