diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f5a0c8e --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +generate-assets-class: + test -f MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Content/MonoGameBlank2dStartKit.mgcb || exit 1 + node ./generate-asset-class.js MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Content/MonoGameBlank2dStartKit.mgcb > MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Content/Assets.cs \ No newline at end of file diff --git a/MonoGameBlank2dStartKit.sln.DotSettings.user b/MonoGameBlank2dStartKit.sln.DotSettings.user index 7448f25..8082b2f 100644 --- a/MonoGameBlank2dStartKit.sln.DotSettings.user +++ b/MonoGameBlank2dStartKit.sln.DotSettings.user @@ -3,4 +3,5 @@ ForceIncluded ForceIncluded ForceIncluded - ForceIncluded \ No newline at end of file + ForceIncluded + ForceIncluded \ No newline at end of file diff --git a/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Components.cs b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Components.cs index a2db485..6053278 100644 --- a/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Components.cs +++ b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Components.cs @@ -1,9 +1,34 @@ +using System; using System.Numerics; +using Microsoft.Xna.Framework.Graphics; using MonoGameBlank2dStartKit.Core.Content; namespace MonoGameBlank2dStartKit.Core; -public readonly record struct Position(Vector2 Vector2); -public readonly record struct Velocity(Vector2 Vector2); +public readonly record struct Position(Vector2 Vector2) +{ + public Position(float x, float y) : this(new Vector2(x, y)) { } +} + +public readonly record struct Velocity(Vector2 Vector2) +{ + public Velocity(float x, float y) : this(new Vector2(x, y)) { } +} + +public readonly record struct Mass(int Value); + +public readonly record struct RotationSpeed(float Speed, float Amount = float.MaxValue, float Drag = 0) +{ +} + +public readonly record struct Rotation(float Value, Vector2 Origin) +{ + public Rotation(float value, Texture2D texture) : this(value, CenterOfTexture(texture)) { } + + private static Vector2 CenterOfTexture(Texture2D texture) + { + return new Vector2((float) texture.Width / 2, (float) texture.Height / 2); + } +} public readonly record struct Image(ImageId ImageId); \ No newline at end of file diff --git a/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Content/Assets.cs b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Content/Assets.cs index 48de28f..7bd9d56 100644 --- a/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Content/Assets.cs +++ b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Content/Assets.cs @@ -1,3 +1,4 @@ + using System; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; @@ -13,8 +14,7 @@ public class Assets var allImageIds = Enum.GetValues(); _images = [ - // TODO: Auto-generate this initialization - content.Load("Images/ball") + content.Load("Images/ball"), ]; } @@ -27,4 +27,4 @@ public class Assets public enum ImageId { Ball -} \ No newline at end of file +} diff --git a/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Content/Images/ball.png b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Content/Images/ball.png index f23ff61..00c9328 100644 Binary files a/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Content/Images/ball.png and b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Content/Images/ball.png differ diff --git a/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/MonoGameBlank2dStartKitGame.cs b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/MonoGameBlank2dStartKitGame.cs index 701c1ed..575baa5 100644 --- a/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/MonoGameBlank2dStartKitGame.cs +++ b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/MonoGameBlank2dStartKitGame.cs @@ -19,6 +19,8 @@ namespace MonoGameBlank2dStartKit.Core public class MonoGameBlank2dStartKitGame : Game { private TextureDrawSystem TextureDrawSystem { get; set; } + private VelocitySystem VelocitySystem { get; set; } + private RotationSystem RotationSystem { get; set; } // Resources for drawing. private readonly GraphicsDeviceManager graphicsDeviceManager; @@ -90,7 +92,9 @@ namespace MonoGameBlank2dStartKit.Core _assets = new Assets(Content); TextureDrawSystem = new TextureDrawSystem(World, _assets, _spriteBatch); - Scenarios.Basic(World); + VelocitySystem = new VelocitySystem(World); + RotationSystem = new RotationSystem(World); + Scenarios.Basic(World, _assets); base.LoadContent(); } @@ -107,7 +111,9 @@ namespace MonoGameBlank2dStartKit.Core || Keyboard.GetState().IsKeyDown(Keys.Escape)) Exit(); - // TODO: Add your update logic here + var elapsedGameTime = gameTime.ElapsedGameTime; + VelocitySystem.Update(elapsedGameTime); + RotationSystem.Update(elapsedGameTime); base.Update(gameTime); } diff --git a/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Scenarios.cs b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Scenarios.cs index cd2e6b6..58ce55a 100644 --- a/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Scenarios.cs +++ b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Scenarios.cs @@ -6,10 +6,13 @@ namespace MonoGameBlank2dStartKit.Core; public static class Scenarios { - public static void Basic(World world) + public static void Basic(World world, Assets assets) { var ball = world.CreateEntity(); world.Set(ball, new Position(new Vector2(10, 10))); world.Set(ball, new Image(ImageId.Ball)); + world.Set(ball, new Velocity(40f, 40f)); + world.Set(ball, new Rotation(0, assets.GetTexture2D(ImageId.Ball))); + world.Set(ball, new RotationSpeed(3f, Drag: 0.03f)); } } \ No newline at end of file diff --git a/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Systems/RotationSystem.cs b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Systems/RotationSystem.cs new file mode 100644 index 0000000..43fc585 --- /dev/null +++ b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Systems/RotationSystem.cs @@ -0,0 +1,42 @@ +using System; +using System.Numerics; +using MonoGameBlank2dStartKit.Core.Utils; +using MoonTools.ECS; + +namespace MonoGameBlank2dStartKit.Core.Systems; + +public class RotationSystem : MoonTools.ECS.System +{ + private readonly Filter _filter; + + public RotationSystem(World world) : base(world) + { + // Doesn't filter on Rotation, because it *should* blow up if RSpeed exists without Rotation. + _filter = FilterBuilder.Include().Build(); + } + + public override void Update(TimeSpan delta) + { + foreach (var entity in _filter.Entities) + { + var currentRotation = World.Get(entity); + var rotationSpeed = World.Get(entity); + float change = rotationSpeed.Speed * delta.SecFloat(); + World.Set(entity, currentRotation with { Value = currentRotation.Value + change }); + + var newSpeed = rotationSpeed.Speed - (rotationSpeed.Drag * delta.SecFloat()); + if (Math.Sign(newSpeed) != Math.Sign(rotationSpeed.Speed)) + { + World.Remove(entity); + } + else + { + World.Set(entity, rotationSpeed with + { + Speed = newSpeed, + Amount = rotationSpeed.Amount - change, + }); + } + } + } +} \ No newline at end of file diff --git a/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Systems/TextureDrawSystem.cs b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Systems/TextureDrawSystem.cs index f15c250..a5ec2d7 100644 --- a/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Systems/TextureDrawSystem.cs +++ b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Systems/TextureDrawSystem.cs @@ -9,23 +9,33 @@ namespace MonoGameBlank2dStartKit.Core.Systems; public class TextureDrawSystem : MoonTools.ECS.System { private readonly Assets _assets; - private readonly Filter _filter; private readonly SpriteBatch _spriteBatch; + private readonly Filter _unrotatedFilter; + private readonly Filter _rotatedFilter; + public TextureDrawSystem(World world, Assets assets, SpriteBatch spriteBatch) : base(world) { _assets = assets; _spriteBatch = spriteBatch; - _filter = FilterBuilder.Include().Include().Build(); + _unrotatedFilter = FilterBuilder.Include().Include().Exclude().Build(); + _rotatedFilter = FilterBuilder.Include().Include().Include().Build(); } public override void Update(TimeSpan delta) { - foreach (var entity in _filter.Entities) + foreach (var entity in _unrotatedFilter.Entities) { var texture = _assets.GetTexture2D(World.Get(entity).ImageId); var position = World.Get(entity).Vector2; _spriteBatch.Draw(texture, position, Color.White); } + foreach (var entity in _rotatedFilter.Entities) + { + var texture = _assets.GetTexture2D(World.Get(entity).ImageId); + var position = World.Get(entity).Vector2; + var rotation = World.Get(entity); + _spriteBatch.Draw(texture, position, null, Color.White, rotation.Value, rotation.Origin, new Vector2(1, 1), SpriteEffects.None, 1); + } } } \ No newline at end of file diff --git a/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Systems/VelocitySystem.cs b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Systems/VelocitySystem.cs new file mode 100644 index 0000000..6c7c26e --- /dev/null +++ b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Systems/VelocitySystem.cs @@ -0,0 +1,30 @@ +using System; +using System.Numerics; +using MonoGameBlank2dStartKit.Core.Utils; +using MoonTools.ECS; + +namespace MonoGameBlank2dStartKit.Core.Systems; + +public class VelocitySystem : MoonTools.ECS.System +{ + private readonly Filter _filter; + + public VelocitySystem(World world) : base(world) + { + _filter = FilterBuilder.Include().Include().Build(); + } + + public override void Update(TimeSpan delta) + { + float deltaSec = delta.SecFloat(); + foreach (var entity in _filter.Entities) + { + var currentPosition = World.Get(entity).Vector2; + var velocity = World.Get(entity).Vector2; + World.Set(entity, new Position( + currentPosition.X + (velocity.X * deltaSec), + currentPosition.Y + (velocity.Y * deltaSec) + )); + } + } +} \ No newline at end of file diff --git a/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Utils/TimeSpanExtensions.cs b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Utils/TimeSpanExtensions.cs new file mode 100644 index 0000000..72a675a --- /dev/null +++ b/MonoGameBlank2dStartKit/MonoGameBlank2dStartKit.Core/Utils/TimeSpanExtensions.cs @@ -0,0 +1,11 @@ +using System; + +namespace MonoGameBlank2dStartKit.Core.Utils; + +public static class TimeSpanExtensions +{ + public static float SecFloat(this TimeSpan span) + { + return (float)span.Ticks / TimeSpan.TicksPerSecond; + } +} \ No newline at end of file diff --git a/generate-asset-class.js b/generate-asset-class.js new file mode 100644 index 0000000..02b1676 --- /dev/null +++ b/generate-asset-class.js @@ -0,0 +1,65 @@ +const fs = require('fs') + +const mgcbFile = process.argv[2] +if (!mgcbFile) { + return 1 +} + +const fileContents = fs.readFileSync(mgcbFile, 'utf8') +/** @type string[] */ +const lines = fileContents.split('\n') + +/** @type {Object.} */ +const types = {} + +lines.forEach(line => { + const prefix = '#begin ' + if (!line.startsWith(prefix)) { + return + } + line = line.substring(prefix.length) + const [type, name] = line.split('/') + types[type] ??= [] + types[type].push(name) +}) + +let imageIdTextureLoads = '' +let imageIdEnumString = '' + +types['Images'].forEach(image => { + const [name, ext] = image.split('.') + imageIdTextureLoads += '\n ' + imageIdTextureLoads += `content.Load("Images/${name}"),` + + imageIdEnumString += '\n ' + imageIdEnumString += name[0].toUpperCase() + name.substring(1) +}) + +console.log(` +using System; +using Microsoft.Xna.Framework.Content; +using Microsoft.Xna.Framework.Graphics; + +namespace MonoGameBlank2dStartKit.Core.Content; + +public class Assets +{ + private readonly Texture2D[] _images; + + public Assets(ContentManager content) + { + var allImageIds = Enum.GetValues(); + _images = + [${imageIdTextureLoads} + ]; + } + + public Texture2D GetTexture2D(ImageId imageId) + { + return _images[(int)imageId]; + } +} + +public enum ImageId +{${imageIdEnumString} +}`) \ No newline at end of file