Déplacement des fonctions d'info sur collision en extérieur
This commit is contained in:
		
							parent
							
								
									73c162486f
								
							
						
					
					
						commit
						7685c5fd75
					
				|  | @ -30,12 +30,10 @@ public: | |||
|     std::unique_ptr<sf::FloatRect> getAABB(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Calcule les informations sur une éventuelle collision de | ||||
|      * cet objet avec un autre : la normale et la profondeur | ||||
|      * Récupère l'identifiant de type de cet objet | ||||
|      */ | ||||
|     virtual bool getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth); | ||||
|     virtual bool getCollisionInfo(Ball& obj, sf::Vector2f& normal, float& depth); | ||||
|     virtual bool getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth); | ||||
|     static constexpr unsigned int TYPE_ID = 0; | ||||
|     unsigned int getTypeId(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Renvoie le rayon de la balle | ||||
|  |  | |||
|  | @ -2,7 +2,6 @@ | |||
| #define __PTF_BLOCK_HPP__ | ||||
| 
 | ||||
| #include <SFML/Graphics.hpp> | ||||
| #include <iostream> | ||||
| #include "object.hpp" | ||||
| #include "engine_state.hpp" | ||||
| 
 | ||||
|  | @ -24,12 +23,10 @@ public: | |||
|     std::unique_ptr<sf::FloatRect> getAABB(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Calcule les informations sur une éventuelle collision de | ||||
|      * cet objet avec un autre : la normale et la profondeur | ||||
|      * Récupère l'identifiant de type de cet objet | ||||
|      */ | ||||
|     virtual bool getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth); | ||||
|     virtual bool getCollisionInfo(Ball& obj, sf::Vector2f& normal, float& depth); | ||||
|     virtual bool getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth); | ||||
|     static constexpr unsigned int TYPE_ID = 1; | ||||
|     unsigned int getTypeId(); | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -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 | ||||
|  | @ -52,12 +52,9 @@ public: | |||
|     virtual std::unique_ptr<sf::FloatRect> getAABB() = 0; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Calcule les informations sur une éventuelle collision de | ||||
|      * cet objet avec un autre : la normale et la profondeur | ||||
|      * Récupère l'identifiant de type de cet objet | ||||
|      */ | ||||
|     virtual bool getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth); | ||||
|     virtual bool getCollisionInfo(Ball& obj, sf::Vector2f& normal, float& depth) = 0; | ||||
|     virtual bool getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth) = 0; | ||||
|     virtual unsigned int getTypeId() = 0; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Détecte s'il y a collision entre cet objet | ||||
|  |  | |||
							
								
								
									
										108
									
								
								src/ball.cpp
								
								
								
								
							
							
						
						
									
										108
									
								
								src/ball.cpp
								
								
								
								
							|  | @ -44,112 +44,8 @@ std::unique_ptr<sf::FloatRect> Ball::getAABB() { | |||
|     )); | ||||
| } | ||||
| 
 | ||||
| bool Ball::getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth) { | ||||
|     return obj.getCollisionInfo(*this, normal, depth); | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| unsigned int Ball::getTypeId() { | ||||
|     return Ball::TYPE_ID; | ||||
| } | ||||
| 
 | ||||
| float Ball::getRadius() { | ||||
|  |  | |||
|  | @ -39,51 +39,6 @@ std::unique_ptr<sf::FloatRect> Block::getAABB() { | |||
|     )); | ||||
| } | ||||
| 
 | ||||
| bool Block::getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth) { | ||||
|     return obj.getCollisionInfo(*this, normal, depth); | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| unsigned int Block::getTypeId() { | ||||
|     return Block::TYPE_ID; | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
|     } | ||||
| } | ||||
|  | @ -1,5 +1,6 @@ | |||
| #include "object.hpp" | ||||
| #include "constants.hpp" | ||||
| #include "collision.hpp" | ||||
| #include <iostream> | ||||
| 
 | ||||
| Object::Object(float x, float y) : | ||||
|  | @ -74,16 +75,12 @@ void Object::draw(sf::RenderWindow& window, ResourceManager& resources) { | |||
| } | ||||
| 
 | ||||
| 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
 | ||||
|     acceleration = getForces(state) * getMassInvert(); | ||||
|     velocity += acceleration * Constants::PHYSICS_TIME; | ||||
| } | ||||
| 
 | ||||
| bool Object::getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth) { | ||||
|     return obj.getCollisionInfo(*this, normal, depth); | ||||
|     // intégration de la vitesse dans la position
 | ||||
|     position += velocity * Constants::PHYSICS_TIME; | ||||
| } | ||||
| 
 | ||||
| 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
 | ||||
|     // 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; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue