Создаем реальный индикатор кислорода из Subnautica: часть 1
Если задуматься, то игр, чье действие разворачивается преимущественно в водной среде, не так уж много. Я погружался в эстетику подводного города Восторг в BioShock, боролся за выживание в Barotrauma и изучал безмятежные океаны ABZÛ. Но ни один из этих проектов не вызывал у меня такого спектра эмоций, как Subnautica. Это тот редкий случай, когда планируешь «заскочить на часок», а обнаруживаешь, что за окном глубокая ночь.
Сейчас в раннем доступе появилась вторая часть, и для меня это стало отличным поводом вернуться к оригиналу. При погружении единственным по-настоящему важным индикатором становится уровень кислорода. Он — ваш главный советчик: стоит ли плыть дальше или пора спешно возвращаться на поверхность. Синтетический голос, монотонно произносящий «oxygen», звучит как приговор, когда времени на раздумья уже не осталось. Именно тогда меня посетила мысль: было бы здорово перенести этот счетчик из виртуальности в физический мир. Так родился этот любопытный DIY-проект.

Мир Subnautica — это планета, поглощенная бесконечным океаном. Атмосфера пригодна для дыхания, но основная часть жизни скрыта под толщей воды. После крушения корабля «Аврора» главный герой оказывается в спасательной капсуле посреди пучины. Единственный шанс выжить — адаптироваться к чужеродной среде и научиться использовать доступные ресурсы.
Именно тогда жизненно важным спутником становится счетчик кислорода. Он превращается в вашего главного друга, арбитра и палача. На нем отображаются две ключевые цифры: текущий запас воздуха и максимально возможный в данных условиях.

Пока у героя нет специализированного снаряжения, запаса хватает лишь на пару десятков секунд. По мере прогресса этот лимит растет, что мгновенно отражается на индикаторе.
Задача проста: считывать эти значения в реальном времени и выводить их на внешний девайс. Алгоритм действий таков: получение данных из игрового процесса, передача их на микроконтроллер и отрисовка интерфейса. Самый сложный этап — первый, так как всё зависит от особенностей игрового движка.
Первый взгляд
Стало очевидно: Subnautica — идеальная площадка для экспериментов. Разработчики из Unknown Worlds Entertainment проявили завидную лояльность к моддинг-сообществу. Для игры уже существуют удобные инструменты, такие как фреймворк Nautilus и загрузчик BepInEx. Их я и решил использовать.
Альтернативные методы, вроде прямого чтения памяти (привет, ArtMoney) или захвата экрана с распознаванием образов, показались мне слишком ненадежными: любой апдейт игры мог бы «сломать» всю систему. Для внешнего дисплея я выбрал круглый экран GC9A01 — это идеальный выбор для аутентичного вида.

Это компактный дисплей с разрешением 240×240 точек, работающий по SPI. В качестве «мозга» я выбрал ESP32 — доступный и функциональный контроллер. Данные планирую отправлять через UDP-широковещательные пакеты, чтобы избежать привязки к конкретному IP-адресу.
Извлечение данных
Чтобы получать доступ к игровым переменным, я написал небольшой плагин на C#. Суть проста: каждые 200 мс опрашивать объект менеджера кислорода:
float oxygen = Player.main.oxygenMgr.GetOxygenAvailable();float capacity = Player.main.oxygenMgr.GetOxygenCapacity();
Перед началом работы я убедился, что установлена актуальная версия игры, и настроил BepInEx. Для активации консоли в файле конфигурации BepInEx.cfg я изменил параметр [Logging.Console] на true.
Окружение для разработки
Для компиляции плагина под Unity 2019.4 потребовался .NET SDK. Мой плагин представляет собой библиотеку классов, которая подгружается в процесс игры при старте. BepInEx автоматически сканирует папку plugins и берет на себя всю работу по интеграции.
Написание кода
Я создал проект OxygenTap.csproj и подключил необходимые библиотеки. Важным моментом стало добавление ссылок на UnityEngine и Assembly-CSharp, чтобы компилятор «видел» игровые объекты. Сам код плагина довольно лаконичен: он использует таймер для периодической отправки данных и выполняет проверку Player.main != null, чтобы избежать ошибок при загрузке главного меню.

Отправка
Для передачи данных я отказался от громоздкого JSON в пользу простого строкового формата с разделителем: O2;123.0;135.0. Это минимизирует накладные расходы и позволяет парсить сообщения без лишних аллокаций памяти. Префикс O2 защищает от «мусора» и дает задел на будущее: если захочется добавить данные о здоровье или уровне голода, система будет легко масштабироваться.
Код для отправки через UdpClient получился максимально компактным. Тестирование с помощью короткого PowerShell-скрипта подтвердило: данные успешно уходят в сеть и корректно принимаются.

Продолжение следует…
Первая фаза проекта прошла успешно. В следующей части я расскажу, как отрисовывал дизайн циферблата на Python и тестировал прошивку в симуляторе Wokwi, пока ожидал поставку железа. Приходилось ли вам автоматизировать игровые элементы? Делитесь опытом в комментариях!
GeForce RTX 4080 расплавилась из-за короткого замыкания в светодиодной подсветке
Марсианский серп глазами зонда Psyche: эффектный таймлапс от NASA
Отказ от воздушного охлаждения: серверы Nvidia переходят на воду с температурой 45 °C
Wink внедрил экстренные оповещения МЧС в интерфейс плеера во время просмотра контента
Более 50% регионов России интегрировали свои сервисы в платформу «Госуслуги»
В России представили первую в мире пальчиковую батарейку с функцией беспроводной зарядки
Солнечный ветер ударил по Земле: скоростная плазма предваряет мощную магнитную бурю
МТС модернизировала сетевую инфраструктуру в Якутске