From db18959575d8bdb1bfed459d0b2d2c0e9e1dff4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matt=C3=A9o=20Delabre?= Date: Fri, 18 Mar 2016 16:48:22 +0100 Subject: [PATCH] Correction des collisions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajout de la correction positionelle, correction du rayon de détection --- include/ball.hpp | 9 ++++--- include/block.hpp | 9 ++++--- include/constants.hpp | 7 +++++ include/object.hpp | 22 +++++++++++++--- src/ball.cpp | 18 ++++++++----- src/block.cpp | 8 +++--- src/engine.cpp | 38 +-------------------------- src/object.cpp | 61 +++++++++++++++++++++++++++++++++++++++++-- 8 files changed, 111 insertions(+), 61 deletions(-) diff --git a/include/ball.hpp b/include/ball.hpp index 8d59a8d..fddaa2e 100644 --- a/include/ball.hpp +++ b/include/ball.hpp @@ -31,11 +31,12 @@ public: std::unique_ptr getAABB(); /** - * Calcule la normale de la collision de cet objet avec un autre + * Calcule les informations sur une éventuelle collision de + * cet objet avec un autre : la normale et la profondeur */ - virtual bool getNormal(Object& obj, sf::Vector2f& normal); - virtual bool getNormal(Ball& obj, sf::Vector2f& normal); - virtual bool getNormal(Block& obj, sf::Vector2f& normal); + virtual bool getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth); + virtual bool getCollisionInfo(Ball& obj, sf::Vector2f& normal, float& depth); + virtual bool getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth); }; #endif diff --git a/include/block.hpp b/include/block.hpp index 3c355e1..b7c1111 100644 --- a/include/block.hpp +++ b/include/block.hpp @@ -25,11 +25,12 @@ public: std::unique_ptr getAABB(); /** - * Calcule la normale de la collision de cet objet avec un autre + * Calcule les informations sur une éventuelle collision de + * cet objet avec un autre : la normale et la profondeur */ - virtual bool getNormal(Object& obj, sf::Vector2f& normal); - virtual bool getNormal(Ball& obj, sf::Vector2f& normal); - virtual bool getNormal(Block& obj, sf::Vector2f& normal); + virtual bool getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth); + virtual bool getCollisionInfo(Ball& obj, sf::Vector2f& normal, float& depth); + virtual bool getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth); }; #endif diff --git a/include/constants.hpp b/include/constants.hpp index 1f4e926..0efcc69 100644 --- a/include/constants.hpp +++ b/include/constants.hpp @@ -28,6 +28,13 @@ namespace Constants { * Taille de la grille des blocs en pixels */ static constexpr float GRID = 32; + + /** + * Correction positionnelle : pourcentage de correction + * et seuil de correction + */ + static constexpr float CORRECTION_PERCENTAGE = .2f; + static constexpr float CORRECTION_THRESHOLD = .01f; } #endif diff --git a/include/object.hpp b/include/object.hpp index 2434c08..7ba12ab 100644 --- a/include/object.hpp +++ b/include/object.hpp @@ -49,11 +49,19 @@ public: virtual std::unique_ptr getAABB() = 0; /** - * Calcule la normale de la collision de cet objet avec un autre + * Calcule les informations sur une éventuelle collision de + * cet objet avec un autre : la normale et la profondeur */ - 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; + virtual bool getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth); + virtual bool getCollisionInfo(Ball& obj, sf::Vector2f& normal, float& depth) = 0; + virtual bool getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth) = 0; + + /** + * Détecte s'il y a collision entre cet objet + * et l'objet passé en paramètre et résoud la collision + * si elle a lieu + */ + void collide(Object& obj); /** * Récupère l'accélération de l'objet @@ -76,6 +84,12 @@ public: */ sf::Vector2f getPosition(); + /** + * Modifie la position de l'objet + * (à utiliser avec précaution, préférer modifier les forces) + */ + void setPosition(sf::Vector2f set_position); + /** * Récupère la masse de l'objet */ diff --git a/src/ball.cpp b/src/ball.cpp index 8096f94..1bd2b15 100644 --- a/src/ball.cpp +++ b/src/ball.cpp @@ -76,11 +76,11 @@ std::unique_ptr Ball::getAABB() { )); } -bool Ball::getNormal(Object& obj, sf::Vector2f& normal) { - return obj.getNormal(*this, normal); +bool Ball::getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth) { + return obj.getCollisionInfo(*this, normal, depth); } -bool Ball::getNormal(Ball& obj, sf::Vector2f& normal) { +bool Ball::getCollisionInfo(Ball& obj, sf::Vector2f& normal, float& depth) { sf::Vector2f dir = obj.getPosition() - getPosition(); float squaredLength = dir.x * dir.x + dir.y * dir.y; @@ -96,17 +96,21 @@ bool Ball::getNormal(Ball& obj, sf::Vector2f& normal) { // les balles sont sur la même position. // Renvoie une normale apte à séparer les deux balles if (length == 0) { + // TODO: supprimer les valeurs magiques + depth = 10; normal.x = 0; normal.y = -1; return true; } + // TODO: supprimer les valeurs magiques // il y a eu collision + depth = 10 - length; normal = dir / length; return true; } -bool Ball::getNormal(Block& obj, sf::Vector2f& normal) { +bool Ball::getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth) { // 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 @@ -165,11 +169,13 @@ bool Ball::getNormal(Block& obj, sf::Vector2f& normal) { // 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) { + if (!isInside && squaredLength >= 10 * 10) { return false; } - normal = prenormal / std::sqrt(squaredLength); + float length = std::sqrt(squaredLength); + depth = 10 - length; + normal = prenormal / length; if (isInside) { normal *= -1.f; diff --git a/src/block.cpp b/src/block.cpp index 52a281b..7c43912 100644 --- a/src/block.cpp +++ b/src/block.cpp @@ -41,16 +41,16 @@ std::unique_ptr Block::getAABB() { )); } -bool Block::getNormal(Object& obj, sf::Vector2f& normal) { - return obj.getNormal(*this, normal); +bool Block::getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth) { + return obj.getCollisionInfo(*this, normal, depth); } -bool Block::getNormal(Ball& obj, sf::Vector2f& normal) { +bool Block::getCollisionInfo(Ball& obj, sf::Vector2f& normal, float& depth) { // TODO: coder cette fonction return false; } -bool Block::getNormal(Block& obj, sf::Vector2f& normal) { +bool Block::getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth) { // TODO: coder cette fonction return false; } diff --git a/src/engine.cpp b/src/engine.cpp index 5bd3cd6..587d4d6 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -55,43 +55,7 @@ void Engine::update() { for (unsigned int j = i + 1; j < state.objects.size(); j++) { Object* objB = state.objects[j]; - - // 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; - } - - // calcule et applique l'impulsion de résolution de la collision - float restitution = std::min(objA->getRestitution(), objB->getRestitution()); - float impulse = (-(1 + restitution) * dotnormal) / - (objA->getMassInvert() + objB->getMassInvert()); - - objA->setVelocity(objA->getVelocity() - objA->getMassInvert() * impulse * normal); - objB->setVelocity(objB->getVelocity() + objB->getMassInvert() * impulse * normal); + objA->collide(*objB); } } } diff --git a/src/object.cpp b/src/object.cpp index 3e41c3f..2a5177a 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -48,8 +48,61 @@ void Object::update(EngineState& state) { velocity += acceleration * state.delta; } -bool Object::getNormal(Object& obj, sf::Vector2f& normal) { - return obj.getNormal(*this, normal); +bool Object::getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth) { + return obj.getCollisionInfo(*this, normal, depth); +} + +void Object::collide(Object& obj) { + // si les objets ne sont pas sur la même couche, + // ils ne peuvent pas entrer en collision + if (getLayer() != obj.getLayer()) { + return; + } + + // si les deux boîtes englobantes des deux objets, ne + // s'intersectent pas, il ne risque pas d'y avoir de collision + if (!getAABB()->intersects(*obj.getAABB())) { + return; + } + + sf::Vector2f normal; + float depth; + + // vérifie plus finement s'il y a collision et récupère + // les informations sur la collision (normale et profondeur) + if (!getCollisionInfo(obj, normal, depth)) { + return; + } + + sf::Vector2f codir = obj.getVelocity() - 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) { + return; + } + + // calcule et applique l'impulsion de résolution de la collision + float restitution = std::min(getRestitution(), obj.getRestitution()); + float impulse = (-(1 + restitution) * dotnormal) / + (getMassInvert() + obj.getMassInvert()); + + setVelocity(getVelocity() - getMassInvert() * impulse * normal); + obj.setVelocity(obj.getVelocity() + obj.getMassInvert() * impulse * normal); + + // correction de la position des objets. En raison de l'imprécision + // des flottants sur la machine, les objets peuvent accumuler une + // erreur de positionnement qui les fait "plonger" les un dans les autres. + if (depth <= Constants::CORRECTION_THRESHOLD) { + return; + } + + sf::Vector2f positionCorrection = depth / (getMassInvert() + + obj.getMassInvert()) * Constants::CORRECTION_PERCENTAGE * normal; + + setPosition(getPosition() - getMassInvert() * positionCorrection); + obj.setPosition(obj.getPosition() + obj.getMassInvert() * positionCorrection); } sf::Vector2f Object::getAcceleration() { @@ -68,6 +121,10 @@ sf::Vector2f Object::getPosition() { return position; } +void Object::setPosition(sf::Vector2f set_position) { + position = set_position; +} + float Object::getMass() { return mass; }