Skip to content

Commit

Permalink
refactor: ecs & di & composition root
Browse files Browse the repository at this point in the history
- Needs refactoring yet & docs update
  • Loading branch information
cherrynik committed Sep 19, 2023
1 parent 74f145a commit c290909
Show file tree
Hide file tree
Showing 16 changed files with 221 additions and 74 deletions.
4 changes: 4 additions & 0 deletions docs/Workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
- [Creating global `const`](#creating-global-const)
- [State Machine](#state-machine)

## MonoGame Pipeline

Don't forget to rebuild `.mgcb` file after changing files in it.

## Makefile

If something happens like commands don't run, variables not evaluated, but the file is correct 99%,
Expand Down
3 changes: 3 additions & 0 deletions src/Apps/GameDesktop/Content/Content.mgcb
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@

#---------------------------------- Content ---------------------------------#

#begin SpriteSheets/Player.aseprite
/copy:SpriteSheets/Player.aseprite

45 changes: 25 additions & 20 deletions src/Apps/GameDesktop/Game.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
using Microsoft.Xna.Framework;
using Features;
using LightInject;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Serilog.Core;
using Serilog;

namespace GameDesktop;

public class Game : Microsoft.Xna.Framework.Game
{
private readonly Logger _logger;
private readonly ILogger _logger;
private readonly Contexts _contexts;
private readonly IServiceContainer _container;

private Entitas.Extended.Feature _rootFeature;
private SpriteBatch _spriteBatch;

public SpriteBatch SpriteBatch { get; private set; }

public Game(
Logger logger,
Contexts contexts)
ILogger logger,
Contexts contexts,
IServiceContainer container)
{
_logger = logger;
_contexts = contexts;
_container = container;

_logger.ForContext<Game>().Verbose("ctor");
}
Expand All @@ -26,10 +33,11 @@ protected override void Initialize()
_logger.ForContext<Game>().Verbose($"Initialize(): start; available {GraphicsDevice}");
_logger.ForContext<Game>().Verbose("SpriteBatch initialization...");

SpriteBatch = new SpriteBatch(GraphicsDevice);
_spriteBatch = new SpriteBatch(GraphicsDevice);

_logger.ForContext<Game>().Verbose("SpriteBatch initialized");


base.Initialize();

_logger.ForContext<Game>().Verbose("Initialize(): end");
Expand All @@ -39,11 +47,14 @@ protected override void LoadContent()
{
_logger.ForContext<Game>().Verbose("LoadContent(): start");

// TODO: DI with ECS?
// TODO: Projects management (external in Libs/External?)
_container.Register(_ => _spriteBatch);
_container.RegisterFrom<RootFeatureCompositionRoot>();

_rootFeature = _container.GetInstance<RootFeature>();

// TODO: Logging with game flags (like LOG_MOVEMENT, etc)?
// TODO: Error handling
// _gameFeature.Initialize();
_rootFeature.Initialize();
_logger.ForContext<Game>().Verbose("LoadContent(): end");
}

Expand All @@ -65,31 +76,25 @@ protected override void EndRun()
_logger.ForContext<Game>().Verbose("Ended");
}

private void FixedUpdate(GameTime fixedGameTime)
{
// _gameFeature.FixedExecute(fixedGameTime);
}
private void FixedUpdate(GameTime fixedGameTime) => _rootFeature.FixedExecute(fixedGameTime);

protected override void Update(GameTime gameTime)
{
FixedUpdate(gameTime);

base.Update(gameTime);
// _gameFeature.Execute(gameTime);
_rootFeature.Execute(gameTime);

LateUpdate(gameTime);
}

private void LateUpdate(GameTime gameTime)
{
// _gameFeature.LateExecute(gameTime);
}
private void LateUpdate(GameTime gameTime) => _rootFeature.LateExecute(gameTime);

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);

// _gameFeature.Draw(gameTime, _spriteBatch);
_rootFeature.Draw(gameTime, _spriteBatch);

base.Draw(gameTime);
}
Expand Down
12 changes: 8 additions & 4 deletions src/Apps/GameDesktop/GameCompositionRoot.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using LightInject;
using Features;
using LightInject;
using Microsoft.Xna.Framework;
using Serilog.Core;
using Serilog;

namespace GameDesktop;

Expand All @@ -18,11 +19,14 @@ private void RegisterGameServices(IServiceRegistry serviceRegistry)
{
serviceRegistry.Register(_ => Contexts.sharedInstance);

serviceRegistry.RegisterFrom<RootFeatureCompositionRoot>();

serviceRegistry.Register(factory =>
{
Game game = new(
factory.GetInstance<Logger>(),
factory.GetInstance<Contexts>()
factory.GetInstance<ILogger>(),
factory.GetInstance<Contexts>(),
factory.GetInstance<IServiceContainer>()
) { IsMouseVisible = IsMouseVisible, Content = { RootDirectory = ContentRootDirectory, } };

// Hack. Resolving cycle dependency issue (fundamental architecture)
Expand Down
1 change: 1 addition & 0 deletions src/Apps/GameDesktop/GameDesktop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
<ItemGroup>
<ProjectReference Include="..\..\Libs\Components\Components.csproj" />
<ProjectReference Include="..\..\Libs\External\Entitas.Extended\Entitas.Extended.csproj" />
<ProjectReference Include="..\..\Libs\Features\Features.csproj" />
<ProjectReference Include="..\..\Libs\Services\Services.csproj" />
</ItemGroup>
<ItemGroup>
Expand Down
8 changes: 5 additions & 3 deletions src/Apps/GameDesktop/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using GameDesktop;
using LightInject;
using Microsoft.Extensions.Configuration;
using Serilog;
using Serilog.Core;

IConfigurationRoot configuration = ConfigurationFactory.Create();
Expand All @@ -18,8 +19,9 @@
};
using ServiceContainer container = new(containerOptions);

container.Register(_ => configuration, new PerContainerLifetime());
container.Register(_ => logger, new PerContainerLifetime());
container.Register<IServiceContainer>(_ => container);
container.Register<IConfiguration>(_ => configuration, new PerContainerLifetime());
container.Register<ILogger>(_ => logger, new PerContainerLifetime());

container.RegisterFrom<GameCompositionRoot>();

Expand All @@ -28,5 +30,5 @@
}
catch (Exception e)
{
logger.Error(e.ToString());
logger.ForContext<Program>().Fatal(e.ToString());
}
84 changes: 84 additions & 0 deletions src/Apps/GameDesktop/RootFeatureCompositionRoot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using Components.Sprites;
using Entitas;
using Features;
using LightInject;
using Microsoft.Xna.Framework.Graphics;
using MonoGame.Aseprite.Sprites;
using Serilog;
using Services;
using Services.Factories;
using Services.Input;
using Services.Movement;
using Stateless;
using Systems;

namespace GameDesktop;

public class RootFeatureCompositionRoot : ICompositionRoot
{
public void Compose(IServiceRegistry serviceRegistry)
{
serviceRegistry.Register<IInputScanner, KeyboardScanner>(new PerContainerLifetime());

serviceRegistry.Register<IMovement, SimpleMovement>(new PerContainerLifetime());

serviceRegistry.Register(factory =>
{
Contexts contexts = factory.GetInstance<Contexts>();
IAllOfMatcher<GameEntity> inputMovableMatcher = GameMatcher.AllOf(GameMatcher.Transform,
GameMatcher.Movable,
GameMatcher.Player);
IGroup<GameEntity> inputMovableGroup = contexts.game.GetGroup(inputMovableMatcher);

var inputScanner = factory.GetInstance<IInputScanner>();
var logger = factory.GetInstance<ILogger>();

return new InputSystem(inputScanner, inputMovableGroup, logger);
}, new PerContainerLifetime());

serviceRegistry.Register(factory =>
{
Contexts contexts = factory.GetInstance<Contexts>();
IAllOfMatcher<GameEntity> movableMatcher = GameMatcher.AllOf(GameMatcher.Transform,
GameMatcher.Movable);
IGroup<GameEntity> movableGroup = contexts.game.GetGroup(movableMatcher);

var movement = factory.GetInstance<IMovement>();
var logger = factory.GetInstance<ILogger>();

return new MovementSystem(movement, movableGroup, logger);
}, new PerContainerLifetime());

serviceRegistry.Register(factory =>
{
GraphicsDevice graphicsDevice = factory.GetInstance<SpriteBatch>().GraphicsDevice;
SpriteSheet spriteSheet =
AnimatedCharactersFactory.LoadSpriteSheet(graphicsDevice, "Content/SpriteSheets/Player.aseprite");

var idleDirAnimations = AnimatedCharactersFactory.CreateAnimations(spriteSheet, "Idle");
var walkingDirAnimations = AnimatedCharactersFactory.CreateAnimations(spriteSheet, "Walking");

return new AnimatedMovementComponent(
new StateMachine<PlayerState, PlayerTrigger>(PlayerState.Idle),
idleDirAnimations,
walkingDirAnimations);
});

serviceRegistry.Register(factory => new CreatePlayerEntitySystem(factory.GetInstance<Contexts>(),
factory.GetInstance<AnimatedMovementComponent>()));

serviceRegistry.Register(factory =>
{
Contexts contexts = factory.GetInstance<Contexts>();
IAllOfMatcher<GameEntity> animatedMovableMatcher = GameMatcher.AllOf(GameMatcher.Movable,
GameMatcher.AnimatedMovement);
IGroup<GameEntity> animatedMovableGroup = contexts.game.GetGroup(animatedMovableMatcher);

var logger = factory.GetInstance<ILogger>();

return new AnimatedMovementSystem(animatedMovableGroup, logger);
});

serviceRegistry.Register<RootFeature>();
}
}
4 changes: 4 additions & 0 deletions src/Libs/Features/Features.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@
<ProjectReference Include="..\Systems\Systems.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="LightInject" Version="6.6.4" />
</ItemGroup>

</Project>
7 changes: 1 addition & 6 deletions src/Libs/Features/PlayerMovementFeature.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
using Entitas;
using Services.Input;
using Services.Movement;
using Systems;
using Systems.Input;
using Systems.Sprites;
using Systems;

namespace Features;

Expand Down
18 changes: 18 additions & 0 deletions src/Libs/Features/RootFeature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Systems;

namespace Features;

public sealed class RootFeature : Entitas.Extended.Feature
{
public RootFeature(InputSystem inputSystem,
MovementSystem movementSystem,
CreatePlayerEntitySystem createPlayerEntitySystem,
AnimatedMovementSystem animatedMovementSystem
)
{
Add(inputSystem);
Add(movementSystem);
Add(createPlayerEntitySystem);
Add(animatedMovementSystem);
}
}
8 changes: 4 additions & 4 deletions src/Libs/Services/Factories/AnimatedCharactersFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

namespace Services.Factories;

public class AnimatedCharactersFactory
public static class AnimatedCharactersFactory
{
private readonly IReadOnlyList<RadDir> _directions =
private static readonly IReadOnlyList<RadDir> Directions =
new[] { RadDir.Right, RadDir.Down, RadDir.Left, RadDir.Up };

public static SpriteSheet LoadSpriteSheet(GraphicsDevice graphicsDevice, string path)
Expand Down Expand Up @@ -41,10 +41,10 @@ private static AnimatedSprite CreateAnimation(SpriteSheet spriteSheet, string ac
return animatedSprite;
}

public Dictionary<RadDir, AnimatedSprite> CreateAnimations(SpriteSheet spriteSheet, string action)
public static Dictionary<RadDir, AnimatedSprite> CreateAnimations(SpriteSheet spriteSheet, string action)
{
Dictionary<RadDir, AnimatedSprite> dictionary =
_directions.ToDictionary(dir => dir, dir => CreateAnimation(spriteSheet, action, dir));
Directions.ToDictionary(dir => dir, dir => CreateAnimation(spriteSheet, action, dir));

// Temp hack
dictionary.Add(RadDir.DownLeft, dictionary[RadDir.Left]);
Expand Down
16 changes: 9 additions & 7 deletions src/Libs/Systems/AnimatedMovementSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@
using Entitas.Extended;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Serilog;
using IExecuteSystem = Entitas.Extended.IExecuteSystem;

namespace Systems.Sprites;
namespace Systems;

public class AnimatedMovementSystem : IExecuteSystem, IDrawSystem
{
private readonly IGroup<GameEntity> _group;
private readonly ILogger _logger;

public AnimatedMovementSystem(IGroup<GameEntity> group)
public AnimatedMovementSystem(IGroup<GameEntity> group, ILogger logger)
{
_group = group;
_logger = logger;
}

public void Execute(GameTime gameTime)
Expand All @@ -22,9 +25,9 @@ public void Execute(GameTime gameTime)
foreach (GameEntity e in entities)
{
Vector2 velocity = e.transform.Velocity;
AnimatedMovementComponent movementAnimatedSprites =
(AnimatedMovementComponent)e.GetComponents().First(component => component is AnimatedMovementComponent);
AnimatedMovementComponent movementAnimatedSprites = e.animatedMovement;

// TODO: refactor and put it in service system
if (velocity.Equals(Vector2.Zero))
{
movementAnimatedSprites.StateMachine.Fire(PlayerTrigger.Stop);
Expand All @@ -42,12 +45,11 @@ public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
{
spriteBatch.Begin(samplerState: SamplerState.PointClamp);

GameEntity[]? entities = _group.GetEntities();
GameEntity[] entities = _group.GetEntities();
foreach (GameEntity e in entities)
{
Vector2 position = e.transform.Position;
AnimatedMovementComponent? movementAnimatedSprites =
(AnimatedMovementComponent)e.GetComponents().First(component => component is AnimatedMovementComponent);
AnimatedMovementComponent movementAnimatedSprites = e.animatedMovement;

movementAnimatedSprites.PlayingAnimation.Draw(spriteBatch, position);
}
Expand Down
Loading

0 comments on commit c290909

Please sign in to comment.