Séparation des trois phases de collisionage
This commit is contained in:
parent
c5a4402369
commit
b9e50580b6
|
@ -2,19 +2,19 @@
|
||||||
#define __PTF_COLLISION_HPP__
|
#define __PTF_COLLISION_HPP__
|
||||||
|
|
||||||
#include "object.hpp"
|
#include "object.hpp"
|
||||||
|
#include "collision_data.hpp"
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace Collision {
|
namespace Collision {
|
||||||
typedef bool (*collision_data)(Object&, Object&, sf::Vector2f&, float&);
|
typedef bool (*collision_detect)(CollisionData&);
|
||||||
typedef std::map<std::pair<unsigned int, unsigned int>, collision_data> collision_dispatcher;
|
typedef std::map<std::pair<unsigned int, unsigned int>, collision_detect> collision_dispatcher;
|
||||||
extern collision_dispatcher dispatch;
|
extern collision_dispatcher dispatch;
|
||||||
|
|
||||||
bool playerToBlock(Object& objA, Object& objB, sf::Vector2f& normal, float& depth);
|
bool playerToBlock(CollisionData& data);
|
||||||
bool blockToPlayer(Object& objA, Object& objB, sf::Vector2f& normal, float& depth);
|
bool blockToPlayer(CollisionData& data);
|
||||||
bool playerToPlayer(Object& objA, Object& objB, sf::Vector2f& normal, float& depth);
|
bool playerToPlayer(CollisionData& data);
|
||||||
bool blockToBlock(Object& objA, Object& objB, sf::Vector2f& normal, float& depth);
|
bool blockToBlock(CollisionData& data);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -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
|
|
@ -43,8 +43,8 @@ namespace Constants {
|
||||||
* Correction positionnelle : pourcentage de correction
|
* Correction positionnelle : pourcentage de correction
|
||||||
* et seuil de correction
|
* et seuil de correction
|
||||||
*/
|
*/
|
||||||
static constexpr float CORRECTION_PERCENTAGE = .8f;
|
static constexpr float CORRECTION_PERCENTAGE = .2f;
|
||||||
static constexpr float CORRECTION_THRESHOLD = .01f;
|
static constexpr float CORRECTION_THRESHOLD = .05f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Masse par défaut d'un objet
|
* Masse par défaut d'un objet
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "engine_state.hpp"
|
#include "engine_state.hpp"
|
||||||
|
#include "collision_data.hpp"
|
||||||
#include "resource_manager.hpp"
|
#include "resource_manager.hpp"
|
||||||
|
|
||||||
class Block;
|
class Block;
|
||||||
|
@ -54,10 +55,26 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Détecte s'il y a collision entre cet objet
|
* Détecte s'il y a collision entre cet objet
|
||||||
* et l'objet passé en paramètre et résoud la collision
|
* et l'objet passé en paramètre
|
||||||
* si elle a lieu
|
|
||||||
*/
|
*/
|
||||||
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
|
* Récupère la boîte englobante de l'objet
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "collision.hpp"
|
#include "collision.hpp"
|
||||||
|
#include "collision_data.hpp"
|
||||||
#include "player.hpp"
|
#include "player.hpp"
|
||||||
#include "block.hpp"
|
#include "block.hpp"
|
||||||
#include "object.hpp"
|
#include "object.hpp"
|
||||||
|
@ -14,9 +15,9 @@ namespace Collision {
|
||||||
{std::make_pair(Block::TYPE_ID, Block::TYPE_ID), &blockToBlock}
|
{std::make_pair(Block::TYPE_ID, Block::TYPE_ID), &blockToBlock}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool playerToBlock(Object& objA, Object& objB, sf::Vector2f& normal, float& depth) {
|
bool playerToBlock(CollisionData& data) {
|
||||||
Player player = dynamic_cast<Player&>(objA);
|
Player player = dynamic_cast<Player&>(data.objA);
|
||||||
Block block = dynamic_cast<Block&>(objB);
|
Block block = dynamic_cast<Block&>(data.objB);
|
||||||
|
|
||||||
// recherche du point le plus proche du centre de la
|
// recherche du point le plus proche du centre de la
|
||||||
// balle sur le bloc. On regarde la position relative
|
// balle sur le bloc. On regarde la position relative
|
||||||
|
@ -80,30 +81,31 @@ namespace Collision {
|
||||||
}
|
}
|
||||||
|
|
||||||
float length = std::sqrt(squaredLength);
|
float length = std::sqrt(squaredLength);
|
||||||
depth = player.getRadius() - length;
|
data.depth = player.getRadius() - length;
|
||||||
|
|
||||||
if (length != 0) {
|
if (length != 0) {
|
||||||
normal = prenormal / length;
|
data.normal = prenormal / length;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInside) {
|
if (isInside) {
|
||||||
normal *= -1.f;
|
data.normal *= -1.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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
|
// la collision Block -> Player est la collision Player -> Block
|
||||||
// avec une normale de collision retournée
|
Object& objT = data.objB;
|
||||||
bool result = playerToBlock(objB, objA, normal, depth);
|
data.objB = data.objA;
|
||||||
normal *= -1.f;
|
data.objA = objT;
|
||||||
return result;
|
|
||||||
|
return playerToBlock(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool playerToPlayer(Object& objA, Object& objB, sf::Vector2f& normal, float& depth) {
|
bool playerToPlayer(CollisionData& data) {
|
||||||
Player playerA = dynamic_cast<Player&>(objA);
|
Player playerA = dynamic_cast<Player&>(data.objA);
|
||||||
Player playerB = dynamic_cast<Player&>(objB);
|
Player playerB = dynamic_cast<Player&>(data.objB);
|
||||||
|
|
||||||
sf::Vector2f dir = playerB.getPosition() - playerA.getPosition();
|
sf::Vector2f dir = playerB.getPosition() - playerA.getPosition();
|
||||||
float squaredLength = dir.x * dir.x + dir.y * dir.y;
|
float squaredLength = dir.x * dir.x + dir.y * dir.y;
|
||||||
|
@ -120,21 +122,21 @@ namespace Collision {
|
||||||
// les balles sont sur la même position.
|
// les balles sont sur la même position.
|
||||||
// Renvoie une normale apte à séparer les deux balles
|
// Renvoie une normale apte à séparer les deux balles
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
depth = totalRadius;
|
data.depth = totalRadius;
|
||||||
normal.x = 0;
|
data.normal.x = 0;
|
||||||
normal.y = -1;
|
data.normal.y = -1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// il y a eu collision
|
// il y a eu collision
|
||||||
depth = totalRadius - length;
|
data.depth = totalRadius - length;
|
||||||
normal = dir / length;
|
data.normal = dir / length;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool blockToBlock(Object& objA, Object& objB, sf::Vector2f& normal, float& depth) {
|
bool blockToBlock(CollisionData& data) {
|
||||||
Block blockA = dynamic_cast<Block&>(objA);
|
Block blockA = dynamic_cast<Block&>(data.objA);
|
||||||
Block blockB = dynamic_cast<Block&>(objB);
|
Block blockB = dynamic_cast<Block&>(data.objB);
|
||||||
|
|
||||||
std::unique_ptr<sf::FloatRect> aabb = blockA.getAABB();
|
std::unique_ptr<sf::FloatRect> aabb = blockA.getAABB();
|
||||||
std::unique_ptr<sf::FloatRect> obj_aabb = blockB.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
|
// on choisit l'axe de pénétration maximale pour calculer la normale
|
||||||
if (overlap_x < overlap_y) {
|
if (overlap_x < overlap_y) {
|
||||||
if (relpos.x < 0) {
|
if (relpos.x < 0) {
|
||||||
normal.x = -1;
|
data.normal.x = -1;
|
||||||
} else {
|
} else {
|
||||||
normal.x = 1;
|
data.normal.x = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
normal.y = 0;
|
data.normal.y = 0;
|
||||||
depth = overlap_x;
|
data.depth = overlap_x;
|
||||||
} else {
|
} else {
|
||||||
if (relpos.y < 0) {
|
if (relpos.y < 0) {
|
||||||
normal.y = -1;
|
data.normal.y = -1;
|
||||||
} else {
|
} else {
|
||||||
normal.y = 1;
|
data.normal.y = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
normal.x = 0;
|
data.normal.x = 0;
|
||||||
depth = overlap_y;
|
data.depth = overlap_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -71,13 +71,19 @@ void Engine::addObject(Object& object) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::update() {
|
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++) {
|
for (unsigned int i = 0; i < state.objects.size(); i++) {
|
||||||
Object* objA = state.objects[i];
|
Object* objA = state.objects[i];
|
||||||
|
|
||||||
for (unsigned int j = i + 1; j < state.objects.size(); j++) {
|
for (unsigned int j = i + 1; j < state.objects.size(); j++) {
|
||||||
Object* objB = state.objects[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);
|
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
|
// intégration de la vitesse dans la position
|
||||||
for (unsigned int i = 0; i < state.objects.size(); i++) {
|
for (unsigned int i = 0; i < state.objects.size(); i++) {
|
||||||
state.objects[i]->updatePosition(state, Constants::PHYSICS_TIME);
|
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é)
|
// intégration des forces dans la vitesse (seconde moitié)
|
||||||
for (unsigned int i = 0; i < state.objects.size(); i++) {
|
for (unsigned int i = 0; i < state.objects.size(); i++) {
|
||||||
state.objects[i]->updateVelocity(state, Constants::PHYSICS_TIME / 2);
|
state.objects[i]->updateVelocity(state, Constants::PHYSICS_TIME / 2);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "object.hpp"
|
#include "object.hpp"
|
||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
#include "collision.hpp"
|
#include "collision.hpp"
|
||||||
|
#include "collision_data.hpp"
|
||||||
|
|
||||||
Object::Object(float x, float y) :
|
Object::Object(float x, float y) :
|
||||||
acceleration(0, 0), velocity(0, 0), position(x, y),
|
acceleration(0, 0), velocity(0, 0), position(x, y),
|
||||||
|
@ -82,28 +83,27 @@ void Object::updatePosition(EngineState& state, float delta) {
|
||||||
position += velocity * 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,
|
// si les objets ne sont pas sur la même couche,
|
||||||
// ils ne peuvent pas entrer en collision
|
// ils ne peuvent pas entrer en collision
|
||||||
if (getLayer() != obj.getLayer()) {
|
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
|
// s'intersectent pas, il ne risque pas d'y avoir de collision
|
||||||
if (!getAABB()->intersects(*obj.getAABB())) {
|
if (!getAABB()->intersects(*obj.getAABB())) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sf::Vector2f normal;
|
return Collision::dispatch[
|
||||||
float depth;
|
// la fonction de détection fine de collision est choisie
|
||||||
|
// en fonction des types des deux objets en question
|
||||||
// vérifie plus finement s'il y a collision et récupère
|
std::make_pair(getTypeId(), obj.getTypeId())
|
||||||
// les informations sur la collision (normale et profondeur)
|
](data);
|
||||||
if (!Collision::dispatch[std::make_pair(getTypeId(), obj.getTypeId())](*this, obj, normal, depth)) {
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
void Object::solveCollision(Object& obj, sf::Vector2f normal) {
|
||||||
// si les deux objets sont de masse infinie, réinitialisation
|
// si les deux objets sont de masse infinie, réinitialisation
|
||||||
// des vitesses en tant que collision
|
// des vitesses en tant que collision
|
||||||
if (getMassInvert() == 0 && obj.getMassInvert() == 0) {
|
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
|
// la tangente est nulle : pas de frottement à générer
|
||||||
// on évite ainsi une division par zéro
|
// on évite ainsi une division par zéro
|
||||||
if (tangent_length != 0) {
|
if (tangent_length == 0) {
|
||||||
tangent /= tangent_length;
|
return;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// correction de la position des objets. En raison de l'imprécision
|
tangent /= tangent_length;
|
||||||
// des flottants sur la machine, les objets peuvent accumuler une
|
|
||||||
// erreur de positionnement qui les fait "plonger" les un dans les autres.
|
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) {
|
if (depth <= Constants::CORRECTION_THRESHOLD) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue