Phase A: wire climate + day/night into the world; design doc

Bump the engine submodule to a684c23 (Climate + day/night ambient). The
world scene now registers UseClimate and UseDayNight, so it darkens at
night and the HUD shows the season and temperature alongside the date.
Add docs/растения.md capturing the whole plant-ecosystem design (growth as
a suitability product, hybrid Mendelian genome, climate, lighting with
shadows, reproduction, persistence) and the A-D phase plan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Leonid Pershin
2026-06-12 16:46:05 +03:00
co-authored by Claude Opus 4.8
parent 147ab203f1
commit aff5a947c5
6 changed files with 117 additions and 3 deletions
+5
View File
@@ -1,6 +1,11 @@
{ {
"hud.world": "LittleSim | seed {0} | {1} | {2} FPS", "hud.world": "LittleSim | seed {0} | {1} | {2} FPS",
"hud.datetime": "day {0}, {1:00}:{2:00}", "hud.datetime": "day {0}, {1:00}:{2:00}",
"hud.climate": "{0} {1}°C",
"season.spring": "spring",
"season.summer": "summer",
"season.autumn": "autumn",
"season.winter": "winter",
"hud.controls": "WASD — camera, wheel — zoom, ` — console, F1 — inspector", "hud.controls": "WASD — camera, wheel — zoom, ` — console, F1 — inspector",
"hud.paused": "PAUSED", "hud.paused": "PAUSED",
+5
View File
@@ -1,6 +1,11 @@
{ {
"hud.world": "LittleSim | сид {0} | {1} | {2} FPS", "hud.world": "LittleSim | сид {0} | {1} | {2} FPS",
"hud.datetime": "день {0}, {1:00}:{2:00}", "hud.datetime": "день {0}, {1:00}:{2:00}",
"hud.climate": "{0} {1}°C",
"season.spring": "весна",
"season.summer": "лето",
"season.autumn": "осень",
"season.winter": "зима",
"hud.controls": "WASD — камера, колесо — зум, ` — консоль, F1 — инспектор", "hud.controls": "WASD — камера, колесо — зум, ` — консоль, F1 — инспектор",
"hud.paused": "ПАУЗА", "hud.paused": "ПАУЗА",
+84
View File
@@ -0,0 +1,84 @@
# Растения как экосистема
Дизайн системы растений: рост зависит от среды, среда описана факторами, реакция растения на
факторы задаётся **генами**, а размножение передаёт гены потомкам с мутацией — появляется
естественный отбор. Документ — единый ориентир для фаз A–D; детали каждой фазы уточняются её
отдельным планом.
## Модель роста
Скорость роста растения в данный момент:
```
скорость = база × пригодность(свет) × пригодность(температура) × пригодность(плодородность)
```
Каждая «пригодность» — число в [0..1] из кривой отклика (движковый `ResponseCurve`, модуль
`MrGameEng.AI`) с **оптимумом** и **шириной толерантности**, которые берутся из генов растения.
Растение вне своих оптимумов растёт медленно или стоит; в своих — быстро. Так все факторы среды
сводятся к одному механизму, а различие видов/особей — целиком в генах.
Фактор «возраст/стадия» остаётся как сейчас (`PlantGrowthStage` в дефах): пригодности умножают
прирост `AgeDays`, стадии переключают спрайт/размер по накопленному возрасту.
## Факторы среды
- **Свет** — амбиент день/ночь (`MrGameEng.Lighting.DayNight`), сэмплируемый в точке
(`SampleAt(world)`); глобальный в фазе A, локальный (с тенями) в фазе D.
- **Температура** — непрерывная, из климата (`MrGameEng.Core.Climate`): сезонный синус по дню года
+ суточные колебания (холоднее ночью).
- **Плодородность** — пока константа типа террейна (`TerrainDef.Fertility`); позже возможна
по-клеточная почва с истощением/восстановлением.
## Гены (диплоидный геном, гибридная экспрессия)
Каждое растение несёт **геном**: по паре аллелей на ген. Экспрессия гибридная:
- **Числовые гены** (оптимум света, толерантность к свету, оптимум температуры, толерантность,
потребность в плодородности, макс. размер, базовая скорость, продолжительность жизни, дальность
расселения, плодовитость, темп мутаций, способность к самоопылению) — фенотип = **смешение**
(среднее двух аллелей). Реализм отбора: промежуточные значения, плавный дрейф.
- **Дискретные гены** (например, цвет/морфа, наличие признака) — **доминант/рецессив**: рецессивный
фенотип виден только в гомозиготе (aa).
Наследование (мейоз): каждый родитель отдаёт по одному случайному аллелю на ген; потомок получает
по одному от каждого. Затем — мутация (с вероятностью из гена темпа мутаций сдвигаем числовой
аллель/переключаем дискретный). Всё детерминировано сидом мира.
Стартовая популяция: геномы генерируются из сида (вид задаёт базовый геном и разброс аллелей в
дефе; особи отклоняются).
## Размножение (полуреалистичное)
- **Перекрёстное опыление (приоритет):** зрелое растение ищет зрелого соседа того же вида в радиусе
(ген дальности; запрос через spatial-hash движка). Есть партнёр → семя с геномом от двух
родителей (Мендель + мутация).
- **Самоопыление (запас):** нет партнёра и ген «способность к самоопылению» позволяет → семя из
одного родителя (само-скрещивание аллелей + мутация).
- **Расселение:** семя падает в клетку в радиусе расселения; прорастает, если клетка подходит
(суша, не занято сверх лимита, пригодность не нулевая).
- **Лимит плотности:** не больше N растений на клетку/площадь — иначе взрыв численности.
- **Старение/смерть:** возраст превышает ген жизни → смерть; или длительный стресс (пригодность
около нуля) → гибель. Смерть освобождает место — баланс с размножением и основа отбора.
## Производительность и сейв
- Представление — **по сущности на растение** (видно в рендере и ECS-инспекторе, индивидуальные
гены). Защита от взрыва: лимит плотности, смерть, разумные радиусы, проверки размножения по
таймеру/сезону (не каждый кадр на каждое растение), spatial-hash для соседей.
- **Персист:** геномы, позиции, возраст и стадия растений сохраняются в `WorldSave` (мир больше не
воспроизводится только из сида — эволюция переживает загрузку).
- **Детерминизм:** мутации/расселение/опыление — на сидированном `Random`, как в остальной симуляции.
## Фазы реализации
- **A — климат и день/ночь (движок).** `Climate` (температура/сезон поверх `Calendar`),
`DayNight` (амбиент, `Renderer2D.AmbientLight` на World-слоях, сэмплируемый `SampleAt`),
затемнение мира ночью, HUD: сезон/температура/время. Фундамент для влияния среды на рост.
- **B — гены и рост по пригодностям (игра).** Компонент `PlantGenome`, расчёт пригодностей
(свет из `DayNight.SampleAt`, температура из `Climate`, плодородность из террейна), замена
плоского `GrowthRate`. Геном виден/правится в инспекторе.
- **C — размножение и смерть (игра).** Перекрёстное/самоопыление, расселение, лимит плотности,
старение/стресс-смерть, персист популяции в сейв.
- **D — полноценное 2D-освещение (движок).** Точечные источники + тени (окклюзия от препятствий);
`DayNight.SampleAt` начинает возвращать локальную освещённость, и затенённые растения растут хуже.
+1 -1
Submodule engine updated: f39ee9e787...a684c23cd9
+12 -1
View File
@@ -13,6 +13,7 @@ using MrGameEng.DevConsole;
using MrGameEng.Graphics; using MrGameEng.Graphics;
using MrGameEng.Input; using MrGameEng.Input;
using MrGameEng.Inspector; using MrGameEng.Inspector;
using MrGameEng.Lighting;
using MrGameEng.Tilemaps; using MrGameEng.Tilemaps;
using MrGameEng.UI; using MrGameEng.UI;
using Myra.Graphics2D; using Myra.Graphics2D;
@@ -74,6 +75,8 @@ public sealed class WorldScene : Scene
this.UseTilemaps(); this.UseTilemaps();
var calendar = Context.UseCalendar(SecondsPerDay); var calendar = Context.UseCalendar(SecondsPerDay);
var climate = Context.UseClimate(ClimateSettings.Default);
this.UseDayNight(renderer); // мир темнеет ночью — амбиент идёт в рендер
// Рельеф и расстановка растений детерминированы сидом мира (независимые потоки seed). // Рельеф и расстановка растений детерминированы сидом мира (независимые потоки seed).
var plants = new PlantSet(content, atlases, device); var plants = new PlantSet(content, atlases, device);
@@ -105,7 +108,15 @@ public sealed class WorldScene : Scene
new GodCameraSystem(camera, input, renderer, console, () => _pause.IsOpen) new GodCameraSystem(camera, input, renderer, console, () => _pause.IsOpen)
); );
UpdateSystems.Add( UpdateSystems.Add(
new HudSystem(Context, content.Languages, hudLabel, "hud.world", _config.Seed, calendar) new HudSystem(
Context,
content.Languages,
hudLabel,
"hud.world",
_config.Seed,
calendar,
climate
)
); );
UpdateSystems.Add(new CallbackSystem(() => Hotkeys(input))); UpdateSystems.Add(new CallbackSystem(() => Hotkeys(input)));
+10 -1
View File
@@ -185,7 +185,8 @@ public sealed class HudSystem(
Myra.Graphics2D.UI.Label label, Myra.Graphics2D.UI.Label label,
string titleKey, string titleKey,
int seed, int seed,
MrGameEng.Core.Calendar? calendar = null MrGameEng.Core.Calendar? calendar = null,
MrGameEng.Core.Climate? climate = null
) : BaseSystem ) : BaseSystem
{ {
private float _accumulated; private float _accumulated;
@@ -206,6 +207,14 @@ public sealed class HudSystem(
var when = calendar is null var when = calendar is null
? "" ? ""
: languages.Format("hud.datetime", calendar.Day, calendar.Hour, calendar.Minute); : languages.Format("hud.datetime", calendar.Day, calendar.Hour, calendar.Minute);
if (climate is not null)
{
var season = languages.Get("season." + climate.Season.ToString().ToLowerInvariant());
when +=
" | "
+ languages.Format("hud.climate", season, (int)MathF.Round(climate.Temperature));
}
label.Text = label.Text =
languages.Format(titleKey, seed, when, fps) + "\n" + languages.Get("hud.controls"); languages.Format(titleKey, seed, when, fps) + "\n" + languages.Get("hud.controls");
} }