From 0c07eba02153439e9b12744d8110655ce50c2bcc Mon Sep 17 00:00:00 2001 From: Leonid Pershin Date: Sun, 14 Jun 2026 23:29:14 +0300 Subject: [PATCH] Liven up animal movement with a walking sway (presentation only) AnimalBobSystem tilts a moving animal side-to-side via Transform2D.Rotation (a gentle waddle) and eases it upright when standing. Phase runs on game time (frozen on pause, faster when sped up) and is offset per entity id so animals sway out of sync. Purely presentational -- the simulation ignores rotation, and the facing system writes scale (not rotation), so no conflict. Corpses/eggs have no brain so they stay still. Build + --check-content clean. Co-Authored-By: Claude Opus 4.8 --- src/LittleSim/Scenes/WorldScene.cs | 1 + src/LittleSim/Sim/AnimalSystems.cs | 46 ++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/LittleSim/Scenes/WorldScene.cs b/src/LittleSim/Scenes/WorldScene.cs index 4412c46..6529dd8 100644 --- a/src/LittleSim/Scenes/WorldScene.cs +++ b/src/LittleSim/Scenes/WorldScene.cs @@ -323,6 +323,7 @@ public sealed class WorldScene : Scene UpdateSystems.Add(new AnimalAppearanceSystem(_needs)); UpdateSystems.Add(new AnimalGrowthSystem(Context.Clock, SecondsPerDay, _animals, CellSize)); UpdateSystems.Add(new AnimalFacingSystem(_animals, CellSize)); + UpdateSystems.Add(new AnimalBobSystem(Context.Clock)); UpdateSystems.Add(new AnimalMortalitySystem(Store, _animals, CellSize)); UpdateSystems.Add(new CorpseSystem(Store, Context.Clock, SecondsPerDay, climate)); UpdateSystems.Add( diff --git a/src/LittleSim/Sim/AnimalSystems.cs b/src/LittleSim/Sim/AnimalSystems.cs index 965f1ca..595d782 100644 --- a/src/LittleSim/Sim/AnimalSystems.cs +++ b/src/LittleSim/Sim/AnimalSystems.cs @@ -1688,6 +1688,52 @@ public sealed class AnimalFacingSystem(AnimalSet animals, int cellSize) } } +/// +/// «Раскачка» при ходьбе (презентация): идущий зверь покачивается из стороны в сторону (наклон спрайта +/// через ), стоящий — плавно выпрямляется. Делает движение живее, не +/// затрагивая симуляцию (она вращение не использует). Фаза покачивания идёт по игровому времени (стоп на +/// паузе, быстрее на ускорении) и сдвинута по id особи — звери качаются вразнобой. Граница sim/presentation +/// цела: система пишет только поворот трансформа. +/// +public sealed class AnimalBobSystem(GameClock clock) : QuerySystem +{ + private const float SwaySpeed = 9f; // скорость набега фазы (рад/сек игрового времени) + private const float SwayAmplitude = 0.08f; // амплитуда наклона (рад ≈ 4.6°) + private const float MinMoveSq = 0.5f * 0.5f; // ближе к цели — считаем стоящим + private const float UprightDecay = 0.85f; // как быстро выпрямляется стоя (за кадр) + + private float _phase; + + protected override void OnUpdate() + { + var dt = clock.DeltaTime; + if (dt <= 0f) + { + return; // пауза — не качаем и не выпрямляем + } + + _phase += dt * SwaySpeed; + foreach (var (brains, transforms, entities) in Query.Chunks) + { + var b = brains.Span; + var t = transforms.Span; + for (var i = 0; i < b.Length; i++) + { + ref var rot = ref t[i].Rotation; + if ((b[i].Target - t[i].Position).LengthSquared() > MinMoveSq) + { + var offset = entities.EntityAt(i).Id * 0.7f; // десинхрон между особями + rot = MathF.Sin(_phase + offset) * SwayAmplitude; + } + else + { + rot *= UprightDecay; // у цели/стоя — плавно к вертикали + } + } + } + } +} + /// /// Рост и стадии (фаза A3): копит возраст в игровых днях, переключает стадию (Baby→Juvenile→Adult→ /// Senior) по порогам из генов и при смене стадии меняет спрайт (детёныш/самка/самец) и размер.