Généralisation des collisions
This commit is contained in:
parent
c5e48cbc1c
commit
affcc09abb
|
@ -30,6 +30,11 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual std::unique_ptr<sf::FloatRect> getAABB() const;
|
virtual std::unique_ptr<sf::FloatRect> getAABB() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère le rayon du bloc
|
||||||
|
*/
|
||||||
|
virtual float getRadius() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opérations de préparation de la texture du bloc
|
* Opérations de préparation de la texture du bloc
|
||||||
*/
|
*/
|
||||||
|
@ -50,6 +55,11 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual unsigned int getTypeId() const;
|
virtual unsigned int getTypeId() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère le type de collision des blocs
|
||||||
|
*/
|
||||||
|
virtual CollisionType getCollisionType() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chargement du bloc depuis le fichier donné
|
* Chargement du bloc depuis le fichier donné
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -6,23 +6,29 @@
|
||||||
class Object;
|
class Object;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Structure qui retient des informations
|
* Type de collision : entre deux rectangles ou
|
||||||
|
* entre deux cercles
|
||||||
|
*/
|
||||||
|
enum class CollisionType {AABB, CIRCLE};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure qui retient les informations
|
||||||
* sur les collisions
|
* sur les collisions
|
||||||
*/
|
*/
|
||||||
struct CollisionData {
|
struct CollisionData {
|
||||||
sf::Vector2f normal;
|
sf::Vector2f normal;
|
||||||
float depth;
|
float depth;
|
||||||
|
|
||||||
Object& objA;
|
Object& obj_a;
|
||||||
Object& objB;
|
Object& obj_b;
|
||||||
|
|
||||||
CollisionData(Object& objA, Object& objB);
|
CollisionData(Object& obj_a, Object& obj_b);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Détermine les informations sur la collision
|
* Détermine les informations sur la collision
|
||||||
* entre les deux objets donnés dans data et
|
* entre les deux objets donnés dans data et stocke
|
||||||
* les stocke dans data
|
* ces informations dans data
|
||||||
*/
|
*/
|
||||||
bool getCollisionData(CollisionData& data);
|
bool getCollisionData(CollisionData& data);
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,11 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual std::unique_ptr<sf::FloatRect> getAABB() const = 0;
|
virtual std::unique_ptr<sf::FloatRect> getAABB() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère le rayon de l'objet
|
||||||
|
*/
|
||||||
|
virtual float getRadius() const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dessine l'objet dans la fenêtre donnée
|
* Dessine l'objet dans la fenêtre donnée
|
||||||
*/
|
*/
|
||||||
|
@ -73,6 +78,11 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual unsigned int getTypeId() const = 0;
|
virtual unsigned int getTypeId() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère le type de collision de cet objet
|
||||||
|
*/
|
||||||
|
virtual CollisionType getCollisionType() const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Charge les propriétés communes à tous les objets
|
* Charge les propriétés communes à tous les objets
|
||||||
* depuis le fichier donné dans l'objet donné
|
* depuis le fichier donné dans l'objet donné
|
||||||
|
|
|
@ -34,6 +34,11 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual std::unique_ptr<sf::FloatRect> getAABB() const;
|
virtual std::unique_ptr<sf::FloatRect> getAABB() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère le rayon du joueur
|
||||||
|
*/
|
||||||
|
virtual float getRadius() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dessine le joueur dans la fenêtre donnée
|
* Dessine le joueur dans la fenêtre donnée
|
||||||
*/
|
*/
|
||||||
|
@ -49,6 +54,11 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual unsigned int getTypeId() const;
|
virtual unsigned int getTypeId() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère le type de collision des joueurs
|
||||||
|
*/
|
||||||
|
virtual CollisionType getCollisionType() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chargement du joueur depuis le fichier donné
|
* Chargement du joueur depuis le fichier donné
|
||||||
*/
|
*/
|
||||||
|
@ -60,11 +70,6 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual void updatePosition();
|
virtual void updatePosition();
|
||||||
|
|
||||||
/**
|
|
||||||
* Renvoie le rayon de la balle du joueur
|
|
||||||
*/
|
|
||||||
float getRadius() const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renvoie le numéro du joueur
|
* Renvoie le numéro du joueur
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -79,6 +79,14 @@ std::unique_ptr<sf::FloatRect> Block::getAABB() const {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float Block::getRadius() const {
|
||||||
|
return Constants::GRID / 2;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int Block::getTypeId() const {
|
unsigned int Block::getTypeId() const {
|
||||||
return TYPE_ID;
|
return TYPE_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CollisionType Block::getCollisionType() const {
|
||||||
|
return CollisionType::AABB;
|
||||||
|
}
|
||||||
|
|
|
@ -9,81 +9,79 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Détermination des informations sur une collision entre
|
* Détermination des informations sur une collision entre
|
||||||
* un joueur et un bloc (normale et profondeur de collision)
|
* un cercle et un rectangle
|
||||||
*/
|
*/
|
||||||
bool playerToBlock(CollisionData& data) {
|
bool circleToAABB(CollisionData& data) {
|
||||||
Player player = dynamic_cast<Player&>(data.objA);
|
Object& circle = data.obj_a;
|
||||||
Block block = dynamic_cast<Block&>(data.objB);
|
Object& aabb = data.obj_b;
|
||||||
|
|
||||||
// recherche du point le plus proche du centre de la
|
// recherche du point le plus proche du centre du cercle
|
||||||
// balle sur le bloc. On regarde la position relative
|
// sur le rectangle. On regarde la position relative du cercle
|
||||||
// du cercle par rapport au bloc
|
// par rapport au rectangle
|
||||||
std::unique_ptr<sf::FloatRect> aabb = block.getAABB();
|
std::unique_ptr<sf::FloatRect> box = aabb.getAABB();
|
||||||
sf::Vector2f relpos = block.getPosition() - player.getPosition();
|
sf::Vector2f relpos = aabb.getPosition() - circle.getPosition();
|
||||||
sf::Vector2f closest = relpos;
|
sf::Vector2f closest = relpos;
|
||||||
|
|
||||||
// on restreint la position relative pour rester
|
// on restreint la position relative pour rester
|
||||||
// à l'intérieur du bloc
|
// à l'intérieur du rectangle
|
||||||
if (closest.x < -aabb->width / 2) {
|
if (closest.x < -box->width / 2) {
|
||||||
closest.x = -aabb->width / 2;
|
closest.x = -box->width / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (closest.x > aabb->width / 2) {
|
if (closest.x > box->width / 2) {
|
||||||
closest.x = aabb->width / 2;
|
closest.x = box->width / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (closest.y < -aabb->height / 2) {
|
if (closest.y < -box->height / 2) {
|
||||||
closest.y = -aabb->height / 2;
|
closest.y = -box->height / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (closest.y > aabb->height / 2) {
|
if (closest.y > box->height / 2) {
|
||||||
closest.y = aabb->height / 2;
|
closest.y = box->height / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// si la position n'a pas été changée, elle
|
// si la position n'a pas été changée, elle était déjà
|
||||||
// était déjà à l'intérieur du cercle : le cercle
|
// à l'intérieur du cercle : le cercle est dans le rectangle
|
||||||
// est dans le bloc
|
float is_inside = false;
|
||||||
float isInside = false;
|
|
||||||
|
|
||||||
if (relpos == closest) {
|
if (relpos == closest) {
|
||||||
isInside = true;
|
is_inside = true;
|
||||||
|
|
||||||
// on se colle au bord le plus proche du bloc
|
// on se colle au bord le plus proche du rectangle
|
||||||
if (std::abs(relpos.x) > std::abs(relpos.y)) {
|
if (std::abs(relpos.x) > std::abs(relpos.y)) {
|
||||||
if (closest.x > 0) {
|
if (closest.x > 0) {
|
||||||
closest.x = aabb->width / 2;
|
closest.x = box->width / 2;
|
||||||
} else {
|
} else {
|
||||||
closest.x = -aabb->width / 2;
|
closest.x = -box->width / 2;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (closest.y > 0) {
|
if (closest.y > 0) {
|
||||||
closest.y = aabb->height / 2;
|
closest.y = box->height / 2;
|
||||||
} else {
|
} else {
|
||||||
closest.y = -aabb->height / 2;
|
closest.y = -box->height / 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// la normale est portée par la direction
|
// la normale est portée par la direction
|
||||||
// du point le plus proche au centre de la balle
|
// du point le plus proche au centre du cercle
|
||||||
sf::Vector2f prenormal = relpos - closest;
|
sf::Vector2f prenormal = relpos - closest;
|
||||||
float squaredLength = prenormal.x * prenormal.x + prenormal.y * prenormal.y;
|
float squared_length = prenormal.x * prenormal.x + prenormal.y * prenormal.y;
|
||||||
|
|
||||||
// si la balle est à l'extérieur et que
|
// si le cercle est à l'extérieur et que la normale est plus
|
||||||
// la normale est plus longue que son rayon,
|
// longue que son rayon, il n'y a pas collision
|
||||||
// il n'y a pas collision
|
if (!is_inside && squared_length >= circle.getRadius() * circle.getRadius()) {
|
||||||
if (!isInside && squaredLength >= player.getRadius() * player.getRadius()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
float length = std::sqrt(squaredLength);
|
float length = std::sqrt(squared_length);
|
||||||
data.depth = player.getRadius() - length;
|
data.depth = circle.getRadius() - length;
|
||||||
|
|
||||||
if (length != 0) {
|
if (length != 0) {
|
||||||
data.normal = prenormal / length;
|
data.normal = prenormal / length;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInside) {
|
if (is_inside) {
|
||||||
data.normal *= -1.f;
|
data.normal *= -1.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,66 +90,66 @@ bool playerToBlock(CollisionData& data) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Détermination des informations sur une collision entre
|
* Détermination des informations sur une collision entre
|
||||||
* un bloc et un joueur (normale et profondeur de collision)
|
* un rectangle et un cercle
|
||||||
*/
|
*/
|
||||||
bool blockToPlayer(CollisionData& data) {
|
bool AABBToCircle(CollisionData& data) {
|
||||||
// la collision Block -> Player est la collision Player -> Block
|
// la collision rectangle -> cercle est la collision cercle -> rectangle
|
||||||
Object& objT = data.objB;
|
Object& transfer = data.obj_b;
|
||||||
data.objB = data.objA;
|
data.obj_b = data.obj_a;
|
||||||
data.objA = objT;
|
data.obj_a = transfer;
|
||||||
|
|
||||||
return playerToBlock(data);
|
return circleToAABB(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Détermination des informations sur une collision entre
|
* Détermination des informations sur une collision entre
|
||||||
* deux joueurs (normale et profondeur de collision)
|
* deux cercles
|
||||||
*/
|
*/
|
||||||
bool playerToPlayer(CollisionData& data) {
|
bool circleToCircle(CollisionData& data) {
|
||||||
Player playerA = dynamic_cast<Player&>(data.objA);
|
Object& circle_a = data.obj_a;
|
||||||
Player playerB = dynamic_cast<Player&>(data.objB);
|
Object& circle_b = data.obj_b;
|
||||||
|
|
||||||
sf::Vector2f dir = playerB.getPosition() - playerA.getPosition();
|
sf::Vector2f dir = circle_b.getPosition() - circle_a.getPosition();
|
||||||
float squaredLength = dir.x * dir.x + dir.y * dir.y;
|
float squared_length = dir.x * dir.x + dir.y * dir.y;
|
||||||
float totalRadius = playerB.getRadius() + playerA.getRadius();
|
float total_radius = circle_b.getRadius() + circle_a.getRadius();
|
||||||
|
|
||||||
// si les deux balles sont à une distance supérieure
|
// si les deux cercles sont à une distance supérieure
|
||||||
// à la somme de leurs deux rayons, il n'y a pas eu collision
|
// à la somme de leurs deux rayons, il n'y a pas eu collision
|
||||||
if (squaredLength > totalRadius * totalRadius) {
|
if (squared_length > total_radius * total_radius) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
float length = std::sqrt(squaredLength);
|
float length = std::sqrt(squared_length);
|
||||||
|
|
||||||
// les balles sont sur la même position.
|
// les cercles sont sur la même position.
|
||||||
// Renvoie une normale apte à séparer les deux balles
|
// Renvoie une normale apte à séparer les deux cercles
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
data.depth = totalRadius;
|
data.depth = total_radius;
|
||||||
data.normal.x = 0;
|
data.normal.x = 0;
|
||||||
data.normal.y = -1;
|
data.normal.y = -1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// il y a eu collision
|
// il y a eu collision
|
||||||
data.depth = totalRadius - length;
|
data.depth = total_radius - length;
|
||||||
data.normal = dir / length;
|
data.normal = dir / length;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Détermination des informations sur une collision entre
|
* Détermination des informations sur une collision entre
|
||||||
* deux blocs (normale et profondeur de collision)
|
* deux rectangles
|
||||||
*/
|
*/
|
||||||
bool blockToBlock(CollisionData& data) {
|
bool AABBToAABB(CollisionData& data) {
|
||||||
Block blockA = dynamic_cast<Block&>(data.objA);
|
Object& aabb_a = data.obj_a;
|
||||||
Block blockB = dynamic_cast<Block&>(data.objB);
|
Object& aabb_b = data.obj_b;
|
||||||
|
|
||||||
std::unique_ptr<sf::FloatRect> aabb = blockA.getAABB();
|
std::unique_ptr<sf::FloatRect> box_a = aabb_a.getAABB();
|
||||||
std::unique_ptr<sf::FloatRect> obj_aabb = blockB.getAABB();
|
std::unique_ptr<sf::FloatRect> box_b = aabb_b.getAABB();
|
||||||
sf::Vector2f relpos = blockB.getPosition() - blockA.getPosition();
|
sf::Vector2f relpos = aabb_b.getPosition() - aabb_a.getPosition();
|
||||||
|
|
||||||
float overlap_x = aabb->width / 2 + obj_aabb->width / 2 - std::abs(relpos.x);
|
float overlap_x = box_a->width / 2 + box_b->width / 2 - std::abs(relpos.x);
|
||||||
float overlap_y = aabb->height / 2 + obj_aabb->height / 2 - std::abs(relpos.y);
|
float overlap_y = box_a->height / 2 + box_b->height / 2 - std::abs(relpos.y);
|
||||||
|
|
||||||
// si il n'y a pas de chauvauchement sur l'axe X et Y, pas de collision
|
// si il n'y a pas de chauvauchement sur l'axe X et Y, pas de collision
|
||||||
if (overlap_x <= 0 || overlap_y <= 0) {
|
if (overlap_x <= 0 || overlap_y <= 0) {
|
||||||
|
@ -187,26 +185,19 @@ bool blockToBlock(CollisionData& data) {
|
||||||
* dans une collision à leur fonction de résolution
|
* dans une collision à leur fonction de résolution
|
||||||
*/
|
*/
|
||||||
std::map<
|
std::map<
|
||||||
std::pair<unsigned int, unsigned int>,
|
std::pair<CollisionType, CollisionType>,
|
||||||
std::function<bool(CollisionData&)>
|
std::function<bool(CollisionData&)>
|
||||||
> collision_map = {
|
> collision_map = {
|
||||||
{std::make_pair(Player::TYPE_ID, Player::TYPE_ID), playerToPlayer},
|
{std::make_pair(CollisionType::CIRCLE, CollisionType::CIRCLE), circleToCircle},
|
||||||
{std::make_pair(Player::TYPE_ID, Block::TYPE_ID), playerToBlock},
|
{std::make_pair(CollisionType::CIRCLE, CollisionType::AABB), circleToAABB},
|
||||||
{std::make_pair(Player::TYPE_ID, GravityBlock::TYPE_ID), playerToBlock},
|
{std::make_pair(CollisionType::AABB, CollisionType::AABB), AABBToAABB},
|
||||||
{std::make_pair(Block::TYPE_ID, Block::TYPE_ID), blockToBlock},
|
{std::make_pair(CollisionType::AABB, CollisionType::CIRCLE), AABBToCircle}
|
||||||
{std::make_pair(Block::TYPE_ID, GravityBlock::TYPE_ID), blockToBlock},
|
|
||||||
{std::make_pair(Block::TYPE_ID, Player::TYPE_ID), blockToPlayer},
|
|
||||||
{std::make_pair(GravityBlock::TYPE_ID, Block::TYPE_ID), blockToBlock},
|
|
||||||
{std::make_pair(GravityBlock::TYPE_ID, GravityBlock::TYPE_ID), blockToBlock},
|
|
||||||
{std::make_pair(GravityBlock::TYPE_ID, Player::TYPE_ID), blockToPlayer}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CollisionData::CollisionData(Object& objA, Object& objB) :
|
CollisionData::CollisionData(Object& obj_a, Object& obj_b) : obj_a(obj_a), obj_b(obj_b) {}
|
||||||
objA(objA), objB(objB) {}
|
|
||||||
|
|
||||||
bool getCollisionData(CollisionData& data) {
|
bool getCollisionData(CollisionData& data) {
|
||||||
return collision_map[std::make_pair(
|
return collision_map[std::make_pair(
|
||||||
data.objA.getTypeId(),
|
data.obj_a.getCollisionType(),
|
||||||
data.objB.getTypeId()
|
data.obj_b.getCollisionType()
|
||||||
)](data);
|
)](data);
|
||||||
}
|
}
|
||||||
|
|
14
src/game.cpp
14
src/game.cpp
|
@ -91,13 +91,13 @@ void Game::update() {
|
||||||
|
|
||||||
// détection des objets en collision
|
// détection des objets en collision
|
||||||
for (unsigned int i = 0; i < getObjects().size(); i++) {
|
for (unsigned int i = 0; i < getObjects().size(); i++) {
|
||||||
ObjectPtr objA = getObjects()[i];
|
ObjectPtr obj_a = getObjects()[i];
|
||||||
|
|
||||||
for (unsigned int j = i + 1; j < getObjects().size(); j++) {
|
for (unsigned int j = i + 1; j < getObjects().size(); j++) {
|
||||||
ObjectPtr objB = getObjects()[j];
|
ObjectPtr obj_b = getObjects()[j];
|
||||||
CollisionData data(*objA, *objB);
|
CollisionData data(*obj_a, *obj_b);
|
||||||
|
|
||||||
if (objA->detectCollision(*objB, data)) {
|
if (obj_a->detectCollision(*obj_b, data)) {
|
||||||
colliding.push_back(data);
|
colliding.push_back(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ void Game::update() {
|
||||||
// résolution des collisions détectées
|
// résolution des collisions détectées
|
||||||
for (unsigned int i = 0; i < colliding.size(); i++) {
|
for (unsigned int i = 0; i < colliding.size(); i++) {
|
||||||
CollisionData& collided = colliding[i];
|
CollisionData& collided = colliding[i];
|
||||||
collided.objA.solveCollision(*this, collided.objB, collided.normal);
|
collided.obj_a.solveCollision(*this, collided.obj_b, collided.normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// intégration de la vitesse dans la position
|
// intégration de la vitesse dans la position
|
||||||
|
@ -122,8 +122,8 @@ void Game::update() {
|
||||||
// application de la correction positionnelle
|
// application de la correction positionnelle
|
||||||
for (unsigned int i = 0; i < colliding.size(); i++) {
|
for (unsigned int i = 0; i < colliding.size(); i++) {
|
||||||
CollisionData& collided = colliding[i];
|
CollisionData& collided = colliding[i];
|
||||||
collided.objA.positionalCorrection(
|
collided.obj_a.positionalCorrection(
|
||||||
collided.objB, collided.normal, collided.depth
|
collided.obj_b, collided.normal, collided.depth
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,12 +108,16 @@ std::unique_ptr<sf::FloatRect> Player::getAABB() const {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float Player::getRadius() const {
|
||||||
|
return 10 * getMass();
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int Player::getTypeId() const {
|
unsigned int Player::getTypeId() const {
|
||||||
return TYPE_ID;
|
return TYPE_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Player::getRadius() const {
|
CollisionType Player::getCollisionType() const {
|
||||||
return 10 * getMass();
|
return CollisionType::CIRCLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int Player::getPlayerNumber() const {
|
unsigned int Player::getPlayerNumber() const {
|
||||||
|
|
Loading…
Reference in New Issue