Игру правят несколько рук одновременно
Живой мир неудобен тем, что его нельзя поставить на паузу ради правки. Игроки ходят по картам, мобы дерутся, сервер держит состояние в памяти. И ровно в этот момент один человек меняет анимацию скорпиона, второй добавляет предмет в таблицу добычи, третий двигает переход между локациями. А ведь к работе подключается ещё и ИИ-агент.
Сломать общий контент в такой схеме может кто угодно, а не только машина. Поэтому важно не кто быстрее внесёт правку, а что окажется в игре после того, как все закончат. Я решил это так. Контент разложен на самостоятельные элементы, у каждого свой репозиторий и своя история. Правки не заливаются в мир напрямую: автор сначала видит построчно, что именно уйдёт.
С самого начала я держал ещё одно условие. Чтобы начать работать, не нужно ничего разворачивать у себя. Достаточно браузера и учётной записи, а живую игру запускает мой сервис.
Контент разложен на элементы, у каждого — свой адрес
Игра здесь не монолит, а сборка: фреймворк, библиотеки механик, миры с картами, анимации существ, наборы картинок предметов, префабы. Каждая из этих единиц — отдельная запись каталога. Сейчас в нём 84 анимации, 27 наборов картинок, 8 миров, 3 библиотеки, фреймворк и сама игра.
Каждый ассет — самостоятельная запись каталога и самостоятельный репозиторий. Анимация существа, набор иконок предметов, мир из нескольких карт — всё это отдельные единицы, которые ставятся в игру, обновляются и откатываются независимо друг от друга.

Так же устроены и локации: мир — это набор карт со своей графикой и своей раскладкой в открытом мире.

Карточка мира показывает не абстрактную «зону», а фактическую раскладку: как карты стыкуются между собой в бесшовном мире.

Карточка игры показывает её полный состав: какой фреймворк, какие библиотеки, какие виды сущностей, действия, слоты экипировки, компоненты, группы событий, префабы, миры, анимации и картинки в ней используются. Отсюда же игра ставится к себе одним нажатием.

Git — это и бэкап, и конструктор
Репозитории разложены по группам: миры, анимации, наборы картинок, фреймворки, библиотеки, форки игр. Контент, взятый из чужой игры, лежит в отдельной подгруппе — происхождение видно по адресу, а не по памяти автора.

Внутри группы — по репозиторию на ассет. Дело не в аккуратности раскладки. Адрес репозитория и есть удостоверение личности ассета: по нему платформа понимает, что «этот скорпион» в вашей игре и «этот скорпион» в моей — один ассет в двух версиях, а не два разных существа с похожими именами.

Один и тот же механизм закрывает две задачи. Первая — страховка: история правок и возможность вернуться к любой прошлой версии. Вторая — сборка игры из готового: чужой мир, чужая анимация, чужая библиотека механик ставятся к вам как деталь конструктора и дальше живут в вашей игре как ваша версия.
Правки видны до того, как попадут в мир
Каждый участник работает в своей копии игры. У элемента при этом две версии: базовая лежит в репозитории, действующая — это базовая с наложенными правками. Пока правка не отправлена, она живёт только у вас.

Ни одна операция не выполняется вслепую. Отправка в репозиторий, обновление из источника, откат — перед каждой показывается построчный список того, что изменится: красное уйдёт, зелёное придёт.

Откат разделён на две глубины. Первая снимает только ваши правки и не трогает данные игроков. Вторая переустанавливает состав игры из репозитория целиком — к ней прибегают, когда копия разъехалась настолько, что чинить построчно уже дороже.

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

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

Поэтому схема и работает. ИИ не решает, что считать одним ассетом, где проходит граница мира и что нельзя тянуть напрямую. Эти рамки задаю я, а машина внутри них исполняет — быстро и без усталости. Уберите рамки — получите ту самую «гору сгенерированного мусора», о которой любят писать в комментариях.
Чего ИИ не делает сам: не придумывает геймдизайн, не решает продуктовые развилки и не принимает решение «выкатывать или нет». Последнее слово — за человеком, который смотрит предпросмотр.
Из утилитарного доделываю две вещи: персональные ключи публикации для каждого участника команды и автоматическое обновление устаревшей базовой версии — пока это отдельное нажатие.
Зачем это всё
Собрать MMO в одиночку невозможно не потому, что код сложный, а потому что контента нужно много и он должен собираться в одно целое. Каталог готовых ассетов с историей версий превращает «нарисовать и запрограммировать всё самому» в «взять готовое, поправить под себя, опубликовать своё». Ровно этим я сейчас и занимаюсь: беру существующий мир, разбираю его на элементы и складываю обратно уже как контент платформы.
И главное, что даёт такой инструментарий, — контроль. Видно, кто и что изменил; правка вносится точечно в один элемент, а история сохраняется целиком, одинаково для человека и для машины.
ИИ получил здесь рабочее место, про которое я писал в части про внедрение ИИ. Дальше начинается разделение труда: сам я работаю в Claude Code, рисование контента (карты, анимации) уходит к ChatGPT, а Claude проверяет результат, грузит его в репозиторий и в игру, гоняет тесты и выставляет нужные настройки. Получается не генератор текста, а исполнитель полного цикла, которому поручают кусок работы целиком.
А в следующей части — как этот подход выглядит на реальной игре: перенос живой MMO на платформу, от карт и переходов между локациями до заселения города мобами.
Вопрос к тем, кто работал в команде над игровым контентом: что у вас чаще ломалось — рассинхрон версий ассетов, затёртые чужие правки или невозможность откатиться на «как было вчера»? Соберу боли под разбор в следующих частях.
История
FPS, Ping, паузы между командами, интерполяция и экстраполяция
Event-driven паттерн, JSON-RPC и почему не сервисная архитектура
Конвейер контента для MMO: импорт анимации, экипировка и обновление игры без патчей
Каталог ассетов и git: как командой вести игру в облаке