Correction des collisions

Ajout de la correction positionelle, correction du rayon de
détection
This commit is contained in:
Mattéo Delabre 2016-03-18 16:48:22 +01:00
parent 80a9a2ef26
commit db18959575
8 changed files with 111 additions and 61 deletions

View File

@ -31,11 +31,12 @@ public:
std::unique_ptr<sf::FloatRect> getAABB(); std::unique_ptr<sf::FloatRect> 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 getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth);
virtual bool getNormal(Ball& obj, sf::Vector2f& normal); virtual bool getCollisionInfo(Ball& obj, sf::Vector2f& normal, float& depth);
virtual bool getNormal(Block& obj, sf::Vector2f& normal); virtual bool getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth);
}; };
#endif #endif

View File

@ -25,11 +25,12 @@ public:
std::unique_ptr<sf::FloatRect> getAABB(); std::unique_ptr<sf::FloatRect> 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 getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth);
virtual bool getNormal(Ball& obj, sf::Vector2f& normal); virtual bool getCollisionInfo(Ball& obj, sf::Vector2f& normal, float& depth);
virtual bool getNormal(Block& obj, sf::Vector2f& normal); virtual bool getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth);
}; };
#endif #endif

View File

@ -28,6 +28,13 @@ namespace Constants {
* Taille de la grille des blocs en pixels * Taille de la grille des blocs en pixels
*/ */
static constexpr float GRID = 32; 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 #endif

View File

@ -49,11 +49,19 @@ public:
virtual std::unique_ptr<sf::FloatRect> getAABB() = 0; virtual std::unique_ptr<sf::FloatRect> 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 getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth);
virtual bool getNormal(Ball& obj, sf::Vector2f& normal) = 0; virtual bool getCollisionInfo(Ball& obj, sf::Vector2f& normal, float& depth) = 0;
virtual bool getNormal(Block& obj, sf::Vector2f& normal) = 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 * Récupère l'accélération de l'objet
@ -76,6 +84,12 @@ public:
*/ */
sf::Vector2f getPosition(); 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 * Récupère la masse de l'objet
*/ */

View File

@ -76,11 +76,11 @@ std::unique_ptr<sf::FloatRect> Ball::getAABB() {
)); ));
} }
bool Ball::getNormal(Object& obj, sf::Vector2f& normal) { bool Ball::getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth) {
return obj.getNormal(*this, normal); 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(); sf::Vector2f dir = obj.getPosition() - getPosition();
float squaredLength = dir.x * dir.x + dir.y * dir.y; 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. // 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) {
// TODO: supprimer les valeurs magiques
depth = 10;
normal.x = 0; normal.x = 0;
normal.y = -1; normal.y = -1;
return true; return true;
} }
// TODO: supprimer les valeurs magiques
// il y a eu collision // il y a eu collision
depth = 10 - length;
normal = dir / length; normal = dir / length;
return true; 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 // 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
// du cercle par rapport au bloc // 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 // si la balle est à l'extérieur et que
// la normale est plus longue que son rayon, // la normale est plus longue que son rayon,
// il n'y a pas collision // il n'y a pas collision
if (!isInside && squaredLength >= 20 * 20) { if (!isInside && squaredLength >= 10 * 10) {
return false; return false;
} }
normal = prenormal / std::sqrt(squaredLength); float length = std::sqrt(squaredLength);
depth = 10 - length;
normal = prenormal / length;
if (isInside) { if (isInside) {
normal *= -1.f; normal *= -1.f;

View File

@ -41,16 +41,16 @@ std::unique_ptr<sf::FloatRect> Block::getAABB() {
)); ));
} }
bool Block::getNormal(Object& obj, sf::Vector2f& normal) { bool Block::getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth) {
return obj.getNormal(*this, normal); 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 // TODO: coder cette fonction
return false; return false;
} }
bool Block::getNormal(Block& obj, sf::Vector2f& normal) { bool Block::getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth) {
// TODO: coder cette fonction // TODO: coder cette fonction
return false; return false;
} }

View File

@ -55,43 +55,7 @@ void Engine::update() {
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);
// 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);
} }
} }
} }

View File

@ -48,8 +48,61 @@ void Object::update(EngineState& state) {
velocity += acceleration * state.delta; velocity += acceleration * state.delta;
} }
bool Object::getNormal(Object& obj, sf::Vector2f& normal) { bool Object::getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth) {
return obj.getNormal(*this, normal); 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() { sf::Vector2f Object::getAcceleration() {
@ -68,6 +121,10 @@ sf::Vector2f Object::getPosition() {
return position; return position;
} }
void Object::setPosition(sf::Vector2f set_position) {
position = set_position;
}
float Object::getMass() { float Object::getMass() {
return mass; return mass;
} }