Игры · san-smith
Все проекты генератор планет · открытый код

vernadsky

Физическая география планеты: рельеф, вода, климат, биомы, стабильные идентификаторы. Не знает об игре.

Карта биомов
Биомы, сид 42

Зачем это нужно

Планету для игры проще сгенерировать, чем нарисовать: карта нужна на тысячу провинций, и она должна быть правдоподобной. Рельеф, реки, климат и биомы связаны между собой, поэтому генерируются стадиями, каждая следующая опирается на предыдущие.

Генератор намеренно нейтрален: он описывает физическую географию и ничего не знает о фракциях, экономике, игровых провинциях и правилах симуляции. Базовая единица деления карты называется территория — географическая единица без игровой семантики. Провинция, государство или морская зона — то, что читатель данных собирает из территорий и клеток уже на своей стороне.

Предыдущая попытка, mapgen, была единым крейтом под конкретную игру. vernadsky — то, во что она выросла: алгоритмы (рельеф, биомы, климат, разбиение на территории) перенесены, а границы проведены заново.

Как устроено

Репозиторий — это Cargo workspace из восьми крейтов. Стадии идут в фиксированном порядке, и каждая работает с тем, что оставила предыдущая:

core → terrain → climate → biome → hydrology → territory → export → tools

Вход всего конвейера — зерно и параметры; skeleton_world(width, height, seed) в vernadsky-core создаёт пустую сетку нужного размера, которую дальше заполняют стадии. Результат — одно значение GeographicWorld, которое можно сразу записать в файл экспорта.

Стадии

  • core (vernadsky-core) — контракт данных: схема GeographicWorld, целочисленные решётки физических величин, пространства идентификаторов, канонический бинарный экспорт. Единственное место, где числа с плавающей точкой превращаются в экспортируемые значения.
  • terrain — рельеф: fBm-шум OpenSimplex2 на цилиндре (карта бесшовна по x, полюса зажимаются), эффект острова, сглаживание, степенная коррекция и нормировка min/max с целевой долей суши. На выходе — метры: уровень моря 0.5 соответствует 0 м, шкала до ±6000 м. Эрозия подключается профилем (температурное выветривание и гидравлические капли из потока terrain.erosion).
  • climate — среднегодовая температура, относительная влажность и осадки по клеткам, пересчитанные в физические единицы (°C, %, мм/год); шум температуры берётся из потока climate.temperature.
  • biome — таблица из шестнадцати биомов как упорядоченные наборы правил (вода → горы → климат), все пороги лежат на решётках, граница между биомами дизерится по клеткам. Реестр biomes-v1 владеет соответствием идентификатор ↔ имя ↔ категория ↔ канонический цвет.
  • hydrology — водоёмы и речная сеть. Водоёмы классифицируются заливкой от полюса: океан, а замкнутые компоненты делятся на моря и озёра по площади. Реки строятся по накоплению стока — маршрутизация по убыванию высоты с каноническими разрешениями ничьих; лёд блокирует сток, пустыни испаряют половину. Порог дренажа задаётся долей от клеток суши (0.12 %), поэтому сеть остаётся тонкой и ветвящейся на любой сетке.
  • territory — разбиение карты на территории: кандидаты на суше взвешиваются по климату и высоте, зёрна берутся равномерно по спектру весов, рост идёт многоисточниковым BFS с заполнением по ближайшему центроиду. Вода делится по связным компонентам, так что море никогда не остаётся одним неделимым пространством. Здесь же — регионы: континенты и морские бассейны как компоненты связности графа соседства территорий.
  • export — производные признаки: стартовый природный потенциал каждой территории суши (комфорт по температуре × влажность × равнинность, в промилле 0…1000) и нейтральные географические факты — близость к морю, устья рек, горный рельеф — как детерминированные запросы, которые потребитель трактует как порты, эстуарии и перевалы.
  • tools — отладочная утилита: рендерит слои мира и пишет экспорт.

Две стадии — territory и hydrology — не потребляют случайность вообще: их результат полностью определяется предыдущими стадиями.

Контракт

Данные, которые отдаёт генератор:

  • Схема GeographicWorld версии 1. Идентификаторы клеток, территорий, регионов, водоёмов, рек и биомов — часть публичного контракта: по ним потребители связывают данные между экспортами, поэтому смена способа вывода идентификаторов — ломающее изменение.
  • Решётки. Все физические величины пересекают границу стадии и попадают в экспорт только как целые значения решёток. Это снимает вопрос платформенной арифметики: сравнивать и хешировать приходится целые.
  • Экспорт. Канонический бинарный формат: magic VGW\0, целые фиксированной ширины little-endian, у каждой переменной секции счётчик u32. В конце — content_hash (FNV-1a 64 по всем предыдущим байтам): это проверка целостности и воспроизводимости.
  • Версии. Файл несёт версию схемы, стратегию идентификаторов, версию реестра решёток и версию реестра биомов (biomes-v1) — файл самодостаточен для интерпретации.

Детерминизм и управляемый дрейф

Генерация — чистая функция от зерна и параметров: одни и те же входы дают байт в байт одинаковый результат на каждом запуске и каждой поддерживаемой платформе.

Случайность берётся из именованных потоков (rng-streams-v1): поток ChaCha8 выводится из зерна мира и имени, закреплённого за стадией. Потоки независимы и перезапускаемы, поэтому добавление новой механики не сдвигает поведение уже существующих.

Чтобы это не разъехалось молча, в репозитории лежат золотые манифесты: набор семян (включая граничные значения) с закреплёнными хешами полных экспортов и канарейка чувствительности хеша. Случайная недетерминированность или дрейф схемы валят тесты.

Дрейф контента при этом бывает осознанным. Таких правок три, и каждая оформлена явно — с повышением версии параметров и перегенерацией золотых манифестов:

  1. Порог дренажа рек стал независим от разрешения сетки (параметры v3).
  2. Шум рельефа нормируется в пространстве карты: число базовых объектов — доля окружности карты (features_across, по умолчанию 1.5), а не частота на клетку, плюс нижний предел детализации (min_feature_cells, по умолчанию 8) против прибрежного «шума». Теперь любое разрешение рисует одну и ту же планету, а не рассыпается в архипелаг при росте сетки.
  3. Сглаживание рельефа стало бесшовным по x (пока в невыпущенном разделе): раньше горизонтальное окно блюра зажималось на краях карты, и рельеф ломался на шве, хотя шум, подъём острова и эрозия уже замыкались по цилиндру.

Как получить такую карту

Сам генератор — библиотека; посмотреть на результат удобнее отладочной утилитой vernadsky-tools. Это инструмент диагностики, не часть контракта генератора: тяжёлые зависимости вроде image живут только в его крейте. Из входов нужен только seed — остальное имеет значения по умолчанию.

cargo run -p vernadsky-tools -- render --seed 42 --grid-height 512 --width 512 --out map

Утилита собирает мир из семени через весь конвейер и растеризует каждый слой в свой PNG: имя файла — <out>.<слой>.png, слой рисуется отдельной картой.

  • map.height.png — высота;
  • map.water.png — вода;
  • map.rivers.png — реки;
  • map.temperature.png, map.humidity.png, map.precipitation.png — климат;
  • map.biome.png — биомы;
  • map.territory.png, map.regions.png — территории и регионы;
  • map.potential.png — природный потенциал.

По умолчанию сетка 48×32, разбиение — 12 территорий суши и 8 водных. Ключи --width и --grid-height задают разрешение, --land-count и --water-count — разбиение, --zoom увеличивает картинку целым множителем (до 16×), если клеток мало и результат нужно рассмотреть.

Тот же мир сохраняется как канонический экспорт и читается обратно:

cargo run -p vernadsky-tools -- dump --seed 42 --width 512 --grid-height 512 --out world.gwb
cargo run -p vernadsky-tools -- validate --input world.gwb
world written to world.gwb (262144 cells, 20 territories, pipeline 1.60s)
valid export world.gwb: 512×512 cells, 20 territories, 7 regions, 4 water bodies, 293 rivers; seed 0x000000000000002a, params version 4

Время конвейера зависит от машины; остальное — из самого файла. Мир можно читать и из экспорта, а не только из семени: render --input world.gwb рисует те же слои по уже собранному файлу.

Что получилось

Одна команда выше, семя 42, сетка 512×512: 262 144 клетки, 20 территорий, 7 регионов, 4 водоёма и 293 реки. Климатические слои рисуются на фиксированных физических шкалах — поэтому картинки разных семян сравнимы между собой. В категориальных слоях правила тоже фиксированы: биомы берут канонические цвета реестра, территории и регионы — детерминированную палитру.

Карта высот

Высота. Слой нормируется по самому миру, а не по общей шкале: тёмное — ниже, светлое — выше. Уровень моря — 0 м, полная шкала высот — ±6000 м.

Карта воды

Вода. Светлое — суша, голубым — вода; оттенок различает океан, море и озеро. В этом мире четыре водоёма.

Карта рек

Реки. 293 реки: подсвечены клетки русел — и на суше, и в воде.

Карта температуры

Температура. Фиксированная шкала −90…+60 °C (палитра RdBu): тёплый пояс у экватора, холодные полюса.

Карта влажности

Влажность. Фиксированная шкала 0…100 % (BrBG).

Карта осадков

Осадки. Фиксированная шкала 0…3000 мм/год (YlGnBu).

Карта биомов

Биомы. Шестнадцать биомов, цвета берутся из реестра biomes-v1 — одного на все слои и экспорты.

Карта территорий

Территории. 20 территорий — 12 на суше и 8 на воде, каждая своим цветом. Заполнение идёт по ближайшему центроиду, поэтому края зон многоугольные, со ступеньками по сетке клеток.

Карта регионов

Регионы. Те же территории, сгруппированные в континенты и морские бассейны: семь регионов вместо двадцати цветов.

Карта природного потенциала

Природный потенциал. По территориям суши, шкала 0…1000 ‰: от бурого к зелёному. Вода и клетки вне территорий остаются фоном.

Карты воспроизводимы: тот же запуск даёт те же файлы, и это свойство под проверкой, а не пожелание — за случайное расхождение отвечают золотые манифесты из раздела выше.

  • ЛицензияMIT / Apache-2.0
  • Версия0.1.0
  • ИзданиеRust 2024
  • MSRV1.92.0
  • Частиcore, terrain, climate, biome, hydrology, territory, export, tools
san-smith · vernadsky games.sansmith.dev