The Melodist»Blog

Designing A Better Entity System (3/27/2018)

If you read my last blog post, you'll know that I was having significant issues with the game's entity system in the amount of work it required to expand upon. Prototyping became extremely difficult and cumbersome, and my lack of progress in designing the game is the manifestation of these issues. I've been thinking about how to move forward in redesigning the game's entity system, and have a few ideas that I'd like to share. I figured it'd be good to gather feedback from more experienced folks and also perhaps help others.

The approach I'm thinking I'll attempt is a more component-based one. Here's a small code snippet that demonstrates what I'm thinking of:

1
2
3
4
5
6
7
8
9
struct BoxComponent {
    v2 pos, vel, size;
};

struct SpriteComponent {
    i16 texture, tx, ty, tw, th;
};

void collide_boxes_with_map(Map *m, BoxComponent *box_array, i32 count) { /* physics code for boxes */ }


In this particular case, something generic to box components can be implemented strictly in terms of BoxComponents (and likewise with other types of components).

Like any component-based system, "entity" just refers to a set of these components, though as I discussed in my previous post, "entity" shouldn't necessarily refer to one monolithic data type that is completely generic. Different types of objects in the game world should be separated by the data that they require.

My thinking is that the data might be arranged like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// we need something pretty specific for the player, 
// so perhaps the player can be its own thing
struct Player {
    BoxComponent box;
    SpriteComponent sprite;
};

// a "character" is basically an NPC...
// in this SoA approach, a single "character" is just
// an index that looks up into these arrays.
struct CharacterSet {
    i32 count;
    BoxComponent box[MAX_CHARACTER_COUNT];
    SpriteComponent sprite[MAX_CHARACTER_COUNT];
};

// a static object is just a static image with no
// physics in the world (like a tree)
//
// static objects don't have physics, so no need to
// include any box components. a single static object
// is simply an index that looks up into the sprite
// array.
//
// this is way more cache friendly, because when we
// loop through all of these to render, the next
// sprite data will be pre-fetched, as it's all
// stored contiguously.
struct StaticObjectSet {
    i32 count;
    SpriteComponent sprite[MAX_STATIC_OBJECT_COUNT];
};


Functions that might update and render all of the "entities" in the map might then look something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
struct Map {
    Player           player;
    CharacterSet     characters;
    StaticObjectSet  static_objects;
};

void update_map(Map *m) {
    collide_boxes_with_map(m, &m->player.box, 1);
    collide_boxes_with_map(m, m->characters.box, m->characters.count);
}

void draw_map(Map *m) {
    draw_sprites(&m->player.sprite, 1);
    draw_sprites(m->characters.sprite, m->characters.count);
    draw_sprites(m->static_objects.sprite, m->static_objects.count);
}


That's the direction I think I'll head in, but I'm still looking for improvements.

What do you think? I'd love to hear feedback and suggestions.

-Delix
Jeremiah Goerdt,
This smells a lot like an ECS. Are you following that architecture at all? It would be pretty cool if you came up with a similar architecture simply by paying attention to your own data and needs.
Ryan Fleury,
CaptainKraft
This smells a lot like an ECS. Are you following that architecture at all? It would be pretty cool if you came up with a similar architecture simply by paying attention to your own data and needs.


I think it essentially follows the ECS architecture, but I have proposed it (with the code in the post) as a more specific solution that uses the context of my game to a far greater extent than something like Unity does (or can, for that matter). Trying to reason about the data, the problem, and my needs pretty much led me to this, but I can't pretend that I wasn't (at least at the subconscious level) thinking about a sort of ECS; I recently attended Mike Acton's talk on the new data-oriented Unity ECS at GDC, so the idea was definitely fresh in my mind. I have tried to simplify it, though, which I think is something that is reasonable for me to do, as I have context.

So, yes and no- I'd be interested to see other potential architectures that might be even better with regards to transforming/storing/simplifying the storage of data. :)
Great approach! I really liked the architecture, I'm currently having similar issues with my own entity system, which is pretty hard to prototype something as well, I use something like a single-inheritance approach where the inherited struct, the Entity, contains all the kinds of information that an entity, maybe, would need and instead of virtual functions I define some function pointers for some events like the update loop, its pretty bad and I was struggling thinking about another way of organizing it, and this simple approach you presented seems pretty useful! May I use it sir? haha

Keep up the good work on the game! It's awesome!
Ryan Fleury,
Thanks for the comment, khofez! I'm glad you liked the architecture, the post, and I'm glad you are enjoying the game development thus far.

khofez
May I use it sir? haha


Of course, but there is no warranty implied! :P

This was an experimental architecture so if it causes some horrible problems, I apologize in advance!