Determines if two Collideable bodies have collided. If they have it emits a CollisionEvent. This is used by ExplosionSystem to create explosion particles, but it could be used by a SoundSystem to play an explosion sound, etc.. Uses a fairly rudimentary 2D partition system, but performs reasonably well.
| 162 | // |
| 163 | // Uses a fairly rudimentary 2D partition system, but performs reasonably well. |
| 164 | class CollisionSystem : public ex::System<CollisionSystem> { |
| 165 | static const int PARTITIONS = 200; |
| 166 | |
| 167 | struct Candidate { |
| 168 | sf::Vector2f position; |
| 169 | float radius; |
| 170 | ex::Entity entity; |
| 171 | }; |
| 172 | |
| 173 | public: |
| 174 | explicit CollisionSystem(sf::RenderTarget &target) : size(target.getSize()) { |
| 175 | size.x = size.x / PARTITIONS + 1; |
| 176 | size.y = size.y / PARTITIONS + 1; |
| 177 | } |
| 178 | |
| 179 | void update(ex::EntityManager &es, ex::EventManager &events, ex::TimeDelta dt) override { |
| 180 | reset(); |
| 181 | collect(es); |
| 182 | collide(events); |
| 183 | }; |
| 184 | |
| 185 | private: |
| 186 | std::vector<std::vector<Candidate>> grid; |
| 187 | sf::Vector2u size; |
| 188 | |
| 189 | void reset() { |
| 190 | grid.clear(); |
| 191 | grid.resize(size.x * size.y); |
| 192 | } |
| 193 | |
| 194 | void collect(ex::EntityManager &entities) { |
| 195 | ex::ComponentHandle<Body> body; |
| 196 | ex::ComponentHandle<Collideable> collideable; |
| 197 | for (ex::Entity entity : entities.entities_with_components(body, collideable)) { |
| 198 | unsigned int |
| 199 | left = static_cast<int>(body->position.x - collideable->radius) / PARTITIONS, |
| 200 | top = static_cast<int>(body->position.y - collideable->radius) / PARTITIONS, |
| 201 | right = static_cast<int>(body->position.x + collideable->radius) / PARTITIONS, |
| 202 | bottom = static_cast<int>(body->position.y + collideable->radius) / PARTITIONS; |
| 203 | Candidate candidate {body->position, collideable->radius, entity}; |
| 204 | unsigned int slots[4] = { |
| 205 | left + top * size.x, |
| 206 | right + top * size.x, |
| 207 | left + bottom * size.x, |
| 208 | right + bottom * size.x, |
| 209 | }; |
| 210 | grid[slots[0]].push_back(candidate); |
| 211 | if (slots[0] != slots[1]) grid[slots[1]].push_back(candidate); |
| 212 | if (slots[1] != slots[2]) grid[slots[2]].push_back(candidate); |
| 213 | if (slots[2] != slots[3]) grid[slots[3]].push_back(candidate); |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | void collide(ex::EventManager &events) { |
| 218 | for (const std::vector<Candidate> &candidates : grid) { |
| 219 | for (const Candidate &left : candidates) { |
| 220 | for (const Candidate &right : candidates) { |
| 221 | if (left.entity == right.entity) continue; |
nothing calls this directly
no outgoing calls
no test coverage detected