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\$ Commented Sep 23, 2020 at 15:54
  • \$\begingroup\$ @dot_Sp0T added additional information in my question. \$\endgroup\$ Commented Sep 24, 2020 at 5:56

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.