From ed508d8b8cc261d841e251a7581ebb1efb2ccd18 Mon Sep 17 00:00:00 2001 From: Dylan Wilson Date: Sat, 9 Jun 2018 23:09:06 +1000 Subject: [PATCH] making progress on the new ecs framework --- Source/Demos/Platformer/EntityFactory.cs | 1 - Source/Demos/Platformer/GameMain.cs | 1 - .../Demos/Platformer/Systems/PlayerSystem.cs | 1 - .../Demos/Platformer/Systems/RenderSystem.cs | 1 - .../Demos/Platformer/Systems/WorldSystem.cs | 1 - Source/Demos/Sandbox/Content/Content.mgcb | 54 ---- Source/Demos/Sandbox/Content/court.png | Bin 274 -> 0 bytes Source/Demos/Sandbox/Content/paddleBlue.png | Bin 821 -> 0 bytes Source/Demos/Sandbox/Content/paddleRed.png | Bin 751 -> 0 bytes Source/Demos/Sandbox/Content/pip.wav | Bin 12634 -> 0 bytes Source/Demos/Sandbox/Content/title-screen.png | Bin 18058 -> 0 bytes Source/Demos/Sandbox/GameMain.cs | 188 +++++--------- Source/Demos/Sandbox/GameObjects/Ball.cs | 9 - .../Demos/Sandbox/GameObjects/GameObject.cs | 15 -- Source/Demos/Sandbox/GameObjects/Paddle.cs | 6 - Source/Demos/Sandbox/Program.cs | 4 +- Source/Demos/Sandbox/Sandbox.csproj | 1 + .../Demos/Sandbox/Screens/PongGameScreen.cs | 240 ------------------ Source/Demos/Sandbox/Screens/TitleScreen.cs | 49 ---- .../ComponentManager.cs | 40 ++- .../ComponentMapper.cs | 22 +- .../ComponentType.cs | 73 +++--- Source/MonoGame.Extended.Entities/Entity.cs | 73 ++++++ .../EntityManager.cs | 37 +++ .../MonoGame.Extended.Entities/EntityWorld.cs | 64 +++++ .../Systems/BaseSystem.cs | 8 + .../Systems/DrawSystem.cs | 13 + .../Systems/UpdateSystem.cs | 13 + .../ComponentMapperTests.cs | 30 ++- .../ComponentTypeTests.cs | 26 +- 30 files changed, 386 insertions(+), 584 deletions(-) delete mode 100644 Source/Demos/Sandbox/Content/court.png delete mode 100644 Source/Demos/Sandbox/Content/paddleBlue.png delete mode 100644 Source/Demos/Sandbox/Content/paddleRed.png delete mode 100644 Source/Demos/Sandbox/Content/pip.wav delete mode 100644 Source/Demos/Sandbox/Content/title-screen.png delete mode 100644 Source/Demos/Sandbox/GameObjects/Ball.cs delete mode 100644 Source/Demos/Sandbox/GameObjects/GameObject.cs delete mode 100644 Source/Demos/Sandbox/GameObjects/Paddle.cs delete mode 100644 Source/Demos/Sandbox/Screens/PongGameScreen.cs delete mode 100644 Source/Demos/Sandbox/Screens/TitleScreen.cs create mode 100644 Source/MonoGame.Extended.Entities/Entity.cs create mode 100644 Source/MonoGame.Extended.Entities/EntityManager.cs create mode 100644 Source/MonoGame.Extended.Entities/EntityWorld.cs create mode 100644 Source/MonoGame.Extended.Entities/Systems/BaseSystem.cs create mode 100644 Source/MonoGame.Extended.Entities/Systems/DrawSystem.cs create mode 100644 Source/MonoGame.Extended.Entities/Systems/UpdateSystem.cs diff --git a/Source/Demos/Platformer/EntityFactory.cs b/Source/Demos/Platformer/EntityFactory.cs index db2caa11..959b3a94 100644 --- a/Source/Demos/Platformer/EntityFactory.cs +++ b/Source/Demos/Platformer/EntityFactory.cs @@ -4,7 +4,6 @@ using Microsoft.Xna.Framework.Graphics; using MonoGame.Extended; using MonoGame.Extended.Animations; using MonoGame.Extended.Animations.SpriteSheets; -using MonoGame.Extended.Entities; using MonoGame.Extended.Entities.Legacy; using MonoGame.Extended.TextureAtlases; using Platformer.Collisions; diff --git a/Source/Demos/Platformer/GameMain.cs b/Source/Demos/Platformer/GameMain.cs index 35db88ac..a90a0186 100644 --- a/Source/Demos/Platformer/GameMain.cs +++ b/Source/Demos/Platformer/GameMain.cs @@ -2,7 +2,6 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MonoGame.Extended; -using MonoGame.Extended.Entities; using MonoGame.Extended.Entities.Legacy; using MonoGame.Extended.Tiled; using MonoGame.Extended.Tiled.Renderers; diff --git a/Source/Demos/Platformer/Systems/PlayerSystem.cs b/Source/Demos/Platformer/Systems/PlayerSystem.cs index a80811f7..3cc1ee23 100644 --- a/Source/Demos/Platformer/Systems/PlayerSystem.cs +++ b/Source/Demos/Platformer/Systems/PlayerSystem.cs @@ -4,7 +4,6 @@ using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using MonoGame.Extended; using MonoGame.Extended.Animations; -using MonoGame.Extended.Entities; using MonoGame.Extended.Entities.Legacy; using MonoGame.Extended.Input; using Platformer.Collisions; diff --git a/Source/Demos/Platformer/Systems/RenderSystem.cs b/Source/Demos/Platformer/Systems/RenderSystem.cs index a76f7d55..4dd045c1 100644 --- a/Source/Demos/Platformer/Systems/RenderSystem.cs +++ b/Source/Demos/Platformer/Systems/RenderSystem.cs @@ -2,7 +2,6 @@ using Microsoft.Xna.Framework.Graphics; using MonoGame.Extended; using MonoGame.Extended.Animations; -using MonoGame.Extended.Entities; using MonoGame.Extended.Entities.Legacy; using MonoGame.Extended.Sprites; diff --git a/Source/Demos/Platformer/Systems/WorldSystem.cs b/Source/Demos/Platformer/Systems/WorldSystem.cs index faa0189a..73fac24c 100644 --- a/Source/Demos/Platformer/Systems/WorldSystem.cs +++ b/Source/Demos/Platformer/Systems/WorldSystem.cs @@ -1,6 +1,5 @@ using Microsoft.Xna.Framework; using MonoGame.Extended; -using MonoGame.Extended.Entities; using MonoGame.Extended.Entities.Legacy; using Platformer.Collisions; diff --git a/Source/Demos/Sandbox/Content/Content.mgcb b/Source/Demos/Sandbox/Content/Content.mgcb index 3b1872f9..f390b809 100644 --- a/Source/Demos/Sandbox/Content/Content.mgcb +++ b/Source/Demos/Sandbox/Content/Content.mgcb @@ -26,18 +26,6 @@ /processorParam:TextureFormat=Color /build:ballGrey.png -#begin court.png -/importer:TextureImporter -/processor:TextureProcessor -/processorParam:ColorKeyColor=255,0,255,255 -/processorParam:ColorKeyEnabled=True -/processorParam:GenerateMipmaps=False -/processorParam:PremultiplyAlpha=True -/processorParam:ResizeToPowerOfTwo=False -/processorParam:MakeSquare=False -/processorParam:TextureFormat=Color -/build:court.png - #begin kenney-rocket-square.fnt /importer:BitmapFontImporter /processor:BitmapFontProcessor @@ -55,45 +43,3 @@ /processorParam:TextureFormat=Color /build:kenney-rocket-square_0.png -#begin paddleBlue.png -/importer:TextureImporter -/processor:TextureProcessor -/processorParam:ColorKeyColor=255,0,255,255 -/processorParam:ColorKeyEnabled=True -/processorParam:GenerateMipmaps=False -/processorParam:PremultiplyAlpha=True -/processorParam:ResizeToPowerOfTwo=False -/processorParam:MakeSquare=False -/processorParam:TextureFormat=Color -/build:paddleBlue.png - -#begin paddleRed.png -/importer:TextureImporter -/processor:TextureProcessor -/processorParam:ColorKeyColor=255,0,255,255 -/processorParam:ColorKeyEnabled=True -/processorParam:GenerateMipmaps=False -/processorParam:PremultiplyAlpha=True -/processorParam:ResizeToPowerOfTwo=False -/processorParam:MakeSquare=False -/processorParam:TextureFormat=Color -/build:paddleRed.png - -#begin pip.wav -/importer:WavImporter -/processor:SoundEffectProcessor -/processorParam:Quality=Best -/build:pip.wav - -#begin title-screen.png -/importer:TextureImporter -/processor:TextureProcessor -/processorParam:ColorKeyColor=255,0,255,255 -/processorParam:ColorKeyEnabled=True -/processorParam:GenerateMipmaps=False -/processorParam:PremultiplyAlpha=True -/processorParam:ResizeToPowerOfTwo=False -/processorParam:MakeSquare=False -/processorParam:TextureFormat=Color -/build:title-screen.png - diff --git a/Source/Demos/Sandbox/Content/court.png b/Source/Demos/Sandbox/Content/court.png deleted file mode 100644 index 06f4a523e6de5a357f57d46f99b9585ef42dc3e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^0YGfP!2~2Z+p`t`DaPU;cPEB*=VV?2IV|apzK#qG z8~eHcB(j3k6nI1yGcfQS24TkI`72U@f|4b!5hcO-X(i=}MX3x0iJ5sNdU>fO3MP66 zdPasH6*8v+6*YOfIEG|6zrD1O7igdBL93F|@Ao%*p5{^Ldmr#*(sriV>~^xZ^DD39 z9Z>yU`ZRDihZ}?X(9wA9vuh`{uUHZk_SN!hG+Y>8^bcguk z9VO1YUkLQAu=q2R$NAU1wcLEaR?ieyaN;&!OIHiWVOqg{yG9^kw|?pYvOvUbnoEsM|rV4}XhR{9*31 zixKG=EU~So+RN{liP6 zy5H!ZhZ3YP=d0W1$>oPO z#Pc7XHL=iqZ>sUb7~gYUyIx2s&$`~R$XD6?#)Ce_h>Kg3j~?;fxu$Z?eEEv{#JA7d zIkWt<4>(jOO*_AJ>y-Yhp}A%D=F5L?Fu2sXY~uCO>+G+B#cnseSieSv|LCH3?QB~X ztUS8X#Nxu58Qjwu59H1BQngjny?W(}(*|MJ_KF8y&O46eZkYV#LZI5;_D5wW4&K_~ zk{htjyI5?;rxLEa^gY)FRm}$CEy6+q1^>V2TujVS*s@v?~ez zf4Q1=XI<^S9(9)c5^vPnFea#7x=%s#V}LE=1*t_!_$pU%%=_y@GSXs}o(f zZXD7$`T0@Ot@aYmmwz}+0*|rsbT16}(|*W^Wv$GU^qw^g!ex(&)ppdnr=1HZ%vx2J z$->>vs(fVarnDVhFBUU@?MeugHg#!xJ>%YtK&hTO@n-HO{oi)`S8p~lO#~)R22WQ% Jmvv4FO#oMPMV9~o diff --git a/Source/Demos/Sandbox/Content/pip.wav b/Source/Demos/Sandbox/Content/pip.wav deleted file mode 100644 index ee26c6d5e407e42ee9a2427300ad56807dcc7141..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12634 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 diff --git a/Source/Demos/Sandbox/Content/title-screen.png b/Source/Demos/Sandbox/Content/title-screen.png deleted file mode 100644 index cdf228a0df89e805d199f07958299a29e6a113eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18058 zcmeIZWk8fs*ETwosE8;C2neWvNK1Dp2+~S-NVl{ML#d!B4MT@?hYUS1pdj7dqtY>; zLk>0H=6Rp*{6Bxsd4DKy$KGqLy<*?%x_0PQ`}~T;iCq0srUy?~VUYn`E6Ppl>8fbOcp+f_9tS zZY89v?t?(QzAg@p{6g9fQH*>)9r>0vvi{TFQRXCkjQZp5^A@hUYcUUyHz0C@<%)BQ zN=S1BQdD%9n-WV>?vFpv#1f}tJ)yr3>X`0J0!Ip#qC38efl`x8e~y|D*D#BGG&uWr zd8tEJy47a#hIKbD5oGugoL5_O|MOz_*2uAzkhD{-mOw6TPDbVS7{PzUv96rAaoj~z znilXnuM1GmchF%nZOn-NM+k!zUhSb)pOmKM9dK*hL}*{^@JI0WH1J-nB9m2rlT86{ zHp`@9?t5=<9hA-LlUe@+bH=o#9x9^oMR!UD*XdZ|-DWiivp&e4R)<7fzL+|GDn5HE zL@&);rU|8qS{UboufmB8zX8(-$&XG9FWEbDa1KtZ#dEP&D!g~Yd?BrV4Fc%zzzjE!sC_ zGap<^1D8Gs{(V~Mi3!S^YQBjcQ9kJ>l1N(4>mgyti-`SV#P)?(d-^jF=YA}T8WSYF z&nf{Em2l^wm$fUEkK}popE;wKp-MQ};Ph~fN?6#ERq_h{pQmILx+-VDQv-@0cV@yeoVwENm*W-`t^SnJoC%SK z@O%gBn>uwWPLHx#dOklgHzHbE=khU5%@13$AZO*M!Cu7w-=`?WVeF}&*={UNNA7I= zTR3Ht@ELQpm=7Vv3?&dTAE>MhFRgboVjhSdPs%{94)GGnO>LNtSZyxaja=;3s_GCtE{Wi4`~hqP3ir=+!t`3z*Qm*5KiXFtcpmE4qJ+15wHLx#z9{#T z=Sapqx`d(e>ySOY53gUm6MC^JY9XR7po;u`42A38jKhBtZ_Tr8_FoGLyJbmhpdco- z3S7Xb>-XTC+7!Z^8BJw~vgF+2u?}&}TNEZB7-!(qGJIw|=@yYOwt1z%axE_Jq1)FP z#ieEVt)yj@&?Vp2VMKI$eeP{snYNk$kNMpK^wU+as>@pVLM?c4m&DR;PboCJ>z*(b zSHDtMrIMuCoSk!`W`0-?NsF2T5K~#oszOa1irR?x0RbLe)0Y67q^j6^Hy{CvmcVvr znqCR^+2Zm|ftH--N1-2k=cXXKx~mFobG4IHNJzBOr`PdCPY23ohE>RYfj6)*k8#)) zNR1UWmuv_Kqn3BZ;fsIENAHb(qb0hmUtdGjl8Q$nO$&@1HfwA{g`|gVjH6C;92+L2 zwds$7MpF-LiO8?1$wwc2Z^;q`3FFs<4JX-nlG3G}y1P?-d^BckIJLDsp+C3>l*{Cg z=J%MHTPu><)Y@-OAU1W2E&r&^d3c)s_l>K0zRxw);Yc-ct+8KvB-fqsubgVrsOjVP@@K4GyGV{#=t~HFcec8y zZ9qxYJw)EHP~AK?DY?v3s zc9W}#(Fe=A7=Dp`10o^ssu*$PjjwYD<*;(8eN(UPro%=V?^l&8jIgiqE%x}WH~da$ zD$MTdo8B(33?dYHpLbO{=<@7ohDizF=n87r*Ikf5#Qf^1eD``8dJ@=8CR&Z0u@O

RdA8%I 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 _transformMapper; - private const int _defaultSize = 32; - - public EcsAspect() + public MyRenderSystem(GraphicsDevice graphicsDevice) { - _allSet = new BitArray(_defaultSize); - _exclusionSet = new BitArray(_defaultSize); - _oneSet = new BitArray(_defaultSize); + _spriteBatch = new SpriteBatch(graphicsDevice); } - public void All(bool[] set) + public override void Initialize(ComponentManager componentManager) { - for (var i = 0; i < set.Length; i++) - _allSet.Set(i, set[i]); - } - - public bool IsInterested(EcsEntity entity) - { - return IsInterested(entity.ComponentBits); - } - - public bool IsInterested(BitArray componentBits) - { - if (componentBits.And(_allSet) == componentBits) - return true; - - return false; - } - } - - public abstract class EcsSystem - { - public virtual void Update(GameTime gameTime) { } - public virtual void Draw(GameTime gameTime) { } - } - - public class EcsEntity - { - internal EcsEntity() - { - ComponentBits = new BitArray(32); - } - - public BitArray ComponentBits { get; } - - public void Attach() - where T : class - { - } - - public void Attach(T component) - where T : class - { - } - - public void Detach() - { - } - - public T Get() - { - throw new NotImplementedException(); - } - } - - public class EcsWorld : DrawableGameComponent - { - private readonly List _entities = new List(); - private readonly List _systems = new List(); - - public EcsWorld(Game game) - : base(game) - { - } - - public EcsEntity CreateEntity() - { - var entity = new EcsEntity(); - _entities.Add(entity); - return entity; - } - - public void DestroyEntity(EcsEntity entity) - { - _entities.Remove(entity); - } - - public void AddSystem(EcsSystem system) - { - _systems.Add(system); - } - - public void RemoveSystem(EcsSystem system) - { - _systems.Remove(system); - } - - public override void Update(GameTime gameTime) - { - foreach (var system in _systems) - system.Update(gameTime); + _transformMapper = componentManager.GetMapper(); } public override void Draw(GameTime gameTime) { - foreach (var system in _systems) - system.Draw(gameTime); + _spriteBatch.Begin(samplerState: SamplerState.PointClamp); + + foreach (var entity in World.EntityManager.Entities) + { + var transform = _transformMapper.GetComponent(entity); + + _spriteBatch.DrawRectangle(transform.Position, new Size2(100, 100), Color.Black); + } + + _spriteBatch.End(); } + } - public class GameMain : Game + public class MainGame : Game { // ReSharper disable once NotAccessedField.Local - private readonly GraphicsDeviceManager _graphics; - private readonly ScreenManager _screenManager; + private GraphicsDeviceManager _graphicsDeviceManager; + private SpriteBatch _spriteBatch; + private EntityWorld _world; - public GameMain() + public MainGame() { - _graphics = new GraphicsDeviceManager(this) - { - PreferredBackBufferWidth = 800, - PreferredBackBufferHeight = 480, - SynchronizeWithVerticalRetrace = false - }; - + _graphicsDeviceManager = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; - IsFixedTimeStep = true; - TargetElapsedTime = TimeSpan.FromSeconds(1f / 60f); - - _screenManager = Components.Add(); + IsMouseVisible = true; } protected override void LoadContent() { - base.LoadContent(); + _spriteBatch = new SpriteBatch(GraphicsDevice); + _world = new EntityWorld(); + _world.RegisterSystem(new MyRenderSystem(GraphicsDevice)); - _screenManager.LoadScreen(new TitleScreen(this), new FadeTransition(GraphicsDevice, Color.Black, 0.5f)); + var entity = _world.CreateEntity(); + entity.Attach(new Transform2(new Vector2(400, 240))); + + } + + protected override void UnloadContent() + { + _spriteBatch.Dispose(); + } + + protected override void Update(GameTime gameTime) + { + var keyboardState = Keyboard.GetState(); + + if (keyboardState.IsKeyDown(Keys.Escape)) + Exit(); + + _world.Update(gameTime); + + base.Update(gameTime); + } + + protected override void Draw(GameTime gameTime) + { + GraphicsDevice.Clear(Color.CornflowerBlue); + + _world.Draw(gameTime); + + base.Draw(gameTime); } } } diff --git a/Source/Demos/Sandbox/GameObjects/Ball.cs b/Source/Demos/Sandbox/GameObjects/Ball.cs deleted file mode 100644 index d0016b0b..00000000 --- a/Source/Demos/Sandbox/GameObjects/Ball.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Microsoft.Xna.Framework; - -namespace Sandbox.GameObjects -{ - public class Ball : GameObject - { - public Vector2 Velocity; - } -} \ No newline at end of file diff --git a/Source/Demos/Sandbox/GameObjects/GameObject.cs b/Source/Demos/Sandbox/GameObjects/GameObject.cs deleted file mode 100644 index 204932e9..00000000 --- a/Source/Demos/Sandbox/GameObjects/GameObject.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Microsoft.Xna.Framework; -using MonoGame.Extended; -using MonoGame.Extended.Sprites; - -namespace Sandbox.GameObjects -{ - public class GameObject - { - public Vector2 Position; - public Vector2 Scale = Vector2.One; - public float Rotation; - public Sprite Sprite; - public RectangleF BoundingRectangle => Sprite.GetBoundingRectangle(Position, 0, Vector2.One); - } -} \ No newline at end of file diff --git a/Source/Demos/Sandbox/GameObjects/Paddle.cs b/Source/Demos/Sandbox/GameObjects/Paddle.cs deleted file mode 100644 index d59a641b..00000000 --- a/Source/Demos/Sandbox/GameObjects/Paddle.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Sandbox.GameObjects -{ - public class Paddle : GameObject - { - } -} \ No newline at end of file diff --git a/Source/Demos/Sandbox/Program.cs b/Source/Demos/Sandbox/Program.cs index e4bb48f1..fc856a78 100644 --- a/Source/Demos/Sandbox/Program.cs +++ b/Source/Demos/Sandbox/Program.cs @@ -5,9 +5,9 @@ namespace Sandbox public static class Program { [STAThread] - private static void Main() + public static void Main() { - using (var game = new GameMain()) + using (var game = new MainGame()) game.Run(); } } diff --git a/Source/Demos/Sandbox/Sandbox.csproj b/Source/Demos/Sandbox/Sandbox.csproj index 30f19912..1e77675a 100644 --- a/Source/Demos/Sandbox/Sandbox.csproj +++ b/Source/Demos/Sandbox/Sandbox.csproj @@ -10,6 +10,7 @@ + diff --git a/Source/Demos/Sandbox/Screens/PongGameScreen.cs b/Source/Demos/Sandbox/Screens/PongGameScreen.cs deleted file mode 100644 index f6cbd7c6..00000000 --- a/Source/Demos/Sandbox/Screens/PongGameScreen.cs +++ /dev/null @@ -1,240 +0,0 @@ -using System; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Audio; -using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Input; -using MonoGame.Extended; -using MonoGame.Extended.BitmapFonts; -using MonoGame.Extended.Input; -using MonoGame.Extended.Screens; -using MonoGame.Extended.Screens.Transitions; -using MonoGame.Extended.Sprites; -using MonoGame.Extended.Tweening; -using Sandbox.GameObjects; - -namespace Sandbox.Screens -{ - public class PongGameScreen : GameScreen - { - private SpriteBatch _spriteBatch; - private Paddle _bluePaddle; - private Paddle _redPaddle; - private Ball _ball; - private Texture2D _court; - private BitmapFont _font; - private SoundEffect _plopSoundEffect; - private int _leftScore; - private int _rightScore; - private readonly FastRandom _random = new FastRandom(); - private Tweener _tweener = new Tweener(); - - public PongGameScreen(Game game) - : base(game) - { - game.IsMouseVisible = false; - } - - public int ScreenWidth => GraphicsDevice.Viewport.Width; - public int ScreenHeight => GraphicsDevice.Viewport.Height; - - public override void LoadContent() - { - base.LoadContent(); - - _spriteBatch = new SpriteBatch(GraphicsDevice); - _plopSoundEffect = Content.Load("pip"); - _font = Content.Load("kenney-rocket-square"); - _court = Content.Load("court"); - - _bluePaddle = new Paddle - { - Position = new Vector2(50, ScreenWidth / 2f), - Sprite = new Sprite(Content.Load("paddleBlue")) - }; - - _redPaddle = new Paddle - { - Position = new Vector2(ScreenWidth - 50, ScreenHeight / 2f), - Sprite = new Sprite(Content.Load("paddleRed")) - }; - - _ball = new Ball - { - Position = new Vector2(ScreenWidth / 2f, ScreenHeight / 2f), - Sprite = new Sprite(Content.Load("ballGrey")), - Velocity = new Vector2(250, 200) - }; - } - - public override void UnloadContent() - { - base.UnloadContent(); - _spriteBatch.Dispose(); - } - - public override void Update(GameTime gameTime) - { - var elapsedSeconds = gameTime.GetElapsedSeconds(); - var mouseState = MouseExtended.GetState(); - var keyboardState = KeyboardExtended.GetState(); - - if (keyboardState.WasKeyJustDown(Keys.Escape)) - ScreenManager.LoadScreen(new TitleScreen(Game), new ExpandTransition(GraphicsDevice, Color.Black)); - - MovePaddlePlayer(mouseState); - - MovePaddleAi(_redPaddle, elapsedSeconds); - - ConstrainPaddle(_bluePaddle); - ConstrainPaddle(_redPaddle); - - MoveBall(elapsedSeconds); - - if (BallHitPaddle(_ball, _bluePaddle)) - { - // TODO: Change the angle of the bounce - //_tweener.TweenTo(_bluePaddle, p => p.Rotation, MathHelper.Pi / 16f, 0.2f) - // //.OnSet(v => new Vector2(v.X, _bluePaddle.Position.Y)) - // .RepeatReverse() - // .Easing(EasingFunctions.ExponentialIn); - - _plopSoundEffect.Play(1.0f, _random.NextSingle(0.5f, 1.0f), -1f); - } - - if (BallHitPaddle(_ball, _redPaddle)) - { - // TODO: Change the angle of the bounce - - //_tweener.TweenTo(_redPaddle, p => p.Position, _redPaddle.Position + new Vector2(15, 0), 0.2f) - // .RepeatReverse() - // .Easing(EasingFunctions.ExponentialIn); - - _plopSoundEffect.Play(1f, _random.NextSingle(-1f, 1f), 1f); - } - - _tweener.Update(elapsedSeconds); - } - - public override void Draw(GameTime gameTime) - { - _spriteBatch.Begin(samplerState: SamplerState.PointClamp); - _spriteBatch.Draw(_court, new Rectangle(0, 0, ScreenWidth, ScreenHeight), Color.White); - - DrawScores(); - - _spriteBatch.Draw(_redPaddle.Sprite, _redPaddle.Position, _redPaddle.Rotation, _redPaddle.Scale); - _spriteBatch.Draw(_bluePaddle.Sprite, _bluePaddle.Position, _bluePaddle.Rotation, _bluePaddle.Scale); - _spriteBatch.Draw(_ball.Sprite, _ball.Position, _ball.Rotation, _ball.Scale); - _spriteBatch.End(); - } - - private void DrawScores() - { - _spriteBatch.DrawString(_font, $"{_leftScore:00}", new Vector2(172, 10), new Color(0.2f, 0.2f, 0.2f), 0, Vector2.Zero, Vector2.One * 4f, SpriteEffects.None, 0); - _spriteBatch.DrawString(_font, $"{_rightScore:00}", new Vector2(430, 10), new Color(0.2f, 0.2f, 0.2f), 0, Vector2.Zero, Vector2.One * 4f, SpriteEffects.None, 0); - } - - private void MovePaddlePlayer(MouseStateExtended mouseState) - { - _bluePaddle.Position.Y = mouseState.Position.Y; - } - - private static bool BallHitPaddle(Ball ball, Paddle paddle) - { - if (ball.BoundingRectangle.Intersects(paddle.BoundingRectangle)) - { - if (ball.BoundingRectangle.Left < paddle.BoundingRectangle.Left) - ball.Position.X = paddle.BoundingRectangle.Left - ball.BoundingRectangle.Width / 2; - - if (ball.BoundingRectangle.Right > paddle.BoundingRectangle.Right) - ball.Position.X = paddle.BoundingRectangle.Right + ball.BoundingRectangle.Width / 2; - - ball.Velocity.X = -ball.Velocity.X; - return true; - } - - return false; - } - - private void MoveBall(float elapsedSeconds) - { - _ball.Position += _ball.Velocity * elapsedSeconds; - - var halfHeight = _ball.BoundingRectangle.Height / 2; - var halfWidth = _ball.BoundingRectangle.Width / 2; - - // top and bottom walls - // TODO: Play 'tink' sound - if (_ball.Position.Y - halfHeight < 0) - { - _ball.Position.Y = halfHeight; - _ball.Velocity.Y = -_ball.Velocity.Y; - } - - if (_ball.Position.Y + halfHeight > ScreenHeight) - { - _ball.Position.Y = ScreenHeight - halfHeight; - _ball.Velocity.Y = -_ball.Velocity.Y; - } - - // left and right is out of bounds - // TODO: Play sound and update score - // TODO: Reset ball to default velocity - if (_ball.Position.X > ScreenWidth + halfWidth && _ball.Velocity.X > 0) - { - _ball.Position = new Vector2(ScreenWidth / 2f, ScreenHeight / 2f); - _ball.Velocity = new Vector2(_random.Next(2, 5) * -100, 100); - _leftScore++; - } - - if (_ball.Position.X < -halfWidth && _ball.Velocity.X < 0) - { - _ball.Position = new Vector2(ScreenWidth / 2f, ScreenHeight / 2f); - _ball.Velocity = new Vector2(_random.Next(2, 5) * 100, 100); - _rightScore++; - } - } - - private void ConstrainPaddle(Paddle paddle) - { - if (paddle.BoundingRectangle.Left < 0) - paddle.Position.X = paddle.BoundingRectangle.Width / 2f; - - if (paddle.BoundingRectangle.Right > ScreenWidth) - paddle.Position.X = ScreenWidth - paddle.BoundingRectangle.Width / 2f; - - if (paddle.BoundingRectangle.Top < 0) - paddle.Position.Y = paddle.BoundingRectangle.Height / 2f; - - if (paddle.BoundingRectangle.Bottom > ScreenHeight) - paddle.Position.Y = ScreenHeight - paddle.BoundingRectangle.Height / 2f; - } - - private void MovePaddleAi(Paddle paddle, float elapsedSeconds) - { - const float difficulty = 0.80f; - var paddleSpeed = Math.Abs(_ball.Velocity.Y) * difficulty; - - if (paddleSpeed < 0) - paddleSpeed = -paddleSpeed; - - //ball moving down - if (_ball.Velocity.Y > 0) - { - if (_ball.Position.Y > paddle.Position.Y) - paddle.Position.Y += paddleSpeed * elapsedSeconds; - else - paddle.Position.Y -= paddleSpeed * elapsedSeconds; - } - - //ball moving up - if (_ball.Velocity.Y < 0) - { - if (_ball.Position.Y < paddle.Position.Y) - paddle.Position.Y -= paddleSpeed * elapsedSeconds; - else - paddle.Position.Y += paddleSpeed * elapsedSeconds; - } - } - } -} diff --git a/Source/Demos/Sandbox/Screens/TitleScreen.cs b/Source/Demos/Sandbox/Screens/TitleScreen.cs deleted file mode 100644 index 826b9328..00000000 --- a/Source/Demos/Sandbox/Screens/TitleScreen.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Input; -using MonoGame.Extended.Input; -using MonoGame.Extended.Screens; -using MonoGame.Extended.Screens.Transitions; - -namespace Sandbox.Screens -{ - public class TitleScreen : GameScreen - { - private SpriteBatch _spriteBatch; - private Texture2D _background; - - public TitleScreen(Game game) - : base(game) - { - game.IsMouseVisible = true; - } - - public override void LoadContent() - { - base.LoadContent(); - _spriteBatch = new SpriteBatch(GraphicsDevice); - _background = Content.Load("title-screen"); - } - - public override void Update(GameTime gameTime) - { - var mouseState = MouseExtended.GetState(); - var keyboardState = KeyboardExtended.GetState(); - - if (keyboardState.WasKeyJustDown(Keys.Escape)) - Game.Exit(); - - if (mouseState.LeftButton == ButtonState.Pressed || keyboardState.WasAnyKeyJustDown()) - ScreenManager.LoadScreen(new PongGameScreen(Game), new FadeTransition(GraphicsDevice, Color.Black, 0.5f)); - } - - public override void Draw(GameTime gameTime) - { - GraphicsDevice.Clear(Color.Magenta); - - _spriteBatch.Begin(samplerState: SamplerState.PointClamp); - _spriteBatch.Draw(_background, new Rectangle(0, 0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height), Color.White); - _spriteBatch.End(); - } - } -} \ No newline at end of file diff --git a/Source/MonoGame.Extended.Entities/ComponentManager.cs b/Source/MonoGame.Extended.Entities/ComponentManager.cs index cbbca454..c8441be1 100644 --- a/Source/MonoGame.Extended.Entities/ComponentManager.cs +++ b/Source/MonoGame.Extended.Entities/ComponentManager.cs @@ -1,23 +1,49 @@ -using MonoGame.Extended.Collections; +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using MonoGame.Extended.Collections; +using MonoGame.Extended.Entities.Systems; namespace MonoGame.Extended.Entities { - public class ComponentManager + public class ComponentManager : UpdateSystem { public ComponentManager() { _mappers = new Bag(); + _componentTypes = new Dictionary(); } private readonly Bag _mappers; + private readonly Dictionary _componentTypes; - public void RegisterComponentType() + private ComponentMapper CreateMapperForType() where T : class { - var index = _mappers.Count; - var componentType = new ComponentType(typeof(T), index); - var mapper = new ComponentMapper(); - _mappers[index] = mapper; + var id = _mappers.Count; + var mapper = new ComponentMapper(id); + _mappers[id] = mapper; + return mapper; + } + + public ComponentMapper GetMapper() + where T : class + { + if (_componentTypes.TryGetValue(typeof(T), out var id)) + return _mappers[id] as ComponentMapper; + + var mapper = CreateMapperForType(); + _componentTypes.Add(typeof(T), mapper.Id); + return mapper; + } + + public override void Initialize(ComponentManager componentManager) + { + // TODO : Okay this is weird. + } + + public override void Update(GameTime gameTime) + { } } } \ No newline at end of file diff --git a/Source/MonoGame.Extended.Entities/ComponentMapper.cs b/Source/MonoGame.Extended.Entities/ComponentMapper.cs index 14dc4f88..b8a18ce7 100644 --- a/Source/MonoGame.Extended.Entities/ComponentMapper.cs +++ b/Source/MonoGame.Extended.Entities/ComponentMapper.cs @@ -5,41 +5,49 @@ namespace MonoGame.Extended.Entities { public abstract class ComponentMapper { - protected ComponentMapper(Type componentType) + protected ComponentMapper(int id, Type componentType) { + Id = id; ComponentType = componentType; } + public int Id { get; } public Type ComponentType { get; } } public class ComponentMapper : ComponentMapper where T : class { - public ComponentMapper() - : base(typeof(T)) + public ComponentMapper(int id) + : base(id, typeof(T)) { Components = new Bag(); } + public int Id { get; } public Bag Components { get; } - public void Put(int entityId, T component) + public void CreateComponent(int entityId, T component) { Components[entityId] = component; } - public T Get(int entityId) + public T GetComponent(Entity entity) + { + return GetComponent(entity.Id); + } + + public T GetComponent(int entityId) { return Components[entityId]; } - public bool Has(int entityId) + public bool HasComponent(int entityId) { return Components[entityId] != null; } - public void Delete(int entityId) + public void DeleteComponent(int entityId) { Components[entityId] = null; } diff --git a/Source/MonoGame.Extended.Entities/ComponentType.cs b/Source/MonoGame.Extended.Entities/ComponentType.cs index d0e2b7d0..ebef996b 100644 --- a/Source/MonoGame.Extended.Entities/ComponentType.cs +++ b/Source/MonoGame.Extended.Entities/ComponentType.cs @@ -1,47 +1,46 @@ - -using System; +using System; namespace MonoGame.Extended.Entities { - public class ComponentType : IEquatable - { - public ComponentType(Type type, int index) - { - Type = type; - Index = index; - } + //public class ComponentType : IEquatable + //{ + // public ComponentType(Type type, int id) + // { + // Type = type; + // Id = id; + // } - public Type Type { get; } - public int Index { get; } + // public Type Type { get; } + // public int Id { get; } - public bool Equals(ComponentType other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Index == other.Index; - } + // public bool Equals(ComponentType other) + // { + // if (ReferenceEquals(null, other)) return false; + // if (ReferenceEquals(this, other)) return true; + // return Id == other.Id; + // } - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((ComponentType) obj); - } + // public override bool Equals(object obj) + // { + // if (ReferenceEquals(null, obj)) return false; + // if (ReferenceEquals(this, obj)) return true; + // if (obj.GetType() != GetType()) return false; + // return Equals((ComponentType) obj); + // } - public override int GetHashCode() - { - return Index; - } + // public override int GetHashCode() + // { + // return Id; + // } - public static bool operator ==(ComponentType left, ComponentType right) - { - return Equals(left, right); - } + // public static bool operator ==(ComponentType left, ComponentType right) + // { + // return Equals(left, right); + // } - public static bool operator !=(ComponentType left, ComponentType right) - { - return !Equals(left, right); - } - } + // public static bool operator !=(ComponentType left, ComponentType right) + // { + // return !Equals(left, right); + // } + //} } diff --git a/Source/MonoGame.Extended.Entities/Entity.cs b/Source/MonoGame.Extended.Entities/Entity.cs new file mode 100644 index 00000000..f5fd05c4 --- /dev/null +++ b/Source/MonoGame.Extended.Entities/Entity.cs @@ -0,0 +1,73 @@ +using System; + +namespace MonoGame.Extended.Entities +{ + public class Entity : IEquatable + { + private readonly ComponentManager _componentManager; + + public Entity(int id, ComponentManager componentManager) + { + Id = id; + _componentManager = componentManager; + } + + public int Id { get; } + + public void Attach(T component) + where T : class + { + var mapper = _componentManager.GetMapper(); + mapper.CreateComponent(Id, component); + } + + public void Detach() + where T : class + { + var mapper = _componentManager.GetMapper(); + mapper.DeleteComponent(Id); + } + + public T Get() + where T : class + { + var mapper = _componentManager.GetMapper(); + return mapper.GetComponent(Id); + } + + public void Destory() + { + throw new NotImplementedException(); + } + + public bool Equals(Entity other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Id == other.Id; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((Entity) obj); + } + + public override int GetHashCode() + { + return Id; + } + + public static bool operator ==(Entity left, Entity right) + { + return Equals(left, right); + } + + public static bool operator !=(Entity left, Entity right) + { + return !Equals(left, right); + } + } +} \ No newline at end of file diff --git a/Source/MonoGame.Extended.Entities/EntityManager.cs b/Source/MonoGame.Extended.Entities/EntityManager.cs new file mode 100644 index 00000000..1636c0ba --- /dev/null +++ b/Source/MonoGame.Extended.Entities/EntityManager.cs @@ -0,0 +1,37 @@ +using Microsoft.Xna.Framework; +using MonoGame.Extended.Collections; +using MonoGame.Extended.Entities.Systems; + +namespace MonoGame.Extended.Entities +{ + public class EntityManager : UpdateSystem + { + public EntityManager(ComponentManager componentManager) + { + _componentManager = componentManager; + Entities = new Bag(128); + } + + private readonly ComponentManager _componentManager; + private int _nextId; + + public Bag Entities { get; } + + public Entity CreateEntity() + { + // TODO: Recycle dead entites + var id = _nextId++; + var entity = new Entity(id, _componentManager); + Entities[id] = entity; + return entity; + } + + public override void Update(GameTime gameTime) + { + } + + public override void Initialize(ComponentManager componentManager) + { + } + } +} \ No newline at end of file diff --git a/Source/MonoGame.Extended.Entities/EntityWorld.cs b/Source/MonoGame.Extended.Entities/EntityWorld.cs new file mode 100644 index 00000000..3f808bb4 --- /dev/null +++ b/Source/MonoGame.Extended.Entities/EntityWorld.cs @@ -0,0 +1,64 @@ +using System; +using Microsoft.Xna.Framework; +using MonoGame.Extended.Collections; +using MonoGame.Extended.Entities.Systems; + +namespace MonoGame.Extended.Entities +{ + public class EntityWorld : SimpleDrawableGameComponent + { + // TODO: Make these private again + public EntityManager EntityManager { get; } + public ComponentManager ComponentManager { get; } + + private readonly Bag _updateSystems; + private readonly Bag _drawSystems; + + public EntityWorld() + { + ComponentManager = new ComponentManager(); + EntityManager = new EntityManager(ComponentManager); + + _updateSystems = new Bag() + { + ComponentManager, + EntityManager + }; + _drawSystems = new Bag(); + } + + // TODO: Move this to world configuration + public void RegisterSystem(BaseSystem system) + { + switch (system) + { + case DrawSystem drawSystem: + _drawSystems.Add(drawSystem); + break; + case UpdateSystem updateSystem: + _updateSystems.Add(updateSystem); + break; + } + + system.World = this; + system.Initialize(ComponentManager); + } + + public Entity CreateEntity() + { + return EntityManager.CreateEntity(); + } + + public override void Update(GameTime gameTime) + { + foreach (var system in _updateSystems) + system.Update(gameTime); + } + + public override void Draw(GameTime gameTime) + { + foreach (var system in _drawSystems) + system.Draw(gameTime); + } + } +} \ No newline at end of file diff --git a/Source/MonoGame.Extended.Entities/Systems/BaseSystem.cs b/Source/MonoGame.Extended.Entities/Systems/BaseSystem.cs new file mode 100644 index 00000000..2785d2b1 --- /dev/null +++ b/Source/MonoGame.Extended.Entities/Systems/BaseSystem.cs @@ -0,0 +1,8 @@ +namespace MonoGame.Extended.Entities.Systems +{ + public abstract class BaseSystem + { + public EntityWorld World { get; internal set; } + public abstract void Initialize(ComponentManager componentManager); + } +} \ No newline at end of file diff --git a/Source/MonoGame.Extended.Entities/Systems/DrawSystem.cs b/Source/MonoGame.Extended.Entities/Systems/DrawSystem.cs new file mode 100644 index 00000000..517a742b --- /dev/null +++ b/Source/MonoGame.Extended.Entities/Systems/DrawSystem.cs @@ -0,0 +1,13 @@ +using Microsoft.Xna.Framework; + +namespace MonoGame.Extended.Entities.Systems +{ + public abstract class DrawSystem : BaseSystem + { + protected DrawSystem() + { + } + + public abstract void Draw(GameTime gameTime); + } +} \ No newline at end of file diff --git a/Source/MonoGame.Extended.Entities/Systems/UpdateSystem.cs b/Source/MonoGame.Extended.Entities/Systems/UpdateSystem.cs new file mode 100644 index 00000000..c1757c86 --- /dev/null +++ b/Source/MonoGame.Extended.Entities/Systems/UpdateSystem.cs @@ -0,0 +1,13 @@ +using Microsoft.Xna.Framework; + +namespace MonoGame.Extended.Entities.Systems +{ + public abstract class UpdateSystem : BaseSystem + { + protected UpdateSystem() + { + } + + public abstract void Update(GameTime gameTime); + } +} \ No newline at end of file diff --git a/Source/Tests/MonoGame.Extended.Entities.Tests/ComponentMapperTests.cs b/Source/Tests/MonoGame.Extended.Entities.Tests/ComponentMapperTests.cs index ea8007c7..346170e9 100644 --- a/Source/Tests/MonoGame.Extended.Entities.Tests/ComponentMapperTests.cs +++ b/Source/Tests/MonoGame.Extended.Entities.Tests/ComponentMapperTests.cs @@ -1,4 +1,6 @@ -using Xunit; +using System; +using MonoGame.Extended.Entities.Systems; +using Xunit; namespace MonoGame.Extended.Entities.Tests { @@ -8,7 +10,7 @@ namespace MonoGame.Extended.Entities.Tests public void CreateComponentManager() { var manager = new ComponentManager(); - + throw new NotImplementedException(); } } @@ -17,7 +19,7 @@ namespace MonoGame.Extended.Entities.Tests [Fact] public void CreateComponentMapper() { - var mapper = new ComponentMapper(); + var mapper = new ComponentMapper(0); Assert.Equal(typeof(object), mapper.ComponentType); Assert.Empty(mapper.Components); @@ -28,14 +30,14 @@ namespace MonoGame.Extended.Entities.Tests { const int entityId = 3; - var mapper = new ComponentMapper(); + var mapper = new ComponentMapper(1); var component = new Transform2(); - mapper.Put(entityId, component); + mapper.CreateComponent(entityId, component); Assert.Equal(typeof(Transform2), mapper.ComponentType); Assert.True(mapper.Components.Count >= 1); - Assert.Same(component, mapper.Get(entityId)); + Assert.Same(component, mapper.GetComponent(entityId)); } [Fact] @@ -43,13 +45,13 @@ namespace MonoGame.Extended.Entities.Tests { const int entityId = 1; - var mapper = new ComponentMapper(); + var mapper = new ComponentMapper(2); var component = new Transform2(); - mapper.Put(entityId, component); - mapper.Delete(entityId); + mapper.CreateComponent(entityId, component); + mapper.DeleteComponent(entityId); - Assert.False(mapper.Has(entityId)); + Assert.False(mapper.HasComponent(entityId)); } [Fact] @@ -57,14 +59,14 @@ namespace MonoGame.Extended.Entities.Tests { const int entityId = 0; - var mapper = new ComponentMapper(); + var mapper = new ComponentMapper(3); var component = new Transform2(); - Assert.False(mapper.Has(entityId)); + Assert.False(mapper.HasComponent(entityId)); - mapper.Put(entityId, component); + mapper.CreateComponent(entityId, component); - Assert.True(mapper.Has(entityId)); + Assert.True(mapper.HasComponent(entityId)); } } } \ No newline at end of file diff --git a/Source/Tests/MonoGame.Extended.Entities.Tests/ComponentTypeTests.cs b/Source/Tests/MonoGame.Extended.Entities.Tests/ComponentTypeTests.cs index f6b14ccf..89538041 100644 --- a/Source/Tests/MonoGame.Extended.Entities.Tests/ComponentTypeTests.cs +++ b/Source/Tests/MonoGame.Extended.Entities.Tests/ComponentTypeTests.cs @@ -3,18 +3,18 @@ using Xunit; namespace MonoGame.Extended.Entities.Tests { - public class ComponentTypeTests - { - [Fact] - public void CreateComponentType() - { - var type = typeof(Sprite); - var componentType = new ComponentType(type, 3); + //public class ComponentTypeTests + //{ + // [Fact] + // public void CreateComponentType() + // { + // var type = typeof(Sprite); + // var componentType = new ComponentType(type, 3); - Assert.Same(type, componentType.Type); - Assert.Equal(3, componentType.Index); - Assert.Equal(new ComponentType(typeof(Sprite), 3), componentType); - Assert.Equal(componentType.Index, componentType.GetHashCode()); - } - } + // Assert.Same(type, componentType.Type); + // Assert.Equal(3, componentType.Id); + // Assert.Equal(new ComponentType(typeof(Sprite), 3), componentType); + // Assert.Equal(componentType.Id, componentType.GetHashCode()); + // } + //} } \ No newline at end of file