using System; using Friflo.Engine.ECS; using Friflo.Engine.ECS.Systems; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MrGameEng.Core; namespace KniWebSpike { // Компоненты симуляции — платформо-независимые, как в LittleSim. public struct DotPosition : IComponent { public float X; public float Y; } public struct DotVelocity : IComponent { public float X; public float Y; } public struct DotTint : IComponent { public byte R; public byte G; public byte B; } /// /// Сцена спайка: 300 «спрайтов» в Friflo EntityStore, движение в Update-фазе /// (детерминированный seed, отскок от краёв), отрисовка в Draw-фазе через /// KNI SpriteBatch (WebGL). Сцена и системные корни — из MrGameEng.Core. /// public sealed class SpikeScene : Scene { private readonly Func _spriteBatch; private readonly Func _pixel; private readonly float _width; private readonly float _height; public SpikeScene(Func spriteBatch, Func pixel, float width, float height) { _spriteBatch = spriteBatch; _pixel = pixel; _width = width; _height = height; } protected override void OnLoad() { var random = new Random(42); for (var i = 0; i < 300; i++) { var angle = (float)(random.NextDouble() * Math.Tau); var speed = 40f + (float)random.NextDouble() * 160f; Store.CreateEntity( new DotPosition { X = (float)random.NextDouble() * _width, Y = (float)random.NextDouble() * _height, }, new DotVelocity { X = MathF.Cos(angle) * speed, Y = MathF.Sin(angle) * speed, }, new DotTint { R = (byte)random.Next(64, 256), G = (byte)random.Next(64, 256), B = (byte)random.Next(64, 256), } ); } UpdateSystems.Add(new BounceSystem(_width, _height)); DrawSystems.Add(new DotDrawSystem(_spriteBatch, _pixel)); } private sealed class BounceSystem : QuerySystem { private readonly float _width; private readonly float _height; public BounceSystem(float width, float height) { _width = width; _height = height; } protected override void OnUpdate() { var delta = Tick.deltaTime; var width = _width; var height = _height; Query.ForEachEntity( (ref DotPosition pos, ref DotVelocity vel, Entity _) => { pos.X += vel.X * delta; pos.Y += vel.Y * delta; if (pos.X < 0f || pos.X > width) { vel.X = -vel.X; pos.X = Math.Clamp(pos.X, 0f, width); } if (pos.Y < 0f || pos.Y > height) { vel.Y = -vel.Y; pos.Y = Math.Clamp(pos.Y, 0f, height); } } ); } } private sealed class DotDrawSystem : QuerySystem { private readonly Func _spriteBatch; private readonly Func _pixel; public DotDrawSystem(Func spriteBatch, Func pixel) { _spriteBatch = spriteBatch; _pixel = pixel; } protected override void OnUpdate() { var batch = _spriteBatch(); var pixel = _pixel(); batch.Begin(); Query.ForEachEntity( (ref DotPosition pos, ref DotTint tint, Entity _) => { batch.Draw( pixel, new Rectangle((int)pos.X, (int)pos.Y, 6, 6), new Color(tint.R, tint.G, tint.B) ); } ); batch.End(); } } } }