
Представляю AdaEngine 1.0.0 - большое обновление открытого игрового движка на Swift.
Первый публичный релиз был очень тревожным и заложил основу: ECS, рендеринг, сцены, ассеты, физику, звук и AdaUI. С тех пор мы (я и пару агентов) работали над тем, чтобы эти возможности складывались в удобный процесс создания игры.
В версии 1.0.0 появился новый Ada Studio с AI-агентами и интерактивными A2UI-инструментами, добавили новый ЯП - AdaScript, добавились нативный запуск на Android, multiplayer, более развитый 3D-рендеринг и облачная инфраструктура для распространения пользовательских игр.
Теперь путь от первой сцены до игры, которой можно поделиться, проходит через связанные между собой инструменты.
Новый Ada Studio
Главное обновление релиза - Ada Studio, среда разработки для Ada Engine. Она объединяет работу с проектом, кодом, сценами, ресурсами, интерфейсами и запуском игры.
Studio поддерживает проекты на Swift и AdaScript. Общая часть интерфейса построена на AdaUI: мы используем собственный UI-фреймворк для повседневного инструмента разработки и развиваем его на реальных задачах.
Editor Mode
Editor Mode - основное рабочее пространство с деревом проекта, вкладками документов, сценой и инспектором. Все что нужно, для комфортной разработки игры.
В нём можно создавать и редактировать 2D- и 3D-сцены, управлять иерархией сущностей, добавлять компоненты, настраивать камеры, освещение, физику и материалы. Инструменты перемещения, вращения и масштабирования помогают работать непосредственно во viewport.
Рабочую область можно разделить на две панели: например, держать код рядом со сценой или одновременно редактировать несколько документов. Такого часто не хватает в Godot.
Agent Mode
На desktop появился Agent Mode — отдельный интерфейс для разработки вместе с AI-агентом.
В центре этого режима находятся разговоры и результаты работы. Можно переключаться между сессиями, передавать агенту выбранный код и изображения, открывать изменённые файлы и возвращаться к обычному редактору.
Оба режима используют одни и те же документы и состояние проекта. Это позволяет свободно переходить от обсуждения задачи к ручному редактированию.
На macOS Studio поддерживает подключение внешних агентов через Agent Client Protocol (ACP). Каталог обнаруживает установленные агенты и предлагает совместимые адаптеры для Codex и Claude Code; также распознаются Gemini CLI, OpenCode, GitHub Copilot и Sloppy ACP. Совместимые дистрибутивы можно установить и подключить из Studio. Авторизация остаётся у выбранного агента.
Разговоры сохраняются вместе с проектом. При переключении сессий остаются их черновики, вложения и выбранный контекст, а выполняющаяся задача продолжает относиться к своему разговору. Результаты агента можно открыть в обычном редакторе и проверить через diff. В каждом окне проекта одновременно выполняется одна задача агента.
Agent Mode и обычный чат в Editor Mode используют общий набор инструментов: выбранный интерфейс меняет организацию рабочего пространства, а доступные действия определяются возможностями текущего host.

Tilemap Editor и работа с ресурсами
В Studio появился встроенный редактор тайловых карт. Он поддерживает рисование и стирание тайлов, слои, палитру, масштабирование и перемещение по карте.
Редактор tileset позволяет настраивать источник тайлов и их геометрию. Для 2D-освещения можно задавать полигоны, перекрывающие свет, и переопределять их для отдельных ячеек карты.
Также доступны редактор текстурных атласов, просмотр изображений и импорт GLB/glTF-моделей. Модель можно открыть в 3D-preview, выбрать анимационный клип и добавить в сцену.

Редактирование карты Medieval Arena: холст, слои и палитра тайлов.
Code Editor и отладка
Редактор кода получил подсветку Swift, AdaScript, YAML и GLSL, автодополнение, подсказки, переход к определению и диагностику ошибок. Доступен поиск внутри файла и по всему проекту.
Для Swift-проектов на macOS интегрирован LLDB: точки останова, пошаговое выполнение, потоки, стек вызовов, переменные, watches и консоль команд.
Для AdaScript доступны диагностика, выполнение в Play и hot reload. Полноценная отладка выполнения AdaScript остаётся следующим этапом развития инструментария.
AI-агенты: от идеи до работающей сцены
Агенты в Studio работают с самим проектом: кодом, сценами, ресурсами, настройками и результатами запуска. На desktop эти возможности доступны через инструменты редактора и MCP, на мобильных устройствах — через встроенный runtime агента.
Контекст проекта и знания о движке
Агент может прочитать структуру проекта, получить точные описания API и компонентов, найти документацию и открыть готовые примеры. Справочные материалы поставляются вместе со Studio и доступны офлайн.
Это помогает агенту выбирать существующие возможности движка и учитывать реальные поля компонентов. Для работы со сценами доступны чтение и структурированные изменения с проверкой версии документа и поддержкой Undo.
Изображения, тайлы и 3D-ресурсы
Изображение можно приложить к разговору как визуальный референс. На мобильных устройствах поддерживаются вставка изображений и пометки на screenshots, чтобы точнее показать желаемый результат или проблему.
Инструменты агента охватывают подготовку игровых ресурсов:
Создание процедурных текстур: цветов, градиентов, checkerboard, шума и flat normal maps.
Генерация и редактирование изображений при настроенном image-provider.
Чтение, изменение и упаковка текстурных атласов.
Создание tileset и редактирование слоёв и ячеек tilemap.
Проверка и импорт GLB/glTF, чтение структуры модели, материалов, скелета и анимационных клипов.
Изменение PBR-параметров и назначение текстур материалам модели.
Поиск справочных материалов в интернете и загрузка ресурсов в проект с последующей проверкой.
Набор инструментов зависит от платформы. Например, импорт и настройка готовой 3D-модели доступны, а создание рига и редактирование скелетных ключевых кадров требуют отдельных инструментов.
Game development harness
Мы добавили встроенный workflow ada-game-builder, который помогает агенту проходить весь цикл разработки: от игровой механики до проверяемого результата.
Он охватывает создание сцен и скриптов, подключение ресурсов, desktop- и touch-управление, HUD, перезапуск игры и проверку результата. Для продолжительных задач workflow использует проектные заметки о дизайне и прогрессе, чтобы сохранять договорённости между сессиями.
Studio поставляется со skills для работы с 3D-ассетами, материалами, анимациями и UI-формами. Агент может находить нужные инструкции и читать их по мере работы. В мобильном Studio можно также импортировать собственные Markdown-skills и задать постоянные инструкции.
Проверка результата и исправление ошибок
Агент может запускать сборку, читать диагностику и логи, проверять сцены и запускать изолированную симуляцию. Мобильная симуляция позволяет передать состояние клавиш, продвинуть игру на несколько кадров и проверить позиции сущностей и значения компонентов.
Для проверки внешнего вида доступны видимый Play и захват завершённого GPU-кадра. На мобильных устройствах агент может передать этот снимок настроенной vision-модели для анализа. Desktop-инструменты также предоставляют CPU-метрики и записи профайлера для встроенных игровых сессий.
Сборка, симуляция и видимый запуск проверяют разные стороны проекта. Логические проверки помогают найти ошибку в поведении, а рендеринг, звук, сеть и сенсорное управление нужно проверять в Play.
Мобильный агент и фоновые задачи
На iPhone и iPad агент работает внутри Studio и использует инструменты текущего проекта. Можно выбрать провайдера и модель, настроить количество шагов, попытки исправления после неудачной проверки и автоматическое открытие Play.
На iOS/iPadOS 26 пользовательские задачи могут продолжаться в фоне через системный механизм continued processing. Activity показывает текущую фазу работы, длительность и ошибки. Доступность фонового времени определяет система; рендеринг Play и захват кадров требуют открытого приложения.
A2UI: интерактивные инструменты прямо в чате
A2UI позволяет агенту отвечать интерактивным интерфейсом. Агент описывает форму декларативно, а Studio отображает её нативными компонентами AdaUI прямо внутри разговора. Элементы могут появляться постепенно по мере получения ответа.
В нашем forms-каталоге доступны текст, строки и колонки, кнопки, поля ввода, переключатели, списки выбора и числовые слайдеры. Значения связаны с моделью данных формы. Обязательные поля и допустимые диапазоны проверяются локально: ошибки отображаются рядом с вводом, а некорректную форму нельзя отправить.

A2UI в Studio: выбор роли NPC, переключатель диалога и слайдер. Форма использует демонстрационные данные; видео показывает локальные controls.
От формы к игровому интерфейсу
Например, можно попросить агента подготовить форму настройки NPC: имя, параметры поведения и варианты диалога. После заполнения формы её значения возвращаются в исходную сессию, и агент продолжает работу с выбранными параметрами.
Сгенерированный UI-preview можно открыть через Open in UI Designer. Studio создаст обычный .ui-ресурс проекта, который можно редактировать визуально или в YAML, сохранять и включать в игру. Экспорт сохраняет нативные компоненты, bindings, текущие значения и проверки формы. Действия кнопок подключаются к логике приложения через обычные механизмы AdaUI.
На iPhone доступен Open UI Source, открывающий экспортированный ресурс в редакторе файлов. Это передача интерфейса в проект: последующие ручные изменения в Designer автоматически не отправляются агенту.

NPCProfile.ui: форма, экспортированная из A2UI и открытая в обычном UI Designer.
Инструменты работы со сценой
Карточка в чате может стать небольшим инструментом редактора. Например, агент создаёт карточку размещения NPC с количеством копий и расстоянием между ними, либо карточку цвета с системной палитрой и кнопкой Apply.
Поддерживаемые локальные действия выполняются непосредственно в Studio, без нового обращения к модели. Карточки умеют дублировать поддерево сущности, менять зарегистрированное цветовое поле и применять несколько изменений сцены одной транзакцией.
Studio проверяет версию сцены перед применением. Если документ был изменён отдельно, устаревшая карточка просит обновить данные. На desktop изменения входят в обычную историю Undo/Redo; на iPhone карточка сохраняет данные для отмены своего последнего применения. Изменения относятся к редактируемой сцене — уже запущенный Play нужно перезапустить.
Нативный UI на desktop и iPhone
Одинаковые карточки доступны в macOS ACP-чате и встроенном чате iPhone. Их состояние и введённые значения сохраняются вместе с сессией. Отправка формы возвращается в разговор, который её создал, и сохраняет текущий черновик сообщения.
В самом движке интеграция выделена в AdaA2UI, адаптер A2UI v0.9.1 с собственным forms-каталогом. Его можно подключать к приложениям на AdaUI и связывать с выбранным агентом через callbacks событий. Так интерактивные ответы становятся частью не только Studio, но и собственных инструментов разработчика.
Studio на desktop и mobile
Desktop Studio предоставляет рабочее пространство с несколькими панелями, инструментами сборки, отладкой и интеграцией Git.
Мобильный Studio адаптирован под iPhone и iPad. На iPhone работа организована через отдельные экраны проекта, файлов, кода, сцены, Play и чата. Это больше похоже на Roblox Build или Horizon Create. Общение с агентом и минимум ручного редактирования. Из важного - возможность быстро посмотреть UGC от других пользователей. Но для собственных публикаций, требуется Ada Cloud Pro подписка.
На iPad доступно более широкое рабочее пространство, приближенное к macOS версии. Но с важным ограничением, что запускать Swift-first проекты нельзя, так как нет доступа к JIT и прочим радостям компиляции.
AdaScript-проекты можно редактировать и запускать во встроенном runtime на мобильном устройстве. Это делает телефон и планшет полезными для прототипирования и проверки игры с сенсорным управлением.

Ada Player
Ada Player позволяет отправлять поддерживаемый проект из Studio на другое устройство для проверки.
Studio обнаруживает Player в локальной сети, подключается по коду сопряжения, передаёт проект и получает сообщения о его выполнении. Это сокращает путь между изменением игры и проверкой на устройстве.
visionOS и пространственные приложения
В 1.0.0 появилась основа для immersive-приложений на visionOS.
AdaEngine может отображать мир в полном immersive-пространстве, использовать отдельные проекции для глаз и поддерживать пространственное взаимодействие с объектами. На совместимых устройствах предусмотрен foveated rendering.
AdaUI-панели можно размещать в пространственных окнах через нативный host. Пример Immersive Garden показывает, как совместить игровой мир и инструменты управления. Это основа для пространственных приложений; полный перенос Studio на visionOS и общий XR-export ещё впереди.
AdaScript: язык игровой логики
AdaScript — скриптовый язык, интегрированный с AdaEngine и Ada Studio. Он позволяет писать игровую логику, определять компоненты и системы, подключать поведение к сущностям и работать с API движка.
Язык использует файлы .ada, поддерживает функции, классы, структуры, коллекции и асинхронные операции. По умолчанию он динамически типизирован; для отдельных файлов или всего проекта можно включить строгую проверку типов.
Структура проекта и папка .ada
AdaScript-проект — это обычная директория с исходниками, ресурсами и конфигурацией. Скрипты с расширением .ada лежат в Sources/, а скрытая папка .ada/ хранит настройки проекта и рабочие данные Studio.
Например, проект небольшой игры может выглядеть так:
MyGame/
MyGame/ ├── .ada/ │ └── project.json ├── Sources/ │ ├── PlayerController.ada │ ├── Health.ada │ ├── GameRules.ada │ └── HealthRegeneration.ada └── Assets/ ├── Scenes/ │ └── Main.ascn ├── Maps/ │ └── Level.tilemap ├── Tiles/ │ └── Level.tileset ├── Textures/ │ └── Player.png └── UI/ └── HUD.ui
Sources/ содержит игровую логику: системы, компоненты, ресурсы и scriptable-поведение. В Assets/ находятся сцены, изображения, модели, звук, тайловые карты и UI-ресурсы. Пути к этим директориям можно изменить в настройках проекта.
Главный конфигурационный файл — .ada/project.json. Он задаёт имя проекта, способ сборки, расположение файлов, точку входа, набор runtime-плагинов и другие параметры. Пример конфигурации для 2D-игры:
.ada/project.json
{ "schemaVersion": 3, "project": { "name": "MyGame" }, "build": { "system": "adascript" }, "paths": { "sources": "Sources", "assets": "Assets", "resourceRoots": ["Assets"] }, "runtime": { "moduleName": "MyGame", "entry": { "scene": "Assets/Scenes/Main.ascn" }, "plugins": { "preset": "game2d", "presetVersion": 1 } }, "editor": { "startupScene": "Assets/Scenes/Main.ascn" } }
build.system выбирает AdaScript-путь. paths определяет исходники и ресурсы, runtime.entry.scene — начальную сцену игры, а runtime.plugins — состав доступных возможностей движка. Пресеты game2d и game3d можно дополнять через enable и disable. editor.startupScene задаёт резервную сцену для запуска внутри редактора.
В этом же файле сохраняются именованные действия ввода в inputActions, параметры запуска и настройки runtime-окна. Основные поля доступны через Project Settings, поэтому для обычной настройки игры можно пользоваться интерфейсом Studio.
По мере работы внутри .ada/ появляются дополнительные файлы и директории:
workspace/— состояние открытых документов и проектные сессии агента.chat-ui/— дополнительное состояние A2UI-карточек и их отмены в мобильном Studio.libraries.lock.json— зафиксированные зависимости AdaScript с точными Git commits.libraries/— установленные исходники этих библиотек.cli-build/— создаваемый CLI кеш сборки.android-signing/— ключи development signing для Android APK, создаваемые при экспорте.publication-images/— обложка и screenshots, выбранные для публикации из Studio.
Эти директории создаются соответствующими инструментами по необходимости. project.json описывает игру, а workspace/ и cli-build/ обслуживают работу редактора и сборку. Для библиотек можно сохранять lock-файл и восстанавливать зафиксированные версии на другом компьютере; сохранение libraries/ вместе с lock-файлом позволяет передать проект с уже установленными зависимостями для офлайн-работы.
В портативном AdaScript-проекте жизненный цикл приложения обеспечивает готовый host Studio: отдельный main() и Package.swift для такого проекта не нужны. В Swift-проекте дополнительно используется Package.swift, а AdaScript подключается через build plugin. Результаты экспорта по умолчанию записываются в Exports/macOS, Exports/Web или Exports/Android.
Аннотации
Аннотации связывают объявления AdaScript с движком:
@systemрегистрирует ECS-систему.@componentи@resourceописывают игровые данные.@queryи@resпредоставляют доступ к компонентам и ресурсам.@scriptableподключает поведение к сущности.@exportделает поля поведения сохраняемыми и доступными в инспекторе.@toolобъявляет инструмент Studio с собственной.ui-панелью и редакторскими действиями.Сетевые аннотации описывают команды, RPC и реплицируемые компоненты.
Что означает id в аннотациях
id — стабильное имя объявления для движка. Например, в @component(id: "game.health") строка game.health обозначает тип компонента Health. Один такой тип можно прикрепить ко множеству сущностей; каждая из них имеет собственные значения здоровья и собственный числовой entity.id.
Идентификаторы используются при регистрации, поиске данных из Swift, сохранении scriptable-поведения, задании порядка систем и описании сетевых схем. Явный id позволяет переименовать класс в исходнике, сохранив его внешнюю идентичность. Изменение самого ID требует обновить ссылки на него и учесть уже сохранённые данные.
Префикс game. — удобная договорённость для организации имён. Он не создаёт отдельное пространство имён языка. В библиотеке можно выбрать свой префикс, например combat.health, чтобы её объявления не конфликтовали с игровыми.
У @system ID по умолчанию совпадает с именем класса; явный ID удобен для @after и @before. Для @scriptable, @tool и сетевых объявлений, таких как @network_command, @rpc и @replicated_component, ID обязателен. Поле version, где оно поддерживается, отдельно описывает версию объявления или схемы. В пределах соответствующего реестра идентификаторы должны быть уникальны.
Данные сущности: @component
Компонент хранит данные отдельной сущности. Например, у каждого персонажа может быть своё текущее и максимальное здоровье:
Health.ada — AdaScript
@component(id: "game.health") struct Health { @export var current = 100.0; @export var maximum = 100.0; }
Поведение сущности: @scriptable
@scriptable описывает поведение, которое можно прикрепить к сущности в сцене. В этом примере скрипт перемещает персонажа по горизонтали, а speed доступен для настройки в инспекторе Studio:
PlayerController.ada — AdaScript
@scriptable(id: "game.player-controller") class PlayerController { @export var speed = 5.0; @component(required: true) var transform: Transform; @res var input: Input; func update(context) { var right = input.getActionStrength("MoveRight"); var left = input.getActionStrength("MoveLeft"); var direction = right - left; var distance = direction * speed * context.deltaTime; var position = transform.position; position[0] += distance; transform.position = position; } }
@component(required: true) связывает свойство с Transform именно той сущности, к которой прикреплён скрипт. @res предоставляет ресурс Input текущего мира. Действия MoveLeft и MoveRight настраиваются в Project Settings → Input Bindings; для этого примера нужен включённый Input plugin.
Для @scriptable доступны ready(context), update(context), fixedUpdate(context), event(events, context) и destroy(context). ready вызывается при прикреплении поведения, а destroy — при его удалении. Асинхронные задачи позволяют ждать загрузку ресурсов, следующий кадр или таймер, сохраняя связь с жизненным циклом игрового объекта.
Общие настройки мира: @resource и @res
Ресурс хранит общие данные мира. Например, скорость восстановления здоровья можно вынести из отдельных персонажей в игровые правила:
GameRules.ada — AdaScript
@resource(id: "game.rules", autoInsert: true) struct GameRules { @export var healthRegenPerSecond = 5.0; }
В SwiftPM-проекте AdaScriptBuildPlugin генерирует backing-типы компонентов и ресурсов. autoInsert: true добавляет ресурс в мир перед установкой систем. Затем система или scriptable-поведение может получить его через @res var rules: GameRules;.
Системы и scheduler: @system
@system регистрирует класс как ECS-систему. Её метод update(context) вызывается выбранным scheduler — этапом выполнения систем мира. Параметр scheduler определяет этот этап, а context.deltaTime передаёт его текущий шаг времени.
В стандартном цикле доступны:
startup— однократная инициализация при первом обновлении scheduler runner; при перезапуске мира она выполняется снова.preUpdate— этап перед основным обновлением.update— основной этап игровой логики; он используется по умолчанию, еслиschedulerне указан.postUpdate— этап после основного обновления.
Например, startup-система может создать сущность с компонентами из нашего проекта:
GameSetup.ada — AdaScript
@system(scheduler: "startup", id: "game.setup") class GameSetup { func update(context) { context.world.spawn([Transform(), Health()]); } }
Даже в startup точка входа системы называется update(context). Конструктор класса создаёт экземпляр скрипта; startup-система получает доступ к игровому миру и выполняет его начальную настройку. Система HealthRegeneration ниже использует update и выполняет свою логику при каждом запуске этого этапа.
Порядок можно задать через @after и @before, указав ID другой системы:
Порядок систем — AdaScript
@after(id: "game.health-regeneration") @system(scheduler: "update", id: "game.health-observer") class HealthObserver { func update(context) { // Логика после HealthRegeneration. } }
Обе системы должны принадлежать одному модулю AdaScript и одному scheduler. Неизвестные ID, зависимости системы от самой себя и циклы отклоняются при валидации. Дополнительные schedulers можно организовать на Swift-стороне хоста. Callback fixedUpdate(context) у @scriptable относится к жизненному циклу поведения; это отдельный механизм от перечисленных стандартных stages.
Работа со множеством сущностей: @query
Система получает подходящие сущности через @query. Здесь она обновляет все компоненты Health, используя общий ресурс GameRules из предыдущего примера:
HealthRegeneration.ada — AdaScript
@system(scheduler: "update", id: "game.health-regeneration") class HealthRegeneration { @query(Health) var characters; @res var rules: GameRules; func update(context) { var amount = rules.healthRegenPerSecond * context.deltaTime; for (var entity in characters) { if (entity.health.current < entity.health.maximum) { var next = entity.health.current + amount; if (next > entity.health.maximum) { next = entity.health.maximum; } entity.health.current = next; } } } }
@system регистрирует систему в scheduler, а @query(Health) выбирает активные сущности с компонентом Health. Внутри цикла компонент доступен как entity.health; изменение его поля записывается обратно в ECS. Такой подход подходит для общей логики, которая обрабатывает множество объектов.
Query может получать несколько компонентов и использовать фильтры. Если в проекте зарегистрированы компоненты Player и Frozen, внутри системы можно объявить такую выборку:
Query с фильтрами — AdaScript
@query(Health, Transform, with: Player, without: Frozen) var players;
В строках этой выборки доступны entity.health и entity.transform. Фильтры проверяют наличие компонентов: в выборку входят сущности с Player, у которых нет Frozen. Данные строки query действуют в пределах текущего обхода; сохраняйте нужные значения, а не сами ссылки на строку, между обновлениями системы.
Асинхронная логика: async/await
В AdaScript можно описывать последовательность действий через async func и await. Ожидание приостанавливает конкретную задачу, пока игра продолжает обновляться. Из обычного callback такую функцию запускают через Tasks.start(...):
DelayedAnnouncement.ada — AdaScript
async func announceLater() { await Time.sleep(1.0); System.print("One game-time second passed"); await Tasks.nextFrame(); System.print("The next frame is ready"); } @system(id: "game.announcement") class AnnouncementSystem { var started = false; func update(context) { if (!started) { started = true; Tasks.start(announceLater()); } } }
Time.sleep использует игровое время scheduler и перестаёт продвигаться при его паузе. Time.sleepRealTime использует монотонные часы; продолжение задачи всё равно выполняется в точке dispatch скриптового runtime. Tasks.nextFrame позволяет дождаться следующего кадра. Tasks.start возвращает handle с status() и cancel().
Асинхронные операции доступны и для ресурсов. Например, загрузка изображения возвращает результат, который можно проверить после await:
Асинхронная загрузка ассета — AdaScript
async func loadPlayerTexture() { var result = await Assets.loadAsync( "@res://Textures/Player.png" ); if (result.isSuccess()) { System.print("Player texture is ready"); } else { System.print(result.message()); } }
Такую функцию также запускают через Tasks.start(loadPlayerTexture()). Для операций с ошибками доступны isSuccess(), value(), errorCode() и message(). Для сохранений предусмотрены асинхронные операции Saves, а Tasks.promise() позволяет связать ожидание с результатом другого callback.
Методы update(context) и callbacks @scriptable остаются синхронными: из них запускается отдельная async-функция. Контекст, строки query, представления ресурсов и commands действуют только в пределах callback. Передавайте в задачу самостоятельные значения — например, число, строку или копию нужных данных. Типы со сроком жизни, привязанным к callback, можно помечать @nonsendable; runtime также проверяет передаваемые значения. Продолжения выполняются в сериализованном скриптовом runtime, а не в потоке фонового I/O.
Данные AdaScript на стороне Swift
В SwiftPM-проекте AdaScriptBuildPlugin генерирует backing-типы для объявленных в AdaScript компонентов и ресурсов, а сгенерированный плагин регистрирует их в движке. Имена этих Swift-типов являются внутренней деталью генерации. Для интеграции используйте имя объявления или его стабильный ID — например, Health или game.health.
После установки AdaScriptPluginsGenerated() компонент можно получить из того же мира по ID и прочитать его отражённые поля. Здесь world — игровой World, а playerID — ID уже существующей сущности с Health:
Чтение Health из Swift
import AdaECS if let component = world.getComponent( named: "game.health", from: playerID ), let descriptor = ComponentReflectionRegistry.descriptor( named: String(reflecting: type(of: component)) ) { let fields = descriptor.readPayload(from: component) if case .double(let current)? = fields["current"] { print("Health:", current) } }
world.getResource(named: "game.rules") аналогично возвращает зарегистрированный ресурс как any Resource. Для чтения конкретного поля сгенерированного ресурса scripting bridge предоставляет SPI:
Чтение GameRules из Swift — SPI
@_spi(Scripting) import AdaECS if let resourceType = RuntimeTypeRegistry.resourceType( named: "game.rules" ), let descriptor = RuntimeResourceReflectionRegistry .descriptor(for: resourceType), let field = descriptor.fields.first(where: { $0.key == "healthRegenPerSecond" }), case .double(let rate)? = world.readResourceField( type: resourceType, field: field ) { print("Regeneration per second:", rate) }
SPI предназначен для интеграции scripting bridge и может меняться отдельно от публичного API. Такие чтения выполняются в согласованной фазе доступа к миру — например, внутри Swift-системы с объявленным доступом к соответствующим данным. Полученные значения можно передать в Swift UI или другую игровую логику.
Для runtime-defined компонентов в портативном runtime и AOT используется world-local схема и payload. Их можно читать без знания сгенерированного Swift-типа:
Runtime-компонент из Swift
import AdaECS if let schema = world.runtimeComponentDescriptor( named: "game.health" ), let health = world.getRuntimeComponent( schema.componentID, from: playerID ), let index = schema.fields.firstIndex(where: { $0.key == "current" }) { print(health.values[index]) }
В native AOT скалярные ресурсы, объявленные компилируемым модулем, хранятся в публичном resource carrier AdaScriptNativeResources. Его values индексируется сначала по ID ресурса, затем по имени поля:
Ресурс native AOT из Swift
import AdaECS import AdaScripting let rules = world.getResource(AdaScriptNativeResources.self) let rate = rules?.values["game.rules"]?["healthRegenPerSecond"] print(rate as Any)
Этот последний путь относится к сборкам с поддержкой native AOT. Если данные изначально объявлены в Swift, их можно получать обычными типизированными ECS API, а AdaScript связывает те же данные через @query, @component и @res. Во всех случаях выбирайте API, соответствующий способу регистрации данных в вашем проекте.
Инструменты для Studio: @tool
В Studio на macOS можно создавать собственные инструменты на AdaScript: подключать панель к правой боковой области, связывать её controls с полями скрипта и выполнять действия над открытой сценой. @tool описывает инструмент, .ui задаёт его интерфейс, а методы AdaScript реализуют поведение.
Инструменты живут рядом с игрой, в отдельной папке проекта:
Модуль инструмента
Tools/ └── LevelHelper/ ├── LevelHelper.ada └── LevelHelper.ui
Каждая папка непосредственно внутри Tools/ образует модуль. Её .ada-файлы и относительные imports компилируются вместе. Зарезервированная папка Tools/ исключается из загрузки игровых скриптов: инструмент работает в редакторе и не становится системой игры.
Например, Level Helper создаёт ряд сущностей с Transform. Аннотация задаёт стабильный ID, имя, версию, API и доступ к документам; activate(editor) подключает панель:
LevelHelper.ada — AdaScript
@tool( id: "studio.level-helper", name: "Level Helper", version: "1.0.0", api: 1, platforms: ["macos"], permissions: [ "editor.documents.read", "editor.documents.write" ] ) class LevelHelper { @export var count = 3.0; @export var status = "Open a scene, then Generate."; func activate(editor) { editor.addPanel( id: "level-helper", title: "Level Helper", location: "right", ui: "LevelHelper.ui" ); } func generate(editor) { if (editor.scenePath == "") { status = "Open a scene first."; return; } var total = Math.clamp(count, 1.0, 32.0); var i = 0; while (i < total) { editor.createEntity( name: "Generated " + (i + 1), x: i * 48.0, y: 0.0 ); i += 1; } status = "Generated " + total + " entities."; } }
Путь LevelHelper.ui разрешается относительно объявляющего .ada-файла и должен оставаться внутри модуля. В Studio API 1 один инструмент может зарегистрировать до восьми панелей с уникальными ID в правой боковой области. При отключении инструмента или закрытии проекта host удаляет его панели; при необходимости скрипт может дополнительно реализовать deactivate().
Интерфейс использует обычный формат .ui, который можно редактировать в UI Designer. Для нашего примера достаточно слайдера, кнопки и текста результата:
LevelHelper.ui
format: ada.ui schemaVersion: 1 inputs: - name: count type: number defaultValue: 3 - name: status type: string defaultValue: Ready actions: - name: generate root: id: level-helper type: VStack arguments: spacing: {value: 12} children: - id: count type: Slider arguments: value: {binding: count} min: {value: 1} max: {value: 32} step: {value: 1} - id: generate type: Button arguments: title: {value: Generate} actions: action: generate - id: status type: Text arguments: text: {binding: status}
Каждый input связывается с одноимённым @export-полем совместимого типа. Поддерживаются строки, booleans, целые и дробные числа. Панель инициализируется значениями экземпляра скрипта; defaultValue в .ui служит для preview в Designer. Перед действием значения controls копируются в поля инструмента, после выполнения — обратно в интерфейс. Действие generate вызывает func generate(editor); callbacks инструмента синхронные.
Изменения сцены выполняются одним пакетом. editor.createEntity(...) подготавливает сущности, а Studio применяет их только после успешного завершения callback. В примере один Generate создаёт весь ряд, один Undo убирает его, Redo восстанавливает, а обычный Save сохраняет сцену. Если callback завершился ошибкой или исходная сцена успела измениться, подготовленный пакет не применяется. Доступны также editor.scenePath, editor.sceneRevision и editor.entityCount; за одно действие можно создать до 256 сущностей.
Включение и доступ управляются в Studio. Инструменты выключены по умолчанию. Кнопка с гаечным ключом открывает Studio Tools; до выполнения кода и отображения .ui Studio предлагает проверить разрешения и выбрать Allow and Enable. Настройки версии, включения и доступа находятся в Settings → Project → Studio Tools. Revoke Access сразу убирает панели и отменяет ожидающие действия. Согласие сохраняется локально для проекта; изменение расположения исходника, API, платформ или набора разрешений требует повторного согласия.
В текущем runtime реализованы разрешения editor.documents.read и editor.documents.write. Статическая схема умеет описывать более широкий набор разрешений, но доступ к сети, процессам, clipboard и файлам workspace через этот host пока не предоставляется.
Reload позволяет дорабатывать инструмент без закрытия проекта. Обновление корректного .ui сохраняет введённые значения. Успешная перезагрузка скрипта заменяет его экземпляр и регистрации; ошибка в новом скрипте или интерфейсе оставляет предыдущую рабочую версию активной и показывает диагностику. Runtime ограничивает время callbacks, циклы, память и рекурсию.
Этот слой доступен на macOS. Поддержка iPadOS, регистрации команд, меню и форматтеров, async-callbacks и распространения инструментов через marketplace остаются следующими этапами. Для панелей используется .ui.
В документации Studio Tools описаны жизненный цикл, bindings, доступ к сцене и reload. Готовый Level Helper можно скопировать в Tools/LevelHelper/ своего проекта.
AdaScript-аддоны: переиспользуемые библиотеки
Игровую логику можно вынести в AdaScript-аддон — библиотеку с функциями, ECS-системами и scriptable-поведением. Например, общую логику урона, инвентарь или контроллер персонажа можно подключать к нескольким играм.
Библиотека хранится в отдельном репозитории. В его корне находится ada-library.json, который описывает идентификатор, версию, список исходников и зависимости:
CombatAddon/
CombatAddon/ ├── ada-library.json └── Sources/ └── Damage.ada
ada-library.json
{ "schemaVersion": 1, "id": "example.combat", "version": "1.0.0", "api": 1, "sources": ["Sources/Damage.ada"], "dependencies": [] }
В sources перечисляются все исходники библиотеки, включая импортируемые вспомогательные файлы. Например, Damage.ada может содержать такую функцию:
CombatAddon/Sources/Damage.ada — AdaScript
func applyDamage(current, damage) { var remaining = current - damage; if (remaining < 0) { return 0; } return remaining; }
В Studio откройте Settings → Project → AdaScript Libraries, укажите публичный GitHub-репозиторий и tag или commit, затем выберите Install / Update. После установки пересоберите игру или перезапустите Play. Текущий провайдер поддерживает публичные GitHub-репозитории.
Функцию из установленного аддона можно импортировать по ID библиотеки, независимо от расположения игрового скрипта:
Импорт в игре — AdaScript
import { applyDamage } from "@example.combat/Sources/Damage"; var health = applyDamage(100.0, 25.0); // 75
Библиотечные @system и @scriptable участвуют в обычной регистрации игрового модуля. Нужные им возможности движка включаются через Runtime Settings. Библиотеки используют общий модуль игры, поэтому их классы, функции и идентификаторы аннотаций должны иметь уникальные имена.
Установка сохраняет исходники в .ada/libraries/, а точные Git commits — в .ada/libraries.lock.json. Библиотеки могут иметь собственные зависимости, также зафиксированные на конкретных commits. Restore Locked Libraries восстанавливает этот набор на другом компьютере. Сборка и запуск используют уже установленные исходники и не скачивают библиотеки автоматически.
Для полностью офлайн-проекта можно сохранить вместе lock-файл и .ada/libraries/. В SwiftPM-игре AdaScriptBuildPlugin включает исходники аддонов в сборку вместе с игровыми скриптами. Так библиотеки помогают делиться готовой логикой, сохраняя повторяемый состав зависимостей.
Hot reload
Studio умеет обновлять поддерживаемую игровую логику AdaScript во время выполнения. Если новая версия скрипта содержит ошибку, предыдущая продолжает работать.
Это помогает быстрее проверять изменения поведения. Изменения схем данных и другие несовместимые изменения могут требовать перезапуска игры.
Новый AOT-компилятор
В релиз входит экспериментальный AOT-путь для AdaScript.
Поддерживаемый код компилируется заранее в нативный код. Этот путь включает интеграцию с ECS, жизненный цикл scriptable-объектов и асинхронные coroutine-задачи.
Studio использует его для экспорта AdaScript-проектов на macOS, Web и Android. Возможности AOT пока ограничены поддерживаемым подмножеством языка; неподдерживаемые конструкции возвращают явную ошибку.
Нативная поддержка Android
AdaEngine получил нативный Android host. Игровой код выполняется на Swift, а рендеринг использует WebGPU через Swan/Dawn поверх Vulkan.
В standalone Studio на macOS можно выбрать Android как цель запуска, обнаружить устройство или эмулятор, собрать APK, установить его и запустить игру. Studio также умеет запускать подготовленный Android Virtual Device.
Экспорт поддерживает AdaScript AOT и подходящие Swift-проекты AdaEngine. Текущий процесс создаёт APK с development signing; подготовка AAB и публикация в Google Play требуют отдельного процесса.

Настройка Android SDK, NDK и JDK в Studio перед экспортом и запуском.
Networking и multiplayer
Новый модуль AdaMultiplayer добавляет инфраструктуру локального и интернет-multiplayer.
Он включает host-authoritative модель, репликацию зарегистрированных ECS-компонентов, типизированные команды и RPC, а также интерполяцию полученного состояния.
Транспорт отделён от игровой логики. Можно использовать локальное соединение, Ada Cloud relay или собственную реализацию транспорта. AdaScript предоставляет аннотации для описания сетевых данных и команд.
Такой подход позволяет сохранить игровую модель внутри ECS и явно определить, какие данные должны передаваться между участниками.
Реплицируемый компонент: состояние игрока
В AdaScript сетевой компонент объявляется через @replicated_component. Он уже является ECS-компонентом, поэтому дополнительная аннотация @component не нужна. Вот небольшой пример движения по одной оси:
AdaScript
@replicated_component( id: "game.network-player", version: 1, authority: "host", visibility: "all_peers" ) struct NetworkPlayer { @network_field(1) var peer = ""; @network_field(2, interpolate: "linear") var x = 0.0; @network_field(3) var health = 100; @local var animationTime = 0.0; }
Host хранит авторитетное состояние и передаёт peer, x и health участникам. Для числовой координаты x включена линейная интерполяция. Поле animationTime остаётся локальным: например, каждый клиент может использовать его для своей анимации персонажа.
id идентифицирует сетевую схему компонента, а числа в @network_field(1), (2), (3) — поля внутри этой схемы. Это стабильные теги протокола, а не индексы массива: при переименовании поля его тег сохраняют. Совместимые сборки должны согласовать id, version, теги и типы полей.
На host компонент можно создать обычной ECS-командой из callback системы, передав идентификатор участника, которому принадлежит игрок:
AdaScript
context.world.spawn([ NetworkPlayer(peer: session.localPeerID) ]);
Здесь session — ресурс AdaScriptMultiplayerState. Для удалённого игрока host передаёт его peer ID вместо session.localPeerID. Сгенерированный путь создания реплицируемого компонента добавляет сущности маркер репликации; вручную собирать snapshot не требуется.
Работа с компонентом и объявление RPC
Peer отправляет намерение двигаться, а host рассчитывает новую координату. Метод receiveMove объявлен как RPC; в его теле мы запоминаем ввод отдельно для каждого отправителя, затем применяем его к соответствующему компоненту через @query:
AdaScript
@system(scheduler: "update", id: "game.network-movement") class NetworkMovementSystem { @res var input: Input; @res var multiplayer: Multiplayer; @res var session: AdaScriptMultiplayerState; @query(NetworkPlayer) var players; var movementByPeer = [:]; @rpc( id: "game.move-player", version: 1, direction: "peer_to_host", delivery: "unreliable_sequenced", channel: "input" ) func receiveMove(@network_field(1) moveX = 0.0) { // source предоставлен транспортом, а не отправителем. if (moveX < -1.0) moveX = -1.0; if (moveX > 1.0) moveX = 1.0; movementByPeer[source] = moveX; } func update(context) { var moveX = input.getActionStrength("MoveRight") - input.getActionStrength("MoveLeft"); if (session.role == "peer") { multiplayer.send(receiveMove(moveX)); return; } if (session.role != "host") return; // Ввод локального игрока host применяет напрямую. movementByPeer[session.localPeerID] = moveX; for (var row in players) { var movement = movementByPeer[row.networkPlayer.peer]; if (movement == null) continue; // Обычная запись в ECS-компонент на host. row.networkPlayer.x += movement * 175.0 * context.deltaTime; } } }
Вызов receiveMove(moveX) создаёт типизированную команду, а multiplayer.send(...) ставит её на отправку. Тело receiveMove выполняется у получателя перед update, где source содержит идентификатор отправителя от транспорта. На отправляющем клиенте тело автоматически не вызывается. Поэтому ввод самого host в примере обрабатывается напрямую.
Мы сопоставляем source с NetworkPlayer.peer, заданным host при создании игрока: клиент управляет своим персонажем и не выбирает чужую сущность через payload. Ввод ограничивается диапазоном от −1 до 1; скорость и итоговую координату определяет host. После записи в row.networkPlayer.x репликация доставляет состояние участникам.
peer_to_host задаёт направление RPC. unreliable_sequenced подходит для регулярно обновляемого ввода: потерянный пакет заменит следующий, а устаревшие пакеты отбрасываются. Для команд, которым нужна гарантированная доставка и порядок, используется reliable_ordered. У RPC, как и у компонента, есть стабильные id, version и теги параметров.
Пример предполагает включённый multiplayer runtime, настроенные действия MoveRight / MoveLeft и создание игроков на host при входе в сессию. Это основа механики движения; в игре также нужны сброс ввода при отключении или таймауте и удаление вышедших игроков.
Подробнее в документации:
AdaScript: multiplayer-схемы и RPC — объявления, отправка команды и выполнение метода у получателя.
AdaMultiplayer: подключение runtime и транспорты — настройка plugin, репликация сущностей и сетевые сессии в Swift.

Новые возможности AdaUI
AdaUI заметно вырос вместе со Studio.
Система анимаций получила транзакции, withAnimation, анимацию bindings и изменения выбранного значения. Для навигации, вкладок, sheets и полноэкранных представлений доступны встроенные переходы.
Мы также улучшили работу с текстом, выделением, направлениями письма, clipping и обновлением интерфейса.
Отдельное большое дополнение — визуальный UI Designer. Интерфейсы можно собирать в .ui-ресурсах, редактировать иерархию, свойства и modifiers, настраивать inputs, actions и bindings. Элементы интерфейса могут связываться с экспортированными полями игрового поведения.
AdaUI продолжает использоваться и в играх, и в самом Studio — это даёт фреймворку постоянную проверку на сложных интерфейсах.

Улучшения 2D
В 2D мы сосредоточились на удобстве создания уровней и корректности отображения.
Тайловые карты получили полноценный workflow внутри Studio: tileset, палитра, слои, редактирование карты и настройка геометрии для света и теней.
Для спрайтов улучшены batching, обработка размеров и обновление culling. Работа с текстурными атласами стала частью редактора ресурсов, что упрощает подготовку и использование графики.

2D-демо из галереи движка: множество спрайтов в одном игровом мире.
Большое обновление 3D
3D-возможности AdaEngine значительно расширились.

Skeletal Garden: анимированные персонажи, материалы, освещение и трёхмерный игровой мир.

Skeletal Garden: ландшафт, растительность и центральная площадка с персонажами.
Физика на Box3D
Добавлена интеграция с Box3D — библиотекой физики трёхмерных тел.
В движке появились 3D-тела, collision shapes и controller персонажа. Эти возможности используются в Skeletal Garden для движения, столкновений, прыжков и перемещения по ландшафту.

Взаимодействие 3D-объектов: запись из архивной галереи демо движка.
Модели и анимация
Поддержка GLB/glTF включает иерархии моделей, PBR-материалы, скелеты и анимационные клипы. Добавлены GPU skinning и независимое воспроизведение анимаций экземпляров модели.
Анимационные графы позволяют смешивать клипы, использовать additive-слои, маски костей и события анимации. Их можно редактировать и просматривать в Studio.
Освещение и эффекты
Развиваются PBR-рендеринг, image-based lighting, локальные источники света и тени.
Добавлены каскадные directional shadows, SSAO, FXAA и настройки окружения для отдельных камер. SSAO помогает передать глубину в местах контакта объектов, а IBL использует окружение для освещения и отражений.
Сравните реальные кадры одной сцены с выключенным и включённым SSAO.
SSAO: контактные тени в Skeletal Garden

Без SSAO

SSAO
MetalFX
На поддерживаемых Metal-устройствах доступны spatial upscaling и temporal reconstruction через MetalFX.
Temporal-путь использует историю кадров и motion vectors камеры, объектов и скелетной анимации. UI компонуется после восстановления изображения и сохраняет выходное разрешение.
MetalFX: spatial и temporal reconstruction, macOS / Metal

Spatial

Temporal
Это статические кадры для просмотра деталей. Качество temporal reconstruction в движении и стоимость рендеринга требуют отдельных измерений.
WebGPU
WebGPU продолжает развиваться как переносимый графический backend AdaEngine. Он используется в Web-направлении и нативном Android host через Vulkan.
При этом доступность эффектов зависит от платформы и возможностей бэкенда.

Кадр Skeletal Garden из нативного WebGPU-запуска.
Производительность
Мы переработали важные пути ECS: создание сущностей, изменение состава компонентов и обход queries. Работа включала сокращение лишних копирований, ARC-операций и выделений памяти, а также исправление времени жизни компонентов.
В локальном Release-бенчмарке операций над 4 000 сущностями получены следующие медианы CPU-времени:
Создание сущностей: 179,18 мс → 4,43 мс.
Добавление и удаление компонентов: 592,45 мс → 20,23 мс.
Сценарий InsertCoW: 299,37 мс → 10,13 мс.
Это сравнение до и после конкретной оптимизации ECS storage. Измерения выполнены на Apple arm64, со Swift 6.2.4, в Release, с тремя прогревами и 30 измеряемыми образцами. Они описывают данные нагрузки, а не общий прирост FPS или сравнение всех возможностей двух релизов. Методика и полные результаты.
Studio также получил Performance panel и Timeline: CPU-время обновления, количество сущностей, память процесса, длительности ECS-систем и история событий с экспортом Chrome Trace. Это помогает находить причины задержек непосредственно во время работы над игрой.

Записанный CPU Timeline: обновления мира, ECS и узлы render graph.
Ada Cloud и пользовательские игры
Ada Cloud расширяет возможности распространения игр.
Для UGC предусмотрены постоянные версии, фиксированные файлы и метаданные, модерация и история публикаций. Новая версия проходит проверку отдельно: текущая одобренная игра остаётся доступной до одобрения обновления.
Community в Studio позволяет находить игры, смотреть описания и screenshots и запускать поддерживаемые AdaScript-публикации во встроенном player. Для совместимых multiplayer-игр доступны Solo, Host и подключение по коду комнаты.
Рядом развивается Ada Store — каталог ресурсов, скриптов, шаблонов и инструментов. Он дополняет библиотеку готовых игр материалами для создания собственных проектов.
Автоматизация через CLI
Standalone Studio на macOS включает adastudio CLI.
Он позволяет проверять проекты, диагностировать окружение, собирать и экспортировать поддерживаемые игры без открытия окна редактора. JSON-диагностика и определённые коды завершения позволяют использовать эти команды в скриптах и автоматизированных процессах.
Давайте делать игры
AdaEngine остаётся открытым проектом на Swift под лицензией MIT.
Версия 1.0.0 объединяет движок, визуальные инструменты, скриптовый язык, AI-workflow и инфраструктуру распространения. Для нас это важный шаг к среде, в которой удобно экспериментировать, создавать игры и делиться результатом.
Посмотрите примеры, попробуйте Studio и присоединяйтесь к развитию AdaEngine на GitHub — кодом, документацией, играми и обратной связью.
Давайте делать игры на Swift и AdaScript.