Séparation des trois phases de collisionage
This commit is contained in:
parent
c5a4402369
commit
b9e50580b6
|
@ -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
|
||||
|
|
|
@ -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
|
||||
* 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue