From a99f1ccd0af594c29e9ce6912354d7bd140d6dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matt=C3=A9o=20Delabre?= Date: Tue, 15 Mar 2016 22:00:03 +0100 Subject: [PATCH] =?UTF-8?q?Impl=C3=A9mentation=20initiale=20des=20collisio?= =?UTF-8?q?ns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/ball.hpp | 7 +++ include/block.hpp | 7 +++ include/object.hpp | 21 +++++++ src/ball.cpp | 146 ++++++++++++++++++++++++++++++++++++++------- src/block.cpp | 15 +++++ src/engine.cpp | 57 +++++++++++++++--- src/object.cpp | 14 ++++- 7 files changed, 237 insertions(+), 30 deletions(-) diff --git a/include/ball.hpp b/include/ball.hpp index a3d2dbd..8d59a8d 100644 --- a/include/ball.hpp +++ b/include/ball.hpp @@ -29,6 +29,13 @@ public: * Récupère la boîte englobante de l'objet */ std::unique_ptr getAABB(); + + /** + * Calcule la normale de la collision de cet objet avec un autre + */ + virtual bool getNormal(Object& obj, sf::Vector2f& normal); + virtual bool getNormal(Ball& obj, sf::Vector2f& normal); + virtual bool getNormal(Block& obj, sf::Vector2f& normal); }; #endif diff --git a/include/block.hpp b/include/block.hpp index 34747bb..3c355e1 100644 --- a/include/block.hpp +++ b/include/block.hpp @@ -23,6 +23,13 @@ public: * Récupère la boîte englobante de l'objet */ std::unique_ptr getAABB(); + + /** + * Calcule la normale de la collision de cet objet avec un autre + */ + virtual bool getNormal(Object& obj, sf::Vector2f& normal); + virtual bool getNormal(Ball& obj, sf::Vector2f& normal); + virtual bool getNormal(Block& obj, sf::Vector2f& normal); }; #endif diff --git a/include/object.hpp b/include/object.hpp index 53cbea4..c037648 100644 --- a/include/object.hpp +++ b/include/object.hpp @@ -5,6 +5,9 @@ #include #include "engine_state.hpp" +class Block; +class Ball; + class Object { private: sf::Vector2f acceleration; @@ -16,6 +19,7 @@ private: float mass; float charge; + float restitution; int layer; protected: @@ -43,6 +47,13 @@ public: */ virtual std::unique_ptr getAABB() = 0; + /** + * Calcule la normale de la collision de cet objet avec un autre + */ + virtual bool getNormal(Object& obj, sf::Vector2f& normal); + virtual bool getNormal(Ball& obj, sf::Vector2f& normal) = 0; + virtual bool getNormal(Block& obj, sf::Vector2f& normal) = 0; + /** * Récupère l'accélération de l'objet */ @@ -84,6 +95,16 @@ public: */ void setCharge(float set_charge); + /** + * Récupère le coefficient de restitution de l'objet + */ + float getRestitution(); + + /** + * Modifie le coefficient de restitution de l'objet + */ + void setRestitution(float set_restitution); + /** * Récupère la couche d'affichage de l'objet */ diff --git a/src/ball.cpp b/src/ball.cpp index 22b7dd1..a7c1911 100644 --- a/src/ball.cpp +++ b/src/ball.cpp @@ -1,31 +1,12 @@ #include "ball.hpp" +#include "block.hpp" #include "constants.hpp" +#include Ball::Ball(float x, float y) : Object(x, y), shape(10) { shape.setOrigin(sf::Vector2f(10, 10)); } -void Ball::draw(sf::RenderWindow& window) { - Object::draw(window); - - // chargement de la texture de test - if (!texture.loadFromFile("./res/ball.png")) { - // erreur - } - - shape.setTexture(&texture); - shape.setPosition(getPosition()); - window.draw(shape); -} - -std::unique_ptr Ball::getAABB() { - return std::unique_ptr(new sf::FloatRect( - getPosition().x - 10, - getPosition().y - 10, - 20, 20 - )); -} - sf::Vector2f Ball::getForces(EngineState& state) { sf::Vector2f forces = Object::getForces(state); @@ -73,3 +54,126 @@ sf::Vector2f Ball::getForces(EngineState& state) { return forces; } + +void Ball::draw(sf::RenderWindow& window) { + Object::draw(window); + + // chargement de la texture de test + if (!texture.loadFromFile("./res/ball.png")) { + // erreur + } + + shape.setTexture(&texture); + shape.setPosition(getPosition()); + window.draw(shape); +} + +std::unique_ptr Ball::getAABB() { + return std::unique_ptr(new sf::FloatRect( + getPosition().x - 10, + getPosition().y - 10, + 20, 20 + )); +} + +bool Ball::getNormal(Object& obj, sf::Vector2f& normal) { + return obj.getNormal(*this, normal); +} + +bool Ball::getNormal(Ball& obj, sf::Vector2f& normal) { + sf::Vector2f dir = obj.getPosition() - getPosition(); + float squaredLength = dir.x * dir.x + dir.y * dir.y; + + // TODO: supprimer les valeurs magiques + // si les deux balles sont à une distance supérieure + // à la somme de leurs deux rayons, il n'y a pas eu collision + if (squaredLength > 20 * 20) { + return false; + } + + float length = std::sqrt(squaredLength); + + // les balles sont sur la même position. + // Renvoie une normale apte à séparer les deux balles + if (length == 0) { + normal.x = 0; + normal.y = -1; + return true; + } + + // il y a eu collision + normal = dir / length; + return true; +} + +bool Ball::getNormal(Block& obj, sf::Vector2f& normal) { + // recherche du point le plus proche du centre de la + // balle sur le bloc. On regarde la position relative + // du cercle par rapport au bloc + std::unique_ptr aabb = obj.getAABB(); + sf::Vector2f relpos = getPosition() - obj.getPosition(); + sf::Vector2f closest = relpos; + + // on restreint la position relative pour rester + // à l'intérieur du bloc + if (closest.x < -aabb->width / 2) { + closest.x = -aabb->width / 2; + } + + if (closest.x > aabb->width / 2) { + closest.x = aabb->width / 2; + } + + if (closest.y < -aabb->height / 2) { + closest.y = -aabb->height / 2; + } + + if (closest.y > aabb->height / 2) { + closest.y = aabb->height / 2; + } + + // si la position n'a pas été changée, elle + // était déjà à l'intérieur du cercle : le cercle + // est dans le bloc + float isInside = false; + + if (relpos == closest) { + isInside = true; + + // on se colle au bord le plus proche du bloc + if (std::abs(relpos.x) > std::abs(relpos.y)) { + if (closest.x > 0) { + closest.x = aabb->width / 2; + } else { + closest.x = -aabb->width / 2; + } + } else { + if (closest.y > 0) { + closest.y = aabb->height / 2; + } else { + closest.y = -aabb->height / 2; + } + } + } + + // la normale est portée par la direction + // du point le plus proche au centre de la balle + sf::Vector2f prenormal = relpos - closest; + float squaredLength = prenormal.x * prenormal.x + prenormal.y * prenormal.y; + + // TODO: supprimer les valeurs magiques + // si la balle est à l'extérieur et que + // la normale est plus longue que son rayon, + // il n'y a pas collision + if (!isInside && squaredLength > 20 * 20) { + return false; + } + + normal = prenormal / std::sqrt(squaredLength); + + if (isInside) { + normal *= -1.f; + } + + return true; +} diff --git a/src/block.cpp b/src/block.cpp index f450dc3..52a281b 100644 --- a/src/block.cpp +++ b/src/block.cpp @@ -1,4 +1,5 @@ #include "block.hpp" +#include "ball.hpp" #include "constants.hpp" Block::Block(float x, float y) : Object(x, y), @@ -39,3 +40,17 @@ std::unique_ptr Block::getAABB() { Constants::GRID, Constants::GRID )); } + +bool Block::getNormal(Object& obj, sf::Vector2f& normal) { + return obj.getNormal(*this, normal); +} + +bool Block::getNormal(Ball& obj, sf::Vector2f& normal) { + // TODO: coder cette fonction + return false; +} + +bool Block::getNormal(Block& obj, sf::Vector2f& normal) { + // TODO: coder cette fonction + return false; +} diff --git a/src/engine.cpp b/src/engine.cpp index d7209d3..9bf61ca 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -49,21 +49,62 @@ void Engine::update() { state.objects[i]->update(state); } - // collisions entre objets + // gère les collisions entre les objets for (unsigned int i = 0; i < state.objects.size(); i++) { Object* objA = state.objects[i]; for (unsigned int j = i + 1; j < state.objects.size(); j++) { Object* objB = state.objects[j]; - // vérifie si on a un risque de collision - if (objA->getAABB()->intersects(*objB->getAABB())) { - // TODO: gestion de la collision => - // calcul de la normale - // calcul de la profondeur du choc - // si profondeur > 0 : - // résolution de la collision + // si les objets ne sont pas sur la même couche, + // ils ne peuvent pas entrer en collision + if (objA->getLayer() != objB->getLayer()) { + continue; } + + // si les deux boîtes englobantes des deux objets, + // il ne risque pas d'y avoir de collision + if (!objA->getAABB()->intersects(*objB->getAABB())) { + continue; + } + + sf::Vector2f normal; + + // vérifie plus finement s'il y a collision et + // calcule la normale + if (!objA->getNormal(*objB, normal)) { + continue; + } + + sf::Vector2f codir = objB->getVelocity() - objA->getVelocity(); + float dotnormal = codir.x * normal.x + codir.y * normal.y; + + // si les directions sont divergentes, pas besoin + // de résoudre la collision + if (dotnormal >= 0) { + continue; + } + + float restitution = std::min(objA->getRestitution(), objB->getRestitution()); + + // calcul de l'inverse des masses de A et B. Pour rappel, + // une masse infinie est modélisée par 0, donc l'inverse + // d'une telle masse est nul + float invMassA = objA->getMass(); + float invMassB = objB->getMass(); + + if (invMassA != 0) { + invMassA = 1.f / invMassA; + } + + if (invMassB != 0) { + invMassB = 1.f / invMassB; + } + + // calcule et applique l'impulsion de résolution de la collision + float impulse = (-(1 + restitution) * dotnormal) / (invMassA + invMassB); + objA->setVelocity(objA->getVelocity() - invMassA * impulse * normal); + objB->setVelocity(objB->getVelocity() + invMassB * impulse * normal); } } } diff --git a/src/object.cpp b/src/object.cpp index 94bf95f..efbff84 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -5,7 +5,7 @@ Object::Object(float x, float y) : acceleration(0, 0), velocity(0, 0), position(x, y), accelerationLine(sf::Lines, 2), velocityLine(sf::Lines, 2), - mass(1.f), charge(0.f), layer(10) {} + mass(1.f), charge(0.f), restitution(0.f), layer(10) {} sf::Vector2f Object::getForces(EngineState& state) { sf::Vector2f forces(0, 0); @@ -48,6 +48,10 @@ void Object::update(EngineState& state) { velocity += acceleration * state.delta; } +bool Object::getNormal(Object& obj, sf::Vector2f& normal) { + return obj.getNormal(*this, normal); +} + sf::Vector2f Object::getAcceleration() { return acceleration; } @@ -80,6 +84,14 @@ void Object::setCharge(float set_charge) { charge = set_charge; } +float Object::getRestitution() { + return restitution; +} + +void Object::setRestitution(float set_restitution) { + restitution = set_restitution; +} + unsigned int Object::getLayer() { return layer; }