Проект

Tile Turf — браузерная игра про захват территории

Изометрическое поле 8×8, кампания из десяти уровней и соперники, которые ускоряются вместе с игроком — всё открывается по ссылке, без установки

  • Pixi.js
  • React 19
  • TanStack Start
  • Cloudflare Workers

Попробуйте сами

Захватывайте плитки и опережайте соперников. Управляйте с клавиатуры или свайпами.

Открыть в новой вкладке
Tile Turf В новой вкладке

Что требовалось от проекта

Игра без установки

Открывается по ссылке и на телефоне, и на ПК. Ни сторов, ни загрузки перед первым запуском: игрок попадает в раунд сразу.

Кампания, а не одна арена

Десять уровней с растущим числом соперников и их скоростью, с обучением и сохранением прогресса между сессиями.

Одно управление на клавиатуру и палец

WASD и стрелки на ПК, экранный джойстик и щипок для масштаба на тач-экранах — без отдельной мобильной версии.

Готовность к сторонним площадкам

Кроме своего хостинга нужна отдельная статическая сборка и упаковка архива под Яндекс.Игры.

Территорию приносит не покраска, а сундук

Плитки под персонажем закрашиваются сами. Но пока они просто закрашены, в счёте их нет — и до сундука их можно потерять.

Сундук фиксирует захваченное

Он превращает закрашенные плитки в очки раунда, очищает их и появляется в другом месте. В HUD накопленные очки идут крупно, а незафиксированные плитки — мелким «+N»: видно, сколько игрок рискует потерять.

Бомба и стрелка переписывают карту

Бомба предупреждает две секунды, а при взрыве оглушает персонажа и снимает с него все плитки. Вращающаяся стрелка закрашивает линию в том направлении, которое было активно в момент срабатывания. Ботинки временно ускоряют бег.

Соперники растут вместе с игроком

На первых двух уровнях бот один, к десятому их четыре, и каждый уровень поднимает их скорость. Уровень идёт до трёх побед в раундах — первым дошёл игрок, уровень пройден.

Как устроены рендер, логика и доставка

Игровой цикл и React живут рядом, поэтому разделение обязанностей здесь — не вкусовщина, а условие плавного кадра.

Рендер и производительность

  • Pixi.js рисует поле, персонажей, миникарту, предметы и опасности
  • На устройствах с грубым указателем ограничен device pixel ratio: полный retina-кадр телефон не тянет
  • Обновления счёта в React throttled — игровой цикл не вызывает лишние перерисовки HUD
  • Отладочный оверлей показывает FPS, время кадра, частоту покраски и число активных ботов

Логика отдельно от рендера

  • Изометрическая проекция, владение плитками, опасности и прогресс — чистые модули без Pixi и React
  • Случайность детерминированная: seeded RNG вместо прямого обращения к системному генератору
  • Прогресс кампании и флаг пройденного обучения хранятся на клиенте
  • Новая механика добавляется отдельным модулем, а не правкой игрового цикла

Доставка и устойчивость

  • Страница отдаётся с сервера, рантайм — Cloudflare Workers
  • Критический сбой серверного рендера превращается в брендированную страницу ошибки, а не в пустой экран
  • Отдельная статическая сборка и упаковка архива под Яндекс.Игры
  • Проверка сборки перед публикацией автоматизирована скриптом

Четыре задачи, которые решали отдельно

  1. 01

    Кадр на телефоне

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

  2. 02

    Тесты для игры, которая всё время движется

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

  3. 03

    Управление пальцем

    На тач-экране некуда поставить джойстик, не закрыв поле. Джойстик появляется там, где игрок начал перетаскивание, масштаб меняется щипком, а отдельные кнопки зума остались для тех, кому щипок неудобен.

  4. 04

    Ошибка на сервере не должна ронять страницу

    Серверная точка входа обёрнута, а неожиданные исключения перехватывает middleware запроса. Игрок в худшем случае видит понятную страницу ошибки в фирменном оформлении.

Результат

Законченный игровой цикл, который открывается по ссылке.

10 уровней в кампании, с ростом числа и скорости соперников
8×8 плиток на поле — изометрическая проекция
4 бота одновременно на поздних уровнях
0 установок: игра стартует по ссылке в браузере

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

Стек технологий

Клиент
React 19, TypeScript, TanStack Start и TanStack Router
Рендеринг
Pixi.js v8 — поле, персонажи, миникарта, предметы и опасности
Стили
Tailwind CSS v4 на CSS-переменных, Radix UI, иконки Lucide
Сборка
Vite, Bun как рантайм и пакетный менеджер
Инфраструктура
Cloudflare Workers, серверный рендер через TanStack Start
Тесты
Playwright — детерминированные сценарии на seeded RNG и ручном тикере

Нужен такой же проект?

Делаем браузерные игры на Pixi.js и React: геймплей, рендер, серверная часть и публикация на площадках.