From a3887417a594e5a02101d82ea5f372f944878003 Mon Sep 17 00:00:00 2001 From: Ludovic 'Archivist' Lagouardette Date: Fri, 15 Mar 2024 17:17:53 +0100 Subject: [PATCH] Most of a second expansion sufficient to make a game done --- Expansion1/CrosshairGizmoComponent.cs | 71 ++++++++++++++------- Expansion1/RectangleGizmoComponent.cs | 2 +- Expansion2/ArrowComponent.cs | 49 ++++++++++++++ Expansion2/ExampleShootingInputComponent.cs | 36 +++++++++++ Expansion2/RaylibAssetManagerModule.cs | 35 ++++++++++ Expansion2/SpriteComponent.cs | 37 +++++++++++ Expansion2/TimeToLiveComponent.cs | 25 ++++++++ Program.cs | 40 ++++++++---- Smoll/Engine.cs | 19 +++++- Smoll/Entity.cs | 12 +++- Smoll/IActionable.cs | 4 +- Smoll/IEngineModule.cs | 6 ++ Smoll/Layer.cs | 17 +++-- Smoll/Transform2D.cs | 4 +- 14 files changed, 310 insertions(+), 47 deletions(-) create mode 100644 Expansion2/ArrowComponent.cs create mode 100644 Expansion2/ExampleShootingInputComponent.cs create mode 100644 Expansion2/RaylibAssetManagerModule.cs create mode 100644 Expansion2/SpriteComponent.cs create mode 100644 Expansion2/TimeToLiveComponent.cs create mode 100644 Smoll/IEngineModule.cs diff --git a/Expansion1/CrosshairGizmoComponent.cs b/Expansion1/CrosshairGizmoComponent.cs index c919761..d5a9e68 100644 --- a/Expansion1/CrosshairGizmoComponent.cs +++ b/Expansion1/CrosshairGizmoComponent.cs @@ -14,30 +14,53 @@ namespace Smoll.Ex1 { } public override void Draw(Smoll.Layer.DrawMode drawMode) { - ownerTransform ??= owner.GetComponent(); - ownerTransform ??= new Transform2DComponent(); - var tr = ownerTransform.AbsoluteTransform(); - Raylib.DrawCircleLines((int)tr.position.Real, (int)tr.position.Imaginary, 5.5f, Color.Black); - - Raylib.DrawLine( - (int)tr.position.Real, - (int)tr.position.Imaginary-10, - (int)tr.position.Real, - (int)tr.position.Imaginary+10, - Color.Lime); - Raylib.DrawLine( - (int)tr.position.Real-10, - (int)tr.position.Imaginary, - (int)tr.position.Real+10, - (int)tr.position.Imaginary, - Color.Red); - Raylib.DrawText( - tr.angle.ToString(), - (int)tr.position.Real+10, - (int)tr.position.Imaginary+10, - 14, - Color.Magenta - ); + if(drawMode == Layer.DrawMode.Debug) { + ownerTransform ??= owner.GetComponent(); + ownerTransform ??= new Transform2DComponent(); + var tr = ownerTransform.AbsoluteTransform(); + Raylib.DrawCircleLines((int)tr.position.Real, (int)tr.position.Imaginary, 5.5f, Color.Black); + + Raylib.DrawLine( + (int)tr.position.Real, + (int)tr.position.Imaginary-10, + (int)tr.position.Real, + (int)tr.position.Imaginary+10, + Color.Lime); + Raylib.DrawLine( + (int)tr.position.Real-10, + (int)tr.position.Imaginary, + (int)tr.position.Real+10, + (int)tr.position.Imaginary, + Color.Red); + Raylib.DrawText( + tr.angle.ToString(), + (int)tr.position.Real+10, + (int)tr.position.Imaginary+10, + 14, + Color.Magenta + ); + Raylib.DrawText( + tr.angle.ToString(), + (int)tr.position.Real+10, + (int)tr.position.Imaginary+10, + 14, + Color.Magenta + ); + Raylib.DrawText( + tr.position.Real.ToString(), + (int)tr.position.Real+10, + (int)tr.position.Imaginary+26, + 14, + Color.Red + ); + Raylib.DrawText( + tr.position.Imaginary.ToString(), + (int)tr.position.Real+10, + (int)tr.position.Imaginary+42, + 14, + Color.Lime + ); + } } } } \ No newline at end of file diff --git a/Expansion1/RectangleGizmoComponent.cs b/Expansion1/RectangleGizmoComponent.cs index 943b3a5..3997ae1 100644 --- a/Expansion1/RectangleGizmoComponent.cs +++ b/Expansion1/RectangleGizmoComponent.cs @@ -32,7 +32,7 @@ namespace Smoll.Ex1 { (float)(0), (float)(0) ), - tr.angle*360f/MathF.PI, + tr.angle*180f/MathF.PI, color ); } diff --git a/Expansion2/ArrowComponent.cs b/Expansion2/ArrowComponent.cs new file mode 100644 index 0000000..edd00c5 --- /dev/null +++ b/Expansion2/ArrowComponent.cs @@ -0,0 +1,49 @@ +using System.Numerics; +using Raylib_cs; + +namespace Smoll.Ex2 { + class ArrowComponent : Smoll.Component { + Complex offset; + public Complex rawValue; + Transform2DComponent? ownerTransform; + + public ArrowComponent(int offset_x = 0, int offset_y = 0) + { + this.offset = offset_x + Complex.ImaginaryOne*offset_y; + } + + public override void OnAttached() { + ownerTransform = owner.GetComponent(); + } + + public Complex Origin() { + ownerTransform ??= owner.GetComponent(); + ownerTransform ??= new Transform2DComponent(); + var tr = ownerTransform.AbsoluteTransform(); + return tr.position + offset*Complex.Exp(Complex.ImaginaryOne * tr.angle) * tr.scale; + } + + public Complex Vector() { + ownerTransform ??= owner.GetComponent(); + ownerTransform ??= new Transform2DComponent(); + var tr = ownerTransform.AbsoluteTransform(); + return rawValue*Complex.Exp(Complex.ImaginaryOne * tr.angle) * tr.scale; + } + + public Tuple OriginAndVector() { + ownerTransform ??= owner.GetComponent(); + ownerTransform ??= new Transform2DComponent(); + var tr = ownerTransform.AbsoluteTransform(); + return Tuple.Create(tr.position + offset*Complex.Exp(Complex.ImaginaryOne * tr.angle) * tr.scale, rawValue*Complex.Exp(Complex.ImaginaryOne * tr.angle) * tr.scale); + } + + public override void Draw(Smoll.Layer.DrawMode drawMode) { + if(drawMode == Layer.DrawMode.Debug) { + var data = OriginAndVector(); + Complex start = data.Item1; + Complex end = data.Item1 + data.Item2; + Raylib.DrawLineEx(new Vector2((float)start.Real, (float)start.Imaginary),new Vector2((float)end.Real, (float)end.Imaginary),2f, Color.Red); + } + } + } +} \ No newline at end of file diff --git a/Expansion2/ExampleShootingInputComponent.cs b/Expansion2/ExampleShootingInputComponent.cs new file mode 100644 index 0000000..4fbdc1d --- /dev/null +++ b/Expansion2/ExampleShootingInputComponent.cs @@ -0,0 +1,36 @@ +using Smoll; +using Raylib_cs; +using System.Numerics; + +namespace Smoll.Ex2 +{ + class ExampleShootingInputComponent : Component { + Transform2DComponent? transform; + ArrowComponent? arrow; + + public ExampleShootingInputComponent() { + } + + public override void OnAttached() + { + base.OnAttached(); + transform = owner.GetComponent(); + arrow = owner.GetComponent(); + } + + public override void Update(float deltaTimeSeconds) + { + transform ??= owner.GetComponent(); + if(transform == null) throw new Exception("Example Input used on immovable object"); + arrow ??= owner.GetComponent(); + if(arrow == null) throw new Exception("Example Shooter used on arrowless object"); + if(Raylib.IsKeyPressed(KeyboardKey.Space)) { + var shoot = new Entity(owner.layers.First()); + var details = arrow.OriginAndVector(); + shoot.Attach(new Transform2DComponent((float)details.Item1.Real,(float)details.Item1.Imaginary,transform.transform.angle, transform.transform.scale)); + shoot.Attach(new TimeToLiveComponent(4f)); + shoot.Attach(new SpriteComponent("Beam.png")); + } + } + } +} \ No newline at end of file diff --git a/Expansion2/RaylibAssetManagerModule.cs b/Expansion2/RaylibAssetManagerModule.cs new file mode 100644 index 0000000..a2f48cf --- /dev/null +++ b/Expansion2/RaylibAssetManagerModule.cs @@ -0,0 +1,35 @@ +using Raylib_cs; + +namespace Smoll.Ex2 +{ + class RaylibAssetManagerModule : IEngineModule { + string path; + + Dictionary images; + Dictionary sounds; + + public RaylibAssetManagerModule(string path = "./assets") { + this.path = path; + images = new Dictionary(); + sounds = new Dictionary(); + } + + public Image GetImage(string name) { + Image image; + if(! images.TryGetValue(name, out image)) { + image = Raylib.LoadImage(path+"/"+name); + images.Add(name, image); + } + return image; + } + + public Sound GetSound(string name) { + Sound sound; + if(! sounds.TryGetValue(name, out sound)) { + sound = Raylib.LoadSound(path+"/"+name); + sounds.Add(name, sound); + } + return sound; + } + } +} \ No newline at end of file diff --git a/Expansion2/SpriteComponent.cs b/Expansion2/SpriteComponent.cs new file mode 100644 index 0000000..104065b --- /dev/null +++ b/Expansion2/SpriteComponent.cs @@ -0,0 +1,37 @@ +using System.Numerics; +using Raylib_cs; + +namespace Smoll.Ex2 { + class SpriteComponent : Smoll.Component , IDisposable{ + Complex offset; + string asset_name; + Transform2DComponent? ownerTransform; + Texture2D texture; + + public SpriteComponent(string asset_name, int offset_x = 0, int offset_y = 0) + { + this.offset = offset_x + Complex.ImaginaryOne*offset_y; + this.asset_name = asset_name; + } + + public override void OnAttached() { + ownerTransform = owner.GetComponent(); + Image image = owner.layers.First().attachedEngine.GetModule().GetImage(asset_name); + texture = Raylib.LoadTextureFromImage(image); + } + + public override void Draw(Smoll.Layer.DrawMode drawMode) { + if(drawMode == Layer.DrawMode.Normal) { + ownerTransform ??= owner.GetComponent(); + ownerTransform ??= new Transform2DComponent(); + var tr = ownerTransform.AbsoluteTransform(); + Raylib.DrawTextureEx(texture, new Vector2((float)tr.position.Real, (float)tr.position.Imaginary), tr.angle*180f/MathF.PI, tr.scale, Color.White); + } + } + + public void Dispose() + { + Raylib.UnloadTexture(texture); + } + } +} \ No newline at end of file diff --git a/Expansion2/TimeToLiveComponent.cs b/Expansion2/TimeToLiveComponent.cs new file mode 100644 index 0000000..4d9a8eb --- /dev/null +++ b/Expansion2/TimeToLiveComponent.cs @@ -0,0 +1,25 @@ +using System.Numerics; +using Raylib_cs; + +namespace Smoll.Ex2 { + class TimeToLiveComponent : Smoll.Component { + float secondsToLive; + float lived = 0f; + + public TimeToLiveComponent(float secondsToLive) + { + this.secondsToLive = secondsToLive; + } + + public override void Update(float deltaTimeSeconds) + { + lived += deltaTimeSeconds; + if(lived > secondsToLive) owner.mustDelete = true; + } + + public override void Draw(Smoll.Layer.DrawMode drawMode) { + if(drawMode == Layer.DrawMode.Debug) { + } + } + } +} \ No newline at end of file diff --git a/Program.cs b/Program.cs index b2b8d2f..8005e82 100644 --- a/Program.cs +++ b/Program.cs @@ -1,24 +1,42 @@ using Raylib_cs; using Smoll; using Smoll.Ex1; +using Smoll.Ex2; + +Raylib.InitWindow(800, 600, "Smoll"); Layer layer = new Layer(); +Layer debugLayer = new Layer(Layer.DrawMode.Debug); + +Engine engine = new Engine(); +engine.AttachModule(new Smoll.Ex2.RaylibAssetManagerModule()); +engine.AttachLayer(layer); +engine.AttachLayer(debugLayer); -var entity = new Entity(layer); -entity.Attach(new Transform2DComponent(128, 128, 0)); -entity.Attach(new RectangleGizmoComponent(128, 64, Color.Blue)); -entity.Attach(new ExampleInputComponent(100f, 0.25f)); -entity.Attach(new CrosshairGizmoComponent()); +/* */ + +var player = new Entity(layer); +debugLayer.Attach(player); +player.Attach(new Transform2DComponent(128, 128, 0, 1)); +player.Attach(new CrosshairGizmoComponent()); +player.Attach(new ExampleInputComponent(100f, 0.25f)); + +var entity = new Entity(player); +entity.Attach(new Transform2DComponent(-16, -16, 0, 4)); +var ship = new SpriteComponent("Ship.png"); +ship.zOrder = 1; +entity.Attach(ship); var entity2 = new Entity(entity); -entity2.Attach(new Transform2DComponent(128, 128, 0)); -entity2.Attach(new RectangleGizmoComponent(64, 64, Color.Red)); -entity2.Attach(new CrosshairGizmoComponent()); +entity2.Attach(new Transform2DComponent(5, -10, 0)); +entity2.Attach(new SpriteComponent("Laser.png")); +var arrow = new ArrowComponent(4, 4); +arrow.rawValue = new System.Numerics.Complex(0, -12); +entity2.Attach(arrow); +entity2.Attach(new ExampleShootingInputComponent()); -Engine engine = new Engine(); -engine.layers.Add(layer); +/* */ -Raylib.InitWindow(800, 600, "Smoll"); while(!Raylib.WindowShouldClose()) { engine.Update(); Raylib.BeginDrawing(); diff --git a/Smoll/Engine.cs b/Smoll/Engine.cs index 7ccc3f7..6e22ab3 100644 --- a/Smoll/Engine.cs +++ b/Smoll/Engine.cs @@ -4,10 +4,20 @@ using System.Collections.Generic; namespace Smoll { public class Engine { public Engine() { + modules = new List(); layers = new List(); } - public List layers; + List layers; + public List modules; + + public void AttachLayer(Layer layer) { + layers.Add(layer); + layer.attachedEngine = this; + } + public void AttachModule(IEngineModule module) { + modules.Add(module); + } public void Update() { foreach (var layer in layers) @@ -22,5 +32,12 @@ namespace Smoll { layer.Draw(); } } + + public T? GetModule() where T : class { + // TODO: Fix this *where* clause + var module = modules.Find((IEngineModule a) => a is T); + if(module == null) return null; + return module as T; + } } } \ No newline at end of file diff --git a/Smoll/Entity.cs b/Smoll/Entity.cs index b0ebea2..bcf022e 100644 --- a/Smoll/Entity.cs +++ b/Smoll/Entity.cs @@ -25,6 +25,7 @@ namespace Smoll { public string name{get;} public bool isEnabled = true; public bool isVisible = true; + public bool mustDelete = false; public Entity(Entity parent) { this.parent = parent; components = new List(); @@ -60,11 +61,12 @@ namespace Smoll { public virtual void Update(float deltaTimeSeconds) { + actionables.RemoveAll(x => x.ScheduledForRemoval()); if(!isEnabled) return; - foreach (var elem in actionables) + for (int i = 0; i < actionables.Count; i++) { - elem.Update(deltaTimeSeconds); - } + actionables[i].Update(deltaTimeSeconds); + } } public virtual void Draw(Layer.DrawMode drawMode) @@ -81,5 +83,9 @@ namespace Smoll { if(component == null) return null; return component as T; } + + public virtual bool ScheduledForRemoval() { + return mustDelete; + } } } \ No newline at end of file diff --git a/Smoll/IActionable.cs b/Smoll/IActionable.cs index fdfc9ac..7b677f8 100644 --- a/Smoll/IActionable.cs +++ b/Smoll/IActionable.cs @@ -8,7 +8,9 @@ namespace Smoll { int IComparable.CompareTo(IActionable? other) { - return other.zOrder - zOrder; + return zOrder - other.zOrder; } + + public virtual bool ScheduledForRemoval() { return false; } } } \ No newline at end of file diff --git a/Smoll/IEngineModule.cs b/Smoll/IEngineModule.cs new file mode 100644 index 0000000..f49c975 --- /dev/null +++ b/Smoll/IEngineModule.cs @@ -0,0 +1,6 @@ +namespace Smoll +{ + public interface IEngineModule { + + } +} \ No newline at end of file diff --git a/Smoll/Layer.cs b/Smoll/Layer.cs index 37584f3..f82f6ab 100644 --- a/Smoll/Layer.cs +++ b/Smoll/Layer.cs @@ -3,11 +3,14 @@ using System.Diagnostics; namespace Smoll { public class Layer { - public Layer() { + public Layer(DrawMode drawMode = DrawMode.Normal) { + this.drawMode = drawMode; actionables = new List(); watch = new Stopwatch(); } + internal Engine attachedEngine; + internal List actionables; public enum DrawMode { @@ -20,6 +23,11 @@ namespace Smoll { Stopwatch watch; + public void Attach(Entity entity) { + entity.layers.Add(this); + actionables.Add(entity); + } + public float Update() { float deltaTime; if(! watch.IsRunning) { @@ -30,10 +38,11 @@ namespace Smoll { watch.Reset(); watch.Start(); } - foreach (var elem in actionables) + actionables.RemoveAll(x => x.ScheduledForRemoval()); + for (int i = 0; i < actionables.Count; i++) { - elem.Update(deltaTime); - } + actionables[i].Update(deltaTime); + } actionables.Sort(); return deltaTime; } diff --git a/Smoll/Transform2D.cs b/Smoll/Transform2D.cs index 19732d9..9377b66 100644 --- a/Smoll/Transform2D.cs +++ b/Smoll/Transform2D.cs @@ -43,9 +43,9 @@ sealed class Transform2DComponent : Component { var component = up.GetComponent(); if(component != null) { Transform2D combined = component.AbsoluteTransform(); - var unscaled_pos = (transform.position)*Complex.Exp(Complex.ImaginaryOne * combined.angle); - combined.position = unscaled_pos + combined.position; combined.scale *= transform.scale; + var scaled_pos = combined.scale*(transform.position)*Complex.Exp(Complex.ImaginaryOne * combined.angle); + combined.position = scaled_pos + combined.position; combined.angle += transform.angle; combined.ForceInvariant(); return combined;