Appearance
12 — Interview questionnaire
How to run this interview
The questions below are written in Russian. Conduct the interview in whatever language the user writes to you in — translate on the fly.
Rules:
- Read the SDK documentation first (order:
README.md). Half the value of this interview is your ability to say "the engine already does that" instead of letting the user design a feature that cannot exist. - One block at a time. Ask a block, get the answers, confirm your understanding in one sentence, move on. Never dump all 14 blocks at once.
- Every question has a "как в tanks" default. State it, and let the user accept it with a single word. Blocks 1, 5, 6, 7, 10 are the ones that really define the game — spend the time there.
- Push back immediately on impossible answers. The list of things that do not exist is at the end of
pitfalls.md. Offer the nearest workable alternative in the same message. - Skip what is already settled. If the user described the game in the first message, do not re-ask; confirm instead.
- After the last block, write the design document and get confirmation before generating anything.
Блок 1. Идентичность игры и пакет
- Как называется игра? Нужен короткий
id(латиница, без пробелов) — он станетmanifest.id, сегментом URL/games/<id>/и должен совпасть с идентификатором, под которым игра зарегистрирована на платформе. В tanks —tanks. - Имя npm-пакета? Конвенция —
@vimp-games/<id>; собственный scope тоже допустим. В tanks —@vimp-games/tanks. - Отображаемое название (
manifest.title) — то, что видит игрок в лобби. В tanks —VIMP Tanks. - Одним абзацем: что это за игра, какой жанр, чем игрок занимается в матче? (Нужно для выбора формы сущностей и снапшота.)
Блок 2. Комната и лобби
- Какой целевой размер комнаты (
roomDefaults.maxPlayers)? Учтите: платформа в любом случае клампит до 8. В tanks — 8. - Какие поля должны быть в форме создания сервера? Важно: хост принимает только
maxPlayers,map,roundTime,mapTime,friendlyFire(плюс каталог карт). Любое собственное поле формы будет принято формой и молча отброшено — настройку, влияющую на правила, через форму комнаты передать нельзя. Если такая настройка нужна, обсудим, как зашить её в конфиг игры или в логику ядра. - Доступные контролы формы — только
text,select,checkbox,radio(в v3 движка нетrange/number/toggle). Числовые границы задаются полямиmin/maxи автогенерируемымregExp. Годится? - Огонь по своим (
friendlyFire) по умолчанию включён или выключен? В tanks — выключен (false), игрок может переключить при создании комнаты. - Длительность раунда и карты по умолчанию? В tanks — 120 с и 600 с; пользовательские значения клампятся в 10 с … 3600 с.
Блок 3. Экран входа (auth)
- Что игрок выбирает перед входом в матч? (Модель/класс/цвет/фракция — любой набор полей.) Ника среди них нет: имя берётся из JWT лобби. В tanks — только модель танка.
- Тексты экрана: заголовок и блок подсказок (обычно раскладка управления — пары «клавиши → действие»). Дайте формулировки.
- Какие из полей должны запоминаться между сессиями (
storage) и какие нужно проверять на стороне хоста (validator)? В tanks — модель запоминается и валидируется по списку моделей.
Блок 4. Правила матча, чат, голосования
- Раунд заканчивается уничтожением всей команды противника — это правило движка. Подходит? Если игра не раундовая, обсудим обходные варианты (одна длинная «партия», один играющий тим, респавн внутри ядра).
- Что происходит с игроком после смерти? В движке он становится наблюдателем за убийцей до конца раунда. Нужен ли респавн внутри раунда — это реализуется в вашем Rust-ядре, не конфигом.
- Нужно ли окно неуязвимости после респавна? В движке такого нет — реализуется в ядре (флаг на акторе + проверка в расчёте урона).
- Голосования: какие типы нужны, кроме встроенных
teamChangeиmapChange? Учтите кулдаун категории 30 с, окно голосования 10 с, одно активное голосование за раз, ничья разрешается случайно. Голосования за кик игрока в движке нет, эндпоинта/banи рейтинга серверов (/like//unlike) тоже нет. - Какие чат-команды нужны игре? Движок своих не разбирает — весь набор объявляет игра (
HostPlugin.chatCommands). Обычный минимум:/rank,/nr(dev), плюс/timeleftи/mapname, если раунд и карта конечны. В tanks есть все четыре и своя/bot, в snakes — только/nr,/rankи/bot. - Какие системные сообщения чата добавляет игра? Буквы групп
s,v,m,cзаняты движком — выберите свою (в tanks этоb). Нужны тексты с плейсхолдерами{0},{1}. - Максимальная длина сообщения чата? По умолчанию 60 символов.
- Нужно ли менять политику киков? Дефолты: неактивность 120 с для игрока (для наблюдателя выключено), кик по средней задержке > 1000 мс, кик после 5 пропущенных пингов, пинг каждые 3 с, проверка неактивности каждые 30 с.
Блок 5. Управление и предикция
- Полный список действий игрока с назначенными клавишами. Для каждого: удержание (движение) или разовое срабатывание (выстрел, смена оружия)? Разовые действия потребляются один раз за тик,
upигнорируется. - Что делают клавиши наблюдателя? Обязательный минимум —
nextPlayerиprevPlayer. В tanks —n/p. - Нужна ли клиентская предикция движения (мгновенный отклик своего актора)? Без неё управление ощущается с задержкой ~100 мс + пинг. Ограничение: предсказываемое состояние — ровно 8 ×
f32. Перечислите, какие величины туда войдут. В tanks — x, y, угол корпуса, угол орудия, vx, vy, нагрузка двигателя, состояние. - Нужна ли предикция выстрела (мгновенный визуальный эффект до прихода авторитетного события)? Если да — потребуется подавление дублей по id автора в событийном блоке.
Блок 6. Оружие и боевая механика
- Список оружия/способностей. Для каждого: тип поражения (мгновенный луч, снаряд с полётом, взрыв по радиусу, что-то иное), урон, дальность, разброс, импульс, тряска камеры.
- Перезарядка задаётся полем
fireRateв секундах (0 — без кулдауна). Укажите значения. - Боезапас: стартовые значения патронов задаются значениями полей панели (
panel.fields[*].value) — то же число уходит в ядро как стартовый ресурс. Сколько и чего? - Есть ли модели/классы актора с разными характеристиками? Для каждой: габариты, ускорение, максимальная скорость, поворот, демпфирование, плотность/трение/отскок, стартовое оружие.
- Нужны ли игровые события, которые движок не понимает (подбор предмета, захват точки, начисление очков вне убийств)? Такие события идут через
custom→HostPlugin.onCoreEvent.
Блок 7. Снапшот и сущности
- Перечислите все сущности, которые видит клиент: игроки, снаряды, эффекты, динамические объекты карты.
- Для каждой сущности — какие поля передавать и с какой точностью (
f32,u8,u16,u32)? Какие из них интерполировать (lerp), какие как угол (lerpAngle), какие дискретно? - Для каждой сущности — форма блока:
indexed8— до 255 адресуемых объектов (игроки);indexed32— много короткоживущих объектов с id (снаряды в полёте);list16— анонимные разовые события (трассеры, взрывы);indexedNoNull8— фиксированные слоты (динамика карты). Ограничение: в hot-буфер (плавный рендер каждый кадр) попадают толькоindexed8иindexedNoNull8. Всё остальное приходит событиями.
- В каждом событийном блоке оружия последнее поле — id автора выстрела (нужно для подавления дублей предсказанного эффекта). Подтвердите.
Блок 8. HUD, статистика, звуки, информ-сообщения
- Какие ресурсы показывает панель? Для каждого: тип ячейки —
bar(полоса, по умолчанию 30 блоков, максимум 100),value(число),weapon(подсвечивается активное),time(M:SS). Ячейка времени раунда обязательна и жёстко привязана к ключуt. - Таблица статистики: движок пишет ровно пять величин —
name,status,score,deaths,latency(это и есть «пинг»). Свои колонки движок заполнять не будет. Нужны заголовки колонок и порядок сортировки (численной). Агрегаты команды считаются методами'='(замена),'+'(накопление),'#'(счёт строк). - Звуки: полный список с приоритетами (важнее — выше; лимит 30 одновременных пространственных голосов) и громкостями. Каждый звук нужен парой
webm+mp3. - Маппинг движковых событий на ваши звуки:
roundStart,victory,defeat,frag,death. Какие из них нужны? - Тексты трёх информ-сообщений (индексы фиксированы): победа команды (
{0}— команда), старт раунда, конец игры. В tanks —{0} WINS!,ROUND START!,GAME OVER!. - Нужно ли голосование сразу после входа в матч (
initialVote)? В tanks — выбор команды. Если игра одно-командная и наблюдателей в ней нет как явления — ответ «нет»: вместоinitialVote/spectatorTeamобъявляетсяgameConfig.noSpectators: true(ровно одна команда, вход прямо в неё), аmodules.voteможно не объявлять вовсе. Если раунд в игре бесконечный — к нему жеgameConfig.endlessRound: true.
Блок 9. Боты (scripted-участники)
- Нужны ли боты? Если да — как они должны себя вести (искать ближайшего противника, патрулировать, держать позицию)? Уточните желаемое поведение, а не числовые константы: частота решений ИИ и прочие параметры — это код Rust-ядра, а не настройка конфига.
- Как боты создаются: чат-командой, голосованием, автоматически при нехватке игроков? В tanks — команда
/botс голосованием. - Префикс имени и модель бота по умолчанию?
Блок 10. Карты и окружение
- Сколько карт нужно в первой версии и какого размера (в тайлах)?
- Тайлы: какой размер тайла (
step) и какие индексы делают стену (physicsStatic)? Картинки тайлов везёт ваш пакет — движок игровых картинок не раздаёт вовсе. Какие файлы тайл-листов вы кладёте вassets/img/(оттуда они попадут вdist/img/и приедут клиенту как${assetsBase}img/<файл>)? - Слои отрисовки: какие индексы тайлов на каком слое (
layers)? - Нужны ли динамические объекты (ящики, бочки)? Для каждого: позиция, размер, плотность, картинка, слой, демпфирование. Картинка — тоже файл из
assets/img/вашего пакета. - Точки респавна: их количество на команду — жёсткая вместимость этой команды на карте. Сколько точек на команду? (Должно покрывать
maxPlayers.) - Масштаб карт (
mapScale, и при необходимостиscaleу отдельной карты)? В tanks — 0.3, у отдельных карт свой. - Сколько карт предлагать в голосовании за ротацию (
mapsInVote)? В tanks — 4.
Блок 11. Канвасы, камера и клиентский UI
- Сколько полотен нужно? В tanks — основное (
vimp) и радар (radar). Для каждого: стартовый размер, соотношение сторон (aspectRatio) или фиксированный размер (fixSize), базовый масштаб (baseScale). - Нужна ли динамическая камера (смещение вперёд по скорости + отдаление) и тряска камеры на попаданиях/взрывах?
- Какие DOM-элементы показывать после авторизации (
initIdList)? Обычно — все полотна плюсpanelиchat. - Есть ли собственные стили (цвета команд, оформление панели)? CSS игры отдаётся движку строкой. Учтите: движковый CSS рассчитан на пять колонок статистики и классы
.line1–.line3. - Будут ли частицы/эффекты в больших количествах (дым, искры)? Если да — планируем пул объектов, иначе сборщик мусора будет заметен.
- Каким частям нужны сервисы
rendererиsoundManager? (Пул сервисов фиксирован этими двумя.)
Блок 12. Сетевые допущения
- Какие сущности обязаны выглядеть плавно (интерполяция/предикция), а какие достаточно показывать по событию?
- Частота отправки кадров: по умолчанию каждый 4-й тик симуляции — 30 кадров/с при тике 120 Гц. Оставляем?
- Есть ли в игре подбираемые предметы, разрушаемые объекты или захватываемые точки? В движке их нет — моделируются ключами снапшота (обычно
indexedNoNull8илиindexed8) плюс логикой ядра. Нужны? - Что должно доезжать гарантированно (события, тряска), а что можно терять (позиции)? По умолчанию кадр уходит по надёжному каналу, если в нём есть события,
forceResetили тряска. - Должна ли игра переживать смену хоста посреди раунда (новый хост продолжает с того же тика)? Да —
gameConfig.migration.midRound: true, и тогда всё состояние ядра обязано быть вGameSim::serialize, а у host-модулей с состоянием —serializeState/restoreState. Нет — при смене хоста раунд начинается заново (мягкий режим, по умолчанию).
Блок 13. Прогрессия
- Нужна ли прогрессия между матчами? Движок хранит
rank(целое число, общий формат для всех игр) иstate(произвольный JSON вашей игры). rankдвижок считает сам: +1 за фраг, −1 за тимкилл. Хука для изменения этого правила нет. Устраивает?- Что положить в
stateи с чего начинать новому игроку (playerState.defaultState)? Данные пишутся изonCoreEventи синхронизируются на конце раунда, смене карты и выходе игрока.
Блок 14. Процесс
- Нужен ли
cargo-тест паритета движения (предиктор против авторитетной симуляции)? Настоятельно рекомендуется — без него расхождение предикции обнаруживается только глазами в игре. - Нужен ли JS↔WASM интеграционный харнесс (тесты, гоняющие настоящее ядро через
pkg-node)? - Локализация: движок не поддерживает переключение языка в рантайме — все тексты (чат, панель, голосования, информы, экран входа) задаются одним набором в конфиге. На каком языке пишем тексты?
- Как запускаете игру локально во время разработки?
npm run dev(standalone-вкладка с ботами) иnpm run simработают без чекаута движка. Нужен ли собственный dev-харнесс сверх шаблонногоdev/main.js? - Публикуем ли в манифесте
entries.wasmNode(путь кcore/pkg-node)? Настоятельно рекомендуется: без него игру нельзя прогнать headless-runner'ом движка (npm run sim), и все проверки контрактов придётся делать глазами в браузере. См.debugging.md. - Какие сценарии отладочных прогонов пишем сразу (движение, стрельба, смерть/респаун, конец раунда, смена карты, голосование)? Сценарий — обычный JSON, формат в
debugging.md. - Реализуем ли
GameClientDef::predicted_state(опционально, дефолтNone) ради покомпонентного отчёта о расхождении предикта? Без него работает грубое сравнение по камере.
Answer → artifact map
| Block | Produces |
|---|---|
| 1 | package.json, scripts/build-game-manifest.js, id/title in both plugins |
| 2 | gameConfig.roomDefaults, gameConfig.roomForm, parts.friendlyFire, timers.roundTime/mapTime |
| 3 | src/config/auth.js (elems, texts, params, validators) |
| 4 | gameConfig.teams/spectatorTeam (или noSpectators, и тогда без initialVote, spectatorTeam и modules.vote), endlessRound, chatMaxLength, idleKickTimeout/rtt, src/host/systemMessages.js, chat commands, client modules.chat.params.messages, modules.vote.params |
| 5 | gameConfig.playerKeys, client modules.controls.keySetList, spectatorKeys, core apply_input, predictor.rs, PLAYER_STATE_LEN layout |
| 6 | src/data/weapons.js, src/data/models.js, core weapon modules, onCoreEvent for custom events |
| 7 | src/config/snapshot.js, Rust build_snapshot_blocks rows, client parts.gameSets |
| 8 | gameConfig.panel + client modules.panel, gameConfig.stat + client modules.stat, src/config/sounds.js, gameConfig.soundCues, gameInform.list |
| 9 | gameConfig.scripted, src/host/<Bot>Manager.js, createModules, bot chat command, core/src/bots/ |
| 10 | src/data/maps/*.js, gameConfig.maps/currentMap/mapScale/mapSetId/mapsInVote, map parts |
| 11 | client modules.canvasManager.canvases, initIdList, styles, parts.bakedAssets, parts.componentDependencies |
| 12 | snapshot kind/class decisions, timers.networkSendRate, extra snapshot keys for pickups/objectives, gameConfig.migration.midRound + GameSim::serialize coverage + module serializeState/restoreState, a checkpointRestore step in the sim scenario |
| 13 | gameConfig.playerState.defaultState, onCoreEvent state writes |
| 14 | vitest.config.js projects, core/src/client/predictor.rs parity tests, tests/, the dev/ harness, entries.wasmNode in the manifest, debug scenarios for npm run sim, optional predicted_state |