← Все статьи

GoKart: гонки в стиле Mario Kart, которые Auto-Pilot собрал за один день

Дайте Auto-Pilot в Agent! одну цель - «создай клон Mario Kart под названием GoKart» - и вернитесь к готовой гонке на Godot 4 с тремя трассами, восемью предметами, ИИ-соперниками и 3 344 пройденными проверками в тестах. Вот что, судя по журналу, произошло на самом деле, включая места, где он застрял.

GoKart на трассе Green Hills: вид с камеры преследования на карт игрока в дрифте на серой дороге с красно-белыми полосатыми стенами, зелёной землёй и синим небом. HUD показывает 1-е место, счётчики круга и времени и мини-карту трассы в левом нижнем углу.
Green Hills, круг 1, дрифт на первом месте. Каждый меш, шейдер и звук в этом кадре сгенерирован из кода.

Вчерашняя статья представила Auto-Pilot: наберите /auto <цель> в Agent! для Mac, и он будет без присмотра выполнять цикл за циклом в сторону этой цели, пока вы не нажмёте Stop All. Эта статья о том, что получилось на выходе, когда я направил его на игру.

Цель, вставленная примерно так, как я её напечатал:

create a Mario Kart clone called GoKart with all Mario Kart effects. I believe Godot 4 can do the Mario Kart effects, but I haven't built any of them yet: drift sparks and boost flames (GPUParticles3D), speed lines and boost blur (screen-space shaders, glow and tonemapping), item effects and tire trails (shaders plus ribbon meshes), kart movement (VehicleBody3D or custom arcade physics). Write unit tests and test the game frequently.

Бюджет: без ограничения по времени, циклы без лимита. Моделью внутри Agent! на каждом цикле была Claude Sonnet 5.5. Первый коммит появился в 12:39. К 16:32 того же дня в репозитории было 33 коммита. Сегодня в нём 47 файлов GDScript, около 5 500 строк GDScript и шейдерного кода и набор юнит-тестов, проходящий 3 344 проверки. Всё это лежит на GitHub: AgentiLoop/GoKart.

Что делает Auto-Pilot, цикл за циклом

Auto-Pilot ведёт в проекте непрерывный журнал в .agent/autopilot/progress.md, и каждый цикл дописывает туда, что он сделал, что предположил, что осталось и не заблокировало ли его что-нибудь. Этот журнал - источник всего, что ниже. Я цитирую его, а не свою память, потому что журнал честнее меня.

Цикл 1 не нашёл на машине Godot. Он выполнил brew install --cask godot, сделал символическую ссылку на бинарник, выполнил git init и написал kart_physics.gd как чистую модель без зависимостей от сцены: разгон, торможение, задний ход, трение, руление, дрифт, зафиксированный в том направлении, в котором вы его начали, и три уровня мини-турбо. Он намеренно выбрал собственную аркадную физику вместо VehicleBody3D и объяснил почему: «чтобы управление в духе Mario Kart было легко покрыть юнит-тестами». Одиннадцать тестов, 17 проверок, первый коммит.

Цикл 2 добавил эффекты, которые я просил поимённо: искры дрифта на GPUParticles3D, окрашенные по уровню мини-турбо, пламя из выхлопа, следы шин из ленточных мешей и экранный шейдер для радиального размытия ускорения, линий скорости и виньетки. 43 проверки.

Цикл 3 построил трассу. Замкнутая кривая Катмулла-Рома, пересэмплированная каждые 3 метра, лента дороги, красно-белые полосатые стены с коллайдерами, площадки ускорения с шейдером бегущих шевронов, восемь упорядоченных чекпоинтов и счётчик кругов, который отказывается засчитывать срезки и круги в обратную сторону. Один из новых тестов - бот-преследователь, который проезжает три полных круга на настоящей физической модели. Он финишировал за 1:02.5. 90 проверок.

Цикл 4 добавил ящики с предметами с радужным френелевским шейдером, рулетку и первые три предмета: гриб, банан и зелёный панцирь, рикошетящий от стен. Попадание закручивает карт на 1,2 секунды. 235 проверок.

А потом он застрял.

Там, где он застрял

Следующий цикл начал добавлять ИИ-карты и безголовую дымовую гонку - и так и не вернулся. Причина, найденная в следующей сессии, - один лишний таб в строке 115 файла main.gd: ошибка разбора, из-за которой дымовой скрипт бесконечно сыпал ошибками, а у shell-команды, которая его запускала, не было ограничения по времени.

Я нажал Stop All и открыл новую сессию с той же целью плюс заметкой капсом, которую не буду воспроизводить целиком. Суть: ты застрял на тестировании дымового прогона, не застревай, поставь ограничение по времени на shell.

Первый цикл второй сессии исправил таб, сократил упреждение ИИ с 10 сэмплов дороги до 6, чтобы ИИ перестал срезать повороты во внутреннюю стену, добавил ограничитель скорости в поворотах и обернул каждый запуск shell в perl -e 'alarm 100; exec @ARGV', потому что в macOS нет команды timeout. С этого момента в конце почти каждого цикла в журнале написано «все запуски шли под ограничениями perl alarm». Он усвоил урок из текста цели и продолжал его применять.

Та сессия выполнила 15 циклов примерно за час с четвертью, и каждый - это отдельная функция: обратный отсчёт на старте с ракетным ускорением за точно выверенный газ; всплески при повышении уровня мини-турбо и вспышка по краю экрана; мини-карта; самонаводящийся красный панцирь и звезда; процедурная модель карта с вращающимися колёсами, поворачивающимися передними колёсами и водителем, который вертит головой; молния, уменьшающая всех соперников; тройные панцири, вращающиеся вокруг карта; синий шипастый панцирь, охотящийся за лидером вдоль дороги; экран результатов с очками; полностью синтезированный звук без единого аудиофайла - от петли двигателя до финишного джингла; титульное меню со второй трассой; настройка числа кругов; третья трасса; и позиционный 3D-гул двигателя у каждого ИИ-карта.

GoKart на трассе Sunset Speedway: карт игрока на полной скорости на песчаной трассе под закатным небом от оранжевого до фиолетового. HUD показывает 4-е место, круг 1 и контур длинной трассы со шпилькой на мини-карте.
Sunset Speedway, вторая трасса, добавленная в цикле 12 вместе с титульным меню. Длиннее Green Hills, со шпилькой и шиканой.

Там, где он был честен

Больше всего в журнале мне нравится череда признаний. Agent! не может посмотреть на PNG, и он говорил об этом цикл за циклом:

Прогон со скриншотами в оконном режиме не записал ошибок, но я не могу просматривать изображения, поэтому я не видел, как рендерятся трасса или HUD.

Я не слышал звуки и не запускал ни дымовую гонку, ни инструмент для скриншотов.

Я не могу просматривать изображения своими инструментами, так что этот обзор нужен человеку.

Поэтому он проверял то, что мог: безголовые проверки сцены, которые инстанцируют настоящую сцену и делают утверждения о состоянии. Запустить молнию, убедиться, что три соперника имеют масштаб 0,5 и крутятся, а молнии потом убраны. Выпустить синий панцирь в плотную стартовую решётку, убедиться во взрыве на 12-м кадре и двух крутящихся картах. Принудительно финишировать игрока, убедиться, что автопилот передаёт карт AiDriver, а панель результатов показывает «1st YOU».

Третья сессия заглохла так же, на этот раз за 240-секундным alarm, который был чересчур щедрым, и я остановил её после одного цикла. Потом посмотрел человек. Этим человеком был я, и целью четвёртой сессии стали мои заметки с плейтеста, слегка причёсанные:

the UI needs to scale with the window. the UI should not be prone to the screen speed effects and blurry. Should be able to use cursor keys. it's not clear when power ups are released. the tops of the walls flicker. Little too hard to steer the kart. hard to keep up with the computer AI karts. maybe have AI difficulty levels Easy Medium and Hard.

Цикл спустя: режим растяжения canvas-items, чтобы UI масштабировался вместе с окном, HUD перенесён на слой канваса поверх оверлея скоростных эффектов, стрелки, Enter и Ctrl для предметов с подсказкой на экране, плавно нарастающее руление, исправление z-fighting на полосах стен (соседние красные и белые сегменты боролись за одни и те же пиксели, поэтому белые стали чуть-чуть крупнее), 4x MSAA и уровни Easy / Medium / Hard, масштабирующие максимальную скорость ИИ до 0,72, 0,85 и 1,0. Тесты выросли с 2 156 до 3 344 проверок - отчасти потому, что он заодно нашёл и исправил уже существовавшую ошибку разбора в tests/test_items.gd, из-за которой набор тестов не загружался.

Там, где он остановил себя сам

Циклы со 2-го по 8-й той последней сессии не внесли в код ни одного изменения. Каждый заново перечитывал репозиторий, перезапускал набор тестов и писал вариант одного и того же абзаца:

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

Это правильное поведение. Цель звучала как «руление ощущается плохо» и «стены мерцают», и никакой безголовый тест не может закрыть такую цель. У Auto-Pilot нет лимита итераций, так что он проверял бы вечно. Сессия закончилась после цикла 8, и дальше GoKart нужен был плейтест, а не ещё один цикл.

GoKart на трассе Frosty Peaks: карт игрока на полной скорости на белоснежной трассе под тёмно-синим сумеречным небом. HUD показывает 2-е место, круг 1, скорость в км/ч и мини-карту.
Frosty Peaks, добавленная в цикле 14 как новая запись в библиотеке трасс. Тесты по трассам подхватили её автоматически.

Об этих скриншотах

Их сделал Agent!, не я, и не вручную. Я попросил три скриншота случайной поездки, и он написал tools/random_drive.gd на 70 строк, который едет по дороге с блуждающим смещением по полосе, вставляет случайные рывки дрифта, в случайные моменты выпускает предмет, который держит, а затем сохраняет кадр каждые несколько сотен физических кадров. Он отработал по одному разу на каждой трассе, и три картинки выше - по одному кадру с каждой. Они есть и в README GoKart.

Что я бы сказал вам перед тем, как вы попробуете

  • Запускайте его в git. У Auto-Pilot нет отмены. Журнал GoKart читается, потому что каждый цикл заканчивался коммитом, и в тот единственный раз, когда цикл пошёл вразнос, ничего не потерялось.
  • Пишите операционные правила в цель. «Поставь ограничение по времени на shell» сработало лучше как часть цели, чем как разовое сообщение, потому что каждый новый цикл перечитывает цель.
  • Ожидайте, что он попросит глаза. Для всего визуального и касающегося ощущений цикл честно остановится, а не соврёт. Закладывайте плейтест между сессиями и возвращайте свои заметки как следующую цель.
  • Stop All - часть рабочего процесса, а не провал. Все три первые сессии GoKart закончились именно им.

GoKart 0.0.1

Godot больше не нужен, чтобы попробовать. GoKart 0.0.1 — первый упакованный релиз, экспортированный из того же репозитория:

  • macOS universal (Apple Silicon и Intel), подписан Developer ID и нотаризован Apple
  • Windows x86_64
  • Linux x86_64 и arm64

Каждая загрузка — один самодостаточный бинарник со встроенными данными игры, а SHA256SUMS.txt лежит на странице релиза, если хотите проверить, что скачали. Сборка для Windows не подписана, так что ждите предупреждение SmartScreen.

Agent! 1.1.87 с Auto-Pilot доступен на странице релизов и в Homebrew. Если предпочитаете запускать GoKart из исходников, ему нужен Godot 4.4 или новее: git clone https://github.com/AgentiLoop/GoKart.git && cd GoKart && godot --path .