28 KiB
Поселение
Здания и развитие города: Ратуша, участки, свободная стройка на сетке, постройка зданий, восстановление города из сохранения. Люди города — отдельный модуль Население, связь только через контракты ядра.
- Ответственность: совместная.
- Сборка:
Bogatyri.Settlement. - Папка:
Assets/Scripts/Modules/Settlement. - Зависит от: ядра. Ресурсы на стройку берёт через
IEconomy.
Стройка на сетке
Игрок сам выбирает, где строить. Участки (BuildSlot) больше не нужны: они и площадки «Застройка» прячутся при запуске, сцену менять не надо.
- Сетка (
BuildGrid). Клетка — 1 единица мира, сетка прямоугольная. Область стройки — прямоугольник вокруг старых участков и Ратуши с запасомpadding(3 клетки). Можно задать руками: поставитьBuildGridна сцену,autoArea = false,areaMin/areaMax. Объект создаётся сам, если его нет. - Занятые клетки: под Ратушей и под каждым построенным зданием. Пересчёт раз в 0.3 с, пока сетка видна, и перед каждой постройкой.
- Размер здания:
BuildingData.footprint(ширина × высота в клетках).0 × 0— посчитать поBoxCollider2Dпрефаба (иначе по картинке), округлив до клеток. Размер здания задаётся там же, в карточке здания. - Призрак (
BuildGhost): полупрозрачная копия картинки здания и пятно клеток под ней — зелёное (свободно) или красное (занято / за границей). Только картинка: скриптов и коллайдеров нет, экономика его не видит. - Постройка:
BuildManager.BuildAt(data, cell)— проверяет клетки и ресурсы, списывает цену, ставит префаб так, чтобы его коллайдер лёг на клетки призрака, публикуетBuildingPlacedEvent.CanBuildAt(data, cell)— та же проверка без постройки. - Старые сохранения: здания встают на свои позиции, занятость считается по тому, какие клетки здание накрывает. Ничего переносить не нужно.
Управление — Meta/Build/BuildModeController (слой приложения):
- «СТРОЙКА» — сетка на земле, подсказка сверху, список зданий (размер в клетках, цена; закрытые по уровню Ратуши — серые).
- «Выбрать» — призрак встаёт в центр экрана или на ближайшее свободное место.
- Тащить призрак пальцем (за пятно) или тапнуть по земле — призрак прыгает туда. Камера в это время стоит (
UIBlocker.WorldInputCaptured). - Нижняя панель: «Поставить» (место свободно и хватает ресурсов), «Другое», «Отмена».
- После постройки режим остаётся; «Здания» сверху — строить дальше, «Выйти» — сетка прячется.
Стройка по времени, лимиты, перенос (01.11.2026, решения 04.11; числа — ТЕСТ)
- Время реальное (решение 04.11): 60 с стройки — 60 реальных секунд, «игровой час» к стройке не относится. Развитие долгосрочное: позже — тиры T1/T2/T3 и внутри них уровни с растущим временем (шкалу согласуют отдельно).
- Стройка и улучшение идут по времени (
BuildTimer). Постройка —buildSeconds(60 с), улучшение до уровня N —upgradeSecondsToLevel(60 с … 2 ч), Ратуша ×2. Время — часы игры (GameClock.UnixNowMs): идёт и в офлайне, и при перемотке DEV. - Пока здание строится, оно полупрозрачное и не работает: его скрипты выключены,
App/CitySyncего не считает (нет мест, жилья, лимитов; Казармы не учат, Терем без героя). При улучшении здание работает на старом уровне, новый — когда время выйдет. - Строители: одновременно не больше
builders(2) строек и улучшений, Ратуша тоже занимает строителя. - Ускорение: 1 кристалл за
secondsPerGem(30) секунд оставшегося времени — кнопка в окне здания. - Лимит зданий по уровню Ратуши (
EconomyConfig.buildingLimits): в списке стройки «построено 2 / 3», сверх лимита — «Лимит: больше на Ратуше N ур.». Здания не из таблицы — без лимита. В окне Ратуши — каких зданий станет больше на следующем уровне. - Перенос: кнопка «Перенести» в окне здания — режим стройки с призраком на месте здания, «Сюда» — здание переезжает бесплатно (
BuildModeController.StartMove,BuildGrid.IgnoredBuilding). Стройка или улучшение продолжаются. - Сноса нет (решение 04.11): удалить здание и вернуть ресурсы нельзя. Перенос — бесплатно, в пределах поселения, уровень и текущие стройка или улучшение сохраняются. Ратушу не переносят.
- Офлайн по временным меткам (решение 04.11): в сохранении хранится момент конца стройки. После входа стройка вступает в силу в тот момент офлайн-расчёта добычи, когда время дошло до её конца (
App/ConstructionClock, событиеGameTimeSimulatedEventот Населения,BuildTimer.MayComplete). Добыча нового здания или уровня идёт с момента завершения (точность — шаг расчёта 10 минут, никогда не раньше конца стройки). - Проверки: таймер из файла принимается, только если он осмыслен (постройка — у здания 1 уровня, улучшение — до уровня выше текущего); завершение только повышает уровень, повторное ничего не даёт; перенос не тратит и не возвращает ресурсы.
Контракт ISettlement
| Свойство | Что значит |
|---|---|
TownHallLevel |
Уровень Ратуши (если город не открыт — из сохранения). |
BuildingCount |
Сколько построек. |
IsCityLoaded |
Открыта ли сейчас сцена города. |
Получить: GameServices.Get<ISettlement>().
События
Публикует:
BuildingPlacedEvent— здание построено (имя здания);TownHallLevelChangedEvent— Ратуша улучшена (новый уровень);SettlementRestoringEvent— город начинает восстанавливаться из сохранения. На негоApp/GameModulesдобавляет Казармы в список стройки.
Слушает: ничего.
Сохранение
Секция "settlement": { "townHallLevel", "townHallTimerStartMs", "townHallTimerEndMs", "townHallTimerTargetLevel", "placedBuildings": [ { "buildingName", "posX", "posY", "level", "timerKind", "timerStartMs", "timerEndMs", "timerTargetLevel" } ] }. Поля таймера (01.11) — стройка или улучшение, которое ещё идёт (timerKind: 0 — нет, 1 — постройка, 2 — улучшение). В старых сохранениях их нет — значит, ничего не строится.
Сброс (ResetState): Ратуша 1 уровня, постройки убраны, участки освобождены, память модуля очищена. Иначе при следующем входе в город вернулись бы старые здания.
Модуль помнит последнее состояние города, поэтому сохранение на карте мира не стирает постройки. При каждом входе в город (BuildManager.Start) здания восстанавливаются один раз. Уровень здания берётся у любого компонента с ILeveledBuilding.
Файлы
| Файл | Что это |
|---|---|
SettlementModule.cs |
Точка входа: ISettlement, сохранение города, восстановление на сцене. |
BuildManager.cs |
Список зданий, постройка на сетке (BuildAt), поиск данных здания по имени. |
BuildGrid.cs |
Сетка стройки: область, занятые клетки, рисунок сетки. Там же BuildFootprint — размер здания в клетках. |
BuildGhost.cs |
Призрак здания в режиме стройки. |
BuildingData.cs |
Описание здания (ScriptableObject): префаб, размер на сетке, цена, нужный уровень Ратуши. |
TownHall.cs |
Ратуша и её улучшение. Раньше файл назывался NewEmptyCSharpScript.cs. |
BuildSlot.cs |
Участок под здание (старая схема, участки прячутся) и связь здания с участком. |
PlacedBuildingId.cs |
Метка построенного здания. Раньше файл назывался PlaceBuilding.cs. |
В слое приложения остаются BuildMenuUi.cs, Meta/Build/* (Казармы, BuildModeController).
3D-поселение (ТЗ «Система строительства поселения» от 04.11.2026)
Переход на 3D (2.5D: наклонная камера без вращения) идёт отдельной сценой Assets/Scenes/City3D.unity. Старый 2D-город (MVP1) работает как раньше, пока 3D его не догонит. Создать сцену: Tools → Bogatyri → 3D-город: создать сцену City3D, затем Play.
Этап I — прототип на примитивах (сделан 04.11.2026):
- Правила места — без Unity, сборка
Bogatyri.Settlement.Placement(SettlementMap), тестыPlacementTests(сценарии ТЗ 01–05, 09–12).- Невидимая сетка; клетка 1 единица мира.
- Здание — прямоугольник клеток с максимальным габаритом с первого уровня, поворот 0/90/180/270.
- Проверки: край карты, зона по уровню Ратуши, река и скалы (постоянные), деревья и валуны (расчищаются), другие здания, технический отступ 1 клетка, перепад высоты под зданием до 0,7 м (больше — «слишком круто», меньше — фундамент).
- Перенос — то же здание с тем же id, целиком или никак.
- Расчистка — один раз на объект.
- Сцена (
Assets/Scripts/City3D): местность 48 × 48 с холмами, река, скалы, 90 деревьев, 22 валуна, трава.- Сетка, открытая зона и следующая зона видны только в режиме стройки и редактирования.
- Новое здание: кнопка типа → куб-призрак тянется пальцем, зелёный или красный с причиной, «Повернуть 90°», «Поставить» / «Отмена».
- Перенос: «Переместить» в меню здания или режим «Редактировать»; «Подтвердить» / «Отмена» возвращает на последнее подтверждённое место.
- Трава прячется под зданием и возвращается, когда его перенесли.
- Камера: перетаскивание, два пальца / колесо, границы карты; при открытии зоны плавно отходит.
- Ратуша −/+ — только для теста зон.
- Рендер: в URP добавлен второй рендерер — обычный 3D (
Assets/Settings/Renderer3D.asset), его использует только камера City3D. 2D-сцены не тронуты.
Предложения на согласование (ТЗ, раздел 12), ТЕСТ: клетка 1 м; отступ 1 клетка; перепад до 0,7 м; зоны — квадраты от центра 16 / 24 / 32 / 40 / 48 клеток для Ратуши 1–5; габариты: Ратуша 4×4, Дом 2×2, Ферма 3×3, Лесопилка и Каменоломня 3×2, Шахта 2×2, Склад и Амбар 2×3, Казармы и Госпиталь 3×3.
Дальше: этап II — стройка через модуль Поселения (цена, таймер по реальному времени, события, сохранение позиции, поворота и id), перенос живых зданий без сброса производства / обучения / лечения; этап III — награда за расчистку, сохранение расчищенного, автодороги, зоны от настоящей Ратуши; этап IV — телефон. Кнопки — временные (IMGUI), окна — с Володей.
Решения и план
- Решение команды от 19.10.2026 (этап 2): Ратуша даёт базовую вместимость жителей, новое здание «Дом» — дополнительные места.
- Лимит населения = база Ратуши + сумма Домов + бонусы, значения из
EconomyConfig. - Уровень поселения задаёт доступные уровни и количество жилья, а также минимальный гражданский резерв.
- Лимит населения = база Ратуши + сумма Домов + бонусы, значения из
- Этап 3: рабочие места в зданиях (
BuildingData), экран распределения рабочих. - Стройка на сетке (27.10.2026, по просьбе Володи): вернулись к свободной стройке — игрок сам выбирает место, сетка показывает границы и красные занятые клетки, у зданий разный размер.
- Этап 4 (утверждён без изменений): доступность зданий зависит от месторождений позиции.
Журнал изменений
| Дата | Кто | Что |
|---|---|---|
| 06.11.2026 | Claude (Саша) | Открытие зоны Ратушей: камера показывает только край новой зоны — старую зону и 2 клетки новой за её границей (ZoneEdgeCells), а не всю зону; ближе, чем была, не подъезжает (City3DCamera.FocusOn(..., onlyZoomOut)). Раньше при ускорении улучшения Ратуши камера улетала слишком далеко. |
| 06.11.2026 | Claude (Валдемар) | Этап 1 перехода на 3D (2D-город больше не развиваем). Сверка: все 12 зданий 2D (8 Data_*.asset + Склад, Амбар, Госпиталь, Терем) есть в 3D с теми же ценами и уровнем Ратуши; ни одна кнопка не ведёт в MVP1. Камера (City3DCamera): центр кадра не уходит за открытые зоны; при самом дальнем отдалении открытая зона видна целиком между HUD сверху и снизу — дальше всего = max(подобранное в DEV, сколько нужно для зоны), считается по проекции камеры (FitDistance); «+ / −» и колесо приближают плавно; перетаскивание считается на высоте земли под пальцем (на холмах карта идёт точно за пальцем); подъезд к призраку — MoveTo, без смены приближения. Ввод: после щипка отпущенный палец больше не считается тапом (не открывается окно здания). SceneListFixer больше не добавляет и не включает MVP1 в Build Settings. Числа камеры — ТЕСТ. |
| 05.11.2026 | Claude (Саша) | Перенос 2D-города надёжнее: если места нет, деревья и валуны под зданием расчищаются (бесплатно, без награды — SettlementMap.ValidateIfCleared, ObstaclesUnder, тест Placement_IfCleared_IgnoresOnlyTreesAndListsThem); если места нет и так — здание не теряется, а ждёт в settlement3d.pendingFrom2D и ставится при входе в город или когда Ратуша открывает зону. DEV → «Камера»: ползунки наклона, перспективы (поле зрения), ближе / дальше всего и текущего отдаления; подобранное запоминается на устройстве (только редактор и отладочная сборка), «Сбросить» — числа из сцены. |
| 05.11.2026 | Claude (Саша) | Решение команды: только 3D-поселение, камера перспективная. (1) В 3D перенесены Золотой рудник (золото, Ратуша 2, 50 камня), Кристалл (Ратуша 6, цена из Data_Kristall) и Терем (HeroCastle, окно героя; фигурка героя гуляет только по 2D-сетке, в 3D — нет). (2) Перенос 2D-города: если в сохранении нет settlement3d, а есть 2D-город, при первом входе в 3D Ратуша получает уровень и улучшение из 2D, остальные здания встают на ближайшие свободные места с теми же уровнями и стройками (City3DController.MoveFrom2D, итог — в Console «[3D] Перенос 2D-города»). Секция settlement в файле не меняется. (3) SceneNavigator.CitySceneName = City3D; MVP1 выключена в Build Settings (сцена и код 2D остаются в репозитории до конца проверки). (4) City3DCamera.perspective (по умолчанию да, поле зрения 40°) — угол, высота и поле зрения ТЕСТ. Меню стройки — в два столбца. |
| 05.11.2026 | Claude (Саша) | 3D: камера сцены City3D ортографическая — у неё расстояние не меняет размер города, поэтому «+ / −» и щипок не приближали. City3DCamera.Apply для ортографической камеры ставит orthographicSize по расстоянию (как видно перспективной камерой с этого расстояния). |
| 05.11.2026 | Claude (Саша) | 3D: дымка идёт за камерой (City3DCamera.Apply: начало и конец — от расстояния камеры), поэтому при отдалении город больше не уходит в серую дымку. Окно здания: кнопка «Улучшить» (Ратуша и уровни зданий) сама становится зелёной, когда ресурсов стало хватать (BuildingWindow.LiveAfford) — не нужно закрывать и открывать окно. |
| 05.11.2026 | Claude (Саша) | 3D: здание, которое ещё строится, теперь видно — оно серее, над ним «Стройка 0:45» (BuildTimer затемнял только 2D-спрайты, поэтому Казармы выглядели готовыми, а «Войска» писали «казарм: 0»). Окно «Войска»: пока Казармы строятся — «Казармы строятся, осталось …», после стройки окно перерисовывается само; «Построить Казармы» в 3D ставит призрак Казарм (City3DController.PlaceBuilding). Кнопки «+ / −» справа — приближение камеры (в Device Simulator колесо мыши не доходит до игры). |
| 05.11.2026 | Claude (Саша) | Интеграция 3D: (1) снимок 3D-города для сохранения до расстановки сохранённых зданий отдаёт загруженное состояние — сохранение в первом кадре (поход вернулся офлайн) больше не затирает 3D-город пустым; (2) ISettlement.TownHallLevel вне города — уровень Ратуши последнего открытого города (2D или 3D), а не всегда 2D-сохранения: на карте мира предел жилья, резерв и открытие войск считаются по 3D-Ратуше. |
| 04.11.2026 | Claude | Решения 04.11: снос убран, остался бесплатный перенос; офлайн по временным меткам — стройка, закончившаяся офлайн, вступает в силу в момент завершения внутри расчёта добычи (ConstructionClock, GameTimeSimulatedEvent, BuildTimer.MayComplete); проверки таймеров из сохранения; уровень при завершении только растёт. |
| 01.11.2026 | Claude | Стройка и улучшение по времени (BuildTimer), 2 строителя, ускорение за кристаллы, офлайн; лимит зданий по уровню Ратуши; перенос и снос зданий из окна. Сохранение: поля таймера у зданий и Ратуши (старые сохранения читаются). Терем богатыря (HeroCastle) и гуляющий герой. Числа — ТЕСТ, EconomyConfig. |
| 04.11.2026 | Claude (Саша) | 3D-поселение, этап I (ТЗ от 04.11): правила места Bogatyri.Settlement.Placement + тесты, сцена City3D на примитивах (местность, зоны, призрак, поворот, перенос, расчистка, камера), 3D-рендерер для её камеры. Ветка feature/3d-settlement. |
| 01.11.2026 | Claude (Саша) | Новое здание «Госпиталь» в списке стройки (Meta/Build/HospitalBootstrap.cs, как Казармы): уровни I–V сохраняются как у остальных (ILeveledBuilding), тап — окно госпиталя. Места — в HospitalConfig. Арта нет: копия обычного здания светло-голубого цвета. |
| 01.11.2026 | Claude (Саша) | Для госпиталя (решения 31.10, п. 7) в ядре есть контракт IHospital.Beds — его будет регистрировать здание госпиталя (уровни I–V, 20–60 мест). Здания пока нет. |
| 31.10.2026 | Claude | Ратуша: тап больше не улучшает сразу, а открывает окно Ратуши (событие TownHall.Tapped, окно — App/BuildingWindow). Улучшение — кнопкой в окне, с ценой и тем, что откроется. |
| 31.10.2026 | Claude | Казармы стали уровневым зданием (ILeveledBuilding, уровень сохраняется как у остальных). Тап открывает окно Казарм: очередь обучения, какие войска открыты и что нужно для остальных, улучшение, кнопка «Нанять войска». Войско открывается, когда хватает и уровня Ратуши, и уровня Казарм; считаются самые прокачанные Казармы, каждые Казармы по-прежнему дают +1 место в очереди. Уровни Казарм: пехота 1, лучники 2, конница 3, осадные 5 (EconomyConfig, soldier.requiredBarracksLevel) — ТЕСТ, согласовать с Димой; цена улучшения общая, тоже не утверждена. |
| 31.10.2026 | Claude | Откат: Население снова самостоятельный модуль (Modules/Population), из Поселения убрано. |
| 28.10.2026 | Claude | Население стало частью Поселения (Settlement/Population, сборка Bogatyri.Settlement.Population) — как на схеме Димы. Подробности — Population.md. |
| 27.10.2026 | Claude | Свободная стройка на сетке: BuildGrid, BuildGhost, BuildingData.footprint, BuildManager.BuildAt / CanBuildAt / FindData. Участки и площадки «Застройка» прячутся. Новый BuildModeController (призрак, «Поставить / Другое / Отмена»), камера не двигается, пока тащат призрак. |
| 19.10.2026 | Claude | ResetState() для сброса сохранений. Убранные здания помечаются, чтобы запись в том же кадре их не сохранила. |
| 19.10.2026 | Claude | Модуль выделен. Стройка и Ратуша платят через IEconomy. Сохранение города перенесено из SaveManager в SettlementModule (секция "settlement"). Восстановление защищено от двойного запуска и от освобождения занятого участка. Файлы переименованы под имена классов. |