From b9e50580b64b5e821d62ab4e0a2c085e9a4b51b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matt=C3=A9o=20Delabre?= Date: Mon, 28 Mar 2016 02:03:56 +0200 Subject: [PATCH] =?UTF-8?q?S=C3=A9paration=20des=20trois=20phases=20de=20c?= =?UTF-8?q?ollisionage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/collision.hpp | 14 ++++---- include/collision_data.hpp | 22 ++++++++++++ include/constants.hpp | 4 +-- include/object.hpp | 23 ++++++++++-- src/collision.cpp | 62 +++++++++++++++++---------------- src/engine.cpp | 24 +++++++++++-- src/object.cpp | 71 ++++++++++++++++++++------------------ 7 files changed, 142 insertions(+), 78 deletions(-) create mode 100644 include/collision_data.hpp diff --git a/include/collision.hpp b/include/collision.hpp index 7f17c37..d611b51 100644 --- a/include/collision.hpp +++ b/include/collision.hpp @@ -2,19 +2,19 @@ #define __PTF_COLLISION_HPP__ #include "object.hpp" +#include "collision_data.hpp" #include #include -#include namespace Collision { - typedef bool (*collision_data)(Object&, Object&, sf::Vector2f&, float&); - typedef std::map, collision_data> collision_dispatcher; + typedef bool (*collision_detect)(CollisionData&); + typedef std::map, collision_detect> collision_dispatcher; extern collision_dispatcher dispatch; - bool playerToBlock(Object& objA, Object& objB, sf::Vector2f& normal, float& depth); - bool blockToPlayer(Object& objA, Object& objB, sf::Vector2f& normal, float& depth); - bool playerToPlayer(Object& objA, Object& objB, sf::Vector2f& normal, float& depth); - bool blockToBlock(Object& objA, Object& objB, sf::Vector2f& normal, float& depth); + bool playerToBlock(CollisionData& data); + bool blockToPlayer(CollisionData& data); + bool playerToPlayer(CollisionData& data); + bool blockToBlock(CollisionData& data); } #endif diff --git a/include/collision_data.hpp b/include/collision_data.hpp new file mode 100644 index 0000000..9eea386 --- /dev/null +++ b/include/collision_data.hpp @@ -0,0 +1,22 @@ +#ifndef __PTF_COLLISION_DATA_HPP__ +#define __PTF_COLLISION_DATA_HPP__ + +#include + +class Object; + +/** + * Structure qui retient des informations sur les + * collisions + */ +struct CollisionData { + sf::Vector2f normal; + float depth; + + Object& objA; + Object& objB; + + CollisionData(Object& objA, Object& objB) : objA(objA), objB(objB) {} +}; + +#endif diff --git a/include/constants.hpp b/include/constants.hpp index 12c127c..a9d9f4e 100644 --- a/include/constants.hpp +++ b/include/constants.hpp @@ -43,8 +43,8 @@ namespace Constants { * Correction positionnelle : pourcentage de correction * et seuil de correction */ - static constexpr float CORRECTION_PERCENTAGE = .8f; - static constexpr float CORRECTION_THRESHOLD = .01f; + static constexpr float CORRECTION_PERCENTAGE = .2f; + static constexpr float CORRECTION_THRESHOLD = .05f; /** * Masse par défaut d'un objet diff --git a/include/object.hpp b/include/object.hpp index a61d398..edc87e9 100644 --- a/include/object.hpp +++ b/include/object.hpp @@ -4,6 +4,7 @@ #include #include #include "engine_state.hpp" +#include "collision_data.hpp" #include "resource_manager.hpp" class Block; @@ -54,10 +55,26 @@ public: /** * 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 + * et l'objet passé en paramètre */ - void collide(Object& obj); + bool detectCollision(Object& obj, CollisionData& data); + + /** + * Résolution de la collision entre cet objet + * et l'objet passé en paramètre selon la normale + * donnée + */ + void solveCollision(Object& obj, sf::Vector2f normal); + + /** + * Application de la correction positionnelle sur + * cet objet et l'objet passé en paramètre après + * une résolution de collision de profondeur donnée. + * En raison de l'imprécision des flottants sur la machine, + * les objets peuvent accumuler une erreur de positionnement + * qui les fait "plonger" les uns dans les autres + */ + void positionalCorrection(Object& obj, sf::Vector2f normal, float depth); /** * Récupère la boîte englobante de l'objet diff --git a/src/collision.cpp b/src/collision.cpp index 01ca290..5773b27 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -1,4 +1,5 @@ #include "collision.hpp" +#include "collision_data.hpp" #include "player.hpp" #include "block.hpp" #include "object.hpp" @@ -14,9 +15,9 @@ namespace Collision { {std::make_pair(Block::TYPE_ID, Block::TYPE_ID), &blockToBlock} }; - bool playerToBlock(Object& objA, Object& objB, sf::Vector2f& normal, float& depth) { - Player player = dynamic_cast(objA); - Block block = dynamic_cast(objB); + bool playerToBlock(CollisionData& data) { + Player player = dynamic_cast(data.objA); + Block block = dynamic_cast(data.objB); // recherche du point le plus proche du centre de la // balle sur le bloc. On regarde la position relative @@ -80,30 +81,31 @@ namespace Collision { } float length = std::sqrt(squaredLength); - depth = player.getRadius() - length; + data.depth = player.getRadius() - length; if (length != 0) { - normal = prenormal / length; + data.normal = prenormal / length; } if (isInside) { - normal *= -1.f; + data.normal *= -1.f; } return true; } - bool blockToPlayer(Object& objA, Object& objB, sf::Vector2f& normal, float& depth) { + bool blockToPlayer(CollisionData& data) { // la collision Block -> Player est la collision Player -> Block - // avec une normale de collision retournée - bool result = playerToBlock(objB, objA, normal, depth); - normal *= -1.f; - return result; + Object& objT = data.objB; + data.objB = data.objA; + data.objA = objT; + + return playerToBlock(data); } - bool playerToPlayer(Object& objA, Object& objB, sf::Vector2f& normal, float& depth) { - Player playerA = dynamic_cast(objA); - Player playerB = dynamic_cast(objB); + bool playerToPlayer(CollisionData& data) { + Player playerA = dynamic_cast(data.objA); + Player playerB = dynamic_cast(data.objB); sf::Vector2f dir = playerB.getPosition() - playerA.getPosition(); float squaredLength = dir.x * dir.x + dir.y * dir.y; @@ -120,21 +122,21 @@ namespace Collision { // les balles sont sur la même position. // Renvoie une normale apte à séparer les deux balles if (length == 0) { - depth = totalRadius; - normal.x = 0; - normal.y = -1; + data.depth = totalRadius; + data.normal.x = 0; + data.normal.y = -1; return true; } // il y a eu collision - depth = totalRadius - length; - normal = dir / length; + data.depth = totalRadius - length; + data.normal = dir / length; return true; } - bool blockToBlock(Object& objA, Object& objB, sf::Vector2f& normal, float& depth) { - Block blockA = dynamic_cast(objA); - Block blockB = dynamic_cast(objB); + bool blockToBlock(CollisionData& data) { + Block blockA = dynamic_cast(data.objA); + Block blockB = dynamic_cast(data.objB); std::unique_ptr aabb = blockA.getAABB(); std::unique_ptr obj_aabb = blockB.getAABB(); @@ -151,22 +153,22 @@ namespace Collision { // on choisit l'axe de pénétration maximale pour calculer la normale if (overlap_x < overlap_y) { if (relpos.x < 0) { - normal.x = -1; + data.normal.x = -1; } else { - normal.x = 1; + data.normal.x = 1; } - normal.y = 0; - depth = overlap_x; + data.normal.y = 0; + data.depth = overlap_x; } else { if (relpos.y < 0) { - normal.y = -1; + data.normal.y = -1; } else { - normal.y = 1; + data.normal.y = 1; } - normal.x = 0; - depth = overlap_y; + data.normal.x = 0; + data.depth = overlap_y; } return true; diff --git a/src/engine.cpp b/src/engine.cpp index cfaaed5..de86510 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -71,13 +71,19 @@ void Engine::addObject(Object& object) { } void Engine::update() { - // gestion des collisions entre les objets + std::vector colliding; + + // détection des objets en collision 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]; - objA->collide(*objB); + CollisionData data(*objA, *objB); + + if (objA->detectCollision(*objB, data)) { + colliding.push_back(data); + } } } @@ -86,11 +92,25 @@ void Engine::update() { state.objects[i]->updateVelocity(state, Constants::PHYSICS_TIME / 2); } + // résolution des collisions détectées + for (unsigned int i = 0; i < colliding.size(); i++) { + CollisionData& collided = colliding[i]; + collided.objA.solveCollision(collided.objB, collided.normal); + } + // intégration de la vitesse dans la position for (unsigned int i = 0; i < state.objects.size(); i++) { state.objects[i]->updatePosition(state, Constants::PHYSICS_TIME); } + // application de la correction positionnelle + for (unsigned int i = 0; i < colliding.size(); i++) { + CollisionData& collided = colliding[i]; + collided.objA.positionalCorrection( + collided.objB, collided.normal, collided.depth + ); + } + // intégration des forces dans la vitesse (seconde moitié) for (unsigned int i = 0; i < state.objects.size(); i++) { state.objects[i]->updateVelocity(state, Constants::PHYSICS_TIME / 2); diff --git a/src/object.cpp b/src/object.cpp index 4d0ec93..ef1e7c4 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -1,6 +1,7 @@ #include "object.hpp" #include "constants.hpp" #include "collision.hpp" +#include "collision_data.hpp" Object::Object(float x, float y) : acceleration(0, 0), velocity(0, 0), position(x, y), @@ -82,28 +83,27 @@ void Object::updatePosition(EngineState& state, float delta) { position += velocity * delta; } -void Object::collide(Object& obj) { +bool Object::detectCollision(Object& obj, CollisionData& data) { // si les objets ne sont pas sur la même couche, // ils ne peuvent pas entrer en collision if (getLayer() != obj.getLayer()) { - return; + return false; } - // si les deux boîtes englobantes des deux objets, ne + // 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; + return false; } - 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 (!Collision::dispatch[std::make_pair(getTypeId(), obj.getTypeId())](*this, obj, normal, depth)) { - return; - } + return Collision::dispatch[ + // la fonction de détection fine de collision est choisie + // en fonction des types des deux objets en question + std::make_pair(getTypeId(), obj.getTypeId()) + ](data); +} +void Object::solveCollision(Object& obj, sf::Vector2f normal) { // si les deux objets sont de masse infinie, réinitialisation // des vitesses en tant que collision if (getMassInvert() == 0 && obj.getMassInvert() == 0) { @@ -140,30 +140,33 @@ void Object::collide(Object& obj) { // la tangente est nulle : pas de frottement à générer // on évite ainsi une division par zéro - if (tangent_length != 0) { - tangent /= tangent_length; - - float magnitude = -(rel_velo.x * tangent.x + rel_velo.y * tangent.y) / - (getMassInvert() + obj.getMassInvert()); - float static_friction = (getStaticFriction() + obj.getStaticFriction()) / 2.f; - float dynamic_friction = (getDynamicFriction() + obj.getDynamicFriction()) / 2.f; - float friction_impulse; - - // utilisation de la loi de Coulomb sur les frottements dynamiques/statiques - // cf https://fr.wikipedia.org/wiki/Loi_de_Coulomb_(m%C3%A9canique) - if (std::abs(magnitude) < collision_impulse * static_friction) { - friction_impulse = magnitude; - } else { - friction_impulse = -collision_impulse * dynamic_friction; - } - - setVelocity(getVelocity() - getMassInvert() * friction_impulse * tangent); - obj.setVelocity(obj.getVelocity() + obj.getMassInvert() * friction_impulse * tangent); + if (tangent_length == 0) { + return; } - // 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. + tangent /= tangent_length; + + float magnitude = -(rel_velo.x * tangent.x + rel_velo.y * tangent.y) / + (getMassInvert() + obj.getMassInvert()); + float static_friction = (getStaticFriction() + obj.getStaticFriction()) / 2.f; + float dynamic_friction = (getDynamicFriction() + obj.getDynamicFriction()) / 2.f; + float friction_impulse; + + // utilisation de la loi de Coulomb sur les frottements dynamiques/statiques + // cf https://fr.wikipedia.org/wiki/Loi_de_Coulomb_(m%C3%A9canique) + if (std::abs(magnitude) < collision_impulse * static_friction) { + friction_impulse = magnitude; + } else { + friction_impulse = -collision_impulse * dynamic_friction; + } + + setVelocity(getVelocity() - getMassInvert() * friction_impulse * tangent); + obj.setVelocity(obj.getVelocity() + obj.getMassInvert() * friction_impulse * tangent); +} + +void Object::positionalCorrection(Object& obj, sf::Vector2f normal, float depth) { + // ne pas corriger les petites erreurs de position + // pour éviter l'instabilité du moteur if (depth <= Constants::CORRECTION_THRESHOLD) { return; }