From 0ec192543d699aa101e162da001e9a6c1f2ee26f Mon Sep 17 00:00:00 2001 From: Dylan Wilson Date: Thu, 31 May 2018 22:12:59 +1000 Subject: [PATCH] refactoring the platformer to use the entity component system --- Source/Demos/Platformer/Collisions/World.cs | 10 +- .../{PlayerComponent.cs => PlayerState.cs} | 2 +- .../Platformer/Components/SpriteComponent.cs | 13 -- .../Components/VelocityComponent.cs | 11 -- Source/Demos/Platformer/Content/test-map.tmx | 2 +- Source/Demos/Platformer/EntityFactory.cs | 30 ++-- Source/Demos/Platformer/GameBase.cs | 9 +- Source/Demos/Platformer/GameMain.cs | 129 ++---------------- .../Demos/Platformer/KeyboardInputService.cs | 35 ----- Source/Demos/Platformer/MainGame.cs | 49 ------- .../Platformer/Systems/MovementSystem.cs | 31 ----- .../Demos/Platformer/Systems/PlayerSystem.cs | 35 ++--- .../Demos/Platformer/Systems/WorldSystem.cs | 56 ++++++++ 13 files changed, 120 insertions(+), 292 deletions(-) rename Source/Demos/Platformer/Components/{PlayerComponent.cs => PlayerState.cs} (66%) delete mode 100644 Source/Demos/Platformer/Components/SpriteComponent.cs delete mode 100644 Source/Demos/Platformer/Components/VelocityComponent.cs delete mode 100644 Source/Demos/Platformer/KeyboardInputService.cs delete mode 100644 Source/Demos/Platformer/MainGame.cs delete mode 100644 Source/Demos/Platformer/Systems/MovementSystem.cs create mode 100644 Source/Demos/Platformer/Systems/WorldSystem.cs diff --git a/Source/Demos/Platformer/Collisions/World.cs b/Source/Demos/Platformer/Collisions/World.cs index 309e35a2..1e0b8739 100644 --- a/Source/Demos/Platformer/Collisions/World.cs +++ b/Source/Demos/Platformer/Collisions/World.cs @@ -27,15 +27,17 @@ namespace Platformer.Collisions public void Update(float deltaTime) { - foreach (var body in Bodies.Where(b => b.BodyType == BodyType.Dynamic)) + var dynamicBodies = Bodies.Where(b => b.BodyType == BodyType.Dynamic).ToArray(); + + foreach (var body in dynamicBodies) { + body.Velocity += Gravity; body.Position += body.Velocity * deltaTime; - body.Velocity += Gravity * deltaTime; } - foreach (var bodyA in Bodies) + foreach (var bodyB in dynamicBodies) { - foreach (var bodyB in Bodies.Where(b => b.BodyType == BodyType.Dynamic)) + foreach (var bodyA in Bodies.Where(b => b.BodyType == BodyType.Static)) { if (bodyA != bodyB && CollisionTester.AabbAabb(bodyA.BoundingBox, bodyB.BoundingBox)) _collisionPairs.Add(new CollisionPair {BodyA = bodyA, BodyB = bodyB}); diff --git a/Source/Demos/Platformer/Components/PlayerComponent.cs b/Source/Demos/Platformer/Components/PlayerState.cs similarity index 66% rename from Source/Demos/Platformer/Components/PlayerComponent.cs rename to Source/Demos/Platformer/Components/PlayerState.cs index d74adf38..5ed82d85 100644 --- a/Source/Demos/Platformer/Components/PlayerComponent.cs +++ b/Source/Demos/Platformer/Components/PlayerState.cs @@ -3,7 +3,7 @@ using MonoGame.Extended.Entities; namespace Platformer.Components { [EntityComponent] - public class PlayerComponent //: EntityComponent + public class PlayerState { } } \ No newline at end of file diff --git a/Source/Demos/Platformer/Components/SpriteComponent.cs b/Source/Demos/Platformer/Components/SpriteComponent.cs deleted file mode 100644 index 1eee2a22..00000000 --- a/Source/Demos/Platformer/Components/SpriteComponent.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.Xna.Framework; -using MonoGame.Extended.Entities; -using MonoGame.Extended.Sprites; - -namespace Platformer.Components -{ - //[EntityComponent] - //public class SpriteComponent //: EntityComponent - //{ - // public Sprite Sprite { get; set; } - // public Color Color { get; set; } - //} -} diff --git a/Source/Demos/Platformer/Components/VelocityComponent.cs b/Source/Demos/Platformer/Components/VelocityComponent.cs deleted file mode 100644 index a0636112..00000000 --- a/Source/Demos/Platformer/Components/VelocityComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.Xna.Framework; -using MonoGame.Extended.Entities; - -namespace Platformer.Components -{ - [EntityComponent] - public class VelocityComponent //: EntityComponent - { - public Vector2 Velocity { get; set; } - } -} \ No newline at end of file diff --git a/Source/Demos/Platformer/Content/test-map.tmx b/Source/Demos/Platformer/Content/test-map.tmx index 9cd837db..27fc0fd4 100644 --- a/Source/Demos/Platformer/Content/test-map.tmx +++ b/Source/Demos/Platformer/Content/test-map.tmx @@ -3,7 +3,7 @@ - eJzFk0EOwCAIBLHgof//cC+YEFJXLMQe5qKrIyqNiNoBrg1kM/fF0QMeMTnvEIUX6z0+w2Zu5kDntBkG2YzD74Nqnr1H5L4Rt4IcWf50RHsHjWX7E/2DasegK5UO1HuVdfCLr8pRwQMekwLo + eJzFlDsSwCAIBTFgkftfOA0FwwxPFKPFNvp0/dKIqB3gmUAmcyuOnvCIyXmHKDwY7/EZNn2RA63TZhhkKw4/D9pzdB+Z80a8CnJUuenI/h3UVgW9g521wL65rux2RJ6/aps9uxP1NMMHET8C2w== diff --git a/Source/Demos/Platformer/EntityFactory.cs b/Source/Demos/Platformer/EntityFactory.cs index bbfe19a9..a92c6cef 100644 --- a/Source/Demos/Platformer/EntityFactory.cs +++ b/Source/Demos/Platformer/EntityFactory.cs @@ -1,7 +1,11 @@ using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Content; +using Microsoft.Xna.Framework.Graphics; using MonoGame.Extended; using MonoGame.Extended.Entities; using MonoGame.Extended.Sprites; +using MonoGame.Extended.TextureAtlases; +using Platformer.Collisions; using Platformer.Components; namespace Platformer @@ -9,28 +13,36 @@ namespace Platformer public class EntityFactory { private readonly EntityManager _entityManager; + private readonly ContentManager _contentManager; - public EntityFactory(EntityManager entityManager) + public EntityFactory(EntityManager entityManager, ContentManager contentManager) { _entityManager = entityManager; + _contentManager = contentManager; } public Entity CreatePlayer(Vector2 position) { + var dudeTexture = _contentManager.Load("hero"); + var dudeAtlas = TextureAtlas.Create("dudeAtlas", dudeTexture, 16, 16); + var entity = _entityManager.CreateEntity(); - entity.Attach(s => s.Color = Color.WhiteSmoke); - entity.Attach(t => t.Position = position); - entity.Attach(); - entity.Attach(); + entity.Attach(new Sprite(dudeAtlas[0])); + entity.Attach(new Transform2(position, 0, Vector2.One * 4)); + entity.Attach(new Body { Position = new Vector2(400, 240), Size = new Vector2(32, 64), BodyType = BodyType.Dynamic }); + entity.Attach(); return entity; } - public Entity CreateTile(int x, int y) + public void CreateTile(int x, int y, int width, int height) { var entity = _entityManager.CreateEntity(); - entity.Attach(s => s.Color = Color.Blue); - entity.Attach(t => t.Position = new Vector2(32 * x, 32 * y)); - return entity; + entity.Attach(new Body + { + Position = new Vector2(x * width + width * 0.5f, y * height + height * 0.5f), + Size = new Vector2(width, height), + BodyType = BodyType.Static + }); } } } \ No newline at end of file diff --git a/Source/Demos/Platformer/GameBase.cs b/Source/Demos/Platformer/GameBase.cs index f0646fe8..97e653ef 100644 --- a/Source/Demos/Platformer/GameBase.cs +++ b/Source/Demos/Platformer/GameBase.cs @@ -5,13 +5,14 @@ using MonoGame.Extended.Entities; namespace Platformer { + // TODO: Are we really benefiting from a base class here? public abstract class GameBase : Game { // ReSharper disable once NotAccessedField.Local protected GraphicsDeviceManager GraphicsDeviceManager { get; } protected IContainer Container { get; private set; } protected EntityComponentSystem EntityComponentSystem { get; private set; } - protected KeyboardInputService KeyboardInputService { get; } + //protected KeyboardInputService KeyboardInputService { get; } public int Width { get; } public int Height { get; } @@ -28,7 +29,7 @@ namespace Platformer IsMouseVisible = true; Window.AllowUserResizing = true; Content.RootDirectory = "Content"; - KeyboardInputService = new KeyboardInputService(); + //KeyboardInputService = new KeyboardInputService(); } protected override void Dispose(bool disposing) @@ -41,7 +42,7 @@ namespace Platformer { var containerBuilder = new ContainerBuilder(); - containerBuilder.RegisterInstance(KeyboardInputService); + //containerBuilder.RegisterInstance(KeyboardInputService); RegisterDependencies(containerBuilder); Container = containerBuilder.Build(); @@ -55,7 +56,7 @@ namespace Platformer protected override void Update(GameTime gameTime) { - KeyboardInputService.Update(); + //KeyboardInputService.Update(); EntityComponentSystem.Update(gameTime); base.Update(gameTime); } diff --git a/Source/Demos/Platformer/GameMain.cs b/Source/Demos/Platformer/GameMain.cs index 8b52574d..6a2656a7 100644 --- a/Source/Demos/Platformer/GameMain.cs +++ b/Source/Demos/Platformer/GameMain.cs @@ -1,26 +1,17 @@ using Autofac; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Input; -using MonoGame.Extended; -using MonoGame.Extended.Entities; -using MonoGame.Extended.Input; -using MonoGame.Extended.Sprites; -using MonoGame.Extended.TextureAtlases; using MonoGame.Extended.Tiled; using MonoGame.Extended.Tiled.Renderers; -using Platformer.Collisions; using Platformer.Systems; namespace Platformer { public class GameMain : GameBase { - private World _world; - private Body _player; private TiledMap _map; private TiledMapRenderer _renderer; - private Entity _playerEntity; + private EntityFactory _entityFactory; public GameMain() { @@ -30,41 +21,18 @@ namespace Platformer { builder.RegisterInstance(new SpriteBatch(GraphicsDevice)); builder.RegisterType(); - builder.RegisterType(); builder.RegisterType(); + builder.RegisterType(); } protected override void LoadContent() { - var dudeTexture = Content.Load("hero"); - var dudeAtlas = TextureAtlas.Create("dudeAtlas", dudeTexture, 16, 16); - var dude = EntityComponentSystem.EntityManager.CreateEntity("dude"); - dude.Attach(new Sprite(dudeAtlas[0])); - dude.Attach(new Transform2(new Vector2(100, 100), 0, Vector2.One * 4)); - - _playerEntity = dude; + _entityFactory = new EntityFactory(EntityComponentSystem.EntityManager, Content); + _entityFactory.CreatePlayer(new Vector2(100, 100)); + // TOOD: Load maps and collision data more nicely :) _map = Content.Load("test-map"); _renderer = new TiledMapRenderer(GraphicsDevice, _map); - - //_spriteBatch = new SpriteBatch(GraphicsDevice); - _world = new World(new Vector2(0, 3600)); - - var floor = new Body - { - Position = new Vector2(400, 480-16), - Size = new Vector2(800, 16), - BodyType = BodyType.Static - }; - _world.Bodies.Add(floor); - - _player = new Body - { - Position = new Vector2(400, 240), - Size = new Vector2(32, 64), - BodyType = BodyType.Dynamic - }; - _world.Bodies.Add(_player); foreach (var tileLayer in _map.TileLayers) { @@ -78,14 +46,7 @@ namespace Platformer { var tileWidth = _map.TileWidth; var tileHeight = _map.TileHeight; - var block = new Body - { - Position = new Vector2(x * tileWidth + tileWidth * 0.5f, y * tileHeight + tileHeight * 0.5f), - Size = new Vector2(tileWidth, tileHeight), - BodyType = BodyType.Static - }; - _world.Bodies.Add(block); - + _entityFactory.CreateTile(x, y, tileWidth, tileHeight); } } } @@ -94,90 +55,22 @@ namespace Platformer protected override void Update(GameTime gameTime) { - var deltaTime = gameTime.GetElapsedSeconds(); - var currentKeyboardState = KeyboardExtended.GetState(); - //var mouseState = Mouse.GetState(); + // TODO: Using global shared input state is really bad! - if (currentKeyboardState.IsKeyDown(Keys.Escape)) - Exit(); + //var keyboardState = KeyboardExtended.GetState(); + + //if (keyboardState.IsKeyDown(Keys.Escape)) + // Exit(); _renderer.Update(gameTime); - _world.Update(deltaTime); - _world.OnCollision = OnCollision; - - //_player.Velocity = new Vector2(0, _player.Velocity.Y); - - if (KeyboardInputService.IsKeyDown(Keys.Right)) - _player.Velocity.X += 1000 * deltaTime; - else if (KeyboardInputService.IsKeyDown(Keys.Left)) - _player.Velocity.X -= 1000 * deltaTime; - else - _player.Velocity.X = _player.Velocity.Y * 0.99f * deltaTime; - - if (currentKeyboardState.WasKeyJustUp(Keys.Up)) - _player.Velocity = new Vector2(_player.Velocity.X, -900); - - //if (mouseState.LeftButton == ButtonState.Pressed) - //{ - // var mousePosition = new Vector2(mouseState.X, mouseState.Y); - // _mouseBox = !_mouseBox.HasValue ? new AABB(mousePosition, mousePosition) : new AABB(_mouseBox.Value.Min, mousePosition); - //} - //else - //{ - // if (_mouseBox.HasValue) - // { - // var box = _mouseBox.Value; - // _world.Bodies.Add(new Body - // { - // BodyType = BodyType.Static, - // Position = box.Min + box.Center, - // Size = new Vector2(box.Width, box.Height) - // }); - // _mouseBox = null; - // } - //} - - _playerEntity.Get().Position = _player.Position; - base.Update(gameTime); } - //private AABB? _mouseBox; - - private static void OnCollision(Manifold manifold) - { - var body = manifold.BodyB.BodyType == BodyType.Dynamic ? manifold.BodyB : manifold.BodyA; - - body.Position -= manifold.Normal * manifold.Penetration; - - if (manifold.Normal.Y < 0 || manifold.Normal.Y > 0) - body.Velocity = new Vector2(body.Velocity.X, 0); - - //if (manifold.Normal.X < 0 || manifold.Normal.X > 0) - // body.Velocity = new Vector2(0, body.Velocity.Y); - } - protected override void Draw(GameTime gameTime) { _renderer.Draw(); - //_spriteBatch.Begin(); - - //foreach (var body in _world.Bodies.Where(b => b.BodyType == BodyType.Dynamic)) - //{ - // var box = body.BoundingBox; - // _spriteBatch.FillRectangle(new RectangleF(box.Min.X, box.Min.Y, box.Width, box.Height), body.BodyType == BodyType.Static ? Color.WhiteSmoke : Color.Green); - //} - - //if (_mouseBox.HasValue) - //{ - // var box = _mouseBox.Value; - // _spriteBatch.DrawRectangle(new RectangleF(box.Min.X, box.Min.Y, box.Width, box.Height), Color.Magenta); - //} - - //_spriteBatch.End(); - base.Draw(gameTime); } } diff --git a/Source/Demos/Platformer/KeyboardInputService.cs b/Source/Demos/Platformer/KeyboardInputService.cs deleted file mode 100644 index 68479fee..00000000 --- a/Source/Demos/Platformer/KeyboardInputService.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Microsoft.Xna.Framework.Input; - -namespace Platformer -{ - public class KeyboardInputService - { - public KeyboardInputService() - { - } - - public KeyboardState CurrentState { get; private set; } - public KeyboardState PreviousState { get; private set; } - - public bool IsKeyDown(Keys key) - { - return CurrentState.IsKeyDown(key); - } - - public bool IsKeyUp(Keys key) - { - return CurrentState.IsKeyUp(key); - } - - public bool IsKeyTapped(Keys key) - { - return PreviousState.IsKeyDown(key) && CurrentState.IsKeyUp(key); - } - - public void Update() - { - PreviousState = CurrentState; - CurrentState = Keyboard.GetState(); - } - } -} \ No newline at end of file diff --git a/Source/Demos/Platformer/MainGame.cs b/Source/Demos/Platformer/MainGame.cs deleted file mode 100644 index c00d42ef..00000000 --- a/Source/Demos/Platformer/MainGame.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Input; - -namespace Platformer -{ - public class MainGame : Game - { - private GraphicsDeviceManager _graphicsDeviceManager; - private SpriteBatch _spriteBatch; - - public MainGame() - { - _graphicsDeviceManager = new GraphicsDeviceManager(this); - Content.RootDirectory = "Content"; - IsMouseVisible = true; - } - - protected override void LoadContent() - { - _spriteBatch = new SpriteBatch(GraphicsDevice); - } - - protected override void UnloadContent() - { - _spriteBatch.Dispose(); - } - - protected override void Update(GameTime gameTime) - { - var keyboardState = Keyboard.GetState(); - - if (keyboardState.IsKeyDown(Keys.Escape)) - Exit(); - - base.Update(gameTime); - } - - protected override void Draw(GameTime gameTime) - { - GraphicsDevice.Clear(Color.CornflowerBlue); - - _spriteBatch.Begin(samplerState: SamplerState.PointClamp); - _spriteBatch.End(); - - base.Draw(gameTime); - } - } -} diff --git a/Source/Demos/Platformer/Systems/MovementSystem.cs b/Source/Demos/Platformer/Systems/MovementSystem.cs deleted file mode 100644 index 57081661..00000000 --- a/Source/Demos/Platformer/Systems/MovementSystem.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.Xna.Framework; -using MonoGame.Extended; -using MonoGame.Extended.Entities; -using Platformer.Components; - -namespace Platformer.Systems -{ - [Aspect(AspectType.All, typeof(VelocityComponent), typeof(Transform2))] - [EntitySystem(GameLoopType.Update, Layer = 0)] - public class MovementSystem : EntityProcessingSystem - { - protected override void Process(GameTime gameTime, Entity entity) - { - var movement = entity.Get(); - var transform = entity.Get(); - - var elapsedSeconds = gameTime.GetElapsedSeconds(); - - transform.Position += movement.Velocity * elapsedSeconds; - movement.Velocity += new Vector2(0, 1600) * elapsedSeconds; - - const int floor = 480 - 64; - - if (transform.Position.Y >= floor) - { - movement.Velocity = new Vector2(movement.Velocity.X, 0); - transform.Position = new Vector2(transform.Position.X, floor); - } - } - } -} \ No newline at end of file diff --git a/Source/Demos/Platformer/Systems/PlayerSystem.cs b/Source/Demos/Platformer/Systems/PlayerSystem.cs index 158094ab..232bbf2d 100644 --- a/Source/Demos/Platformer/Systems/PlayerSystem.cs +++ b/Source/Demos/Platformer/Systems/PlayerSystem.cs @@ -1,35 +1,38 @@ +using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; +using MonoGame.Extended; using MonoGame.Extended.Entities; +using MonoGame.Extended.Input; +using Platformer.Collisions; using Platformer.Components; namespace Platformer.Systems { - [Aspect(AspectType.All, typeof(VelocityComponent), typeof(PlayerComponent))] + [Aspect(AspectType.All, typeof(Body), typeof(PlayerState), typeof(Transform2))] [EntitySystem(GameLoopType.Update, Layer = 0)] public class PlayerSystem : EntityProcessingSystem { - private readonly KeyboardInputService _inputService; - - public PlayerSystem(KeyboardInputService inputService) - { - _inputService = inputService; - } - protected override void Process(GameTime gameTime, Entity entity) { - var movement = entity.Get(); + var player = entity.Get(); + var transform = entity.Get(); + var body = entity.Get(); + var keyboardState = KeyboardExtended.GetState(); - movement.Velocity = new Vector2(0, movement.Velocity.Y); + if (keyboardState.IsKeyDown(Keys.Right)) + body.Velocity.X += 150; - if (_inputService.IsKeyDown(Keys.Left) || _inputService.IsKeyDown(Keys.A)) - movement.Velocity = new Vector2(-260, movement.Velocity.Y); + if (keyboardState.IsKeyDown(Keys.Left)) + body.Velocity.X -= 150; - if (_inputService.IsKeyDown(Keys.Right) || _inputService.IsKeyDown(Keys.D)) - movement.Velocity = new Vector2(260, movement.Velocity.Y); + if (keyboardState.WasKeyJustUp(Keys.Up)) + body.Velocity.Y -= 550 + Math.Abs(body.Velocity.X) * 0.4f; - if (_inputService.IsKeyTapped(Keys.Up) || _inputService.IsKeyTapped(Keys.W)) - movement.Velocity = new Vector2(movement.Velocity.X, -600); + body.Velocity.X *= 0.7f; + + // TODO: Can we remove this? + transform.Position = body.Position; } } } \ No newline at end of file diff --git a/Source/Demos/Platformer/Systems/WorldSystem.cs b/Source/Demos/Platformer/Systems/WorldSystem.cs new file mode 100644 index 00000000..d1cbf192 --- /dev/null +++ b/Source/Demos/Platformer/Systems/WorldSystem.cs @@ -0,0 +1,56 @@ +using Microsoft.Xna.Framework; +using MonoGame.Extended; +using MonoGame.Extended.Entities; +using Platformer.Collisions; + +namespace Platformer.Systems +{ + [Aspect(AspectType.All, typeof(Body))] + [EntitySystem(GameLoopType.Update, Layer = 0)] + public class WorldSystem : EntityProcessingSystem + { + private readonly World _world; + + public WorldSystem() + { + _world = new World(new Vector2(0, 60)) {OnCollision = OnCollision}; + } + + public override void OnEntityAdded(Entity entity) + { + var body = entity.Get(); + _world.Bodies.Add(body); + } + + public override void OnEntityRemoved(Entity entity) + { + var body = entity.Get(); + _world.Bodies.Remove(body); + } + + protected override void Process(GameTime gameTime) + { + var elapsedSeconds = gameTime.GetElapsedSeconds(); + _world.Update(elapsedSeconds); + + base.Process(gameTime); + } + + protected override void Process(GameTime gameTime, Entity entity) + { + } + + private static void OnCollision(Manifold manifold) + { + var body = manifold.BodyB.BodyType == BodyType.Dynamic ? manifold.BodyB : manifold.BodyA; + + body.Position -= manifold.Normal * manifold.Penetration; + + if (manifold.Normal.Y < 0 || manifold.Normal.Y > 0) + body.Velocity.Y = 0; + + //if (manifold.Normal.X < 0 || manifold.Normal.X > 0) + // body.Velocity = new Vector2(0, body.Velocity.Y); + } + } +} \ No newline at end of file