From 601f47c599c695400c91173b3ade2bbd10b6c570 Mon Sep 17 00:00:00 2001 From: Nik Cherednik Date: Fri, 20 Oct 2023 19:17:38 +0300 Subject: [PATCH] MG-34: Collision (#12) * chore: init rect collision feat & sys * chore: draw rect collision component & conditional debug * feat!: draft. collision working * refactor: collision system split wit debugging * feat: fixed time step * chore: entities spawn pos changed --- MonoGame.sln | 14 +++++ .../Components/ComponentsCompositionRoot.cs | 20 ++++++- .../DebugRootFeatureCompositionRoot.cs | 36 ++++++++++++ .../Entities/PlayerEntityCompositionRoot.cs | 10 +++- .../Entities/StaticEntityCompositionRoot.cs | 10 +++- .../Features/CameraFeatureCompositionRoot.cs | 11 ++-- .../Features/InputFeatureCompositionRoot.cs | 2 +- .../MovementFeatureCompositionRoot.cs | 17 +++++- .../Features/RootFeatureCompositionRoot.cs | 21 ++++--- .../WorldInitializeFeatureCompositionRoot.cs | 2 +- .../FundamentalCompositionRoot.cs | 2 +- .../CompositionRoots/GameCompositionRoot.cs | 7 ++- src/Apps/GameDesktop/Game.cs | 21 +++++-- .../Components/{ => Data}/CameraComponent.cs | 2 +- .../{ => Data}/MovementAnimationComponent.cs | 4 +- .../RectangleCollisionComponent.cs | 2 +- .../Components/{ => Data}/SpriteComponent.cs | 4 +- .../{World => Data}/TransformComponent.cs | 2 +- src/Libs/Components/DrawableComponent.cs | 7 --- .../GeneratedExtended/GameEntity.cs | 7 ++- src/Libs/Components/Tags/DrawableComponent.cs | 9 +++ .../{World => Tags}/MovableComponent.cs | 2 +- .../{World => Tags}/PlayerComponent.cs | 2 +- src/Libs/Entities/PlayerEntity.cs | 7 ++- src/Libs/Entities/StaticEntity.cs | 7 ++- .../Features.Debugging/DebugRootFeature.cs | 11 ++++ .../Features.Debugging.csproj | 14 +++++ src/Libs/Features/Features.csproj | 1 + src/Libs/Features/MovementFeature.cs | 5 +- src/Libs/Features/RootFeature.cs | 13 ++++- .../DrawRectangleCollisionComponentsSystem.cs | 50 +++++++++++++++++ .../Systems.Debugging.csproj | 17 ++++++ src/Libs/Systems/AnimatedMovementSystem.cs | 1 + src/Libs/Systems/CollisionSystem.cs | 56 +++++++++++++++++++ src/Libs/Systems/DefaultDrawSystem.cs | 4 ++ src/Libs/Systems/InputSystem.cs | 8 ++- src/Libs/Systems/MovementSystem.cs | 5 +- 37 files changed, 350 insertions(+), 63 deletions(-) create mode 100644 src/Apps/GameDesktop/CompositionRoots/DebugFeatures/DebugRootFeatureCompositionRoot.cs rename src/Libs/Components/{ => Data}/CameraComponent.cs (86%) rename src/Libs/Components/{ => Data}/MovementAnimationComponent.cs (94%) rename src/Libs/Components/{World => Data}/RectangleCollisionComponent.cs (82%) rename src/Libs/Components/{ => Data}/SpriteComponent.cs (81%) rename src/Libs/Components/{World => Data}/TransformComponent.cs (93%) delete mode 100644 src/Libs/Components/DrawableComponent.cs create mode 100644 src/Libs/Components/Tags/DrawableComponent.cs rename src/Libs/Components/{World => Tags}/MovableComponent.cs (70%) rename src/Libs/Components/{World => Tags}/PlayerComponent.cs (70%) create mode 100644 src/Libs/Features.Debugging/DebugRootFeature.cs create mode 100644 src/Libs/Features.Debugging/Features.Debugging.csproj create mode 100644 src/Libs/Systems.Debugging/DrawRectangleCollisionComponentsSystem.cs create mode 100644 src/Libs/Systems.Debugging/Systems.Debugging.csproj create mode 100644 src/Libs/Systems/CollisionSystem.cs diff --git a/MonoGame.sln b/MonoGame.sln index 9957322..d6d5a9d 100644 --- a/MonoGame.sln +++ b/MonoGame.sln @@ -29,6 +29,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Features", "src\Libs\Featur EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Entities", "src\Libs\Entities\Entities.csproj", "{8232C7AA-E217-465B-95A3-C42D1000FA3B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Systems.Debugging", "src\Libs\Systems.Debugging\Systems.Debugging.csproj", "{7AD01EBF-370B-4F35-9AEC-172A889D643F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Features.Debugging", "src\Libs\Features.Debugging\Features.Debugging.csproj", "{8E6812DB-F7B8-411D-BD15-4BCFA2BF6C28}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -70,6 +74,14 @@ Global {8232C7AA-E217-465B-95A3-C42D1000FA3B}.Debug|Any CPU.Build.0 = Debug|Any CPU {8232C7AA-E217-465B-95A3-C42D1000FA3B}.Release|Any CPU.ActiveCfg = Release|Any CPU {8232C7AA-E217-465B-95A3-C42D1000FA3B}.Release|Any CPU.Build.0 = Release|Any CPU + {7AD01EBF-370B-4F35-9AEC-172A889D643F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7AD01EBF-370B-4F35-9AEC-172A889D643F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7AD01EBF-370B-4F35-9AEC-172A889D643F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7AD01EBF-370B-4F35-9AEC-172A889D643F}.Release|Any CPU.Build.0 = Release|Any CPU + {8E6812DB-F7B8-411D-BD15-4BCFA2BF6C28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E6812DB-F7B8-411D-BD15-4BCFA2BF6C28}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E6812DB-F7B8-411D-BD15-4BCFA2BF6C28}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E6812DB-F7B8-411D-BD15-4BCFA2BF6C28}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {5E3B6238-A92E-4703-8527-1C2410D7906A} = {F5258F7E-B0BA-466D-8CF8-4DAB84722BE3} @@ -84,5 +96,7 @@ Global {6EEBFA40-2814-43D0-A9DC-4A98F4837A1B} = {FE9CE932-6F98-4455-A80F-2A0CDB6DB46E} {F4C957C8-3B10-4614-99F4-E2F2398F38ED} = {7AE4DC46-59EB-4F69-9240-C7363F0778AA} {8232C7AA-E217-465B-95A3-C42D1000FA3B} = {7AE4DC46-59EB-4F69-9240-C7363F0778AA} + {7AD01EBF-370B-4F35-9AEC-172A889D643F} = {7AE4DC46-59EB-4F69-9240-C7363F0778AA} + {8E6812DB-F7B8-411D-BD15-4BCFA2BF6C28} = {7AE4DC46-59EB-4F69-9240-C7363F0778AA} EndGlobalSection EndGlobal diff --git a/src/Apps/GameDesktop/CompositionRoots/Components/ComponentsCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/Components/ComponentsCompositionRoot.cs index 159eeeb..445a2e4 100644 --- a/src/Apps/GameDesktop/CompositionRoots/Components/ComponentsCompositionRoot.cs +++ b/src/Apps/GameDesktop/CompositionRoots/Components/ComponentsCompositionRoot.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using Components; -using Components.World; +using Components.Data; using GameDesktop.Resources.Internal; using LightInject; using Microsoft.Xna.Framework; @@ -10,7 +10,7 @@ namespace GameDesktop.CompositionRoots.Components; -public class ComponentsCompositionRoot : ICompositionRoot +internal class ComponentsCompositionRoot : ICompositionRoot { private static readonly string PlayerSpriteSheetPath = System.IO.Path.Join( Environment.GetEnvironmentVariable(EnvironmentVariable.AppBaseDirectory), @@ -22,6 +22,7 @@ public void Compose(IServiceRegistry serviceRegistry) RegisterMovementAnimationComponent(serviceRegistry); RegisterCameraComponent(serviceRegistry); RegisterTransformComponent(serviceRegistry); + RegisterRectangleCollisionComponent(serviceRegistry); } private static void RegisterSpriteComponent(IServiceRegistry serviceRegistry) @@ -55,6 +56,19 @@ private static void RegisterCameraComponent(IServiceRegistry serviceRegistry) => private static void RegisterTransformComponent(IServiceRegistry serviceRegistry) { - serviceRegistry.RegisterTransient(); + serviceRegistry.RegisterSingleton(_ => + { + return new TransformComponent { Position = new(316, 116) }; + }, "Player"); + + serviceRegistry.RegisterSingleton(_ => + { + return new TransformComponent { Position = new(300, 100) }; + }, "StaticEntity"); + } + + private static void RegisterRectangleCollisionComponent(IServiceRegistry serviceRegistry) + { + serviceRegistry.RegisterTransient(_ => new RectangleCollisionComponent { Size = new Rectangle(0, 0, 8, 8) }); } } diff --git a/src/Apps/GameDesktop/CompositionRoots/DebugFeatures/DebugRootFeatureCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/DebugFeatures/DebugRootFeatureCompositionRoot.cs new file mode 100644 index 0000000..a74508c --- /dev/null +++ b/src/Apps/GameDesktop/CompositionRoots/DebugFeatures/DebugRootFeatureCompositionRoot.cs @@ -0,0 +1,36 @@ +using System; +using Entitas; +using Features.Debugging; +using GameDesktop.Resources.Internal; +using LightInject; +using Serilog; +using Systems.Debugging; + +namespace GameDesktop.CompositionRoots.DebugFeatures; + +internal class DebugRootFeatureCompositionRoot : ICompositionRoot +{ + private static readonly IMatcher[] Matchers = { GameMatcher.RectangleCollision, GameMatcher.Transform }; + + public void Compose(IServiceRegistry serviceRegistry) + { + RegisterSystems(serviceRegistry); + RegisterFeature(serviceRegistry); + } + + private static void RegisterSystems(IServiceRegistry serviceRegistry) + { + serviceRegistry.RegisterSingleton(factory => + { + var getGroup = + factory.GetInstance[], IGroup>>(Matcher.AllOf); + IGroup group = getGroup(Matchers); + + return new DrawRectangleCollisionComponentsSystem(factory.GetInstance(), group, + factory.GetInstance()); + }); + } + + private static void RegisterFeature(IServiceRegistry serviceRegistry) => + serviceRegistry.RegisterSingleton(); +} diff --git a/src/Apps/GameDesktop/CompositionRoots/Entities/PlayerEntityCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/Entities/PlayerEntityCompositionRoot.cs index 6f37738..cdf1c6e 100644 --- a/src/Apps/GameDesktop/CompositionRoots/Entities/PlayerEntityCompositionRoot.cs +++ b/src/Apps/GameDesktop/CompositionRoots/Entities/PlayerEntityCompositionRoot.cs @@ -1,9 +1,10 @@ -using Entities; +using Components.Data; +using Entities; using LightInject; namespace GameDesktop.CompositionRoots.Entities; -public class PlayerEntityCompositionRoot : ICompositionRoot +internal class PlayerEntityCompositionRoot : ICompositionRoot { public void Compose(IServiceRegistry serviceRegistry) { @@ -11,5 +12,8 @@ public void Compose(IServiceRegistry serviceRegistry) } private static void RegisterEntity(IServiceRegistry serviceRegistry) => - serviceRegistry.RegisterTransient(); + serviceRegistry.RegisterTransient(factory => new PlayerEntity(factory.GetInstance(), + factory.GetInstance(), + factory.GetInstance("Player"), factory.GetInstance(), + factory.GetInstance())); } diff --git a/src/Apps/GameDesktop/CompositionRoots/Entities/StaticEntityCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/Entities/StaticEntityCompositionRoot.cs index 72d05cd..e9d0d99 100644 --- a/src/Apps/GameDesktop/CompositionRoots/Entities/StaticEntityCompositionRoot.cs +++ b/src/Apps/GameDesktop/CompositionRoots/Entities/StaticEntityCompositionRoot.cs @@ -1,9 +1,10 @@ -using Entities; +using Components.Data; +using Entities; using LightInject; namespace GameDesktop.CompositionRoots.Entities; -public class StaticEntityCompositionRoot : ICompositionRoot +internal class StaticEntityCompositionRoot : ICompositionRoot { public void Compose(IServiceRegistry serviceRegistry) { @@ -12,5 +13,8 @@ public void Compose(IServiceRegistry serviceRegistry) private static void RegisterEntity(IServiceRegistry serviceRegistry) => - serviceRegistry.RegisterTransient(); + serviceRegistry.RegisterTransient(factory => new StaticEntity(factory.GetInstance(), + factory.GetInstance("StaticEntity"), + factory.GetInstance(), + factory.GetInstance())); } diff --git a/src/Apps/GameDesktop/CompositionRoots/Features/CameraFeatureCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/Features/CameraFeatureCompositionRoot.cs index 2482ed4..28c2588 100644 --- a/src/Apps/GameDesktop/CompositionRoots/Features/CameraFeatureCompositionRoot.cs +++ b/src/Apps/GameDesktop/CompositionRoots/Features/CameraFeatureCompositionRoot.cs @@ -1,5 +1,6 @@ using System; using Entitas; +using Entitas.Extended; using Features; using GameDesktop.Resources.Internal; using LightInject; @@ -7,7 +8,7 @@ namespace GameDesktop.CompositionRoots.Features; -public class CameraFeatureCompositionRoot : ICompositionRoot +internal class CameraFeatureCompositionRoot : ICompositionRoot { private static readonly IMatcher[] Matchers = { GameMatcher.Transform, GameMatcher.Drawable }; @@ -19,15 +20,15 @@ public void Compose(IServiceRegistry serviceRegistry) private static void RegisterSystem(IServiceRegistry serviceRegistry) { - serviceRegistry.RegisterFallback((type, s) => true, request => + serviceRegistry.RegisterSingleton(factory => { var getGroup = - request.ServiceFactory.GetInstance[], IGroup>>(Matcher.AllOf); + factory.GetInstance[], IGroup>>(Matcher.AllOf); IGroup group = getGroup(Matchers); - // return new CameraFollowingSystem(request.ServiceFactory.GetInstance(), group); + // return new CameraFollowingSystem(factory.GetInstance(), group); return new DefaultDrawSystem(group); - }, new PerContainerLifetime()); + }); } private static void RegisterFeature(IServiceRegistry serviceRegistry) => diff --git a/src/Apps/GameDesktop/CompositionRoots/Features/InputFeatureCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/Features/InputFeatureCompositionRoot.cs index 1306d49..d1e6812 100644 --- a/src/Apps/GameDesktop/CompositionRoots/Features/InputFeatureCompositionRoot.cs +++ b/src/Apps/GameDesktop/CompositionRoots/Features/InputFeatureCompositionRoot.cs @@ -9,7 +9,7 @@ namespace GameDesktop.CompositionRoots.Features; -public class InputFeatureCompositionRoot : ICompositionRoot +internal class InputFeatureCompositionRoot : ICompositionRoot { private static readonly IMatcher[] Matchers = { diff --git a/src/Apps/GameDesktop/CompositionRoots/Features/MovementFeatureCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/Features/MovementFeatureCompositionRoot.cs index 2e336b9..7efe4bf 100644 --- a/src/Apps/GameDesktop/CompositionRoots/Features/MovementFeatureCompositionRoot.cs +++ b/src/Apps/GameDesktop/CompositionRoots/Features/MovementFeatureCompositionRoot.cs @@ -10,8 +10,13 @@ namespace GameDesktop.CompositionRoots.Features; -public class MovementFeatureCompositionRoot : ICompositionRoot +internal class MovementFeatureCompositionRoot : ICompositionRoot { + private static readonly IMatcher[] CollisionMatchers = + { + GameMatcher.RectangleCollision, GameMatcher.Transform + }; + private static readonly IMatcher[] MovableMatchers = { GameMatcher.Transform, GameMatcher.Movable }; private static readonly IMatcher[] AnimatedMovableMatchers = @@ -31,6 +36,16 @@ private static void RegisterImpl(IServiceRegistry serviceRegistry) => private static void RegisterSystems(IServiceRegistry serviceRegistry) { + serviceRegistry.RegisterSingleton(factory => + { + var getGroup = factory.GetInstance[], IGroup>>(Matcher.AllOf); + IGroup group = getGroup(CollisionMatchers); + + var logger = factory.GetInstance(); + + return new CollisionSystem(group, logger); + }); + serviceRegistry.RegisterSingleton(factory => { var movement = factory.GetInstance(); diff --git a/src/Apps/GameDesktop/CompositionRoots/Features/RootFeatureCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/Features/RootFeatureCompositionRoot.cs index fabe1b9..3edf6d1 100644 --- a/src/Apps/GameDesktop/CompositionRoots/Features/RootFeatureCompositionRoot.cs +++ b/src/Apps/GameDesktop/CompositionRoots/Features/RootFeatureCompositionRoot.cs @@ -1,11 +1,12 @@ using Features; using GameDesktop.CompositionRoots.Components; +using GameDesktop.CompositionRoots.DebugFeatures; using GameDesktop.CompositionRoots.Entities; using LightInject; namespace GameDesktop.CompositionRoots.Features; -public class RootFeatureCompositionRoot : ICompositionRoot +internal class RootFeatureCompositionRoot : ICompositionRoot { public void Compose(IServiceRegistry serviceRegistry) { @@ -23,19 +24,18 @@ public void Compose(IServiceRegistry serviceRegistry) RegisterFeatures(serviceRegistry); - // Main entry point - serviceRegistry.RegisterSingleton(); +#if DEBUG + serviceRegistry.RegisterFrom(); +#endif + + RegisterEntryPoint(serviceRegistry); } - private static void RegisterFundamental(IServiceRegistry serviceRegistry) - { + private static void RegisterFundamental(IServiceRegistry serviceRegistry) => serviceRegistry.RegisterFrom(); - } - private static void RegisterComponents(IServiceRegistry serviceRegistry) - { + private static void RegisterComponents(IServiceRegistry serviceRegistry) => serviceRegistry.RegisterFrom(); - } private static void RegisterEntities(IServiceRegistry serviceRegistry) { @@ -50,4 +50,7 @@ private static void RegisterFeatures(IServiceRegistry serviceRegistry) serviceRegistry.RegisterFrom(); serviceRegistry.RegisterFrom(); } + + private static void RegisterEntryPoint(IServiceRegistry serviceRegistry) => + serviceRegistry.RegisterSingleton(); } diff --git a/src/Apps/GameDesktop/CompositionRoots/Features/WorldInitializeFeatureCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/Features/WorldInitializeFeatureCompositionRoot.cs index 24b07c3..e6c7fd5 100644 --- a/src/Apps/GameDesktop/CompositionRoots/Features/WorldInitializeFeatureCompositionRoot.cs +++ b/src/Apps/GameDesktop/CompositionRoots/Features/WorldInitializeFeatureCompositionRoot.cs @@ -3,7 +3,7 @@ namespace GameDesktop.CompositionRoots.Features; -public class WorldInitializeFeatureCompositionRoot : ICompositionRoot +internal class WorldInitializeFeatureCompositionRoot : ICompositionRoot { public void Compose(IServiceRegistry serviceRegistry) { diff --git a/src/Apps/GameDesktop/CompositionRoots/FundamentalCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/FundamentalCompositionRoot.cs index 6e58379..f6631a1 100644 --- a/src/Apps/GameDesktop/CompositionRoots/FundamentalCompositionRoot.cs +++ b/src/Apps/GameDesktop/CompositionRoots/FundamentalCompositionRoot.cs @@ -9,7 +9,7 @@ namespace GameDesktop.CompositionRoots; -public class FundamentalCompositionRoot : ICompositionRoot +internal class FundamentalCompositionRoot : ICompositionRoot { public void Compose(IServiceRegistry serviceRegistry) { diff --git a/src/Apps/GameDesktop/CompositionRoots/GameCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/GameCompositionRoot.cs index 4107273..53584e4 100644 --- a/src/Apps/GameDesktop/CompositionRoots/GameCompositionRoot.cs +++ b/src/Apps/GameDesktop/CompositionRoots/GameCompositionRoot.cs @@ -5,15 +5,18 @@ namespace GameDesktop.CompositionRoots; -public class GameCompositionRoot : ICompositionRoot +internal class GameCompositionRoot : ICompositionRoot { + private const float TargetFramesPerSecond = 120.0f; private const bool IsMouseVisible = true; public void Compose(IServiceRegistry serviceRegistry) { serviceRegistry.Register(factory => { - Game game = new(factory.GetInstance(), factory.GetInstance()) + Game game = new(factory.GetInstance(), + factory.GetInstance(), + TargetFramesPerSecond) { IsMouseVisible = IsMouseVisible, Content = { RootDirectory = AppVariable.ContentRootDirectory, } }; diff --git a/src/Apps/GameDesktop/Game.cs b/src/Apps/GameDesktop/Game.cs index fbe5e3a..c1b48b7 100644 --- a/src/Apps/GameDesktop/Game.cs +++ b/src/Apps/GameDesktop/Game.cs @@ -1,4 +1,5 @@ -using Features; +using System; +using Features; using GameDesktop.CompositionRoots.Features; using LightInject; using Microsoft.Xna.Framework; @@ -9,17 +10,21 @@ namespace GameDesktop; public class Game : Microsoft.Xna.Framework.Game { + private readonly float _targetTimeStep; + private readonly ILogger _logger; private readonly IServiceContainer _container; private Entitas.Extended.Feature _rootFeature; private SpriteBatch _spriteBatch; + private float _accumulatedTime; - public Game(ILogger logger, IServiceContainer container) + public Game(ILogger logger, IServiceContainer container, float targetFramesPerSecond) { _logger = logger; _container = container; + _targetTimeStep = 1 / targetFramesPerSecond; _logger.ForContext().Verbose("ctor"); } @@ -75,11 +80,19 @@ protected override void EndRun() _logger.ForContext().Verbose("Ended"); } - private void FixedUpdate(GameTime fixedGameTime) => _rootFeature.FixedExecute(fixedGameTime); + // maybe fixed update is incorrect. needs review in the future + private void FixedUpdate(GameTime gameTime) => _rootFeature.FixedExecute(gameTime); protected override void Update(GameTime gameTime) { - FixedUpdate(gameTime); + _accumulatedTime += (float)gameTime.ElapsedGameTime.TotalSeconds; + + while (_accumulatedTime >= _targetTimeStep) + { + FixedUpdate(gameTime); + + _accumulatedTime -= _targetTimeStep; + } base.Update(gameTime); _rootFeature.Execute(gameTime); diff --git a/src/Libs/Components/CameraComponent.cs b/src/Libs/Components/Data/CameraComponent.cs similarity index 86% rename from src/Libs/Components/CameraComponent.cs rename to src/Libs/Components/Data/CameraComponent.cs index 837a8e0..745245d 100644 --- a/src/Libs/Components/CameraComponent.cs +++ b/src/Libs/Components/Data/CameraComponent.cs @@ -2,7 +2,7 @@ using Entitas.CodeGeneration.Attributes; using Microsoft.Xna.Framework; -namespace Components; +namespace Components.Data; [Unique] public class CameraComponent : IComponent diff --git a/src/Libs/Components/MovementAnimationComponent.cs b/src/Libs/Components/Data/MovementAnimationComponent.cs similarity index 94% rename from src/Libs/Components/MovementAnimationComponent.cs rename to src/Libs/Components/Data/MovementAnimationComponent.cs index 7c4bb40..78e83e5 100644 --- a/src/Libs/Components/MovementAnimationComponent.cs +++ b/src/Libs/Components/Data/MovementAnimationComponent.cs @@ -1,9 +1,9 @@ -using Entitas; +using Components.Tags; using Microsoft.Xna.Framework; using MonoGame.Aseprite.Sprites; using Services.Math; -namespace Components; +namespace Components.Data; public class MovementAnimationComponent : DrawableComponent { diff --git a/src/Libs/Components/World/RectangleCollisionComponent.cs b/src/Libs/Components/Data/RectangleCollisionComponent.cs similarity index 82% rename from src/Libs/Components/World/RectangleCollisionComponent.cs rename to src/Libs/Components/Data/RectangleCollisionComponent.cs index 858b1ba..42c682d 100644 --- a/src/Libs/Components/World/RectangleCollisionComponent.cs +++ b/src/Libs/Components/Data/RectangleCollisionComponent.cs @@ -1,7 +1,7 @@ using Entitas; using Microsoft.Xna.Framework; -namespace Components.World; +namespace Components.Data; public class RectangleCollisionComponent : IComponent { diff --git a/src/Libs/Components/SpriteComponent.cs b/src/Libs/Components/Data/SpriteComponent.cs similarity index 81% rename from src/Libs/Components/SpriteComponent.cs rename to src/Libs/Components/Data/SpriteComponent.cs index 0ddb3c8..370dff6 100644 --- a/src/Libs/Components/SpriteComponent.cs +++ b/src/Libs/Components/Data/SpriteComponent.cs @@ -1,7 +1,7 @@ -using Entitas; +using Components.Tags; using MonoGame.Aseprite.Sprites; -namespace Components; +namespace Components.Data; public class SpriteComponent : DrawableComponent { diff --git a/src/Libs/Components/World/TransformComponent.cs b/src/Libs/Components/Data/TransformComponent.cs similarity index 93% rename from src/Libs/Components/World/TransformComponent.cs rename to src/Libs/Components/Data/TransformComponent.cs index ebb976b..657132c 100644 --- a/src/Libs/Components/World/TransformComponent.cs +++ b/src/Libs/Components/Data/TransformComponent.cs @@ -1,7 +1,7 @@ using Entitas; using Microsoft.Xna.Framework; -namespace Components.World; +namespace Components.Data; public class TransformComponent : IComponent { diff --git a/src/Libs/Components/DrawableComponent.cs b/src/Libs/Components/DrawableComponent.cs deleted file mode 100644 index dd8daf9..0000000 --- a/src/Libs/Components/DrawableComponent.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Entitas; - -namespace Components; - -public class DrawableComponent : IComponent -{ -} diff --git a/src/Libs/Components/GeneratedExtended/GameEntity.cs b/src/Libs/Components/GeneratedExtended/GameEntity.cs index 0979085..9cb0416 100644 --- a/src/Libs/Components/GeneratedExtended/GameEntity.cs +++ b/src/Libs/Components/GeneratedExtended/GameEntity.cs @@ -1,5 +1,5 @@ using Components; -using Components.World; +using Components.Data; using Microsoft.Xna.Framework; public partial class GameEntity @@ -25,4 +25,9 @@ public void AddSprite(SpriteComponent component) isDrawable = true; AddSprite(newSprite: component.Sprite); } + + public void AddRectangleCollision(RectangleCollisionComponent component) + { + AddRectangleCollision(newSize: component.Size); + } } diff --git a/src/Libs/Components/Tags/DrawableComponent.cs b/src/Libs/Components/Tags/DrawableComponent.cs new file mode 100644 index 0000000..5374a23 --- /dev/null +++ b/src/Libs/Components/Tags/DrawableComponent.cs @@ -0,0 +1,9 @@ +using Entitas; + +// Tag components are the empty ones and prefixed with is- (e.g. isPlayer) +// When auto-generated +namespace Components.Tags; + +public class DrawableComponent : IComponent +{ +} diff --git a/src/Libs/Components/World/MovableComponent.cs b/src/Libs/Components/Tags/MovableComponent.cs similarity index 70% rename from src/Libs/Components/World/MovableComponent.cs rename to src/Libs/Components/Tags/MovableComponent.cs index e07dff6..0cbefb9 100644 --- a/src/Libs/Components/World/MovableComponent.cs +++ b/src/Libs/Components/Tags/MovableComponent.cs @@ -1,6 +1,6 @@ using Entitas; -namespace Components.World; +namespace Components.Tags; public class MovableComponent : IComponent { diff --git a/src/Libs/Components/World/PlayerComponent.cs b/src/Libs/Components/Tags/PlayerComponent.cs similarity index 70% rename from src/Libs/Components/World/PlayerComponent.cs rename to src/Libs/Components/Tags/PlayerComponent.cs index d2a7f5e..e1c00af 100644 --- a/src/Libs/Components/World/PlayerComponent.cs +++ b/src/Libs/Components/Tags/PlayerComponent.cs @@ -1,6 +1,6 @@ using Entitas; -namespace Components.World; +namespace Components.Tags; public class PlayerComponent : IComponent { diff --git a/src/Libs/Entities/PlayerEntity.cs b/src/Libs/Entities/PlayerEntity.cs index 73b6902..f42b3d1 100644 --- a/src/Libs/Entities/PlayerEntity.cs +++ b/src/Libs/Entities/PlayerEntity.cs @@ -1,5 +1,5 @@ using Components; -using Components.World; +using Components.Data; namespace Entities; @@ -8,7 +8,8 @@ public class PlayerEntity public PlayerEntity(Contexts contexts, MovementAnimationComponent movementAnimationComponent, TransformComponent transformComponent, - CameraComponent cameraComponent) + CameraComponent cameraComponent, + RectangleCollisionComponent rectangleCollisionComponent) { GameEntity entity = contexts.game.CreateEntity(); @@ -17,6 +18,6 @@ public PlayerEntity(Contexts contexts, entity.AddMovementAnimation(movementAnimationComponent); entity.AddTransform(transformComponent); entity.AddCamera(cameraComponent); - // e.AddRectangleCollision(new Rectangle(0, 0, 16, 16)); + entity.AddRectangleCollision(rectangleCollisionComponent); } } diff --git a/src/Libs/Entities/StaticEntity.cs b/src/Libs/Entities/StaticEntity.cs index 5a2edc7..a3b33e2 100644 --- a/src/Libs/Entities/StaticEntity.cs +++ b/src/Libs/Entities/StaticEntity.cs @@ -1,5 +1,6 @@ using Components; -using Components.World; +using Components.Data; +using Microsoft.Xna.Framework; namespace Entities; @@ -7,11 +8,13 @@ public class StaticEntity { public StaticEntity(Contexts contexts, TransformComponent transformComponent, - SpriteComponent spriteComponent) + SpriteComponent spriteComponent, + RectangleCollisionComponent rectangleCollision) { GameEntity e = contexts.game.CreateEntity(); e.AddTransform(transformComponent); e.AddSprite(spriteComponent); + e.AddRectangleCollision(rectangleCollision); } } diff --git a/src/Libs/Features.Debugging/DebugRootFeature.cs b/src/Libs/Features.Debugging/DebugRootFeature.cs new file mode 100644 index 0000000..23867f6 --- /dev/null +++ b/src/Libs/Features.Debugging/DebugRootFeature.cs @@ -0,0 +1,11 @@ +using Systems.Debugging; + +namespace Features.Debugging; + +public sealed class DebugRootFeature : Entitas.Extended.Feature +{ + public DebugRootFeature(DrawRectangleCollisionComponentsSystem drawRectangleCollisionComponentsSystem) + { + Add(drawRectangleCollisionComponentsSystem); + } +} diff --git a/src/Libs/Features.Debugging/Features.Debugging.csproj b/src/Libs/Features.Debugging/Features.Debugging.csproj new file mode 100644 index 0000000..42d7fb2 --- /dev/null +++ b/src/Libs/Features.Debugging/Features.Debugging.csproj @@ -0,0 +1,14 @@ + + + + net7.0 + enable + enable + + + + + + + + diff --git a/src/Libs/Features/Features.csproj b/src/Libs/Features/Features.csproj index 39bb8f8..6298e7b 100644 --- a/src/Libs/Features/Features.csproj +++ b/src/Libs/Features/Features.csproj @@ -9,6 +9,7 @@ + diff --git a/src/Libs/Features/MovementFeature.cs b/src/Libs/Features/MovementFeature.cs index 3b0c7c3..62bdfec 100644 --- a/src/Libs/Features/MovementFeature.cs +++ b/src/Libs/Features/MovementFeature.cs @@ -4,9 +4,12 @@ namespace Features; public sealed class MovementFeature : Entitas.Extended.Feature { - public MovementFeature(MovementSystem movementSystem, + public MovementFeature( + CollisionSystem collisionSystem, + MovementSystem movementSystem, AnimatedMovementSystem animatedMovementSystem) { + Add(collisionSystem); Add(movementSystem); Add(animatedMovementSystem); } diff --git a/src/Libs/Features/RootFeature.cs b/src/Libs/Features/RootFeature.cs index 6b1fb79..815b056 100644 --- a/src/Libs/Features/RootFeature.cs +++ b/src/Libs/Features/RootFeature.cs @@ -1,4 +1,6 @@ -namespace Features; +using Features.Debugging; + +namespace Features; public sealed class RootFeature : Entitas.Extended.Feature { @@ -12,4 +14,13 @@ public RootFeature(WorldInitializeFeature worldInitializeFeature, Add(movementFeature); Add(cameraFeature); } + + public RootFeature(DebugRootFeature debugRootFeature, + WorldInitializeFeature worldInitializeFeature, + InputFeature inputFeature, + MovementFeature movementFeature, + CameraFeature cameraFeature) : this(worldInitializeFeature, inputFeature, movementFeature, cameraFeature) + { + Add(debugRootFeature); + } } diff --git a/src/Libs/Systems.Debugging/DrawRectangleCollisionComponentsSystem.cs b/src/Libs/Systems.Debugging/DrawRectangleCollisionComponentsSystem.cs new file mode 100644 index 0000000..2b2bacb --- /dev/null +++ b/src/Libs/Systems.Debugging/DrawRectangleCollisionComponentsSystem.cs @@ -0,0 +1,50 @@ +using Entitas; +using Entitas.Extended; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Serilog; + +namespace Systems.Debugging; + +public class DrawRectangleCollisionComponentsSystem : IDrawSystem +{ + private readonly Contexts _contexts; + private readonly IGroup _group; + private readonly ILogger _logger; + + public DrawRectangleCollisionComponentsSystem(Contexts contexts, IGroup group, ILogger logger) + { + _contexts = contexts; + _group = group; + _logger = logger; + } + + // TODO: Move the logic in the release features + // TODO: Pivot & anchors system + // TODO: Make circle colliders & relative system (sloped collision) + public void Draw(GameTime gameTime, SpriteBatch spriteBatch) + { + GameEntity[] entities = _group.GetEntities(); + + spriteBatch.Begin(samplerState: SamplerState.PointClamp); + + Color[] colors = { Color.Black, Color.White }; + int k = 0; + + foreach (GameEntity e in entities) + { + var texture = new Texture2D(spriteBatch.GraphicsDevice, 1, 1); + texture.SetData(new[] { colors[k] }); + + ++k; + if (k == colors.Length) + k = 0; + + spriteBatch.Draw(texture, e.transform.Position, sourceRectangle: e.rectangleCollision.Size, + Color.White); + } + + spriteBatch.End(); + } + +} diff --git a/src/Libs/Systems.Debugging/Systems.Debugging.csproj b/src/Libs/Systems.Debugging/Systems.Debugging.csproj new file mode 100644 index 0000000..e5ff67c --- /dev/null +++ b/src/Libs/Systems.Debugging/Systems.Debugging.csproj @@ -0,0 +1,17 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + diff --git a/src/Libs/Systems/AnimatedMovementSystem.cs b/src/Libs/Systems/AnimatedMovementSystem.cs index fc19a9d..2133102 100644 --- a/src/Libs/Systems/AnimatedMovementSystem.cs +++ b/src/Libs/Systems/AnimatedMovementSystem.cs @@ -1,4 +1,5 @@ using Components; +using Components.Data; using Entitas; using Entitas.Extended; using Microsoft.Xna.Framework; diff --git a/src/Libs/Systems/CollisionSystem.cs b/src/Libs/Systems/CollisionSystem.cs new file mode 100644 index 0000000..310c08a --- /dev/null +++ b/src/Libs/Systems/CollisionSystem.cs @@ -0,0 +1,56 @@ +using Entitas; +using Entitas.Extended; +using Microsoft.Xna.Framework; +using Serilog; + +namespace Systems; + +// Input & Collision systems both have to be fixed execute systems, +// otherwise it'll lead to the desynchronized behaviour. +public class CollisionSystem : IFixedExecuteSystem +{ + private readonly IGroup _group; + private readonly ILogger _logger; + + public CollisionSystem(IGroup group, ILogger logger) + { + _group = group; + _logger = logger; + } + + // TODO: Optimize efficiency + public void FixedExecute(GameTime gameTime) + { + GameEntity[] entities = _group.GetEntities(); + + for (int i = 0; i < entities.Length; ++i) + { + GameEntity first = entities[i]; + + for (int j = i + 1; j < entities.Length; ++j) + { + GameEntity second = entities[j]; + + if (AreIntersecting(first, second)) + { + first.transform.Velocity = Vector2.Zero; + second.transform.Velocity = Vector2.Zero; + } + } + } + } + + private static bool AreIntersecting(GameEntity first, GameEntity second) + { + Rectangle firstRectangle = BuildRectangle(first); + Rectangle secondRectangle = BuildRectangle(second); + + Rectangle intersect = Rectangle.Intersect(firstRectangle, secondRectangle); + + return !intersect.IsEmpty; + + Rectangle BuildRectangle(GameEntity x) => new((int)(x.transform.Position.X + x.transform.Velocity.X), + (int)(x.transform.Position.Y + x.transform.Velocity.Y), x.rectangleCollision.Size.Width, + x.rectangleCollision.Size.Height); + } +} diff --git a/src/Libs/Systems/DefaultDrawSystem.cs b/src/Libs/Systems/DefaultDrawSystem.cs index c6531de..b57564a 100644 --- a/src/Libs/Systems/DefaultDrawSystem.cs +++ b/src/Libs/Systems/DefaultDrawSystem.cs @@ -38,4 +38,8 @@ public void Draw(GameTime gameTime, SpriteBatch spriteBatch) spriteBatch.End(); } + // TODO: When camera following feature is enabled, + // Make it working with other drawing systems, e.g. debug drawing. + // Thus, draw relatively to camera. + // Share rendering results with other systems (sorting, etc) } diff --git a/src/Libs/Systems/InputSystem.cs b/src/Libs/Systems/InputSystem.cs index 8e4e755..d6f5289 100644 --- a/src/Libs/Systems/InputSystem.cs +++ b/src/Libs/Systems/InputSystem.cs @@ -1,13 +1,15 @@ using Entitas; +using Entitas.Extended; using Microsoft.Xna.Framework; using Serilog; using Services.Input; -using IExecuteSystem = Entitas.Extended.IExecuteSystem; namespace Systems; +// Input & Collision systems both have to be fixed execute systems, +// otherwise it'll lead to the desynchronized behaviour. [Input] -public class InputSystem : IExecuteSystem +public class InputSystem : IFixedExecuteSystem { private readonly IInputScanner _inputScanner; private readonly IGroup _group; @@ -20,7 +22,7 @@ public InputSystem(IInputScanner inputScanner, IGroup group, ILogger _logger = logger; } - public void Execute(GameTime gameTime) + public void FixedExecute(GameTime gameTime) { try { diff --git a/src/Libs/Systems/MovementSystem.cs b/src/Libs/Systems/MovementSystem.cs index e27e4e7..30ed296 100644 --- a/src/Libs/Systems/MovementSystem.cs +++ b/src/Libs/Systems/MovementSystem.cs @@ -7,8 +7,7 @@ namespace Systems; -// TODO: correct fixed execution in the game loop -public class MovementSystem : IFixedExecuteSystem +public class MovementSystem : IExecuteSystem { private readonly IGroup _group; private readonly IMovement _movement; @@ -21,7 +20,7 @@ public MovementSystem(IMovement movement, IGroup group, ILogger logg _logger = logger; } - public void FixedExecute(GameTime fixedGameTime) + public void Execute(GameTime fixedGameTime) { try {