6
\$\begingroup\$

I'm working on a 2d RPG game in C# using an Entity-Component-System architecture, and I'm currently working on movement. Right now, I'm wondering how to organise the code so it looks a bit more.. pretty, or dignified, than it is. Currently, I have four components:

VelocityComponent - Contains the speed (velocity) of an Entity. The behaviour currently is that if an Entity does not contain a VelocityComponent, then we continue onto the next Entity, because we cannot move if we haven't a velocity.

TransformComponent - Contains the Width, Height, X and Y fields for the Entity. Basically, the position. Similar to VelocityComponent, if an Entity does not have a TransformComponent, then the GPSSystem just continues the next iteration, because we cannot move anywhere if we don't have a position.

MovementComponent - Contains a pair of co-ordinates for use as a destination position, and a boolean indicating if the Entity is currently moving.

MoveFromKeyboardComponent - Signals whether or not a key is pressed so that the Entity that this Component is attached to should move or not.

The update loop currently goes that the GPSSystem collects a list of all entities (by accessing the EntityManager. The EntityManager simply contains a key-value pairing of Guid to Entity of all active entities. All systems have access to it), then iterates over the whole lot.. It'd be better if I just posted the code;

    /// <summary>
    /// Update the GPS System.
    /// </summary>
    /// <param name="gameTime"></param>
    public override void Update(GameTime gameTime)
    {
        // Handle the movement.
        foreach (Entity entity in entityManager.GetEntities())
        {
            VelocityComponent velocity = (VelocityComponent)entity.GetComponent("Velocity");
            MovementComponent movement = (MovementComponent)entity.GetComponent("Movement");
            TransformComponent transform = (TransformComponent)entity.GetComponent("Transform");
            MovementFromKeyboardComponent movementFromKeyboard = 
                (MovementFromKeyboardComponent)entity.GetComponent("MovementFromKeyboard");

            // can't do any movement manipulation without a transform (position) component.
            if (transform == null || velocity == null)
                continue;
        }

        base.Update(gameTime);
    }

However, I am unsure of how to proceed after this point. I just don't know how to manage the movement. I am aiming to have a movement style similar to Pokémon, where all Entities move one square (16 x 16) at a time and have smooth movement across those tiles, however the player can hold down a button and keep moving square after square, and when he releases that key, he stops moving (and snaps to the nearest tile if he is in the middle of two).

So, on to the question: Am I going about this correctly? Have I got too many (or too few) components for movement? And lastly, how would I manage the fact that an Entity may have a MovementComponent and a MoveFromKeyboardComponent? I'm just confused I suppose. I am not looking for the code for that, I am just wondering whether I am going about this correctly? This is my first whack at an entity component system, you see.

Best regards.

EDIT 1: I should add I have now changed the structure of the System class, such that it now has a ComponentAdded and ComponentRemoved method; that is, we no longer iterate over every Entity in existence every Update(), instead we store the entities that have components we want to listen to in a data structure in the class. My question remains the same, though.

\$\endgroup\$
5
  • \$\begingroup\$ There should probably be a boolean which dictates whether the entity is walking. If it's set to true you can't apply movement until it reaches its destination. So your "snapping" is simply go from A to B and set walking to false. If walking is false and key is pressed. Walking is true. This boolean could be in your movement component. I don't know if this is your own framework. But it seems a bit redundant to iterate over every entity. Some entities are not relevant and shouldn't be processed by this "system". Take a look at how Artemis (java) handles its entity structure. \$\endgroup\$
    – Sidar
    Commented Aug 12, 2012 at 5:16
  • \$\begingroup\$ Or at least find a way where only relevant entities should be processed. \$\endgroup\$
    – Sidar
    Commented Aug 12, 2012 at 5:22
  • 3
    \$\begingroup\$ What is the purpose of separating movement and velocity? It sounds like you're taking a good idea (components) and trying to stretch them to crazy academic purist levels. A PhysicsBody component that handles velocity, acceleration, forces, etc. is just fine. Component systems are just another design pattern. If you find yourself trying to find a way to make a design pattern fit, your problem is the design pattern; just drop it, and focus on writing code that works instead of fitting into some idealized architecture. \$\endgroup\$ Commented Aug 12, 2012 at 8:33
  • \$\begingroup\$ @Sidar: there is - The boolean is inside MovementComponent. But yeah, that makes sense, thank you. Also, I've changed it so it no longer iterates over every entity - instead, I have a ComponentAdded() callback that is called whenever a Component is added to an Entity. A system indicates what types of components it is interested in when you register it, so that callback is called there and then. \$\endgroup\$
    – Dan
    Commented Aug 12, 2012 at 14:14
  • \$\begingroup\$ @sean: This is my first ECS implementation so it will be a bit... sketchy. :P VelocityComponent will be a PhysicsComponent later on, but I named it VelocityComponent simply for - well, simplicity - because I haven't done anything related to physics yet. Right now, I am just trying to get a 16x16 blue box to move around on the screen responding to input. :P I did actually initially try using OOP but I found it too bloated when I wrote things, so I decided to try out this approach - and I really like it ^^ \$\endgroup\$
    – Dan
    Commented Aug 12, 2012 at 14:16

3 Answers 3

6
\$\begingroup\$

If your design works with respect to your constraints, then, in some sense, it is correct. The following is how I would approach things, just in case you find it useful:

I would replace the VelocityComponent with a PhysicsComponent.

I like the idea that an entity is similar to a "thing" in your game, implying all entities, whether renderable or not, have a transform. With this in mind, I would not use a TransformComponent and instead have the transform as a field in Entity (or a subclass).

I would include the position, rotation, and scaling in the transform. I would not include the width and the height. If the width and height variables are for a bounding box, I would move them to a ColliderComponent.

I would break apart the MovementComponent. I would put the state code (i.e. the boolean indicating motion) in a StateComponent, where the StateComponent would be the hub for all information regarding the object's state. I would move the target information into a ControllerComponent.

I'm not sure what the exact purpose of signaling from MoveFromKeyboardComponent is. I would merge this component into ControllerComponent and have ControllerComponent drive the game object (by using the interface of PhysicsComponent to add forces/change velocity and by using the interface of StateComponent to know when to do what).

\$\endgroup\$
3
  • \$\begingroup\$ Thank you for your input. As stated above, Velocity will become Physics, later on. :) I was considering merging Transform with Entity, so I shall do that now - thank you. The width and height are actually for rendering as well, that said, upon reflection, I think it would make more sense in a collision component - the spritecomponent that I will no doubt end up creating would have the width/height of the sprite anyway. StateComponent sounds like a great idea, thank you :P MoveFromKeyboardComponent is a component that has booleans depending on whether certain inputs are pressed. The idea was \$\endgroup\$
    – Dan
    Commented Aug 12, 2012 at 14:19
  • \$\begingroup\$ to set the booleans to true/false based on input so the player could keep moving until the key was released, at which point the boolean is set to false and the player keeps moving to the nearest exact tile \$\endgroup\$
    – Dan
    Commented Aug 12, 2012 at 14:19
  • \$\begingroup\$ @Neglected Another thing you might want to do with the StateComponent (that I forgot to mention) is to make it a state event dispatcher. Other components register handlers with a particular state event. A state event is something like (Moving, Stay), where the second component takes one of three values (Enter, Exit, Stay). Something like movement logic then falls out of handling the current state (based on user input) and requesting the StateComponent to change state when appropriate. \$\endgroup\$
    – Derek E
    Commented Aug 13, 2012 at 3:03
1
\$\begingroup\$

In my game engine I have an entity manager to keep track of entities and their components and I have a list of "processes" to act upon these components. Those processes take care of very elemental tasks.

Here is an example on the processes that would be called upon in one update call inside the game loop (em = entity manager):

    // Call processes
    accelerate.run(em, em.getGroup("ACCEL"));
    move.run(em, em.getGroup("MOVABLE"));
    collide.run(em, em.getGroup("COLLIDABLE"));
    resolveCollision.run(em, em.getEntitiesWithComponent(CollisionResult.class));
    render.run(em);

Now what happens is that I poll key input in accelerate and change the acceleration component of those entities (in this case the player). Then in move I move all objects according to their acceleration component.

Method run() inside accelerate:

    public void run(EntityManager entityManager, List<Integer> group) {
            for(Integer e : group) {
                // Get components
                Accel accel = entityManager.getComponent(e, Accel.class); // Used to control char movement
                Velocity vel = entityManager.getComponent(e, Velocity.class); // Used to control char movement

                // Update components
                // By default no acceleration
                vel.setDx(0);
                vel.setDy(0);

                if(inputHandler.keyActive(KeyEvent.VK_RIGHT)) { vel.setDx(vel.getDx() + accel.getAccel() * loop.getDt()); } // Accelerate char right
                if(inputHandler.keyActive(KeyEvent.VK_LEFT)) { vel.setDx(vel.getDx() - accel.getAccel() * loop.getDt()); } // Accelerate char left
                if(inputHandler.keyActive(KeyEvent.VK_UP)) { vel.setDy(vel.getDy() - accel.getAccel() * loop.getDt()); } // Accelerate char up
                if(inputHandler.keyActive(KeyEvent.VK_DOWN)) { vel.setDy(vel.getDy() + accel.getAccel() * loop.getDt()); } // Accelerate char down
            }
        }

When I want to move an entity I simply update the data in it's acceleration component.

Method run() inside move:

public void run(EntityManager entityManager, List<Integer> group) {
        // Loop through the entities
        for(Integer e : group) {
            Position pos = entityManager.getComponent(e, Position.class);
            Velocity vel = entityManager.getComponent(e, Velocity.class);

            // Update the entity components with their new position
            pos.setX(pos.getX() + vel.getDx());
            pos.setY(pos.getY() + vel.getDy());
        }
    }

Now the main difference I think between your implementation and mine is that instead of a specific component like MoveFromKeyboardComponent, I have specified groups of entities to update. I have processes that act upon groups of components. I have entities that I can add to as many groups as I want. I have fewer components and more processes.

\$\endgroup\$
0
\$\begingroup\$

I think there is something wrong in how you handle systems and entity fetching.

Your EntityManager/Scene shouldn't have a dictionary of all entity GUIDS, but rather, an dictionary-of-arrays that looks something like this:

entities = 
{
    "Position": [c_Position, c_Position, "nothing"],
    "Movement": [c_Movement, "nothing", "nothing"],
    "Sprite": [c_Sprite, c_Sprite, "nothing"],
    "Input": [c_Input, "nothing", "nothing"],
}

As you can see, there are 4 types of components here. c_Position is an instance of the position component, and is just a normal vector. c_Movement is too, just a vector. If you want something to stop, then just set its movement vector to {x: 0, y: 0}, or delete it.

All systems would now have a neat way of requesting entities that interest them: entities["nameOfComp"].

As for the input, that could be a system that has access to hardware stuff, and it is the one that actually knows the state. Then, it iterates over entities["Input"] and changes their movement components accordingly.

\$\endgroup\$
4
  • \$\begingroup\$ (Turn all "should"'s into "could"'s, I'm not expert, this is just the way I'd do it.) \$\endgroup\$
    – jcora
    Commented Aug 12, 2012 at 10:27
  • 1
    \$\begingroup\$ The reason I have a Dictionary<Guid, Entity> is to adopt the approach that you have, but to be able to access Entities via Guid too (Unless I missed something then please enlighten me) :) \$\endgroup\$
    – Dan
    Commented Aug 12, 2012 at 14:13
  • \$\begingroup\$ Well can you access all the entities by a specific component? For example, can you get all instances of Moveable components and GUIDs asociated with them, fast? If so, then you're OK, but if you have to write a function that fetches you required instances, you're in trouble. \$\endgroup\$
    – jcora
    Commented Aug 12, 2012 at 23:22
  • \$\begingroup\$ Yes, yes I can, :P \$\endgroup\$
    – Dan
    Commented Aug 13, 2012 at 17:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .