Делаю игру с ИИ: первый прототип HellTrip

Вторая часть про HellTrip: как в Godot появились бой, кооператив, сохранения и 2.5D, где я спотыкался и как это чинил. С деревом талантов программиста и шаблоном smoke-теста.

Содержание
  1. Красный квадрат
  2. Камера на двоих
  3. Сохранение из прошлого
  4. Как я понял, что мне нужно 2.5D
  5. Таланты: от ярости к токенам и дронам
  6. Три круга ревью
  7. Smoke-тест: как агент ловит старые ошибки
  8. Что дальше

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

Графика в прототипе временная, герой и враги собраны из прямоугольников. Запись вылазки есть в конце статьи.

Что уже есть в прототипе
Что уже есть в прототипе

Красный квадрат

Проект в Godot я создал сам, остальное взял на себя агент. Через несколько минут на экране появился красный квадрат, которым можно управлять. Агент сначала понял задачу как вид сверху, пришлось объяснить: нет, сбоку, как в Katanaut.

Дальше вечер шёл по кругу. Агент добавляет рывок, удар, врага, здоровье. Я запускаю и пишу: работает, что дальше.

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

Камера на двоих

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

Причин набралось три. Камеру сдвинули вправо, как для одиночной игры. Параметр zoom в Godot работает не так, как ожидаешь: значение больше единицы приближает. А границы камеры не пускали её за край уровня, хотя игрок туда уходил.

Потом стало ясно, что одной камерой это не решить. Если игроки могут разойтись бесконечно, в кадре их не удержать. Агент добавил привязку: дальше заданного расстояния игроки не расходятся. А я попросил, чтобы они проходили друг сквозь друга, иначе в узком месте один запирает другого.

Сохранение из прошлого

Когда появились опыт, уровни и таланты, их понадобилось сохранять. Сохранение повесили на F5, загрузку на F9. Я прокачался, открыл талант, нажал F5 и перезапустил игру. Прогресс пропал.

Агент добавил в сохранение мир: каких врагов убили, какие предметы подобрали. Проверяю снова: урон и уровень опять старые.

На третий раз агент открыл сам файл на диске. Там лежало состояние до прокачки: F5 у меня просто не перезаписывал файл, и загрузка честно поднимала прошлое. В этот раз сломался не код, а проверка.

Тогда игра научилась писать на экране, что сохранила: «Saved L2 DMG2». Подсказку с запасными клавишами «F5/5/0 save» видно на скриншотах прототипа до сих пор.

Как я понял, что мне нужно 2.5D

Через три недели я вернулся к проекту и попросил три тестовых уровня посложнее: многоярусных, с разными врагами. Агент собрал их генератором из кода, и вышло странно. Платформы то красные, то серые без всякой логики, персонаж висит в воздухе ниже платформ, где-то стоят невидимые стены. Генератор убрали и сделали один коридор. Последней невидимой стеной оказалась старая граница арены, которую забыли расширить.

И тут я понял, что промахнулся с жанром. По перемещению мне нужны были не платформы, а «Черепашки-ниндзя»: ходишь не только влево и вправо, но и в глубину экрана. А по деревне из концептов хотелось ходить как в Diablo 3. Агент объяснил, что 3D для этого не нужно, хватит 2.5D: персонаж ходит по плоскости, а кто ниже на экране, тот рисуется поверх.

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

Таланты: от ярости к токенам и дронам

Классы я хотел как в Diablo. У каждого свои базовые удары. Удары копят классовый ресурс, у воина ярость, у мага мана. Ресурс тратится на способности, которые открываются талантами. Ветка талантов делится минимум на две специализации.

Агент собрал под это каркас: классы, ресурсы, таланты и ветки описываются в файлах данных. Программист получил ярость, удар «Раскол ярости» и две пробные ветки. Это работает в прототипе до сих пор.

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

У программиста оружие — клавиатура. Ярость сменилась на Tokens: они копятся от ударов и понемногу восстанавливаются сами. В бой берутся три навыка из открытых. Ветки две: «Грубая сила» для ближнего боя и «Автоматизация» с боевым дроном. В каждой четыре уровня, и чем глубже талант, тем больше очков нужно вложить в ветку.

Дерево талантов программиста. Дизайн, в прототипе пока работает первая версия с яростью
Дерево талантов программиста. Дизайн, в прототипе пока работает первая версия с яростью

Названия взяты из мира программиста. Несколько любимых:

  • Return — программист метает клавиатуру, и она бьёт врагов по пути туда и обратно.
  • Горячие клавиши — включает разогрев: после трёх попаданий клавиатура загорается и поджигает врагов.
  • Out of Memory — сжигает весь запас Tokens в удар по области. Чем больше потрачено, тем сильнее удар.
  • sudo — после дальнего навыка следующий ближний выполняется «с повышенными правами» и бьёт сильнее. В интерфейсе это видно как $ sudo.
  • Hello, World! — вызывает боевого дрона. При появлении он ненадолго отвлекает на себя обычных врагов.
  • CI/CD — дрон возвращается за новым аккумулятором, а старый сбрасывает на врагов как бомбу. Потом стремительно возвращается в бой.
  • 16 GB RAM — программист ставит первую локальную LLM, и команды дрону становятся дешевле.

Дальние уровни «Автоматизации» ещё проектируются, числа ещё не сбалансированы. В прототипе пока работает первая версия с яростью, перенести новое дерево в игру ещё предстоит.

Три круга ревью

Ещё через неделю я попросил агента изучить проект целиком и сделать код-ревью. Оценка вышла неприятно честной: архитектура 7 из 10, готовность 5 из 10. Дальше я повторял одно и то же: исправь всё и посмотри ещё раз.

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

Что сохранять, чтобы незаконченный бой пережил загрузку
Что сохранять, чтобы незаконченный бой пережил загрузку

Каждое исправление открывало следующий стык. Поэтому уже в первом круге агент завёл smoke-тест и дополнял его с каждым кругом.

Smoke-тест: как агент ловит старые ошибки

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

Тест не ответит, удобно ли играть. Зато он помнит прошлые ошибки, и на ручную проверку остаётся то, что видно только в игре.

Ниже минимальная версия для любого проекта на Godot 4. Её можно скопировать, не раскрывая, или открыть и посмотреть.

Запуск из папки проекта:

Терминал
godot --headless --path . res://tests/SmokeTest.tscn

Чтобы агент пользовался тестом сам, добавьте правило в AGENTS.md или CLAUDE.md проекта:

AGENTS.mdMarkdown
## Проверка- После каждой правки запускай `godot --headless --path . res://tests/SmokeTest.tscn`.- Найденную ошибку сначала добавь в tests/smoke_test.gd отдельной проверкой, потом исправляй.- Тест не должен трогать сохранения игрока: используй отдельный слот.- В отчёте перечисли, что проверено тестом, а что осталось непроверенным.

Что дальше

Логика есть, теперь нужен персонаж, которого можно использовать в игре. Первые пробы генерации не дали нужного результата. В следующей части сравниваю три пути: готовые кадры анимации, видео из картинки и сборку персонажа из частей с ригом. В итоге эти попытки привели меня к 3D-моделям.

Короткие демонстрации публикую в Telegram. Описание проекта есть на странице Godot × ИИ.

Видео прототипа

Одиночная вылазка в прототипе HellTrip, 43 секунды. Графика временная, запись без звука.