Егор Урванов

Архитектура игры «Сходка»: C4 и критические пути

Схемы C4 до уровня компонентов и четыре критических пути через sequence: запуск, шаг симуляции, реплика игрока и кадр.

Игра «Сходка» — субботний вечер IT-сообщества в баре Батуми, 19:00 → 01:00, около 18 минут: браузер, без сервера и сборки, three.js лежит рядом. Здесь описано, как она устроена: схемы C4 до уровня компонентов и четыре пути, на которых всё держится.

C4: контекст и контейнеры

Уровень 1. Система и её окружение

Игрокмышь, касание«Сходка»игра в браузереlocalStorageкачество, звукТесты в Nodeсимуляция без экранаиграетнастройкигоняют мир
Своего сервера нет и внешних библиотек по сети нет: three.js лежит в папке игры. Открыть можно двойным щелчком по index.html.

Уровень 2. Контейнеры

Страница и реестрindex.html, l.jsmanifest.jsСборкаmain.jsцикл, ввод, снимокСодержаниеcontent/*данные вечераСимуляцияsim/*мир без DOM и THREEЗал и людиrender/*three.jsИнтерфейсui/*DOM поверх холстаЯдроcore.jsRNG, Bus, Loopгрузит частиstep, act, viewпоза, свет, кадрattachчитаетact / событияутилиты
Симуляция ничего не знает об экране: её рисуют отдельно, и она же работает в Node. Содержание — только данные.

C4 уровня 3: компоненты контекстов

Все части — обычные скрипты с реестром L: L.def объявляет часть, L.use берёт её и выполняет при первом обращении. Циклическая зависимость падает сразу с понятной ошибкой. Контексты общаются через шину событий мира (world.bus) и вызов world.act.

Симуляция

Worldшаг, act, viewТолпаcrowd.jsХодьбаnav.jsРежиссёрdirector.jsМозг людейbrain.jsРазговорtalk.jsСчётscore.jsСодержание(другой контекст)создаёт людейupdateupdateupdateupdateпутиступени, парычитает
Компоненты симуляции вечера
  • World держит часы (180 с реального времени на игровой час), людей, игрока, места и шину событий; наружу даёт step, act, view.
  • Crowd строит гостей из ролей, складов и нужд, а расписание прихода сводит к кривой вечера инвариантом, а не подбором.
  • Brain решает за гостей: место, бар, курилка, группы разговора. Director запускает события и сообщения в телефон.
  • Talk разыгрывает карточки-реплики; Score считает знакомства, пары, фото и звание.

Зал и люди

Залscene.jsКамераcamera.jsКачествоquality.jsТекстурыtextures.jsМебель, мелочьprops.jsЛюдиpeople.jsОбликlooks.jsПлан залаcontent/layout.jsракурсsample(dt)запечённый светдекороблик, через сборкустолы, стены
Компоненты отрисовки зала и людей
  • Зал строит SushiGO из плана и декора, 2–4 настоящих источника света, остальной свет запечён в текстуры; эффекты: дождь, саксофон, караоке и другие.
  • Quality переключает low / mid / high по частоте кадров: быстро вниз, медленно вверх и не выше ступени, на которой уже падали.
  • Люди: каждый человек — один SkinnedMesh, анимация процедурная по позе, настроению и «говорит ли».

Интерфейс

Uiслушает world.busHUDвремя, силы, биркиПанель разговораtalk.jsТелефонphone.jsЭкранызаставка, анкета, итогDom, Iconsвёрстка и иконкиМирworld.act, busact / события
Компоненты интерфейса
  • Ui подписывается на события мира (talk:line, ladder, pair, chat, toast, end) и отдаёт действия игрока в world.act.
  • Своя шина ui.bus: пауза, качество, звук, «ещё одна суббота».

Четыре критических пути

1. Запуск и новый вечер

Игрокindex.htmlmainЗал и людиИнтерфейсWorldоткрываетскрипты по спискуL.use(main)Scene, PeopleUiloop.startзаставка, анкета, чатпоехалиstart()new World(seed)attach(world)вечер идёт
Зал живёт с первого кадра, за заставкой. Мир создаётся только после анкеты; `?seed=N` даёт тот же вечер.

2. Шаг симуляции

LoopWorldРежиссёрМозгРазговорСчётstep(1/30)события, чатместа, группыходьба игрокаответ собеседникасчётдо 8 шагов за кадрhour ≥ 25: finish → end
Время идёт фиксированными шагами в 1/30 с, поэтому вечер с одним сидом одинаков на любой машине.

3. Реплика игрока и её последствия

ИгрокИнтерфейсWorldРазговорСчёткарточка, 1–4act(say)say(card)трата силtalk:lineпауза ответаоценка репликиtalk:line, revealступеньtalk:cards, ladder
Симпатия растёт или падает, карточка человека раскрывается по одному полю за ход, ступени ведут от «видел» до «договорились». Срыв или усталость закрывают разговор.

4. Кадр

LoopmainWorldЛюдиЗалИнтерфейсupdate × nrender(dt)view()позиции, позыadd / setчас, следить за игрокомupdate(dt)render(dt)quality.sampleupdate(dt)конец → снимок, итог
Мир отдаёт плоский список «кто где в какой позе», сборка переносит его на людей в 3D. Ошибка в кадре идёт в консоль, цикл не умирает.

Правила, на которых всё держится

  • Симуляция не знает об экране. Ни DOM, ни THREE: её видно только через view() и шину событий.
  • Содержание — данные. Движок получает CONTENT целиком; истории и числа правятся без движка, числа перекрывает TUNING.
  • Один сид — один вечер. Генератор случайных чисел с сидом и фиксированный шаг дают повторяемость.
  • Инвариант вместо подбора. Число людей в часы пика сводится в коридор формулой.
  • Слабая машина — первая. Качество подстраивается по кадрам, свет в основном запечён.

Слабые места

Где Что не так
Сборка большая main.js на 300 строк держит ввод, снимок для итога и метки на полу вместе с циклом.
Интерфейс угадывает форму мира Ui терпит и Map, и массив людей, и разные имена флагов: договор о форме данных размыт.
Музыканты вне симуляции Саксофонист и певица существуют только в сборке и не видны миру.
Большие файлы зала scene.js и people.js по 850–950 строк.
Тестовый набор отдельно sim/fixture.js дублирует схему содержания и может разойтись с настоящей.

Об игре

Страница игрыСходкаСубботний вечер IT-сообщества в баре в Батуми — 3D-игра про знакомства
© 2026 Егор Урванов