Skip to content

Commit

Permalink
refactor: ecs & good workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
cherrynik committed Sep 20, 2023
1 parent c290909 commit a60b29a
Show file tree
Hide file tree
Showing 12 changed files with 169 additions and 165 deletions.
47 changes: 33 additions & 14 deletions src/Apps/GameDesktop/RootFeatureCompositionRoot.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Components.Sprites;
using Components;
using Components.World;
using Entitas;
using Features;
using LightInject;
Expand All @@ -9,19 +10,32 @@
using Services.Factories;
using Services.Input;
using Services.Movement;
using Stateless;
using Systems;

namespace GameDesktop;

public class RootFeatureCompositionRoot : ICompositionRoot
{
public void Compose(IServiceRegistry serviceRegistry)
{
RegisterServices(serviceRegistry);
RegisterInputSystem(serviceRegistry);
RegisterMovementSystem(serviceRegistry);
RegisterCreatePlayerEntitySystem(serviceRegistry);
RegisterAnimatedMovementSystem(serviceRegistry);

serviceRegistry.Register<RootFeature>();
}

private static void RegisterServices(IServiceRegistry serviceRegistry)
{
serviceRegistry.Register<IInputScanner, KeyboardScanner>(new PerContainerLifetime());

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

private static void RegisterInputSystem(IServiceRegistry serviceRegistry)
{
serviceRegistry.Register(factory =>
{
Contexts contexts = factory.GetInstance<Contexts>();
Expand All @@ -35,7 +49,10 @@ public void Compose(IServiceRegistry serviceRegistry)

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

private static void RegisterMovementSystem(IServiceRegistry serviceRegistry)
{
serviceRegistry.Register(factory =>
{
Contexts contexts = factory.GetInstance<Contexts>();
Expand All @@ -48,37 +65,39 @@ public void Compose(IServiceRegistry serviceRegistry)

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

private static void RegisterCreatePlayerEntitySystem(IServiceRegistry serviceRegistry)
{
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");
var idleAnimations = AnimatedCharactersFactory.CreateAnimations(spriteSheet, "Idle");
var walkingAnimations = AnimatedCharactersFactory.CreateAnimations(spriteSheet, "Walking");

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

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

serviceRegistry.Register<CreatePlayerEntitySystem>(new PerContainerLifetime());
}

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

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

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

serviceRegistry.Register<RootFeature>();
}, new PerContainerLifetime());
}
}
16 changes: 16 additions & 0 deletions src/Libs/Components/GeneratedExtended/GameEntity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Components;
using Components.World;

public partial class GameEntity
{
// TODO: The kind of code generator?
public void AddMovementAnimation(MovementAnimationComponent component) =>
AddMovementAnimation(newFacingDirection: component.FacingDirection,
newPlayingAnimation: component.PlayingAnimation,
newIdleAnimations: component.IdleAnimations,
newWalkingAnimations: component.WalkingAnimations,
newHasStopped: component.HasStopped);

public void AddTransform(TransformComponent component) =>
AddTransform(newPosition: component.Position, newVelocity: component.Velocity);
}
1 change: 0 additions & 1 deletion src/Libs/Components/Jenny.properties
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ Jenny.PostProcessors = Jenny.Plugins.AddFileHeaderPostProcessor, \
Jenny.Server.Port = 3333
Jenny.Client.Host = localhost
Jenny.Plugins.ProjectPath = ../Systems/Systems.csproj
Jenny.Plugins.ProjectPath = ../Entities/Entities.csproj
Jenny.Plugins.ProjectPath = Components.csproj
Entitas.CodeGeneration.Plugins.Contexts = Game, \
Input, \
Expand Down
31 changes: 31 additions & 0 deletions src/Libs/Components/MovementAnimationComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Entitas;
using Microsoft.Xna.Framework;
using MonoGame.Aseprite.Sprites;
using Services.Math;

namespace Components;

public class MovementAnimationComponent : IComponent
{
private const Direction DefaultFacing = Direction.Down;

public AnimatedSprite PlayingAnimation;
public Dictionary<Direction, AnimatedSprite> IdleAnimations;
public Dictionary<Direction, AnimatedSprite> WalkingAnimations;

public Vector2 FacingDirection = Vector2.UnitY;
public bool HasStopped;

public MovementAnimationComponent()

Check warning on line 19 in src/Libs/Components/MovementAnimationComponent.cs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest)

Non-nullable field 'PlayingAnimation' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 19 in src/Libs/Components/MovementAnimationComponent.cs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest)

Non-nullable field 'IdleAnimations' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 19 in src/Libs/Components/MovementAnimationComponent.cs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest)

Non-nullable field 'WalkingAnimations' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 19 in src/Libs/Components/MovementAnimationComponent.cs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest)

Non-nullable field 'PlayingAnimation' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 19 in src/Libs/Components/MovementAnimationComponent.cs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest)

Non-nullable field 'IdleAnimations' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 19 in src/Libs/Components/MovementAnimationComponent.cs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest)

Non-nullable field 'WalkingAnimations' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 19 in src/Libs/Components/MovementAnimationComponent.cs

View workflow job for this annotation

GitHub Actions / Build (windows-latest)

Non-nullable field 'PlayingAnimation' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 19 in src/Libs/Components/MovementAnimationComponent.cs

View workflow job for this annotation

GitHub Actions / Build (windows-latest)

Non-nullable field 'IdleAnimations' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 19 in src/Libs/Components/MovementAnimationComponent.cs

View workflow job for this annotation

GitHub Actions / Build (windows-latest)

Non-nullable field 'WalkingAnimations' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 19 in src/Libs/Components/MovementAnimationComponent.cs

View workflow job for this annotation

GitHub Actions / Build (windows-latest)

Non-nullable field 'PlayingAnimation' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 19 in src/Libs/Components/MovementAnimationComponent.cs

View workflow job for this annotation

GitHub Actions / Build (windows-latest)

Non-nullable field 'IdleAnimations' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 19 in src/Libs/Components/MovementAnimationComponent.cs

View workflow job for this annotation

GitHub Actions / Build (windows-latest)

Non-nullable field 'WalkingAnimations' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
{
}

public MovementAnimationComponent(Dictionary<Direction, AnimatedSprite> idleAnimations,
Dictionary<Direction, AnimatedSprite> walkingAnimations)
{
IdleAnimations = idleAnimations;
WalkingAnimations = walkingAnimations;

PlayingAnimation = idleAnimations[DefaultFacing];
}
}
91 changes: 0 additions & 91 deletions src/Libs/Components/Sprites/AnimatedMovementComponent.cs

This file was deleted.

1 change: 0 additions & 1 deletion src/Libs/Components/World/RectangleCollisionComponent.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Entitas;
using Microsoft.Xna.Framework;
using Anchor = Services.Math.RadDir;

namespace Components.World;

Expand Down
4 changes: 4 additions & 0 deletions src/Libs/External/Entitas.Extended/Entitas.Extended.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\Components\Components.csproj" />
</ItemGroup>

</Project>
24 changes: 12 additions & 12 deletions src/Libs/Services/Factories/AnimatedCharactersFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,24 @@ namespace Services.Factories;

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

public static SpriteSheet LoadSpriteSheet(GraphicsDevice graphicsDevice, string path)
{
AsepriteFile asepriteFile = AsepriteFile.Load(path);
return SpriteSheetProcessor.Process(graphicsDevice, asepriteFile);
}

private static string BuildTag(string action, RadDir dir) => $"{action}{dir.ToString()}";
private static string BuildTag(string action, Direction dir) => $"{action}{dir.ToString()}";

private static AnimatedSprite CreateAnimation(SpriteSheet spriteSheet, string action, RadDir direction)
private static AnimatedSprite CreateAnimation(SpriteSheet spriteSheet, string action, Direction direction)
{
AnimatedSprite animatedSprite;

if (direction == RadDir.Left)
if (direction == Direction.Left)
{
string rightAnimationTag = BuildTag(action, RadDir.Right);
string rightAnimationTag = BuildTag(action, Direction.Right);

animatedSprite = spriteSheet.CreateAnimatedSprite(rightAnimationTag);
animatedSprite.FlipHorizontally = true;
Expand All @@ -41,16 +41,16 @@ private static AnimatedSprite CreateAnimation(SpriteSheet spriteSheet, string ac
return animatedSprite;
}

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

// Temp hack
dictionary.Add(RadDir.DownLeft, dictionary[RadDir.Left]);
dictionary.Add(RadDir.DownRight, dictionary[RadDir.Right]);
dictionary.Add(RadDir.UpLeft, dictionary[RadDir.Left]);
dictionary.Add(RadDir.UpRight, dictionary[RadDir.Right]);
dictionary.Add(Direction.DownLeft, dictionary[Direction.Left]);
dictionary.Add(Direction.DownRight, dictionary[Direction.Right]);
dictionary.Add(Direction.UpLeft, dictionary[Direction.Left]);
dictionary.Add(Direction.UpRight, dictionary[Direction.Right]);

return dictionary;
}
Expand Down
10 changes: 5 additions & 5 deletions src/Libs/Services/Math/MathUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Services.Math;

public enum RadDir
public enum Direction
{
Right,
UpRight,
Expand All @@ -27,13 +27,13 @@ private static double RadDir(float x, float y, int sectors)
return System.Math.Floor(radians / (2 * System.Math.PI) * sectors);
}

public static RadDir Rad8Dir(Vector2 dir) => (RadDir)RadDir(dir.X, dir.Y, sectors: 8);
public static Direction Rad8Dir(Vector2 dir) => (Direction)RadDir(dir.X, dir.Y, sectors: 8);

// Useful as MonoGame has Y-flipped coordinate system
public static RadDir Rad8DirYFlipped(Vector2 dir) => (RadDir)RadDir(dir.X, -dir.Y, sectors: 8);
public static Direction Rad8DirYFlipped(Vector2 dir) => (Direction)RadDir(dir.X, -dir.Y, sectors: 8);

public static RadDir Rad4Dir(Vector2 dir) => (RadDir)RadDir(dir.X, dir.Y, sectors: 4);
public static Direction Rad4Dir(Vector2 dir) => (Direction)RadDir(dir.X, dir.Y, sectors: 4);

// Useful as MonoGame has Y-flipped coordinate system
public static RadDir Rad4DirYFlipped(Vector2 dir) => (RadDir)RadDir(dir.X, dir.Y, sectors: 4);
public static Direction Rad4DirYFlipped(Vector2 dir) => (Direction)RadDir(dir.X, dir.Y, sectors: 4);
}
Loading

0 comments on commit a60b29a

Please sign in to comment.