← На главную
Практический разбор

Как я создаю HTML5-игры с AI-агентами и собственным SDK

Обложка статьи о разработке HTML5-игр с AI

AI очень помогает писать код, но одного AI для разработки игр недостаточно.

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

В играх много состояния и побочных эффектов. Без архитектуры AI начинает генерировать разные решения для однотипных задач.

Мой подход прост: AI-агенты пишут код, но работают внутри заранее определённой архитектуры.

Поэтому я создал собственный SDK для HTML5-игр:

Block Puzzle — полноценная игра на этом SDK. В ней есть загрузочная сцена, предзагрузка, JSON-вёрстка, JSON-префабы, FSM, команды, сервисы сцены, доменная модель, компоненты, перетаскивание, очки, завершение игры, рекорд, звуки и сборка для продакшена.

Зачем нужен SDK

Задача SDK — не спрятать весь игровой код.

Его задача — определить правила проекта:

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

После этого агенту не нужно каждый раз придумывать структуру. Он должен работать внутри существующих границ.

В Block Puzzle класс сцены почти пуст:

export class GameScene extends FsmDrivenScene {}

Это хорошее состояние класса сцены. В нём не должна находиться вся игровая логика.

Два основных потока разделены:

Управление сценой
FSM Команда Сервис сцены Доменная модель
Визуальный слой
JSON-вёрстка Узел Компонент / действие

Это важно для разработки с AI. Агенту гораздо проще изменить небольшой класс с одной ответственностью, чем работать с большим файлом сцены, в котором смешаны правила игры, объекты PIXI, анимации, ввод и сохранение прогресса.

Пример: размещение блока

Возьмём основное действие в Block Puzzle: игрок перетаскивает фигуру на поле.

Проблема

Плохой вариант — поместить всё в один компонент.

Компонент, отвечающий за всё

  • события указателя
  • предпросмотр размещения
  • изменение поля
  • очистка линий
  • подсчёт очков
  • обновление интерфейса
  • эффекты
  • проверка завершения игры

Такой код может работать, но его трудно развивать.

Реальный поток выполнения

В текущей архитектуре упрощённый поток выглядит так:

1 Компонент перетаскивания обрабатывает ввод и предпросмотр, затем отправляет PlacementRequested
2 FSM + команда переходит в resolvingPlacement и запускает PlaceBlockCommand
3 Сервис игрового процесса проверяет размещение и обновляет состояние сессии
4 Поле / слоты / очки доменная модель применяет правила игры
5 События + визуальные компоненты PlacementCompleted, BoardChanged, BlocksChanged и ScoreChanged обновляют экран

В результате правила игры не зависят от PIXI, а визуальные компоненты не считают очки.

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

Структура кода

Код верхнего уровня остаётся небольшим. FSM решает, когда нужно обработать размещение:

playing: {
  on: {
    [BlockPuzzleEvents.PlacementRequested]: {
      target: 'resolvingPlacement',
      actions: ['PlaceBlockCommand'],
    },
  },
}

Команда — лишь граница между событием FSM и сервисом сцены:

export class PlaceBlockCommand extends BaseTypedCommand<PlaceBlockCommandPayload> {
  protected override readonly inputDecoder =
    BlockPuzzleEventSchemas.PlacementRequest;

  protected override execute(payload: PlaceBlockCommandPayload): void {
    this.getSceneService(BlockPuzzleServices.Gameplay).place(payload);
  }
}

Сервис отвечает за обновление сессии и делегирует правила доменной модели:

export class BlockPuzzleGameplayService extends BaseSceneService {
  place(request: PlacementRequest): void {
    // Read current session state and validate the requested shape.
    // Apply board, slots, line-clear, and scoring domain rules.
    // Build a MoveResult for visual components and follow-up commands.
    // Emit placement, board, blocks, line-clear, and score events.
  }
}

Что относится к SDK

Я стараюсь сосредоточить игровой код на конкретной игре.

Переиспользуемую инфраструктуру следует переносить в SDK, когда она встречается в разных проектах.

Оркестрация

  • жизненный цикл сцены
  • FSM
  • команды
  • события
  • сервисы сцены
  • DI в области сцены

Вёрстка и взаимодействие

  • узлы вёрстки
  • компоненты и действия
  • префабы
  • адаптивная вёрстка
  • маршрутизация ввода
  • перетаскивание

Данные и ресурсы

  • типизированное декодирование конфигурации
  • настройки и хранилище
  • манифест ресурсов
  • наборы ресурсов
  • разрешение текстур

Исполнение и безопасность

  • менеджер экрана
  • звук
  • владение твинами
  • безопасность асинхронных операций в области жизни
  • физика и частицы
  • интеграции с платформами
  • ошибки с немедленным обнаружением

Как я работаю с AI-агентами

Мой обычный процесс:

  1. Описать задачу и ограничить её объём.
  2. Дать агенту прочитать локальные правила проекта и текущую реализацию.
  3. Внести изменение в рамках существующей архитектуры.
  4. Если в игровом коде появляются повторяющиеся вспомогательные методы, проверить, не должны ли они стать частью SDK.
  5. После изменения запустить typecheck и lint.
  6. Проверить результат как при обычном код-ревью.

Главное правило: агент не должен подменять архитектуру проекта.

Добавляя новую возможность, я сначала выбираю, где ей место:

Задача Где ей место
правило игры доменная модель
шаг сценария использования команда
состояние сцены во время работы сервис сцены
визуальное поведение компонент / действие
повторяющаяся визуальная структура префаб
значения, заданные автором JSON-вёрстка / конфигурация
общая задача жизненного цикла / ввода / ресурсов / твинов SDK

Так AI помогает быстрее писать код, не нарушая структуру проекта.

Почему не всё должно находиться в игровом коде

Можно оставить SDK минимальным и решать всё внутри конкретной игры. На первом проекте это может казаться быстрее.

Проблема проявляется в следующих проектах. Начинают копироваться проверки жизненного цикла, вспомогательные методы для ресурсов, адаптивности и префабов, а также отладочные хуки. Со временем становится непонятно, какая версия правильная.

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

Следующие темы

Далее я хочу разобрать отдельные части и практические подробности работы с моим SDK на реальных проектах:

  • адаптивная и отзывчивая вёрстка;
  • умная загрузка ресурсов;
  • FSM в игровых сценах;
  • компоненты и действия;
  • безопасные твины и асинхронные операции;
  • отладочные сервисы для DevTools;
  • процесс работы с Codex, VS Code, Figma и Photopea;
  • итерации игрового процесса на примере Block Puzzle;
  • и другие подобные практические темы.

Основная мысль: AI полезен в разработке игр, когда у проекта есть чёткие границы.

Без границ AI ускоряет хаос. С границами AI ускоряет разработку.