When you have pure tile-based yes-or-no walkability, then there are a lot of optimizations you can do because:
- Tiles never move
- Tiles always have the same size
- 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.