From aa2a23d8feb5366127e5edff951412dfe2554163 Mon Sep 17 00:00:00 2001 From: cherrynik Date: Wed, 28 Feb 2024 19:45:02 +0300 Subject: [PATCH] feat: `CollisionSystem` - Code clean-up --- .../Components/ComponentsCompositionRoot.cs | 11 +- .../Entities/PlayerEntityCompositionRoot.cs | 4 +- .../Entities/StaticEntityCompositionRoot.cs | 4 +- .../Features/RootFeatureCompositionRoot.cs | 6 +- .../Scellecs.Morpeh.Extended/Feature.cs | 35 +++-- src/Libs/Systems/CollisionSystem.cs | 137 +++++++++++------- src/Libs/Systems/InputSystem.cs | 2 +- 7 files changed, 118 insertions(+), 81 deletions(-) diff --git a/src/Apps/GameDesktop/CompositionRoots/Components/ComponentsCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/Components/ComponentsCompositionRoot.cs index 0fd1277..d966186 100644 --- a/src/Apps/GameDesktop/CompositionRoots/Components/ComponentsCompositionRoot.cs +++ b/src/Apps/GameDesktop/CompositionRoots/Components/ComponentsCompositionRoot.cs @@ -127,8 +127,15 @@ private static void RegisterTransformComponent(IServiceRegistry serviceRegistry) private static void RegisterRectangleColliderComponent(IServiceRegistry serviceRegistry) { - serviceRegistry.RegisterTransient(_ => - new RectangleColliderComponent { Size = new(0, 0, 8, 8) }); + serviceRegistry.RegisterSingleton(_ => + new RectangleColliderComponent + { + Size = new(0, 0, 8, 8), + // IsTrigger = true + }, "DummyEntity"); + + serviceRegistry.RegisterSingleton(_ => + new RectangleColliderComponent { Size = new(0, 0, 8, 8) }, "PlayerEntity"); } private static void RegisterInventoryComponent(IServiceRegistry serviceRegistry) diff --git a/src/Apps/GameDesktop/CompositionRoots/Entities/PlayerEntityCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/Entities/PlayerEntityCompositionRoot.cs index 9838c46..6b5b8f1 100644 --- a/src/Apps/GameDesktop/CompositionRoots/Entities/PlayerEntityCompositionRoot.cs +++ b/src/Apps/GameDesktop/CompositionRoots/Entities/PlayerEntityCompositionRoot.cs @@ -1,8 +1,6 @@ using Components.Data; using Components.Render.Animation; using Components.Tags; -using Entities; -using Entities.Factories; using Entities.Factories.Characters; using LightInject; @@ -22,7 +20,7 @@ private static void RegisterEntity(IServiceRegistry serviceRegistry) => factory.GetInstance(), factory.GetInstance("PlayerEntity"), factory.GetInstance(), - factory.GetInstance(), + factory.GetInstance("PlayerEntity"), factory.GetInstance(), factory.GetInstance("PlayerEntity"), factory.GetInstance())); diff --git a/src/Apps/GameDesktop/CompositionRoots/Entities/StaticEntityCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/Entities/StaticEntityCompositionRoot.cs index 18f0fd8..f081f80 100644 --- a/src/Apps/GameDesktop/CompositionRoots/Entities/StaticEntityCompositionRoot.cs +++ b/src/Apps/GameDesktop/CompositionRoots/Entities/StaticEntityCompositionRoot.cs @@ -1,7 +1,5 @@ using Components.Data; using Components.Render.Static; -using Entities; -using Entities.Factories; using Entities.Factories.Characters; using LightInject; @@ -20,5 +18,5 @@ private static void RegisterEntity(IServiceRegistry serviceRegistry) => new NameComponent("Dummy"), // factory.GetInstance("Dummy") factory.GetInstance("DummyEntity"), factory.GetInstance(), - factory.GetInstance())); + factory.GetInstance("DummyEntity"))); } diff --git a/src/Apps/GameDesktop/CompositionRoots/Features/RootFeatureCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/Features/RootFeatureCompositionRoot.cs index 145d8b0..76a67c2 100644 --- a/src/Apps/GameDesktop/CompositionRoots/Features/RootFeatureCompositionRoot.cs +++ b/src/Apps/GameDesktop/CompositionRoots/Features/RootFeatureCompositionRoot.cs @@ -91,7 +91,7 @@ private static void RegisterEntryPoint(IServiceRegistry serviceRegistry) var panel = new Panel(); var rightBottomText = new Label() { - Text = "Pre-Alpha v0.1.0", + Text = "Pre-Alpha v0.1.1", HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Bottom, Left = -30, @@ -122,7 +122,9 @@ private static void RegisterEntryPoint(IServiceRegistry serviceRegistry) // ⚠ Order-sensitive zone ⚠ var movement = new Feature(factory.GetInstance(), factory.GetInstance(), new InputSystem(factory.GetInstance(), new KeyboardInput()), - new MovementSystem(factory.GetInstance(), new SimpleMovement())); + new CollisionSystem(factory.GetInstance()), + new MovementSystem(factory.GetInstance(), new SimpleMovement()) + ); var preRender = new Feature(factory.GetInstance(), factory.GetInstance(), diff --git a/src/Libs/External/Scellecs.Morpeh.Extended/Feature.cs b/src/Libs/External/Scellecs.Morpeh.Extended/Feature.cs index 7a16166..6bb47bd 100644 --- a/src/Libs/External/Scellecs.Morpeh.Extended/Feature.cs +++ b/src/Libs/External/Scellecs.Morpeh.Extended/Feature.cs @@ -2,7 +2,6 @@ public class Feature(World world, SystemsEngine systemsEngine) { - public readonly SystemsEngine SystemsEngine = systemsEngine; public World World { get; set; } = world; public Feature(World world, SystemsEngine systemsEngine, params IInitializer[] systems) : this(world, systemsEngine) @@ -10,41 +9,49 @@ public Feature(World world, SystemsEngine systemsEngine, params IInitializer[] s foreach (var system in systems) Add(system); } - public void OnAwake() => SystemsEngine.Initializers.Initialize(); + public void OnAwake() + { + systemsEngine.Initializers.Initialize(); + systemsEngine.Systems.Initialize(); + systemsEngine.FixedSystems.Initialize(); + systemsEngine.CleanupSystems.Initialize(); + systemsEngine.LateSystems.Initialize(); + systemsEngine.RenderSystems.Initialize(); + } - public virtual void OnUpdate(float deltaTime) => SystemsEngine.Systems.Update(deltaTime); + public void OnUpdate(float deltaTime) => systemsEngine.Systems.Update(deltaTime); - public virtual void OnFixedUpdate(float deltaTime) => SystemsEngine.FixedSystems.FixedUpdate(deltaTime); + public void OnFixedUpdate(float deltaTime) => systemsEngine.FixedSystems.FixedUpdate(deltaTime); - public virtual void OnLateUpdate(float deltaTime) + public void OnLateUpdate(float deltaTime) { - SystemsEngine.LateSystems.LateUpdate(deltaTime); - SystemsEngine.CleanupSystems.CleanupUpdate(deltaTime); + systemsEngine.LateSystems.LateUpdate(deltaTime); + systemsEngine.CleanupSystems.CleanupUpdate(deltaTime); } - public virtual void OnRender(float deltaTime) => SystemsEngine.RenderSystems.Update(deltaTime); + public void OnRender(float deltaTime) => systemsEngine.RenderSystems.Update(deltaTime); protected void Add(IInitializer initializer) { switch (initializer) { case IRenderSystem renderSystem: - SystemsEngine.RenderSystems.AddSystem(renderSystem); + systemsEngine.RenderSystems.AddSystem(renderSystem); break; case ICleanupSystem cleanupSystem: - SystemsEngine.CleanupSystems.AddSystem(cleanupSystem); + systemsEngine.CleanupSystems.AddSystem(cleanupSystem); break; case ILateSystem lateSystem: - SystemsEngine.LateSystems.AddSystem(lateSystem); + systemsEngine.LateSystems.AddSystem(lateSystem); break; case IFixedSystem fixedSystem: - SystemsEngine.FixedSystems.AddSystem(fixedSystem); + systemsEngine.FixedSystems.AddSystem(fixedSystem); break; case ISystem system: - SystemsEngine.Systems.AddSystem(system); + systemsEngine.Systems.AddSystem(system); break; default: - SystemsEngine.Initializers.AddInitializer(initializer); + systemsEngine.Initializers.AddInitializer(initializer); break; } } diff --git a/src/Libs/Systems/CollisionSystem.cs b/src/Libs/Systems/CollisionSystem.cs index bc9c167..8287b69 100644 --- a/src/Libs/Systems/CollisionSystem.cs +++ b/src/Libs/Systems/CollisionSystem.cs @@ -1,56 +1,81 @@ -// 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); -// } -// } +using Components.Data; +using Microsoft.Xna.Framework; +using Scellecs.Morpeh; +using Vector2 = System.Numerics.Vector2; + +namespace Systems; + +// Input & Collision systems both have to be fixed execute systems, +// otherwise it'll lead to the desynchronized behaviour. +public class CollisionSystem(World world) : IFixedSystem +{ + public World World { get; set; } = world; + + public void OnAwake() + { + } + + public void OnUpdate(float deltaTime) + { + Filter filter = World.Filter + .With() + .With() + .Build(); + + foreach (Entity e in filter) + { + ref var leftTransform = ref e.GetComponent(); + ref var leftCollider = ref e.GetComponent(); + + foreach (Entity other in filter) + { + if (e.Equals(other)) continue; + + ref var rightTransform = ref other.GetComponent(); + ref var rightCollider = ref other.GetComponent(); + + if (!AreColliding(new(leftTransform, leftCollider), + new(rightTransform, rightCollider))) continue; + + if (leftCollider.IsTrigger || rightCollider.IsTrigger) HandleTrigger(); + else HandleCollision(ref leftTransform, ref rightTransform); + } + } + } + + private static void HandleTrigger() + { + // raise event of triggered + } + + private static void HandleCollision(ref TransformComponent left, ref TransformComponent right) + { + left.Velocity = Vector2.Zero; + right.Velocity = Vector2.Zero; + } + + private static bool AreColliding(Tuple first, + Tuple second) + { + var left = BuildRectangle(first); + var right = BuildRectangle(second); + + var intersect = Rectangle.Intersect(left, right); + + return !intersect.IsEmpty; + + Rectangle BuildRectangle(Tuple target) + { + (TransformComponent transform, RectangleColliderComponent rectCollider) = target; + + return new Rectangle((int)(transform.Position.X + transform.Velocity.X), + (int)(transform.Position.Y + transform.Velocity.Y), + rectCollider.Size.Width, + rectCollider.Size.Height); + } + } + + public void Dispose() + { + } +} diff --git a/src/Libs/Systems/InputSystem.cs b/src/Libs/Systems/InputSystem.cs index c489a13..9a813d3 100644 --- a/src/Libs/Systems/InputSystem.cs +++ b/src/Libs/Systems/InputSystem.cs @@ -5,7 +5,7 @@ namespace Systems; -public class InputSystem(World world, IInputScanner inputScanner) : ISystem +public class InputSystem(World world, IInputScanner inputScanner) : IFixedSystem { public World World { get; set; } = world;