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();
}
}
}
}