Опросник для интервью
Как вести интервью
Ведите интервью на том языке, на котором пишет пользователь — переводите вопросы на лету.
Правила:
- Сначала прочитайте документацию SDK (порядок:
README.md). Половина пользы от интервью — ваша способность сказать «это движок уже делает», а не дать пользователю проектировать функцию, которой не может быть. - По одному блоку. Задайте блок, получите ответы, подтвердите понимание одной фразой, переходите дальше. Никогда не вываливайте все 14 блоков сразу.
- У каждого вопроса есть вариант «как в tanks». Назовите его и дайте пользователю принять одним словом. Блоки 1, 5, 6, 7, 10 по-настоящему определяют игру — потратьте время на них.
- Сразу возражайте на невозможные ответы. Список того, чего не существует, — в конце
pitfalls.md. В том же сообщении предложите ближайшую рабочую замену. - Пропускайте то, что уже решено. Если пользователь описал игру в первом сообщении, не переспрашивайте, а подтвердите.
- После последнего блока напишите документ-проект и получите подтверждение до генерации чего-либо.
Блок 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;range,number,toggleиsegmented— их постоянные алиасы (см. Схема формы). Числовые границы задаются полями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-раннером движка (npm run sim), и все проверки контрактов придётся делать глазами в браузере. См.debugging.md. - Какие сценарии отладочных прогонов пишем сразу (движение, стрельба, смерть/респаун, конец раунда, смена карты, голосование)? Сценарий — обычный JSON, формат в
debugging.md. - Реализуем ли
GameClientDef::predicted_state(опционально, дефолтNone) ради покомпонентного отчёта о расхождении предикта? Без него работает грубое сравнение по камере.
Соответствие ответ → артефакт
| Блок | Порождает |
|---|---|
| 1 | package.json, scripts/build-game-manifest.js, id/title в обоих плагинах |
| 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, чат-команды, клиентские modules.chat.params.messages, modules.vote.params |
| 5 | gameConfig.playerKeys, клиентские modules.controls.keySetList, spectatorKeys, apply_input ядра, predictor.rs, раскладка PLAYER_STATE_LEN |
| 6 | src/data/weapons.js, src/data/models.js, модули оружия ядра, onCoreEvent для своих событий |
| 7 | src/config/snapshot.js, строки Rust build_snapshot_blocks, клиентские parts.gameSets |
| 8 | gameConfig.panel + клиентские modules.panel, gameConfig.stat + клиентские modules.stat, src/config/sounds.js, gameConfig.soundCues, gameInform.list |
| 9 | gameConfig.scripted, src/host/<Bot>Manager.js, createModules, чат-команда ботов, core/src/bots/ |
| 10 | src/data/maps/*.js, gameConfig.maps/currentMap/mapScale/mapSetId/mapsInVote, части карт |
| 11 | клиентские modules.canvasManager.canvases, initIdList, styles, parts.bakedAssets, parts.componentDependencies |
| 12 | решения по kind/class снапшота, timers.networkSendRate, дополнительные ключи снапшота для подбираемых предметов и целей, gameConfig.migration.midRound + покрытие GameSim::serialize + serializeState/restoreState модулей, шаг checkpointRestore в сценарии sim |
| 13 | gameConfig.playerState.defaultState, записи состояния из onCoreEvent |
| 14 | проекты vitest.config.js, тесты паритета core/src/client/predictor.rs, tests/, обвязка dev/, entries.wasmNode в манифесте, отладочные сценарии для npm run sim, необязательный predicted_state |