1
\$\begingroup\$

Inspired by the Entity Component System Architecture, I started to refactor my 2D tiled-based game to follow the rule "favor composition over inheritance".

However, after writing the movement related components and system, I wasn't sure how to continue and implement the AIControllerSystem:

On one hand, I want to decuple as much as I can the MovementSystem and AIControllerSystem, and on the other hand I want to be able to send a movement action from AIControllerSystem to MovementSystem so I won't need to reimplement the movement logic.

Currently, my movement code looks like this:

public class PositionComponent : IComponent
{
    public int X { get; set; }
    public int Y { get; set; }
    public Direction FacingDirection { get; set; }
}

public class MovementComponent : IComponent
{
    public int Speed { get; set; }
    public Direction Direction { get; set; }
    public bool IsMoving { get; set; }
    public byte OffsetInTile { get; set; }
}

public class MovementSystem : ISystem
{
    private long _timer;

    // states the behavior depends on
    private PositionComponent _positionComponent;
    private MovementComponent _movementComponent;

    public MovementSystem(PositionComponent positionComponent, MovementComponent movementComponent)
    {
        this._positionComponent = positionComponent;
        this._movementComponent = movementComponent;
        this._timer = 0;
    }
    public void Update(long elapsedTime)
    {
        // some logic
    }
    
    public bool CanMove(Direction moveDirection)
    {
        return !this._movementComponent.IsMoving;
    }
    
    public void Move(Direction moveDirection, bool updateFacing=true)
    {
        if (this.CanMove())
        {
            this._timer = 0;
            this._movementComponent.IsMoving = true;
            this._movementComponent.Direction = moveDirection;
            if (updateFacing)
                this._positionComponent.FacingDirection = moveDirection;
        }
    }
}

My Approach

I thought about several ways to handle the AIControllerSystem->MovementSystem dependency:

  1. Let the AIControllerSystem hold a reference to the MovementSystem, and send messages via a simple method call.
  2. Let the AIControllerSystem send messages to the MovementSystem via a message queue.
  3. Declare interface IMovable for movable game objects, and let the AIControllerSystem hold a reference to such interface.
  4. Sticking to the ECS architecture, expose the MovementComponent and PositionComponent to the AIControllerSystem, and "communicate" through them.

Personaly, I think options 3 and 4 are better, although option 4 may lead to code duplications (e.g. check if move is valid, unless the MovementComponent will be changed).

Option 3 will allow me to add game objects like the following:

public class Player : IMovable
{
    // states
    MovementComponent _movementComponent;
    PositionComponent _positionComponent;
    
    // behaviors
    MovementSystem _movementSystem
    
    
    //...
}

public class NPC : IMovable, IAIControllable
{
    // states
    MovementComponent _movementComponent;
    PositionComponent _positionComponent;
    
    // behaviors
    MovementSystem _movementSystem
    AIControllerSystem _aiControllerSystem // will be dependent on the NPC
    
    //...
}

I know my design is not "pure" ECS or even close to, but I think this hybrid approach is pretty modular to easily perform changes and add game objects with unique behavior combinations.

What do you think?

Thanks.

Edit

Some clarifications:

  • The MovementSystem is responsible for updating position (old tile -> neighbor tile) based on Speed, while the OffsetInTile represent a fixed point current movement progress (0-255).
  • For now, the AIControllerSystem is responsible for the movement "thinking" and operating of non-player entities (like NPCs).
  • The problem in short: keeping the described systems decupled, while letting the AIControllerSystem to communicate with the MovementSystem to perform movement actions.
\$\endgroup\$
2
  • \$\begingroup\$ I am not sure what your question/problem is? What is it your movement system does? What is it your AI system does? Why do they clash / need to interact? While there's no black and white approach to implementing things, a system should generally do as granular a task as possible. E.g. a MovementSystem decides what the new position of an Entity is based on the current Position component and Speed component... \$\endgroup\$ – dot_Sp0T Sep 23 '20 at 15:54
  • \$\begingroup\$ @dot_Sp0T added additional information in my question. \$\endgroup\$ – barak1412 Sep 24 '20 at 5:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.