Séparation des trois phases de collisionage

This commit is contained in:
Mattéo Delabre 2016-03-28 02:03:56 +02:00
parent c5a4402369
commit b9e50580b6
7 changed files with 142 additions and 78 deletions

View File

@ -2,19 +2,19 @@
#define __PTF_COLLISION_HPP__
#include "object.hpp"
#include "collision_data.hpp"
#include <SFML/Graphics.hpp>
#include <utility>
#include <string>
namespace Collision {
typedef bool (*collision_data)(Object&, Object&, sf::Vector2f&, float&);
typedef std::map<std::pair<unsigned int, unsigned int>, collision_data> collision_dispatcher;
typedef bool (*collision_detect)(CollisionData&);
typedef std::map<std::pair<unsigned int, unsigned int>, 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

View File

@ -0,0 +1,22 @@
#ifndef __PTF_COLLISION_DATA_HPP__
#define __PTF_COLLISION_DATA_HPP__
#include <SFML/Graphics.hpp>
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

View File

@ -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

View File

@ -4,6 +4,7 @@
#include <SFML/Graphics.hpp>
#include <memory>
#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

View File

@ -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<Player&>(objA);
Block block = dynamic_cast<Block&>(objB);
bool playerToBlock(CollisionData& data) {
Player player = dynamic_cast<Player&>(data.objA);
Block block = dynamic_cast<Block&>(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<Player&>(objA);
Player playerB = dynamic_cast<Player&>(objB);
bool playerToPlayer(CollisionData& data) {
Player playerA = dynamic_cast<Player&>(data.objA);
Player playerB = dynamic_cast<Player&>(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<Block&>(objA);
Block blockB = dynamic_cast<Block&>(objB);
bool blockToBlock(CollisionData& data) {
Block blockA = dynamic_cast<Block&>(data.objA);
Block blockB = dynamic_cast<Block&>(data.objB);
std::unique_ptr<sf::FloatRect> aabb = blockA.getAABB();
std::unique_ptr<sf::FloatRect> 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;

View File

@ -71,13 +71,19 @@ void Engine::addObject(Object& object) {
}
void Engine::update() {
// gestion des collisions entre les objets
std::vector<CollisionData> 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);

View File

@ -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;
}