mirror of
https://github.com/Floriansylvain/SFMLplayground.git
synced 2026-08-19 11:43:24 +02:00
feat: collision resolution & spatial grid for balls
This commit is contained in:
@@ -79,3 +79,33 @@ void Ball::handleWallCollision() {
|
||||
|
||||
m_shape.setPosition(pos);
|
||||
}
|
||||
|
||||
void Ball::resolveCollision(Ball& other) {
|
||||
sf::Vector2f posA = getPosition();
|
||||
sf::Vector2f posB = other.getPosition();
|
||||
sf::Vector2f delta = posB - posA;
|
||||
float dist = std::sqrt(delta.x * delta.x + delta.y * delta.y);
|
||||
float minDist = m_radius + other.m_radius;
|
||||
|
||||
if (dist >= minDist || dist < 1e-6f) return;
|
||||
|
||||
sf::Vector2f normal = delta / dist;
|
||||
float overlap = minDist - dist;
|
||||
m_shape.move(-normal * (overlap / 2.f));
|
||||
other.m_shape.move(normal * (overlap / 2.f));
|
||||
|
||||
sf::Vector2f vA = m_velocity;
|
||||
sf::Vector2f vB = other.m_velocity;
|
||||
float vA_n = vA.x * normal.x + vA.y * normal.y;
|
||||
float vB_n = vB.x * normal.x + vB.y * normal.y;
|
||||
|
||||
float restitution = Constants::RESTITUTION;
|
||||
float vA_n_new = vB_n * restitution;
|
||||
float vB_n_new = vA_n * restitution;
|
||||
|
||||
m_velocity += (vA_n_new - vA_n) * normal;
|
||||
other.m_velocity += (vB_n_new - vB_n) * normal;
|
||||
|
||||
m_atRest = false;
|
||||
other.m_atRest = false;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ class Ball : public PhysicalObject {
|
||||
void update(float dt) override;
|
||||
void draw(sf::RenderWindow& window) override;
|
||||
void applyImpulse(const sf::Vector2f& impulse) override;
|
||||
void resolveCollision(Ball& other);
|
||||
|
||||
sf::Vector2f getPosition() const;
|
||||
sf::Vector2f getVelocity() const;
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ void DebugDraw::addVelocityLine(sf::VertexArray& lines, const Ball* ball) {
|
||||
float velLength = VectorMath::length(velocity);
|
||||
float clampedLength = std::max(0.f, std::min(velLength, 100.f));
|
||||
|
||||
addLine(lines, ballCenter, velocity, clampedLength, sf::Color::Blue);
|
||||
addLine(lines, ballCenter, velocity, clampedLength, sf::Color::Red);
|
||||
}
|
||||
|
||||
void DebugDraw::drawBatchedLines(sf::RenderWindow& window,
|
||||
|
||||
+54
-1
@@ -1,7 +1,10 @@
|
||||
#include "Game.hpp"
|
||||
|
||||
#include <SFML/Window/Event.hpp>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "BallFactory.hpp"
|
||||
#include "Constants.hpp"
|
||||
@@ -57,9 +60,59 @@ void Game::handleMouseClick(const sf::Vector2i &mousePos) {
|
||||
}
|
||||
}
|
||||
|
||||
Game::Grid Game::buildSpatialGrid() {
|
||||
const float cellSize = 2 * Constants::BALL_RADIUS;
|
||||
const float safeCellSize = std::max(cellSize, 0.001f);
|
||||
Grid grid;
|
||||
for (auto &object : m_objects) {
|
||||
if (Ball *ball = dynamic_cast<Ball *>(object.get())) {
|
||||
const sf::Vector2f &pos = ball->getPosition();
|
||||
if (std::isfinite(pos.x) && std::isfinite(pos.y)) {
|
||||
int cellX = static_cast<int>(std::floor(pos.x / safeCellSize));
|
||||
int cellY = static_cast<int>(std::floor(pos.y / safeCellSize));
|
||||
grid[{cellX, cellY}].push_back(ball);
|
||||
}
|
||||
}
|
||||
}
|
||||
return grid;
|
||||
}
|
||||
|
||||
void Game::resolveSpatialCollisions(const Grid &grid) {
|
||||
static const Cell forwardNeighbors[] = {
|
||||
{0, 0}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}};
|
||||
for (const auto &[cell, cellBalls] : grid) {
|
||||
for (const auto &offset : forwardNeighbors) {
|
||||
Cell neighborCell = {cell.first + offset.first,
|
||||
cell.second + offset.second};
|
||||
auto neighborIt = grid.find(neighborCell);
|
||||
if (neighborIt == grid.end()) continue;
|
||||
if (neighborCell == cell) {
|
||||
for (size_t i = 0; i < cellBalls.size(); ++i) {
|
||||
for (size_t j = i + 1; j < cellBalls.size(); ++j) {
|
||||
cellBalls[i]->resolveCollision(*cellBalls[j]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Ball *ballA : cellBalls) {
|
||||
for (Ball *ballB : neighborIt->second) {
|
||||
ballA->resolveCollision(*ballB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Game::update() {
|
||||
float dt = m_clock.restart().asSeconds() * m_timeScale;
|
||||
for (auto &object : m_objects) object->update(dt);
|
||||
if (dt > 0.1f) dt = 0.1f;
|
||||
|
||||
for (auto &object : m_objects) {
|
||||
object->update(dt);
|
||||
}
|
||||
|
||||
auto grid = buildSpatialGrid();
|
||||
resolveSpatialCollisions(grid);
|
||||
}
|
||||
|
||||
void Game::render() {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <SFML/System/Vector2.hpp>
|
||||
#include <SFML/Window/Event.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "Ball.hpp"
|
||||
@@ -35,6 +36,18 @@ class Game {
|
||||
void update();
|
||||
void render();
|
||||
|
||||
struct CellHash {
|
||||
std::size_t operator()(const std::pair<int, int>& k) const {
|
||||
return static_cast<std::size_t>(k.first) * 73856093 ^
|
||||
static_cast<std::size_t>(k.second) * 19349663;
|
||||
}
|
||||
};
|
||||
|
||||
using Cell = std::pair<int, int>;
|
||||
using Grid = std::unordered_map<Cell, std::vector<Ball*>, CellHash>;
|
||||
Grid buildSpatialGrid();
|
||||
void resolveSpatialCollisions(const Grid& grid);
|
||||
|
||||
public:
|
||||
Game();
|
||||
void run();
|
||||
|
||||
Reference in New Issue
Block a user