2
\$\begingroup\$

I'm currently making a 2D topdown dungeon crawler using pure ECS and Data Oriented Design. So I only use structs of arrays for all my components. I have now added tilemap generation, but was wondering what option to pick for tile collision. Should I add the tile collisionbox as an entity or somehow include it in my CollisionSystem?

I am already using collision boxes to check collision for entities. But In my mind, doing so for every tile against every entity might get pretty laggy.

I currently store my terrain data in a linked graph like this:

struct Tile{
    struct Tile* up;
    struct Tile* right;
    struct Tile* down;
    struct Tile* left;

    Position pos;
    bool walkable;
};

I am not using chunks.

\$\endgroup\$
  • 1
    \$\begingroup\$ Shouldn't the terrain be divided in chunks and added as components of entities? Otherwise you might want to tell us how you're doing your physics? If you're using an ECS, why would you "include" any "data" into a "System"? Also, what is different with the terrain that you can't use the same pattern you've used for the other items in your simulation? \$\endgroup\$ – Vaillancourt Dec 17 '19 at 20:49
  • \$\begingroup\$ In order to provide a good answer to this question, we would need to know: 1. What option did you pick for other kinds of collisions in your game? 2. How do you store your terrain data? As a polygon mesh, 2d heightmap, 3d array of voxels or something else? 3. Do you have only one terrain in your game or is it split into multiple chunks which might make sense to represent as separate entities? \$\endgroup\$ – Philipp Dec 18 '19 at 11:44
  • \$\begingroup\$ So you just have a simple tilemap and not a 3d terrain? OK, that changes everything. \$\endgroup\$ – Philipp Dec 18 '19 at 15:13
1
\$\begingroup\$

When you have pure tile-based yes-or-no walkability, then there are a lot of optimizations you can do because:

  1. Tiles never move
  2. Tiles always have the same size
  3. Tiles are always found at coordinates which are multiples of the tile size

Your collision system doesn't need to check every tile for collisions with entities, because tiles won't move and won't themselves be affected by collisions (there might be exceptions to this, but those should better be handled on the entity-side). It certainly doesn't need to check tiles for colliding with other tiles. So handling tiles like any other entity would indeed be a waste of CPU cycles.

I would in fact not handle tiles as entities at all, unless I had a game with very "smart" tiles which have so much functionality that they make sense to have multiple components.

You only need to check mobile entities for collisions with tiles, you only need to do so when an entity moves from the border of one tile to the next and you only need to check that specific tile. I assume your CollisionSystem already knows the old and the new (or intended) position of each mobile entity, because you would need that for interpolating collision detection of fast-moving entities and for resolving blocking events.

Your collision system should be able to tell if a specific world position is blocked or not by simply querying your tile graph for the tile at a specific position and checking if that tile is walkable. Unfortunately the data-structure you are using for tiles right now isn't very optimized for that use-case. If you want to get a tile at a specific position, then you need to start with some tile you know and then traverse the graph until you got the one you need. So you might want to create a secondary data-structure which is optimized for retrieving the tile at a specific world location.

The most common solution is to use a 2d array for the tilemap with coordinates representing the position. That way you can obtain a tile at a given position in constant time by simply doing tile[position.x / TILE_WIDTH][position.y / TILE_HEIGHT]. But when you are using a graph to store your tilemap data, then you likely intend to use a map of unlimited size and without a meaningful origin. That would make an array a bad choice. In that case you could use a hash table (std::unordered_map<Position, Tile*>, possibly with a custom hash function) or a 2-d tree to map positions to tiles.

| improve this answer | |
\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.