Déplacement des fonctions d'info sur collision en extérieur

This commit is contained in:
Mattéo Delabre 2016-03-25 19:15:51 +01:00
parent 73c162486f
commit 7685c5fd75
8 changed files with 211 additions and 176 deletions

View File

@ -30,12 +30,10 @@ public:
std::unique_ptr<sf::FloatRect> getAABB(); std::unique_ptr<sf::FloatRect> getAABB();
/** /**
* Calcule les informations sur une éventuelle collision de * Récupère l'identifiant de type de cet objet
* cet objet avec un autre : la normale et la profondeur
*/ */
virtual bool getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth); static constexpr unsigned int TYPE_ID = 0;
virtual bool getCollisionInfo(Ball& obj, sf::Vector2f& normal, float& depth); unsigned int getTypeId();
virtual bool getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth);
/** /**
* Renvoie le rayon de la balle * Renvoie le rayon de la balle

View File

@ -2,7 +2,6 @@
#define __PTF_BLOCK_HPP__ #define __PTF_BLOCK_HPP__
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
#include <iostream>
#include "object.hpp" #include "object.hpp"
#include "engine_state.hpp" #include "engine_state.hpp"
@ -24,12 +23,10 @@ public:
std::unique_ptr<sf::FloatRect> getAABB(); std::unique_ptr<sf::FloatRect> getAABB();
/** /**
* Calcule les informations sur une éventuelle collision de * Récupère l'identifiant de type de cet objet
* cet objet avec un autre : la normale et la profondeur
*/ */
virtual bool getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth); static constexpr unsigned int TYPE_ID = 1;
virtual bool getCollisionInfo(Ball& obj, sf::Vector2f& normal, float& depth); unsigned int getTypeId();
virtual bool getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth);
}; };
#endif #endif

20
include/collision.hpp Normal file
View File

@ -0,0 +1,20 @@
#ifndef __PTF_COLLISION_HPP__
#define __PTF_COLLISION_HPP__
#include "object.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;
extern collision_dispatcher dispatch;
bool ballToBlock(Object& objA, Object& objB, sf::Vector2f& normal, float& depth);
bool blockToBall(Object& objA, Object& objB, sf::Vector2f& normal, float& depth);
bool ballToBall(Object& objA, Object& objB, sf::Vector2f& normal, float& depth);
bool blockToBlock(Object& objA, Object& objB, sf::Vector2f& normal, float& depth);
}
#endif

View File

@ -52,12 +52,9 @@ public:
virtual std::unique_ptr<sf::FloatRect> getAABB() = 0; virtual std::unique_ptr<sf::FloatRect> getAABB() = 0;
/** /**
* Calcule les informations sur une éventuelle collision de * Récupère l'identifiant de type de cet objet
* cet objet avec un autre : la normale et la profondeur
*/ */
virtual bool getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth); virtual unsigned int getTypeId() = 0;
virtual bool getCollisionInfo(Ball& obj, sf::Vector2f& normal, float& depth) = 0;
virtual bool getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth) = 0;
/** /**
* Détecte s'il y a collision entre cet objet * Détecte s'il y a collision entre cet objet

View File

@ -44,112 +44,8 @@ std::unique_ptr<sf::FloatRect> Ball::getAABB() {
)); ));
} }
bool Ball::getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth) { unsigned int Ball::getTypeId() {
return obj.getCollisionInfo(*this, normal, depth); return Ball::TYPE_ID;
}
bool Ball::getCollisionInfo(Ball& obj, sf::Vector2f& normal, float& depth) {
sf::Vector2f dir = getPosition() - obj.getPosition();
float squaredLength = dir.x * dir.x + dir.y * dir.y;
float totalRadius = getRadius() + obj.getRadius();
// si les deux balles sont à une distance supérieure
// à la somme de leurs deux rayons, il n'y a pas eu collision
if (squaredLength > totalRadius * totalRadius) {
return false;
}
float length = std::sqrt(squaredLength);
// 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;
return true;
}
// il y a eu collision
depth = totalRadius - length;
normal = dir / length;
return true;
}
bool Ball::getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth) {
// recherche du point le plus proche du centre de la
// balle sur le bloc. On regarde la position relative
// du cercle par rapport au bloc
std::unique_ptr<sf::FloatRect> aabb = obj.getAABB();
sf::Vector2f relpos = getPosition() - obj.getPosition();
sf::Vector2f closest = relpos;
// on restreint la position relative pour rester
// à l'intérieur du bloc
if (closest.x < -aabb->width / 2) {
closest.x = -aabb->width / 2;
}
if (closest.x > aabb->width / 2) {
closest.x = aabb->width / 2;
}
if (closest.y < -aabb->height / 2) {
closest.y = -aabb->height / 2;
}
if (closest.y > aabb->height / 2) {
closest.y = aabb->height / 2;
}
// si la position n'a pas été changée, elle
// était déjà à l'intérieur du cercle : le cercle
// est dans le bloc
float isInside = false;
if (relpos == closest) {
isInside = true;
// on se colle au bord le plus proche du bloc
if (std::abs(relpos.x) > std::abs(relpos.y)) {
if (closest.x > 0) {
closest.x = aabb->width / 2;
} else {
closest.x = -aabb->width / 2;
}
} else {
if (closest.y > 0) {
closest.y = aabb->height / 2;
} else {
closest.y = -aabb->height / 2;
}
}
}
// la normale est portée par la direction
// du point le plus proche au centre de la balle
sf::Vector2f prenormal = relpos - closest;
float squaredLength = prenormal.x * prenormal.x + prenormal.y * prenormal.y;
// si la balle est à l'extérieur et que
// la normale est plus longue que son rayon,
// il n'y a pas collision
if (!isInside && squaredLength >= getRadius() * getRadius()) {
return false;
}
float length = std::sqrt(squaredLength);
depth = getRadius() - length;
if (length != 0) {
normal = prenormal / length;
}
if (isInside) {
normal *= -1.f;
}
return true;
} }
float Ball::getRadius() { float Ball::getRadius() {

View File

@ -39,51 +39,6 @@ std::unique_ptr<sf::FloatRect> Block::getAABB() {
)); ));
} }
bool Block::getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth) { unsigned int Block::getTypeId() {
return obj.getCollisionInfo(*this, normal, depth); return Block::TYPE_ID;
}
bool Block::getCollisionInfo(Ball& obj, sf::Vector2f& normal, float& depth) {
// la collision Block -> Ball est la collision Ball -> Block
// avec une normale de collision retournée
bool result = obj.getCollisionInfo(*this, normal, depth);
normal *= -1.f;
return result;
}
bool Block::getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth) {
std::unique_ptr<sf::FloatRect> aabb = getAABB();
std::unique_ptr<sf::FloatRect> obj_aabb = obj.getAABB();
sf::Vector2f relpos = getPosition() - obj.getPosition();
float overlap_x = aabb->width / 2 + obj_aabb->width / 2 - std::abs(relpos.x);
float overlap_y = aabb->height / 2 + obj_aabb->height / 2 - std::abs(relpos.y);
// si il n'y a pas de chauvauchement sur l'axe X et Y, pas de collision
if (overlap_x <= 0 || overlap_y <= 0) {
return false;
}
// 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;
} else {
normal.x = 1;
}
normal.y = 0;
depth = overlap_x;
} else {
if (relpos.y < 0) {
normal.y = -1;
} else {
normal.y = 1;
}
normal.x = 0;
depth = overlap_y;
}
return true;
} }

175
src/collision.cpp Normal file
View File

@ -0,0 +1,175 @@
#include "collision.hpp"
#include "ball.hpp"
#include "block.hpp"
#include "object.hpp"
#include <utility>
#include <iostream>
namespace Collision {
// initialisation du dictionnaire associant les types
// impliqués dans une collision à leur fonction de résolution
collision_dispatcher dispatch = {
{std::make_pair(Ball::TYPE_ID, Block::TYPE_ID), &ballToBlock},
{std::make_pair(Block::TYPE_ID, Ball::TYPE_ID), &blockToBall},
{std::make_pair(Ball::TYPE_ID, Ball::TYPE_ID), &ballToBall},
{std::make_pair(Block::TYPE_ID, Block::TYPE_ID), &blockToBlock}
};
bool ballToBlock(Object& objA, Object& objB, sf::Vector2f& normal, float& depth) {
Ball ball = dynamic_cast<Ball&>(objA);
Block block = dynamic_cast<Block&>(objB);
// recherche du point le plus proche du centre de la
// balle sur le bloc. On regarde la position relative
// du cercle par rapport au bloc
std::unique_ptr<sf::FloatRect> aabb = block.getAABB();
sf::Vector2f relpos = block.getPosition() - ball.getPosition();
sf::Vector2f closest = relpos;
// on restreint la position relative pour rester
// à l'intérieur du bloc
if (closest.x < -aabb->width / 2) {
closest.x = -aabb->width / 2;
}
if (closest.x > aabb->width / 2) {
closest.x = aabb->width / 2;
}
if (closest.y < -aabb->height / 2) {
closest.y = -aabb->height / 2;
}
if (closest.y > aabb->height / 2) {
closest.y = aabb->height / 2;
}
// si la position n'a pas été changée, elle
// était déjà à l'intérieur du cercle : le cercle
// est dans le bloc
float isInside = false;
if (relpos == closest) {
isInside = true;
// on se colle au bord le plus proche du bloc
if (std::abs(relpos.x) > std::abs(relpos.y)) {
if (closest.x > 0) {
closest.x = aabb->width / 2;
} else {
closest.x = -aabb->width / 2;
}
} else {
if (closest.y > 0) {
closest.y = aabb->height / 2;
} else {
closest.y = -aabb->height / 2;
}
}
}
// la normale est portée par la direction
// du point le plus proche au centre de la balle
sf::Vector2f prenormal = relpos - closest;
float squaredLength = prenormal.x * prenormal.x + prenormal.y * prenormal.y;
// si la balle est à l'extérieur et que
// la normale est plus longue que son rayon,
// il n'y a pas collision
if (!isInside && squaredLength >= ball.getRadius() * ball.getRadius()) {
return false;
}
float length = std::sqrt(squaredLength);
depth = ball.getRadius() - length;
if (length != 0) {
normal = prenormal / length;
}
if (isInside) {
normal *= -1.f;
}
return true;
}
bool blockToBall(Object& objA, Object& objB, sf::Vector2f& normal, float& depth) {
// la collision Block -> Ball est la collision Ball -> Block
// avec une normale de collision retournée
bool result = ballToBlock(objB, objA, normal, depth);
normal *= -1.f;
return result;
}
bool ballToBall(Object& objA, Object& objB, sf::Vector2f& normal, float& depth) {
Ball ballA = dynamic_cast<Ball&>(objA);
Ball ballB = dynamic_cast<Ball&>(objB);
sf::Vector2f dir = ballB.getPosition() - ballA.getPosition();
float squaredLength = dir.x * dir.x + dir.y * dir.y;
float totalRadius = ballB.getRadius() + ballA.getRadius();
// si les deux balles sont à une distance supérieure
// à la somme de leurs deux rayons, il n'y a pas eu collision
if (squaredLength > totalRadius * totalRadius) {
return false;
}
float length = std::sqrt(squaredLength);
// 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;
return true;
}
// il y a eu collision
depth = totalRadius - length;
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);
std::unique_ptr<sf::FloatRect> aabb = blockA.getAABB();
std::unique_ptr<sf::FloatRect> obj_aabb = blockB.getAABB();
sf::Vector2f relpos = blockA.getPosition() - blockB.getPosition();
float overlap_x = aabb->width / 2 + obj_aabb->width / 2 - std::abs(relpos.x);
float overlap_y = aabb->height / 2 + obj_aabb->height / 2 - std::abs(relpos.y);
// si il n'y a pas de chauvauchement sur l'axe X et Y, pas de collision
if (overlap_x <= 0 || overlap_y <= 0) {
return false;
}
// 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;
} else {
normal.x = 1;
}
normal.y = 0;
depth = overlap_x;
} else {
if (relpos.y < 0) {
normal.y = -1;
} else {
normal.y = 1;
}
normal.x = 0;
depth = overlap_y;
}
return true;
}
}

View File

@ -1,5 +1,6 @@
#include "object.hpp" #include "object.hpp"
#include "constants.hpp" #include "constants.hpp"
#include "collision.hpp"
#include <iostream> #include <iostream>
Object::Object(float x, float y) : Object::Object(float x, float y) :
@ -74,16 +75,12 @@ void Object::draw(sf::RenderWindow& window, ResourceManager& resources) {
} }
void Object::update(EngineState& state) { void Object::update(EngineState& state) {
// intégration de la vitesse dans la position
position += velocity * Constants::PHYSICS_TIME;
// intégration des forces appliquées sur l'objet dans la vitesse // intégration des forces appliquées sur l'objet dans la vitesse
acceleration = getForces(state) * getMassInvert(); acceleration = getForces(state) * getMassInvert();
velocity += acceleration * Constants::PHYSICS_TIME; velocity += acceleration * Constants::PHYSICS_TIME;
}
bool Object::getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth) { // intégration de la vitesse dans la position
return obj.getCollisionInfo(*this, normal, depth); position += velocity * Constants::PHYSICS_TIME;
} }
void Object::collide(Object& obj) { void Object::collide(Object& obj) {
@ -104,7 +101,7 @@ void Object::collide(Object& obj) {
// vérifie plus finement s'il y a collision et récupère // vérifie plus finement s'il y a collision et récupère
// les informations sur la collision (normale et profondeur) // les informations sur la collision (normale et profondeur)
if (!getCollisionInfo(obj, normal, depth)) { if (!Collision::dispatch[std::make_pair(getTypeId(), obj.getTypeId())](*this, obj, normal, depth)) {
return; return;
} }