# Godot: удобство разработки как устройство движка **Синхронизация Faset, 18.09.2026.** Принятые решения — [ARCHITECTURE.md](../ARCHITECTURE.md), этапы до/после MVP — [PLAN.md](../../PLAN.md). Ниже сохранено исследование чужих исходников; это не отчёт о реализованных возможностях Faset. Рекомендации, помеченные **superseded**, остаются только историей рассмотренных вариантов. Для Faset приняты authoring objects/components с JSON и постоянными IDs, EnTT runtime, явная C++ metadata schema (`TypeId`/`FieldId`) и собственный retained C++ UI. Godot inheritance/live editing исследованы как сравнение. MVP Faset использует nested composition и overrides без variants/Apply to template, отдельный Player со snapshot сцены и MCP только в редакторе: authoring/import/build/PlayStop/editor logs, без чтения или изменения runtime world. Исследован публичный [репозиторий Godot](https://github.com/godotengine/godot), скачанный командой `git clone --depth 1` в `../../../godot`. Зафиксирован commit `9c776068d6ed23acd0c78bfe534272d1d2a3a619`, дата commit `2026-09-17T12:55:51-05:00`; [version.py:3](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/version.py#L3) сообщает **4.8.0 dev**, а не стабильный релиз. Размер рабочего каталога с Git на момент исследования — около 429 МБ. Исходники не изменялись. Локального `AGENTS.md` и `graphify-out/graph.json` в клоне нет; применены указания `локальный AGENTS.md`. Изучены тела функций в `Node`, `Resource`, `SceneState/PackedScene`, `PropertyUtils`, `EditorInspector`, `EditorUndoRedoManager`, run/debugger, filesystem/importer и `EditorPlugin`. Официальная документация `stable` сверена 17 сентября 2026 года для общих пользовательских контрактов; детали реализации относятся именно к указанному dev commit. Сборка, запуск редактора и пользовательский плейтест не проводились. Выводы о полезности и предложенные критерии — инженерная оценка, а не измерение скорости работы пользователей. Самый ценный перенос из Godot — **связанный путь от данных до действия пользователя**: свойства объекта автоматически становятся редактируемыми; изменение проходит через undo, сериализацию, обновление интерфейса и при необходимости live editing. Панели удобны благодаря этому общему основанию. В Faset принят общий authoring contract, при этом runtime хранится в EnTT, UI и renderer реализуются отдельно; live editing Godot не становится runtime MCP API. ## 1. Сцена как повторно используемая сборка, Resource как явная модель данных **Сценарий.** Дизайнер собирает дверь из визуальной части, коллизии и скрипта, сохраняет её отдельно и ставит пять экземпляров в уровень. Геометрия и общая конфигурация используются совместно; состояние открытия и специально локализованный материал принадлежат конкретному экземпляру. Такой же способ сборки должен работать для оружия, UI-панели и комнаты. **Что делает код.** `SceneState::instantiate` создаёт вложенный `PackedScene` тем же механизмом, что и корневую сцену: [packed_scene.cpp:305](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/scene/resources/packed_scene.cpp#L305), [342](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/scene/resources/packed_scene.cpp#L342). Сохранение определяется отдельной связью ownership: `_parse_node` пропускает узлы, не принадлежащие сохраняемой сцене и не являющиеся editable instance: [870](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/scene/resources/packed_scene.cpp#L870). Это отличается от самого положения в дереве. `Node::add_child` проверяет наличие родителя и циклы [node.cpp:1711](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/scene/main/node.cpp#L1711), а `set_owner` отдельно требует, чтобы owner был предком [2303](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/scene/main/node.cpp#L2303). Особенно полезна семантика локальных ресурсов. `SceneState::make_local_resource` возвращает обычный ресурс без копирования, а для `local_to_scene` ищет уже созданную копию в remap текущей сцены и только затем дублирует: [packed_scene.cpp:781](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/scene/resources/packed_scene.cpp#L781). Поэтому две ссылки внутри экземпляра могут указывать на одну локальную копию, тогда как другой экземпляр получает другую. Рекурсивное копирование учитывает `ALWAYS_DUPLICATE`, `NEVER_DUPLICATE` и remap cache: [resource.cpp:278](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/core/io/resource.cpp#L278). Это существенно точнее, чем кнопка «скопировать все поля». Официальные [Resources](https://docs.godotengine.org/en/stable/tutorials/scripting/resources.html) подтверждают общую модель: узлы используют данные ресурсов, ресурсы бывают внешними и встроенными, пользовательские ресурсы получают сериализацию и Inspector. [PackedScene](https://docs.godotengine.org/en/stable/classes/class_packedscene.html) явно связывает сохранение с `owner`. **Перенос.** Нужны authoring scene/prefab, typed asset references и три понятных режима данных: общий asset, локальное состояние экземпляра, явно созданная уникальная копия. В UI полезнее показывать «изменение затронет все экземпляры», имя источника и переход к нему, чем рассчитывать на знание ref-counting. Внутреннее дерево редактора можно компилировать в ECS; исходники Godot не обязывают переносить его runtime object model. **Чего избегать.** Не делать ownership невидимым условием сохранения для обычного автора: узел, видимый в редакторе, но не попадающий в файл, создаёт неприятный сюрприз. Не смешивать «встроен в файл» и «уникален для экземпляра» — это разные свойства. Избегать глубоко вложенных shared mutable ресурсов без индикации области воздействия. **Минимальный прототип и приёмка.** Одна дверь, два references на её материал внутри сцены, пять экземпляров, external `DoorConfig`. Изменение config обновляет все экземпляры; local material меняется только у одной двери, сохраняя sharing её внутренних ссылок; save/reload сохраняет эти отношения. Пользователь должен объяснить область воздействия изменения, глядя на интерфейс, без чтения документации. ## 2. Наследование и overrides должны быть обозримыми и обратимыми **Сценарий.** Есть базовый противник и тяжёлый вариант. Вариант меняет здоровье и скорость, но наследует анимации. Автор повышает базовую скорость, хочет понять, какой вариант получил изменение, и отменить только локальное переопределение здоровья. **Что делает код.** При сериализации наследуемой сцены сохраняется ссылка на базовый `PackedScene`: [packed_scene.cpp:1428](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/scene/resources/packed_scene.cpp#L1428). `_parse_node` сравнивает значение со значением по умолчанию и не сохраняет совпадающее; pinned properties обходят это исключение: [1058](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/scene/resources/packed_scene.cpp#L1058). У «default» есть происхождение: `PropertyUtils::get_property_default_value` сначала ищет override в instantiation/inheritance stack, затем экспортированный default скрипта и, наконец, native class default: [property_utils.cpp:80](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/scene/property_utils.cpp#L80), [152](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/scene/property_utils.cpp#L152). Inspector использует тот же вычислитель для revert и определения, отличается ли текущее значение: [editor_inspector.cpp:906](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/inspector/editor_inspector.cpp#L906). Клик revert отправляет обычный `emit_changed`, а не тайно меняет память отдельным способом: [1271](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/inspector/editor_inspector.cpp#L1271). Даже свёрнутая секция показывает число изменённых свойств [2463](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/inspector/editor_inspector.cpp#L2463). Таким образом, provenance данных поддерживает конкретные affordances интерфейса. **Перенос.** Хранить базовую ссылку и sparse overrides; у каждого значения иметь понятное происхождение. Нужны «перейти к источнику», «вернуть наследуемое значение», список overrides и отдельное «зафиксировать это значение», включая совпадение с нынешним default. Для Faset MVP приняты обычные и вложенные экземпляры; прежнее предложение одного уровня prefab variation **superseded**. Variant inheritance и Apply overrides to template отложены. Patch использует instance chain/ObjectId/ComponentId/FieldId, а не имя или путь узла; точные правила — в [архитектурном исследовании](01-architecture.md). **Чего избегать.** Нельзя вычислять revert просто из zero/default C++ конструктора: пользователь ожидает значение базы. Нельзя считать равенство чисел доказательством отсутствия намеренного override. Переименование узла, изменение структуры базы, удаление свойства и изменение типа требуют явной политики миграции. Source показывает сложность этих случаев; он не доказывает, что произвольная цепочка inheritance безошибочна. Официальная [Scene organization](https://docs.godotengine.org/en/stable/tutorials/best_practices/scene_organization.html) также предупреждает о связности сцен через пути и рекомендует самостоятельные под-сцены и явно передаваемые зависимости. Это хороший предел для обещаний prefab workflow. **Прототип и приёмка Faset.** Шаблон → вложенный экземпляр → два размещения в сцене. Изменить default базы, добавить override, pin совпадающее значение, выполнить revert/undo, затем save/reload. Override layer должен сохранять только намеренные отличия; UI должен показывать, откуда пришло значение. При удалении target свойства нужен видимый orphaned override, а не молчаливое исчезновение пользовательского решения — это предлагаемое улучшение собственного движка. ## 3. Inspector и Undo — единый редакторский транзакционный слой **Сценарий.** Автор тянет slider скорости, меняет несколько объектов сразу, переключается на другую сцену и нажимает Undo. Он ожидает возврата целого жеста в правильном документе и восстановления связанных свойств, а не сотни микрошагов или поломанного объекта. **Что делает код.** Inspector получает property list [editor_inspector.cpp:4480](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/inspector/editor_inspector.cpp#L4480), фильтрует editor flags [4635](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/inspector/editor_inspector.cpp#L4635), отдаёт тип, имя, hints и usage Inspector plugins; exclusive plugin останавливает выбор следующих обработчиков [5105](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/inspector/editor_inspector.cpp#L5105). Значит, тип данных и metadata действительно определяют редактор поля. `EditorInspector::_edit_set` создаёт action с `MERGE_ENDS`, записывает do/undo property, сохраняет связанные свойства и вызывает расширяемые undo hooks: [5703](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/inspector/editor_inspector.cpp#L5703), [5754](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/inspector/editor_inspector.cpp#L5754). В action входят refresh и notifications; затем `commit_action`: [5789](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/inspector/editor_inspector.cpp#L5789). Это помогает сохранить согласованность UI и состояния объекта. Менеджер выбирает историю сцены, built-in resource, global или remote: [editor_undo_redo_manager.cpp:63](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/editor_undo_redo_manager.cpp#L63). Custom context позволяет явно указать принадлежность [149](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/editor_undo_redo_manager.cpp#L149). Commit ведёт saved version и инвалидирует несовместимые redo branches [246](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/editor_undo_redo_manager.cpp#L246). Официальный [EditorUndoRedoManager](https://docs.godotengine.org/en/stable/classes/class_editorundoredomanager.html) подтверждает отдельные истории и предупреждает, что автоматическое определение context иногда ошибается. **Перенос.** Сначала сделать универсальную команду редактирования `{document, object, property, before, after}` с transaction boundary, merge key, dirty revision и event notifications. Drag gesture — одна команда; изменение взаимосвязанных параметров — одна транзакция. Serializer, Inspector, gizmos, bulk-edit и plugins используют одинаковые descriptor IDs. Пользовательская UI-специализация должна посылать изменения в этот слой. **Чего избегать.** Не добавлять undo как последнюю оболочку вокруг готовых инструментов: побочные изменения уже будут потеряны. Не делать document context догадкой без возможности задать его явно. Нельзя копировать всю глобальную историю Godot без анализа: в этом source global edits могут очищать redo других сцен; собственному UI нужно ясно объяснять такую модель. Не считать каждый setter чистым присваиванием. **Прототип и приёмка.** Две открытые сцены, общий ресурс и custom slider. 100 обновлений drag отменяются одним жестом; редактирование другой сцены не отменяет неожиданно первую; Undo восстанавливает и зависимое значение. Сохранение, Undo к saved revision, Redo и новая ветка должны правильно менять dirty indicator. Custom property widget получает это поведение без собственного Undo кода. ## 4. Запуск отдельной сцены и два явно различимых пути live editing **Сценарий.** Разработчик проверяет экран инвентаря без прохождения главного меню, меняет расстояние взаимодействия у работающей двери и выясняет, почему runtime объект получил неожиданное значение. Важна ясность: он сейчас меняет исходную сцену или только живой экземпляр. **Что делает код.** Run bar выбирает отдельную current/custom scene либо project main; для несохранённой сцены запускает save flow: [editor_run_bar.cpp:312](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/run/editor_run_bar.cpp#L312). Затем autosave/build hook, старт debugger server и subprocess: [350](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/run/editor_run_bar.cpp#L350). `EditorRun::run` передаёт проект и `--remote-debug`: [editor_run.cpp:51](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/run/editor_run.cpp#L51). Это инструментальное выполнение сцены, не специальный режим в игровом bootstrap. Для editor-authored edits undo notify подключён к debugger: [editor_debugger_node.cpp:245](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/debugger/editor_debugger_node.cpp#L245). `_property_changed` отправляет node-path/property/value либо resource path: [script_editor_debugger.cpp:1508](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/debugger/script_editor_debugger.cpp#L1508). Runtime `LiveEditor::_node_set_func` ищет instances редактируемой сцены и применяет изменение; root transform чужого instance специально сохраняется: [scene_debugger.cpp:871](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/scene/debugger/scene_debugger.cpp#L871). Remote Inspector использует другой путь: `update_remote_object` посылает runtime ObjectID [script_editor_debugger.cpp:281](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/debugger/script_editor_debugger.cpp#L281), runtime находит объект и делает `set` [scene_debugger.cpp:760](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/scene/debugger/scene_debugger.cpp#L760). Из этих тел нельзя вывести автоматическое сохранение remote-правки в исходную сцену. [Debugger panel](https://docs.godotengine.org/en/stable/tutorials/scripting/debug/debugger_panel.html) служит официальной сверкой общего debug workflow; конкретное различие путей подтверждено локальным source. **Принятый перенос.** Separate Player process и запуск snapshot текущей сцены либо project entry point. Сохраняется authoring context; Stop → incremental C++ build → Play создаёт новый snapshot. **Superseded для MVP:** remote tree/property edits, пересылка authoring transactions в runtime и Apply runtime value в источник. MCP управляет запуском на стороне редактора, но не инспектирует и не меняет runtime world; в Player и экспорте MCP отсутствует. Маленький test context позволяет проверить сцену без прохождения всей игры. **Чего избегать.** Не обещать произвольный state-preserving hot reload на основании property live edit. В показанном коде ref-counted resources без пути не пересылаются этим способом; структура сцены и instance mapping тоже накладывают ограничения. Runtime deletion, повторно использованные IDs, invalid paths и разрыв соединения должны быть нормальными состояниями protocol. **Прототип и приёмка Faset.** Запустить дверную сцену отдельным Player, изменить исходный документ и Undo во время игры: текущий snapshot остаётся прежним. После Stop/Play запускается новая версия; runtime изменения не записаны в source. Проверить два экземпляра, сбой Player, повторный запуск и сохранение редакторского контекста. У MCP отсутствуют runtime query/mutation tools, у Player — MCP endpoint. Измерять время build/start отдельно, без обещания фиксированной latency. ## 5. Неразрушающий импорт и устойчивые ссылки — часть UX, а не только build pipeline **Сценарий.** Художник меняет исходную текстуру или glTF, возвращается в редактор, получает обновление с прежними настройками. Другая машина воспроизводит импорт; перенос ассета в редакторе не требует ручного поиска всех ссылок. **Что делает код.** `_reimport_file` читает сохранённые `params`, importer и UID из sidecar `.import`: [editor_file_system.cpp:2826](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/file_system/editor_file_system.cpp#L2826). Добавляет недостающие defaults, вызывает importer с source и отдельным output base path: [2899](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/file_system/editor_file_system.cpp#L2899). Затем пишет importer version, UID, output paths и options [2930](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/file_system/editor_file_system.cpp#L2930). Порядок options сохраняется специально для уменьшения шума diff; source/destination checksums вынесены в отдельный `.md5`: [3018](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/file_system/editor_file_system.cpp#L3018). Generated base path находится в imported-files directory и определяется исходным путём [resource_importer.cpp:541](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/core/io/resource_importer.cpp#L541). Reimport проверяет source и destination checksum [editor_file_system.cpp:727](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/file_system/editor_file_system.cpp#L727). При редакторском move `FileSystemDock` вызывает `rename_dependencies` для owners и собирает открытые сцены для reload [filesystem_dock.cpp:1688](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/docks/filesystem_dock.cpp#L1688). Значит, UID не отменяет maintenance зависимостей и путей. Официальный [Import process](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/import_process.html) подтверждает разделение source, настроек импорта и generated data. [Import configuration](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_3d_scenes/import_configuration.html) описывает настройку импортируемых 3D scenes и ограничение прямого редактирования производных данных. **Перенос.** Source + маленький versioned recipe + disposable derived cache. Stable asset ID, dependency index, понятный progress/status и транзакционный remap при move. User overrides хранить отдельно от регенерируемого результата. Для начала достаточно PNG и одного mesh format; remote DDC и сотни импортёров преждевременны. **Чего избегать.** Не записывать ручные правки в файл, который importer завтра перезапишет. Не делать GPU cache source of truth. Не обещать полную защиту всех строковых путей одним UID: scene/script references и пользовательские данные требуют правил. Проверка checksum не означает, что importer доказанно детерминирован. **Прототип и приёмка.** Изменить source, поменять import recipe, перенести asset через browser, удалить derived cache, открыть проект на чистой машине. Связи и recipe сохраняются, cache восстанавливается, Git diff отражает пользовательские решения. Ошибка importer оставляет диагностируемое состояние; для своего движка разумно дополнительно сохранять последнюю успешную версию результата до завершения нового импорта. ## 6. Предметные инструменты и локальная диагностика вместо обязательной правки движка **Сценарий.** Команда делает редактор волн противников: специальный Inspector, dock со списком волн, gizmo радиуса появления и предупреждение у spawner без точки назначения. Всё должно участвовать в обычном Undo и исчезать корректно при выключении расширения. **Что делает код.** `EditorPlugin` предоставляет registration/removal для docks [editor_plugin.cpp:134](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/plugins/editor_plugin.cpp#L134), inspector plugins [513](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/plugins/editor_plugin.cpp#L513), importers с отложенным filesystem rescan [445](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/plugins/editor_plugin.cpp#L445), viewport input/overlay [327](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/plugins/editor_plugin.cpp#L327). Inspector поддерживает undo hooks [427](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/plugins/editor_plugin.cpp#L427). Официальные [Inspector plugins](https://docs.godotengine.org/en/stable/tutorials/plugins/editor/inspector_plugins.html) описывают протокол выбора property editor и отправку изменений через `emit_changed`. Диагностика тоже имеет контракт: Node вызывает пользовательский `_get_configuration_warnings`, а `update_configuration_warnings` отправляет сигнал только для редактируемой сцены [node.cpp:3564](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/scene/main/node.cpp#L3564). Scene tree собирает сообщения и добавляет warning button прямо на соответствующий item [scene_tree_editor.cpp:594](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/scene/scene_tree_editor.cpp#L594). Это образец объяснения ошибки в её контексте, без поиска в общем логе. **Перенос.** Дать небольшой стабильный editor API: selection, property descriptors, commands, preview drawing, asset picker, dock lifecycle и structured validation. Для первого custom tool лучше десять согласованных точек расширения, чем доступ ко всем внутренним singleton. Вычисляемые warning должны объяснять исправление: «укажите маршрут», а не «invalid state». Кнопка исправления — отдельная undoable command. **Чего избегать.** Не давать plugin обходить transaction layer ради удобства. Не исполнять автоматически тяжёлую генерацию на каждый paint/tick. User tool code внутри редактора требует аккуратных lifecycle и error boundaries. Официальное [Running code in the editor](https://docs.godotengine.org/en/latest/tutorials/plugins/running_code_in_the_editor.html) отдельно предупреждает: tool/EditorScript изменения сами по себе не гарантируют Undo и dirty marking. Это ограничение надо учитывать при заимствовании, а не считать `@tool` бесплатной безопасной магией. **Прототип и приёмка.** Расширение spawner работает без пересборки ядра, валидирует отсутствие маршрута, редактирует параметры custom widget и рисует radius. Enable/disable дважды не оставляет widgets/callbacks; исправление warning отменяется; исключение пользовательского инструмента не повреждает сцену; сцена сохраняется через общий слой, без своего serializer. ## Порядок заимствования 1. Typed properties/resources, document identity и полноценные editor transactions. 2. Scene/prefab composition, явные sharing/local semantics, provenance и revert. 3. Отдельный Player со snapshot сцены и надёжный Stop/build/Play; remote/live workflow выше оставлен сравнением Godot. 4. Source/recipe/cache import, stable asset references и reference-aware move. 5. Plugin API и локальная validation как проверка качества предыдущих контрактов. Для сопоставления с Unreal и третьим движком: Godot здесь особенно ценен связью authoring model с ежедневным циклом разработки. Его node hierarchy не следует навязывать высокопроизводительному runtime, а editor transactions и provenance полезны почти независимо от renderer. Главный критерий будущего прототипа — можно ли создать, настроить, проверить, переиспользовать и исправить небольшой игровой объект без специальных обходных путей и без потери авторских изменений. Обычный glTF/GLB import Faset работает без Blender add-on. Используется официальный Blender; optional Python add-on обеспечивает IDs и удобный экспорт, но не модифицирует движок Blender. Без устойчивых source IDs соответствие частей после rename не гарантируется.