KNI spike: engine core + Friflo render via WebGL in the browser
spikes/KniWeb (outside LittleSim.sln): a kni-blazor-gl template project (KNI 4.2.9001, net8.0) referencing MrGameEng.Core directly. A mini-host in the GameHost mold drives EngineContext/GameClock/Scene phases over KNI's Game; the scene moves 300 Friflo entities in the update phase and draws them with SpriteBatch (WebGL). Verified in a real browser: sprites render and animate, browser console is clean. Decision (docs/web-client.md): path A — KNI — is the primary route for the web client; the core runs in Blazor WASM unchanged thanks to the Core/Host split. Known follow-ups: per-platform compilation of the graphics libraries against nkast.* packages, shader compatibility for Renderer2D, HTTP-served content instead of the filesystem. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
8a7e2cce52
commit
ce8f8ba2ed
@@ -0,0 +1,148 @@
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Сцена спайка: 300 «спрайтов» в Friflo EntityStore, движение в Update-фазе
|
||||
/// (детерминированный seed, отскок от краёв), отрисовка в Draw-фазе через
|
||||
/// KNI SpriteBatch (WebGL). Сцена и системные корни — из MrGameEng.Core.
|
||||
/// </summary>
|
||||
public sealed class SpikeScene : Scene
|
||||
{
|
||||
private readonly Func<SpriteBatch> _spriteBatch;
|
||||
private readonly Func<Texture2D> _pixel;
|
||||
private readonly float _width;
|
||||
private readonly float _height;
|
||||
|
||||
public SpikeScene(Func<SpriteBatch> spriteBatch, Func<Texture2D> 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<DotPosition, DotVelocity>
|
||||
{
|
||||
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<DotPosition, DotTint>
|
||||
{
|
||||
private readonly Func<SpriteBatch> _spriteBatch;
|
||||
private readonly Func<Texture2D> _pixel;
|
||||
|
||||
public DotDrawSystem(Func<SpriteBatch> spriteBatch, Func<Texture2D> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user