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