diff --git a/Kamni/src/Engine/Component.cs b/Kamni/src/Engine/Component.cs new file mode 100644 index 0000000..1ad7a11 --- /dev/null +++ b/Kamni/src/Engine/Component.cs @@ -0,0 +1,11 @@ +namespace Kamni.Engine; + +public class Component +{ + public Entity E { get; internal init; } = null!; + + public virtual void Update(FrameInfo frame) + { + + } +} \ No newline at end of file diff --git a/Kamni/src/Engine/Entity.cs b/Kamni/src/Engine/Entity.cs new file mode 100644 index 0000000..bd54085 --- /dev/null +++ b/Kamni/src/Engine/Entity.cs @@ -0,0 +1,48 @@ +using Raylib_cs; + +namespace Kamni.Engine; + +public class Entity +{ + public readonly long Id; + public Vector2 Position; + public Vector2 Scale; + public float Rotation; + + private readonly Dictionary _components = new(); + + internal Entity(long id) + { + Id = id; + } + + internal void InvokeUpdate(FrameInfo frame) + { + foreach (var (type, component) in _components) + { + component.Update(frame); + } + } + + public T AddComponent() where T : Component, new() + { + var component = new T() { E = this }; + _components.Add(typeof(T), component); + return component; + } + + public T? TryGetComponent() where T : Component + { + if (_components.TryGetValue(typeof(T), out var component)) + return (T)component; + return null; + } + + public T GetComponent() where T : Component + { + var component = TryGetComponent(); + if (component is null) + throw new Exception($"Component '{typeof(T).AssemblyQualifiedName}' not found in entity {Id}"); + return component; + } +} \ No newline at end of file diff --git a/Kamni/src/Engine/FrameInfo.cs b/Kamni/src/Engine/FrameInfo.cs new file mode 100644 index 0000000..df94b75 --- /dev/null +++ b/Kamni/src/Engine/FrameInfo.cs @@ -0,0 +1,3 @@ +namespace Kamni.Engine; + +public record struct FrameInfo(long Number, float deltaT); \ No newline at end of file diff --git a/Kamni/src/Engine/Scene.cs b/Kamni/src/Engine/Scene.cs new file mode 100644 index 0000000..4ba209b --- /dev/null +++ b/Kamni/src/Engine/Scene.cs @@ -0,0 +1,31 @@ +namespace Kamni.Engine; + +public class Scene +{ + // TODO: Replace List with some structure with fast Remove(id) + private readonly List _entities = new(); + + public IReadOnlyList Entities => _entities; + + public Entity CreateEntity() + { + Entity e = new Entity(_entities.Count); + _entities.Add(e); + return e; + } + + public void RemoveEntity(Entity e) + { + // waste 8 bytes in the list to store null at index e.Id + // TODO: remove memory leak + _entities[(int)e.Id] = null; + } + + internal void InvokeUpdate(FrameInfo frame) + { + foreach (var entity in _entities) + { + entity?.InvokeUpdate(frame); + } + } +} \ No newline at end of file diff --git a/Kamni/src/Engine/Sprite.cs b/Kamni/src/Engine/Sprite.cs new file mode 100644 index 0000000..9499154 --- /dev/null +++ b/Kamni/src/Engine/Sprite.cs @@ -0,0 +1,8 @@ +using Raylib_cs; + +namespace Kamni.Engine; + +public class Sprite +{ + public Texture2D Texture; +} \ No newline at end of file diff --git a/Kamni/src/Program.cs b/Kamni/src/Program.cs index dbd9f02..622c162 100644 --- a/Kamni/src/Program.cs +++ b/Kamni/src/Program.cs @@ -1,14 +1,16 @@ global using System; global using System.Numerics; +global using System.Collections.Generic; global using DTLib.Logging; global using DTLib.Filesystem; global using File = DTLib.Filesystem.File; global using Directory = DTLib.Filesystem.Directory; global using Path = DTLib.Filesystem.Path; +global using static Raylib_cs.Raylib; using System.Linq; using System.Runtime.InteropServices; +using Kamni.Engine; using Raylib_cs; -using static Raylib_cs.Raylib; namespace Kamni; @@ -17,6 +19,9 @@ public static class Program public static readonly Version GameVersion = new(0, 1, 0); public static Settings Settings { get; private set; } = null!; public static ILogger Logger { get; private set; } = null!; + + public static Camera2D Camera = new(new Vector2(), new Vector2(), 0, 1); + public static int Main(string[] args) { @@ -52,43 +57,79 @@ public static class Program SetWindowMinSize(480, 360); InitWindow(Settings.WindowMode == WindowMode.Window ? 720 : 0 , Settings.WindowMode == WindowMode.Window ? 480 : 0, $"Kamni v{GameVersion}"); + + var scene = new Scene(); + var octagon0 = scene.CreateEntity().AddComponent(); + var octagon1 = scene.CreateEntity().AddComponent(); + var octagon2 = scene.CreateEntity().AddComponent(); + var octagon3 = scene.CreateEntity().AddComponent(); + var octagon4 = scene.CreateEntity().AddComponent(); + var octagon5 = scene.CreateEntity().AddComponent(); + var octagon6 = scene.CreateEntity().AddComponent(); + + octagon0.Color = Color.Red; + octagon0.E.Position = new(0, -100); + octagon1.Color = Color.Pink; + octagon1.E.Position = new(-100, -50); + octagon2.Color = Color.Orange; + octagon2.E.Position = new(100, -50); + octagon3.Color = Color.Purple; + octagon3.E.Position = new(-150, 25); + octagon4.Color = Color.Yellow; + octagon4.E.Position = new(150, 25); + octagon5.Color = Color.SkyBlue; + octagon5.E.Position = new(-60, 75); + octagon6.Color = Color.Green; + octagon6.E.Position = new(60, 75); + + FrameInfo frameInfo = new FrameInfo(); while (!WindowShouldClose()) { BeginDrawing(); - DrawFrame(); + frameInfo.Number++; + frameInfo.deltaT = GetFrameTime(); + + Vector2 center = new(GetRenderWidth() / 2f, GetRenderHeight() / 2f); + Camera.Offset = center; + + ClearBackground(Color.DarkBlue); + + BeginMode2D(Camera); + scene.InvokeUpdate(frameInfo); + EndMode2D(); + + DrawFPS(5, 5); + EndDrawing(); } CloseWindow(); return 0; } - - static float[] randomRotations = []; - static float sumT = 999999; - static void DrawFrame() + + class PolygonalSprite : Component { + public Color Color = Color.White; + public int Sides = 5; + public int Radius = 32; - Vector2 center = new(GetRenderWidth() / 2f, GetRenderHeight() / 2f); - float deltaT = GetFrameTime(); - sumT += deltaT; - if (sumT > 0.5) + static float sumT = 999999; + + public override void Update(FrameInfo frame) { - sumT = 0; - randomRotations = Enumerable.Range(0, 7).Select(_ => (float)Random.Shared.Next(-30, 30)).ToArray(); + DrawPoly(E.Position, Sides, Radius, E.Rotation, Color); + + sumT += frame.deltaT; + if (sumT > 0.5) + { + sumT = 0; + E.Rotation = Random.Shared.Next(-30, 30); + } } - ClearBackground(Color.DarkBlue); - DrawPoly(new(center.X, center.Y - 100), 5, 32, randomRotations[0], Color.Red); - DrawPoly(new(center.X - 100, center.Y - 50), 5, 32, randomRotations[1], Color.Pink); - DrawPoly(new(center.X + 100, center.Y - 50), 5, 32, randomRotations[2], Color.Orange); - DrawPoly(new(center.X - 150, center.Y + 25), 5, 32, randomRotations[3], Color.Purple); - DrawPoly(new(center.X + 150, center.Y + 25), 5, 32, randomRotations[4], Color.Yellow); - DrawPoly(new(center.X - 60, center.Y + 75), 5, 32, randomRotations[3], Color.SkyBlue); - DrawPoly(new(center.X + 60, center.Y + 75), 5, 32, randomRotations[4], Color.Green); - // DrawPoly(new (center.X + 75, center.Y + 25), 5, 32, 0, Color.Orange); - DrawFPS(5, 5); } + [UnmanagedCallersOnly(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])] private static unsafe void RaylibLogHandler(int logLevel_, sbyte* text_, sbyte* args_) {