It is just a design choice whether you handle collisions centralized in
World or in each
Entity -- the algorithmic effort remains the same. I suggested
World because this class has a view "from outside" and because I wanted to move the responsibility for collision detection and response out of each
Entity. If the functionality becomes more complex, it may also be worthwhile to create a new class dedicated to collision. Especially since
World is already big and has many responsibilities. Where would you store your grid data structure, if not in
World (or the new
Collision class)?
Entity is certainly not the right place for it.
Let's talk about the grid cells. Each cell contains a list of entities that are currently inside, and an API to insert/remove/clear/access them.
std::set makes it very simple to maintain them, but you can also use a sequential container like
std::vector.
class Cell
{
private:
typedef std::set<Entity*> Container;
public:
void addEntity(Entity& entity);
void removeEntity(Entity& entities);
void clear();
Container::iterator begin();
Container::iterator end();
private:
Container mEntities;
};
Since cells are aligned regularly, it's standing to reason to use a random access container for fast access to a certain cell. You can also provide a function
getCellAt() that returns the cell for a specific (x,y) index pair. Internally it maps 2D indices to 1D indices.
std::vector<Cell> mCells;
Cell& getCellAt(sf::Vector2u index2D);
Given a fixed
cellSize, you could then find the corresponding cell for each entity in constant time. You just divide its position by the cell size and floor the result (by casting to
unsigned int). If negative coordinates are possible, offset them before. The entities being assigned to cells can be fetched using the command architecture.
const sf::Vector2f cellSize = ...;
sf::Vector2f pos = entity.getPosition();
sf::Vector2u cellIndex(
static_cast<unsigned int>(pos.x / cellSize.x),
static_cast<unsigned int>(pos.y / cellSize.y));
Now that you know which cell to take, you can store a pointer to the entity in this cell.
Cell& cell = getCellAt(cellIndex);
cell.addEntity(entity);
In the update step, you then perform a collision between all possible pairs inside a cell, and between pairs of one entity being inside and the other being in a neighbor cell. This is very similar to what we've already done in the book, but there are far less entities to check (because of grid locality) and the iteration can be done iteratively instead of recursively.
This is now a quite detailed description of how you could possibly implement collision detection based on grid cells. Please try it out and experiment a bit on your own before asking for every tiny detail