From d86f767cceb142dbcb9a655c33adc9ea0a85ccbb Mon Sep 17 00:00:00 2001 From: Dylan Wilson Date: Sat, 9 Jun 2018 00:02:07 +1000 Subject: [PATCH] rewriting ecs from scratch --- .../Platformer/AutofacDependencyResolver.cs | 1 + Source/Demos/Platformer/EntityFactory.cs | 1 + Source/Demos/Platformer/GameBase.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 | 99 ++++++++ Source/Demos/Sandbox/Content/ballGrey.png | Bin 0 -> 573 bytes Source/Demos/Sandbox/Content/court.png | Bin 0 -> 274 bytes .../Sandbox/Content/kenney-rocket-square.bmfc | 55 ++++ .../Sandbox/Content/kenney-rocket-square.fnt | 105 ++++++++ .../Content/kenney-rocket-square_0.png | Bin 0 -> 2203 bytes Source/Demos/Sandbox/Content/paddleBlue.png | Bin 0 -> 821 bytes Source/Demos/Sandbox/Content/paddleRed.png | Bin 0 -> 751 bytes Source/Demos/Sandbox/Content/pip.wav | Bin 0 -> 12634 bytes Source/Demos/Sandbox/Content/title-screen.png | Bin 0 -> 18058 bytes Source/Demos/Sandbox/GameMain.cs | 156 ++++++++++++ Source/Demos/Sandbox/GameObjects/Ball.cs | 9 + .../Demos/Sandbox/GameObjects/GameObject.cs | 15 ++ Source/Demos/Sandbox/GameObjects/Paddle.cs | 6 + Source/Demos/Sandbox/Icon.bmp | Bin 0 -> 262282 bytes Source/Demos/Sandbox/Icon.ico | Bin 0 -> 147541 bytes Source/Demos/Sandbox/Program.cs | 14 + Source/Demos/Sandbox/Sandbox.csproj | 21 ++ .../Demos/Sandbox/Screens/PongGameScreen.cs | 240 ++++++++++++++++++ Source/Demos/Sandbox/Screens/TitleScreen.cs | 49 ++++ Source/Demos/Sandbox/app.manifest | 42 +++ .../ComponentManager.cs | 23 ++ .../ComponentMapper.cs | 47 ++++ .../ComponentType.cs | 47 ++++ .../{ => Legacy}/Aspect.cs | 8 +- .../{ => Legacy}/AspectAttribute.cs | 2 +- .../{ => Legacy}/BitVector.cs | 4 +- .../{ => Legacy}/DefaultDependencyResolver.cs | 2 +- .../{ => Legacy}/DependencyResolver.cs | 2 +- .../{ => Legacy}/Entity.cs | 3 +- .../{ => Legacy}/EntityComponentDelegate.cs | 2 +- .../{ => Legacy}/EntityComponentPool.cs | 2 +- .../EntityComponentPoolAttribute.cs | 2 +- .../{ => Legacy}/EntityComponentSystem.cs | 5 +- .../{ => Legacy}/EntityComponentType.cs | 2 +- .../{ => Legacy}/EntityDelegate.cs | 2 +- .../{ => Legacy}/EntityManager.cs | 2 +- .../{ => Legacy}/EntityProcessingSystem.cs | 2 +- .../{ => Legacy}/EntitySystemAttribute.cs | 2 +- .../{ => Legacy}/EntityTemplate.cs | 2 +- .../{ => Legacy}/EntityTemplateAttribute.cs | 2 +- .../{ => Legacy}/PoolableComponent.cs | 2 +- .../{ => Legacy}/ProcessingSystem.cs | 2 +- .../{ => Legacy}/SystemManager.cs | 8 +- Source/MonoGame.Extended.sln | 11 + Source/MonoGame.Extended/Collections/Bag.cs | 2 +- .../ComponentMapperTests.cs | 70 +++++ .../ComponentTypeTests.cs | 20 ++ .../EntityTemplateTests.cs | 7 +- .../Implementation/EntityTemplateBasic.cs | 1 + .../EntityTemplateUsingManager.cs | 1 + 58 files changed, 1068 insertions(+), 37 deletions(-) create mode 100644 Source/Demos/Sandbox/Content/Content.mgcb create mode 100644 Source/Demos/Sandbox/Content/ballGrey.png create mode 100644 Source/Demos/Sandbox/Content/court.png create mode 100644 Source/Demos/Sandbox/Content/kenney-rocket-square.bmfc create mode 100644 Source/Demos/Sandbox/Content/kenney-rocket-square.fnt create mode 100644 Source/Demos/Sandbox/Content/kenney-rocket-square_0.png create mode 100644 Source/Demos/Sandbox/Content/paddleBlue.png create mode 100644 Source/Demos/Sandbox/Content/paddleRed.png create mode 100644 Source/Demos/Sandbox/Content/pip.wav create mode 100644 Source/Demos/Sandbox/Content/title-screen.png create mode 100644 Source/Demos/Sandbox/GameMain.cs create mode 100644 Source/Demos/Sandbox/GameObjects/Ball.cs create mode 100644 Source/Demos/Sandbox/GameObjects/GameObject.cs create mode 100644 Source/Demos/Sandbox/GameObjects/Paddle.cs create mode 100644 Source/Demos/Sandbox/Icon.bmp create mode 100644 Source/Demos/Sandbox/Icon.ico create mode 100644 Source/Demos/Sandbox/Program.cs create mode 100644 Source/Demos/Sandbox/Sandbox.csproj create mode 100644 Source/Demos/Sandbox/Screens/PongGameScreen.cs create mode 100644 Source/Demos/Sandbox/Screens/TitleScreen.cs create mode 100644 Source/Demos/Sandbox/app.manifest create mode 100644 Source/MonoGame.Extended.Entities/ComponentManager.cs create mode 100644 Source/MonoGame.Extended.Entities/ComponentMapper.cs create mode 100644 Source/MonoGame.Extended.Entities/ComponentType.cs rename Source/MonoGame.Extended.Entities/{ => Legacy}/Aspect.cs (96%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/AspectAttribute.cs (91%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/BitVector.cs (98%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/DefaultDependencyResolver.cs (87%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/DependencyResolver.cs (86%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/Entity.cs (99%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/EntityComponentDelegate.cs (66%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/EntityComponentPool.cs (93%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/EntityComponentPoolAttribute.cs (98%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/EntityComponentSystem.cs (98%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/EntityComponentType.cs (98%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/EntityDelegate.cs (57%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/EntityManager.cs (99%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/EntityProcessingSystem.cs (99%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/EntitySystemAttribute.cs (98%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/EntityTemplate.cs (98%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/EntityTemplateAttribute.cs (98%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/PoolableComponent.cs (98%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/ProcessingSystem.cs (98%) rename Source/MonoGame.Extended.Entities/{ => Legacy}/SystemManager.cs (98%) create mode 100644 Source/Tests/MonoGame.Extended.Entities.Tests/ComponentMapperTests.cs create mode 100644 Source/Tests/MonoGame.Extended.Entities.Tests/ComponentTypeTests.cs diff --git a/Source/Demos/Platformer/AutofacDependencyResolver.cs b/Source/Demos/Platformer/AutofacDependencyResolver.cs index 15d3eba3..8fba167a 100644 --- a/Source/Demos/Platformer/AutofacDependencyResolver.cs +++ b/Source/Demos/Platformer/AutofacDependencyResolver.cs @@ -2,6 +2,7 @@ using System.Linq; using Autofac; using MonoGame.Extended.Entities; +using MonoGame.Extended.Entities.Legacy; namespace Platformer { diff --git a/Source/Demos/Platformer/EntityFactory.cs b/Source/Demos/Platformer/EntityFactory.cs index 42243789..db2caa11 100644 --- a/Source/Demos/Platformer/EntityFactory.cs +++ b/Source/Demos/Platformer/EntityFactory.cs @@ -5,6 +5,7 @@ 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; using Platformer.Components; diff --git a/Source/Demos/Platformer/GameBase.cs b/Source/Demos/Platformer/GameBase.cs index 97e653ef..a29d9e81 100644 --- a/Source/Demos/Platformer/GameBase.cs +++ b/Source/Demos/Platformer/GameBase.cs @@ -2,6 +2,7 @@ using Autofac; using Microsoft.Xna.Framework; using MonoGame.Extended.Entities; +using MonoGame.Extended.Entities.Legacy; namespace Platformer { diff --git a/Source/Demos/Platformer/GameMain.cs b/Source/Demos/Platformer/GameMain.cs index 2c7f8608..35db88ac 100644 --- a/Source/Demos/Platformer/GameMain.cs +++ b/Source/Demos/Platformer/GameMain.cs @@ -3,6 +3,7 @@ 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; using Platformer.Systems; diff --git a/Source/Demos/Platformer/Systems/PlayerSystem.cs b/Source/Demos/Platformer/Systems/PlayerSystem.cs index a7878123..a80811f7 100644 --- a/Source/Demos/Platformer/Systems/PlayerSystem.cs +++ b/Source/Demos/Platformer/Systems/PlayerSystem.cs @@ -5,6 +5,7 @@ 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; using Platformer.Components; diff --git a/Source/Demos/Platformer/Systems/RenderSystem.cs b/Source/Demos/Platformer/Systems/RenderSystem.cs index 25683bb9..a76f7d55 100644 --- a/Source/Demos/Platformer/Systems/RenderSystem.cs +++ b/Source/Demos/Platformer/Systems/RenderSystem.cs @@ -3,6 +3,7 @@ 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; namespace Platformer.Systems diff --git a/Source/Demos/Platformer/Systems/WorldSystem.cs b/Source/Demos/Platformer/Systems/WorldSystem.cs index c7efdfb5..faa0189a 100644 --- a/Source/Demos/Platformer/Systems/WorldSystem.cs +++ b/Source/Demos/Platformer/Systems/WorldSystem.cs @@ -1,6 +1,7 @@ using Microsoft.Xna.Framework; using MonoGame.Extended; using MonoGame.Extended.Entities; +using MonoGame.Extended.Entities.Legacy; using Platformer.Collisions; namespace Platformer.Systems diff --git a/Source/Demos/Sandbox/Content/Content.mgcb b/Source/Demos/Sandbox/Content/Content.mgcb new file mode 100644 index 00000000..3b1872f9 --- /dev/null +++ b/Source/Demos/Sandbox/Content/Content.mgcb @@ -0,0 +1,99 @@ + +#----------------------------- Global Properties ----------------------------# + +/outputDir:bin/$(Platform) +/intermediateDir:obj/$(Platform) +/platform:DesktopGL +/config: +/profile:Reach +/compress:False + +#-------------------------------- References --------------------------------# + +/reference:..\..\..\MonoGame.Extended.Content.Pipeline\bin\netstandard2.0\MonoGame.Extended.Content.Pipeline.dll + +#---------------------------------- Content ---------------------------------# + +#begin ballGrey.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: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 +/build:kenney-rocket-square.fnt + +#begin kenney-rocket-square_0.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: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/ballGrey.png b/Source/Demos/Sandbox/Content/ballGrey.png new file mode 100644 index 0000000000000000000000000000000000000000..c2d8aaa943183309772ef0c47aaa3b0838ee3d64 GIT binary patch literal 573 zcmV-D0>b@?P)N2bZe?^J zG%heMGBNQWX_Wu~0lG;qSY}_blzz2aSM2L@NPycOKQysd;nZd;r{CV`_>*}g>CGc6R)oQfc?RR-kq)PcL zAUvZDeVtC{MGt>$7%V6C2=txwA|HCH-*3TQy0Tu`VUTc0SQ zG9+&ZgG$(JHgiIuJkOn=x+vBNEj(5)E+EMTacMf85;%D(7&-cX;N$TqE|-hAUa#VG zI$3>IDCFf=$vdCV;&!_km1SxAtmMhdCtiN~xp62|Rh8*u$6I}FD37Ya=k;o}GL!2b z6PU&Ken4R4H7c49zT52-fMQJ0!{MOX`~AKGOWlt&j29taz2IJRVL@KutRe(M1Ga$y z-EPWv9OoQMH^H(|dUsrCf00000 LNkvXXu0mjfz6bP% literal 0 HcmV?d00001 diff --git a/Source/Demos/Sandbox/Content/court.png b/Source/Demos/Sandbox/Content/court.png new file mode 100644 index 0000000000000000000000000000000000000000..06f4a523e6de5a357f57d46f99b9585ef42dc3e6 GIT binary patch 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`ZRDi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/Demos/Sandbox/Content/kenney-rocket-square_0.png b/Source/Demos/Sandbox/Content/kenney-rocket-square_0.png new file mode 100644 index 0000000000000000000000000000000000000000..596c7366b0f7adb79bfb67000fc16cbea61e23bd GIT binary patch literal 2203 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 Jmvv4FO#oMPMV9~o literal 0 HcmV?d00001 diff --git a/Source/Demos/Sandbox/Content/pip.wav b/Source/Demos/Sandbox/Content/pip.wav new file mode 100644 index 0000000000000000000000000000000000000000..ee26c6d5e407e42ee9a2427300ad56807dcc7141 GIT binary patch literal 12634 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 literal 0 HcmV?d00001 diff --git a/Source/Demos/Sandbox/Content/title-screen.png b/Source/Demos/Sandbox/Content/title-screen.png new file mode 100644 index 0000000000000000000000000000000000000000..cdf228a0df89e805d199f07958299a29e6a113eb GIT binary patch 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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 + 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); + } + + public override void Draw(GameTime gameTime) + { + foreach (var system in _systems) + system.Draw(gameTime); + } + } + + public class GameMain : Game + { + // ReSharper disable once NotAccessedField.Local + private readonly GraphicsDeviceManager _graphics; + private readonly ScreenManager _screenManager; + + public GameMain() + { + _graphics = new GraphicsDeviceManager(this) + { + PreferredBackBufferWidth = 800, + PreferredBackBufferHeight = 480, + SynchronizeWithVerticalRetrace = false + }; + + Content.RootDirectory = "Content"; + IsFixedTimeStep = true; + TargetElapsedTime = TimeSpan.FromSeconds(1f / 60f); + + _screenManager = Components.Add(); + } + + protected override void LoadContent() + { + base.LoadContent(); + + _screenManager.LoadScreen(new TitleScreen(this), new FadeTransition(GraphicsDevice, Color.Black, 0.5f)); + } + } +} diff --git a/Source/Demos/Sandbox/GameObjects/Ball.cs b/Source/Demos/Sandbox/GameObjects/Ball.cs new file mode 100644 index 00000000..d0016b0b --- /dev/null +++ b/Source/Demos/Sandbox/GameObjects/Ball.cs @@ -0,0 +1,9 @@ +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 new file mode 100644 index 00000000..204932e9 --- /dev/null +++ b/Source/Demos/Sandbox/GameObjects/GameObject.cs @@ -0,0 +1,15 @@ +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 new file mode 100644 index 00000000..d59a641b --- /dev/null +++ b/Source/Demos/Sandbox/GameObjects/Paddle.cs @@ -0,0 +1,6 @@ +namespace Sandbox.GameObjects +{ + public class Paddle : GameObject + { + } +} \ No newline at end of file diff --git a/Source/Demos/Sandbox/Icon.bmp b/Source/Demos/Sandbox/Icon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2b481653e241818d2894e4ef33234eaff9aad701 GIT binary patch literal 262282 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 RJoIeLznU7AhJ`o~_y6Yy51#-4 literal 0 HcmV?d00001 diff --git a/Source/Demos/Sandbox/Program.cs b/Source/Demos/Sandbox/Program.cs new file mode 100644 index 00000000..e4bb48f1 --- /dev/null +++ b/Source/Demos/Sandbox/Program.cs @@ -0,0 +1,14 @@ +using System; + +namespace Sandbox +{ + public static class Program + { + [STAThread] + private static void Main() + { + using (var game = new GameMain()) + game.Run(); + } + } +} diff --git a/Source/Demos/Sandbox/Sandbox.csproj b/Source/Demos/Sandbox/Sandbox.csproj new file mode 100644 index 00000000..30f19912 --- /dev/null +++ b/Source/Demos/Sandbox/Sandbox.csproj @@ -0,0 +1,21 @@ + + + WinExe + netcoreapp2.0 + + + + + + + + + + + + + + + + + diff --git a/Source/Demos/Sandbox/Screens/PongGameScreen.cs b/Source/Demos/Sandbox/Screens/PongGameScreen.cs new file mode 100644 index 00000000..f6cbd7c6 --- /dev/null +++ b/Source/Demos/Sandbox/Screens/PongGameScreen.cs @@ -0,0 +1,240 @@ +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 new file mode 100644 index 00000000..826b9328 --- /dev/null +++ b/Source/Demos/Sandbox/Screens/TitleScreen.cs @@ -0,0 +1,49 @@ +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/Demos/Sandbox/app.manifest b/Source/Demos/Sandbox/app.manifest new file mode 100644 index 00000000..0acf5bcd --- /dev/null +++ b/Source/Demos/Sandbox/app.manifest @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true/pm + + + + diff --git a/Source/MonoGame.Extended.Entities/ComponentManager.cs b/Source/MonoGame.Extended.Entities/ComponentManager.cs new file mode 100644 index 00000000..cbbca454 --- /dev/null +++ b/Source/MonoGame.Extended.Entities/ComponentManager.cs @@ -0,0 +1,23 @@ +using MonoGame.Extended.Collections; + +namespace MonoGame.Extended.Entities +{ + public class ComponentManager + { + public ComponentManager() + { + _mappers = new Bag(); + } + + private readonly Bag _mappers; + + public void RegisterComponentType() + where T : class + { + var index = _mappers.Count; + var componentType = new ComponentType(typeof(T), index); + var mapper = new ComponentMapper(); + _mappers[index] = mapper; + } + } +} \ No newline at end of file diff --git a/Source/MonoGame.Extended.Entities/ComponentMapper.cs b/Source/MonoGame.Extended.Entities/ComponentMapper.cs new file mode 100644 index 00000000..14dc4f88 --- /dev/null +++ b/Source/MonoGame.Extended.Entities/ComponentMapper.cs @@ -0,0 +1,47 @@ +using System; +using MonoGame.Extended.Collections; + +namespace MonoGame.Extended.Entities +{ + public abstract class ComponentMapper + { + protected ComponentMapper(Type componentType) + { + ComponentType = componentType; + } + + public Type ComponentType { get; } + } + + public class ComponentMapper : ComponentMapper + where T : class + { + public ComponentMapper() + : base(typeof(T)) + { + Components = new Bag(); + } + + public Bag Components { get; } + + public void Put(int entityId, T component) + { + Components[entityId] = component; + } + + public T Get(int entityId) + { + return Components[entityId]; + } + + public bool Has(int entityId) + { + return Components[entityId] != null; + } + + public void Delete(int entityId) + { + Components[entityId] = null; + } + } +} \ No newline at end of file diff --git a/Source/MonoGame.Extended.Entities/ComponentType.cs b/Source/MonoGame.Extended.Entities/ComponentType.cs new file mode 100644 index 00000000..d0e2b7d0 --- /dev/null +++ b/Source/MonoGame.Extended.Entities/ComponentType.cs @@ -0,0 +1,47 @@ + +using System; + +namespace MonoGame.Extended.Entities +{ + public class ComponentType : IEquatable + { + public ComponentType(Type type, int index) + { + Type = type; + Index = index; + } + + public Type Type { get; } + public int Index { get; } + + public bool Equals(ComponentType other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Index == other.Index; + } + + 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 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/Aspect.cs b/Source/MonoGame.Extended.Entities/Legacy/Aspect.cs similarity index 96% rename from Source/MonoGame.Extended.Entities/Aspect.cs rename to Source/MonoGame.Extended.Entities/Legacy/Aspect.cs index 9ff553b4..cb229720 100644 --- a/Source/MonoGame.Extended.Entities/Aspect.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/Aspect.cs @@ -34,11 +34,9 @@ // // -------------------------------------------------------------------------------------------------------------------- -using System.Linq.Expressions; - -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { - internal class Aspect + public class Aspect { // match entities with components using boolean logic: // (A and B and C and ..) AND (D or E or F or ...) AND NOT(G or H or I or ..) @@ -87,7 +85,7 @@ namespace MonoGame.Extended.Entities NorMask = new BitVector(norMask); } - internal bool Matches(BitVector componentBits) + public bool Matches(BitVector componentBits) { AndMask.And(componentBits, ref Result); if (!Result.Equals(AndMask)) diff --git a/Source/MonoGame.Extended.Entities/AspectAttribute.cs b/Source/MonoGame.Extended.Entities/Legacy/AspectAttribute.cs similarity index 91% rename from Source/MonoGame.Extended.Entities/AspectAttribute.cs rename to Source/MonoGame.Extended.Entities/Legacy/AspectAttribute.cs index 467ccbdb..eb90bb3f 100644 --- a/Source/MonoGame.Extended.Entities/AspectAttribute.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/AspectAttribute.cs @@ -1,6 +1,6 @@ using System; -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { public enum AspectType { diff --git a/Source/MonoGame.Extended.Entities/BitVector.cs b/Source/MonoGame.Extended.Entities/Legacy/BitVector.cs similarity index 98% rename from Source/MonoGame.Extended.Entities/BitVector.cs rename to Source/MonoGame.Extended.Entities/Legacy/BitVector.cs index 8eb43691..b24d6f8f 100644 --- a/Source/MonoGame.Extended.Entities/BitVector.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/BitVector.cs @@ -49,9 +49,9 @@ SOFTWARE. using System; -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { - internal sealed class BitVector + public sealed class BitVector { // XPerY=n means that n Xs can be stored in 1 Y. private const int _bitsPerInt32 = 32; diff --git a/Source/MonoGame.Extended.Entities/DefaultDependencyResolver.cs b/Source/MonoGame.Extended.Entities/Legacy/DefaultDependencyResolver.cs similarity index 87% rename from Source/MonoGame.Extended.Entities/DefaultDependencyResolver.cs rename to Source/MonoGame.Extended.Entities/Legacy/DefaultDependencyResolver.cs index e7b46b1f..50ef786c 100644 --- a/Source/MonoGame.Extended.Entities/DefaultDependencyResolver.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/DefaultDependencyResolver.cs @@ -1,6 +1,6 @@ using System; -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { public class DefaultDependencyResolver : DependencyResolver { diff --git a/Source/MonoGame.Extended.Entities/DependencyResolver.cs b/Source/MonoGame.Extended.Entities/Legacy/DependencyResolver.cs similarity index 86% rename from Source/MonoGame.Extended.Entities/DependencyResolver.cs rename to Source/MonoGame.Extended.Entities/Legacy/DependencyResolver.cs index aba9dd95..a4a3be91 100644 --- a/Source/MonoGame.Extended.Entities/DependencyResolver.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/DependencyResolver.cs @@ -1,6 +1,6 @@ using System; -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { public abstract class DependencyResolver { diff --git a/Source/MonoGame.Extended.Entities/Entity.cs b/Source/MonoGame.Extended.Entities/Legacy/Entity.cs similarity index 99% rename from Source/MonoGame.Extended.Entities/Entity.cs rename to Source/MonoGame.Extended.Entities/Legacy/Entity.cs index db4f562c..d428e22e 100644 --- a/Source/MonoGame.Extended.Entities/Entity.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/Entity.cs @@ -37,9 +37,10 @@ using System; using System.Runtime.CompilerServices; using MonoGame.Extended.Collections; + // ReSharper disable InconsistentNaming -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { public sealed class Entity : IPoolable { diff --git a/Source/MonoGame.Extended.Entities/EntityComponentDelegate.cs b/Source/MonoGame.Extended.Entities/Legacy/EntityComponentDelegate.cs similarity index 66% rename from Source/MonoGame.Extended.Entities/EntityComponentDelegate.cs rename to Source/MonoGame.Extended.Entities/Legacy/EntityComponentDelegate.cs index f1c893ae..4df6aa41 100644 --- a/Source/MonoGame.Extended.Entities/EntityComponentDelegate.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/EntityComponentDelegate.cs @@ -1,4 +1,4 @@ -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { public delegate void EntityComponentDelegate(Entity entity, object component); } \ No newline at end of file diff --git a/Source/MonoGame.Extended.Entities/EntityComponentPool.cs b/Source/MonoGame.Extended.Entities/Legacy/EntityComponentPool.cs similarity index 93% rename from Source/MonoGame.Extended.Entities/EntityComponentPool.cs rename to Source/MonoGame.Extended.Entities/Legacy/EntityComponentPool.cs index 4ff1c853..fac8073a 100644 --- a/Source/MonoGame.Extended.Entities/EntityComponentPool.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/EntityComponentPool.cs @@ -1,6 +1,6 @@ using MonoGame.Extended.Collections; -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { internal interface IComponentPool { diff --git a/Source/MonoGame.Extended.Entities/EntityComponentPoolAttribute.cs b/Source/MonoGame.Extended.Entities/Legacy/EntityComponentPoolAttribute.cs similarity index 98% rename from Source/MonoGame.Extended.Entities/EntityComponentPoolAttribute.cs rename to Source/MonoGame.Extended.Entities/Legacy/EntityComponentPoolAttribute.cs index 8b0376b4..08652c7d 100644 --- a/Source/MonoGame.Extended.Entities/EntityComponentPoolAttribute.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/EntityComponentPoolAttribute.cs @@ -37,7 +37,7 @@ using System; using MonoGame.Extended.Collections; -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { [AttributeUsage(AttributeTargets.Class, Inherited = false)] public sealed class EntityComponentPoolAttribute : Attribute diff --git a/Source/MonoGame.Extended.Entities/EntityComponentSystem.cs b/Source/MonoGame.Extended.Entities/Legacy/EntityComponentSystem.cs similarity index 98% rename from Source/MonoGame.Extended.Entities/EntityComponentSystem.cs rename to Source/MonoGame.Extended.Entities/Legacy/EntityComponentSystem.cs index c33a4262..d5e9ab9c 100644 --- a/Source/MonoGame.Extended.Entities/EntityComponentSystem.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/EntityComponentSystem.cs @@ -37,7 +37,7 @@ using Microsoft.Xna.Framework; using MonoGame.Extended.Animations; using MonoGame.Extended.Sprites; -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { public sealed class EntityComponentSystem : DrawableGameComponent { @@ -211,9 +211,8 @@ namespace MonoGame.Extended.Entities return; var processingSystem = _dependencyResolver.Resolve(typeInfo.AsType()); - var entityProcessingSystem = processingSystem as EntityProcessingSystem; - if (entityProcessingSystem != null) + if (processingSystem is EntityProcessingSystem entityProcessingSystem) entityProcessingSystem.Aspect = new Aspect(andMask, orMask, norMask); _systemManager.AddSystem(processingSystem, systemAttribute.GameLoopType, systemAttribute.Layer, SystemExecutionType.Synchronous); diff --git a/Source/MonoGame.Extended.Entities/EntityComponentType.cs b/Source/MonoGame.Extended.Entities/Legacy/EntityComponentType.cs similarity index 98% rename from Source/MonoGame.Extended.Entities/EntityComponentType.cs rename to Source/MonoGame.Extended.Entities/Legacy/EntityComponentType.cs index c6922a07..0c7181db 100644 --- a/Source/MonoGame.Extended.Entities/EntityComponentType.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/EntityComponentType.cs @@ -37,7 +37,7 @@ using System; using System.Diagnostics; -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { [DebuggerDisplay("Index:{" + nameof(Index) + "}")] public sealed class EntityComponentType diff --git a/Source/MonoGame.Extended.Entities/EntityDelegate.cs b/Source/MonoGame.Extended.Entities/Legacy/EntityDelegate.cs similarity index 57% rename from Source/MonoGame.Extended.Entities/EntityDelegate.cs rename to Source/MonoGame.Extended.Entities/Legacy/EntityDelegate.cs index 21cb76b3..ec270b54 100644 --- a/Source/MonoGame.Extended.Entities/EntityDelegate.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/EntityDelegate.cs @@ -1,4 +1,4 @@ -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { public delegate void EntityDelegate(Entity entity); } \ No newline at end of file diff --git a/Source/MonoGame.Extended.Entities/EntityManager.cs b/Source/MonoGame.Extended.Entities/Legacy/EntityManager.cs similarity index 99% rename from Source/MonoGame.Extended.Entities/EntityManager.cs rename to Source/MonoGame.Extended.Entities/Legacy/EntityManager.cs index f2f30d37..b9dff309 100644 --- a/Source/MonoGame.Extended.Entities/EntityManager.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/EntityManager.cs @@ -40,7 +40,7 @@ using System.Diagnostics; using System.Reflection; using MonoGame.Extended.Collections; -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { public class EntityManager { diff --git a/Source/MonoGame.Extended.Entities/EntityProcessingSystem.cs b/Source/MonoGame.Extended.Entities/Legacy/EntityProcessingSystem.cs similarity index 99% rename from Source/MonoGame.Extended.Entities/EntityProcessingSystem.cs rename to Source/MonoGame.Extended.Entities/Legacy/EntityProcessingSystem.cs index 6c590576..e47740ff 100644 --- a/Source/MonoGame.Extended.Entities/EntityProcessingSystem.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/EntityProcessingSystem.cs @@ -37,7 +37,7 @@ using System.Collections.Generic; using Microsoft.Xna.Framework; -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { public abstract class EntityProcessingSystem : ProcessingSystem { diff --git a/Source/MonoGame.Extended.Entities/EntitySystemAttribute.cs b/Source/MonoGame.Extended.Entities/Legacy/EntitySystemAttribute.cs similarity index 98% rename from Source/MonoGame.Extended.Entities/EntitySystemAttribute.cs rename to Source/MonoGame.Extended.Entities/Legacy/EntitySystemAttribute.cs index 2a405a7c..f5892738 100644 --- a/Source/MonoGame.Extended.Entities/EntitySystemAttribute.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/EntitySystemAttribute.cs @@ -36,7 +36,7 @@ using System; -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { [AttributeUsage(AttributeTargets.Class, Inherited = false)] public sealed class EntitySystemAttribute : Attribute diff --git a/Source/MonoGame.Extended.Entities/EntityTemplate.cs b/Source/MonoGame.Extended.Entities/Legacy/EntityTemplate.cs similarity index 98% rename from Source/MonoGame.Extended.Entities/EntityTemplate.cs rename to Source/MonoGame.Extended.Entities/Legacy/EntityTemplate.cs index 58299061..1660e95c 100644 --- a/Source/MonoGame.Extended.Entities/EntityTemplate.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/EntityTemplate.cs @@ -34,7 +34,7 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { public abstract class EntityTemplate { diff --git a/Source/MonoGame.Extended.Entities/EntityTemplateAttribute.cs b/Source/MonoGame.Extended.Entities/Legacy/EntityTemplateAttribute.cs similarity index 98% rename from Source/MonoGame.Extended.Entities/EntityTemplateAttribute.cs rename to Source/MonoGame.Extended.Entities/Legacy/EntityTemplateAttribute.cs index 07da8b05..e2c4f052 100644 --- a/Source/MonoGame.Extended.Entities/EntityTemplateAttribute.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/EntityTemplateAttribute.cs @@ -36,7 +36,7 @@ using System; -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { [AttributeUsage(AttributeTargets.Class, Inherited = false)] public sealed class EntityTemplateAttribute : Attribute diff --git a/Source/MonoGame.Extended.Entities/PoolableComponent.cs b/Source/MonoGame.Extended.Entities/Legacy/PoolableComponent.cs similarity index 98% rename from Source/MonoGame.Extended.Entities/PoolableComponent.cs rename to Source/MonoGame.Extended.Entities/Legacy/PoolableComponent.cs index 44876f42..cf68579d 100644 --- a/Source/MonoGame.Extended.Entities/PoolableComponent.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/PoolableComponent.cs @@ -36,7 +36,7 @@ using MonoGame.Extended.Collections; -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { public abstract class PoolableComponent : IPoolable { diff --git a/Source/MonoGame.Extended.Entities/ProcessingSystem.cs b/Source/MonoGame.Extended.Entities/Legacy/ProcessingSystem.cs similarity index 98% rename from Source/MonoGame.Extended.Entities/ProcessingSystem.cs rename to Source/MonoGame.Extended.Entities/Legacy/ProcessingSystem.cs index 49d615f1..3e0061af 100644 --- a/Source/MonoGame.Extended.Entities/ProcessingSystem.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/ProcessingSystem.cs @@ -38,7 +38,7 @@ using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { public abstract class ProcessingSystem { diff --git a/Source/MonoGame.Extended.Entities/SystemManager.cs b/Source/MonoGame.Extended.Entities/Legacy/SystemManager.cs similarity index 98% rename from Source/MonoGame.Extended.Entities/SystemManager.cs rename to Source/MonoGame.Extended.Entities/Legacy/SystemManager.cs index c15ffd69..b53f4bda 100644 --- a/Source/MonoGame.Extended.Entities/SystemManager.cs +++ b/Source/MonoGame.Extended.Entities/Legacy/SystemManager.cs @@ -40,7 +40,7 @@ using System.Diagnostics; using Microsoft.Xna.Framework; using MonoGame.Extended.Collections; -namespace MonoGame.Extended.Entities +namespace MonoGame.Extended.Entities.Legacy { public enum GameLoopType { @@ -149,10 +149,8 @@ namespace MonoGame.Extended.Entities system.EntityComponentSystem = _manager; Systems.Add(system); - - - var processingSystem = system as EntityProcessingSystem; - if (processingSystem != null) + + if (system is EntityProcessingSystem processingSystem) { processingSystem.Index = ProcessingSystems.Count; ProcessingSystems.Add(processingSystem); diff --git a/Source/MonoGame.Extended.sln b/Source/MonoGame.Extended.sln index a8fffb61..b2a703a1 100644 --- a/Source/MonoGame.Extended.sln +++ b/Source/MonoGame.Extended.sln @@ -97,6 +97,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gui", "Demos\Gui\Gui.csproj EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGame.Extended.Collisions.Tests", "Tests\MonoGame.Extended.Collisions.Tests\MonoGame.Extended.Collisions.Tests.csproj", "{CB439E84-F0F6-4790-8CD1-8A66C3D7B4DA}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sandbox", "Demos\Sandbox\Sandbox.csproj", "{778B49B7-CA0E-4BC5-82EA-326E8019379C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -297,6 +299,14 @@ Global {CB439E84-F0F6-4790-8CD1-8A66C3D7B4DA}.Release|Any CPU.Build.0 = Release|Any CPU {CB439E84-F0F6-4790-8CD1-8A66C3D7B4DA}.Release|x86.ActiveCfg = Release|Any CPU {CB439E84-F0F6-4790-8CD1-8A66C3D7B4DA}.Release|x86.Build.0 = Release|Any CPU + {778B49B7-CA0E-4BC5-82EA-326E8019379C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {778B49B7-CA0E-4BC5-82EA-326E8019379C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {778B49B7-CA0E-4BC5-82EA-326E8019379C}.Debug|x86.ActiveCfg = Debug|Any CPU + {778B49B7-CA0E-4BC5-82EA-326E8019379C}.Debug|x86.Build.0 = Debug|Any CPU + {778B49B7-CA0E-4BC5-82EA-326E8019379C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {778B49B7-CA0E-4BC5-82EA-326E8019379C}.Release|Any CPU.Build.0 = Release|Any CPU + {778B49B7-CA0E-4BC5-82EA-326E8019379C}.Release|x86.ActiveCfg = Release|Any CPU + {778B49B7-CA0E-4BC5-82EA-326E8019379C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -314,6 +324,7 @@ Global {34841DEB-EEC0-477C-B19D-CBB6DF49ED39} = {E5A148A1-DE7B-4D17-ABE8-831B9673B863} {8AC0C265-E986-4AB1-8FF7-4A0582695B0D} = {932F8931-4A8D-4205-BFCF-98E794207786} {CB439E84-F0F6-4790-8CD1-8A66C3D7B4DA} = {E5A148A1-DE7B-4D17-ABE8-831B9673B863} + {778B49B7-CA0E-4BC5-82EA-326E8019379C} = {932F8931-4A8D-4205-BFCF-98E794207786} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8ED5A62D-25EC-4331-9F99-BDD1E10C02A0} diff --git a/Source/MonoGame.Extended/Collections/Bag.cs b/Source/MonoGame.Extended/Collections/Bag.cs index bd5967ca..64e03ba3 100644 --- a/Source/MonoGame.Extended/Collections/Bag.cs +++ b/Source/MonoGame.Extended/Collections/Bag.cs @@ -55,7 +55,7 @@ namespace MonoGame.Extended.Collections public T this[int index] { - get { return _items[index]; } + get => _items[index]; set { EnsureCapacity(index + 1); diff --git a/Source/Tests/MonoGame.Extended.Entities.Tests/ComponentMapperTests.cs b/Source/Tests/MonoGame.Extended.Entities.Tests/ComponentMapperTests.cs new file mode 100644 index 00000000..ea8007c7 --- /dev/null +++ b/Source/Tests/MonoGame.Extended.Entities.Tests/ComponentMapperTests.cs @@ -0,0 +1,70 @@ +using Xunit; + +namespace MonoGame.Extended.Entities.Tests +{ + public class ComponentManagerTests + { + [Fact] + public void CreateComponentManager() + { + var manager = new ComponentManager(); + + } + } + + public class ComponentMapperTests + { + [Fact] + public void CreateComponentMapper() + { + var mapper = new ComponentMapper(); + + Assert.Equal(typeof(object), mapper.ComponentType); + Assert.Empty(mapper.Components); + } + + [Fact] + public void PutAndGetComponent() + { + const int entityId = 3; + + var mapper = new ComponentMapper(); + var component = new Transform2(); + + mapper.Put(entityId, component); + + Assert.Equal(typeof(Transform2), mapper.ComponentType); + Assert.True(mapper.Components.Count >= 1); + Assert.Same(component, mapper.Get(entityId)); + } + + [Fact] + public void DeleteComponent() + { + const int entityId = 1; + + var mapper = new ComponentMapper(); + var component = new Transform2(); + + mapper.Put(entityId, component); + mapper.Delete(entityId); + + Assert.False(mapper.Has(entityId)); + } + + [Fact] + public void HasComponent() + { + const int entityId = 0; + + var mapper = new ComponentMapper(); + var component = new Transform2(); + + Assert.False(mapper.Has(entityId)); + + mapper.Put(entityId, component); + + Assert.True(mapper.Has(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 new file mode 100644 index 00000000..f6b14ccf --- /dev/null +++ b/Source/Tests/MonoGame.Extended.Entities.Tests/ComponentTypeTests.cs @@ -0,0 +1,20 @@ +using MonoGame.Extended.Sprites; +using Xunit; + +namespace MonoGame.Extended.Entities.Tests +{ + 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()); + } + } +} \ No newline at end of file diff --git a/Source/Tests/MonoGame.Extended.Entities.Tests/EntityTemplateTests.cs b/Source/Tests/MonoGame.Extended.Entities.Tests/EntityTemplateTests.cs index 10aa6b2e..617e0a06 100644 --- a/Source/Tests/MonoGame.Extended.Entities.Tests/EntityTemplateTests.cs +++ b/Source/Tests/MonoGame.Extended.Entities.Tests/EntityTemplateTests.cs @@ -1,12 +1,9 @@ -using System; -using Microsoft.Xna.Framework; -using MonoGame.Extended.Entities; +using MonoGame.Extended.Entities.Legacy; using MonoGame.Extended.Gui.Tests.Implementation; using Xunit; -namespace MonoGame.Extended.Gui.Tests +namespace MonoGame.Extended.Entities.Tests { - public class EntityTemplateTests { [Fact] diff --git a/Source/Tests/MonoGame.Extended.Entities.Tests/Implementation/EntityTemplateBasic.cs b/Source/Tests/MonoGame.Extended.Entities.Tests/Implementation/EntityTemplateBasic.cs index a4d7bfe6..b2b48f9d 100644 --- a/Source/Tests/MonoGame.Extended.Entities.Tests/Implementation/EntityTemplateBasic.cs +++ b/Source/Tests/MonoGame.Extended.Entities.Tests/Implementation/EntityTemplateBasic.cs @@ -1,4 +1,5 @@ using MonoGame.Extended.Entities; +using MonoGame.Extended.Entities.Legacy; namespace MonoGame.Extended.Gui.Tests.Implementation { diff --git a/Source/Tests/MonoGame.Extended.Entities.Tests/Implementation/EntityTemplateUsingManager.cs b/Source/Tests/MonoGame.Extended.Entities.Tests/Implementation/EntityTemplateUsingManager.cs index 260345e1..8d70553b 100644 --- a/Source/Tests/MonoGame.Extended.Entities.Tests/Implementation/EntityTemplateUsingManager.cs +++ b/Source/Tests/MonoGame.Extended.Entities.Tests/Implementation/EntityTemplateUsingManager.cs @@ -1,4 +1,5 @@ using MonoGame.Extended.Entities; +using MonoGame.Extended.Entities.Legacy; namespace MonoGame.Extended.Gui.Tests.Implementation {