Implémentation initiale des collisions
This commit is contained in:
		
							parent
							
								
									6a34c31ae9
								
							
						
					
					
						commit
						a99f1ccd0a
					
				|  | @ -29,6 +29,13 @@ public: | |||
|      * Récupère la boîte englobante de l'objet | ||||
|      */ | ||||
|     std::unique_ptr<sf::FloatRect> getAABB(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Calcule la normale de la collision de cet objet avec un autre | ||||
|      */ | ||||
|     virtual bool getNormal(Object& obj, sf::Vector2f& normal); | ||||
|     virtual bool getNormal(Ball& obj, sf::Vector2f& normal); | ||||
|     virtual bool getNormal(Block& obj, sf::Vector2f& normal); | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -23,6 +23,13 @@ public: | |||
|      * Récupère la boîte englobante de l'objet | ||||
|      */ | ||||
|     std::unique_ptr<sf::FloatRect> getAABB(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Calcule la normale de la collision de cet objet avec un autre | ||||
|      */ | ||||
|     virtual bool getNormal(Object& obj, sf::Vector2f& normal); | ||||
|     virtual bool getNormal(Ball& obj, sf::Vector2f& normal); | ||||
|     virtual bool getNormal(Block& obj, sf::Vector2f& normal); | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -5,6 +5,9 @@ | |||
| #include <memory> | ||||
| #include "engine_state.hpp" | ||||
| 
 | ||||
| class Block; | ||||
| class Ball; | ||||
| 
 | ||||
| class Object { | ||||
| private: | ||||
|     sf::Vector2f acceleration; | ||||
|  | @ -16,6 +19,7 @@ private: | |||
| 
 | ||||
|     float mass; | ||||
|     float charge; | ||||
|     float restitution; | ||||
|     int layer; | ||||
| 
 | ||||
| protected: | ||||
|  | @ -43,6 +47,13 @@ public: | |||
|      */ | ||||
|     virtual std::unique_ptr<sf::FloatRect> getAABB() = 0; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Calcule la normale de la collision de cet objet avec un autre | ||||
|      */ | ||||
|     virtual bool getNormal(Object& obj, sf::Vector2f& normal); | ||||
|     virtual bool getNormal(Ball& obj, sf::Vector2f& normal) = 0; | ||||
|     virtual bool getNormal(Block& obj, sf::Vector2f& normal) = 0; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Récupère l'accélération de l'objet | ||||
|      */ | ||||
|  | @ -84,6 +95,16 @@ public: | |||
|      */ | ||||
|     void setCharge(float set_charge); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Récupère le coefficient de restitution de l'objet | ||||
|      */ | ||||
|     float getRestitution(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Modifie le coefficient de restitution de l'objet | ||||
|      */ | ||||
|     void setRestitution(float set_restitution); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Récupère la couche d'affichage de l'objet | ||||
|      */ | ||||
|  |  | |||
							
								
								
									
										146
									
								
								src/ball.cpp
								
								
								
								
							
							
						
						
									
										146
									
								
								src/ball.cpp
								
								
								
								
							|  | @ -1,31 +1,12 @@ | |||
| #include "ball.hpp" | ||||
| #include "block.hpp" | ||||
| #include "constants.hpp" | ||||
| #include <array> | ||||
| 
 | ||||
| Ball::Ball(float x, float y) : Object(x, y), shape(10) { | ||||
|     shape.setOrigin(sf::Vector2f(10, 10)); | ||||
| } | ||||
| 
 | ||||
| void Ball::draw(sf::RenderWindow& window) { | ||||
|     Object::draw(window); | ||||
| 
 | ||||
| 	// chargement de la texture de test
 | ||||
| 	if (!texture.loadFromFile("./res/ball.png")) { | ||||
|     	// erreur
 | ||||
| 	} | ||||
| 
 | ||||
| 	shape.setTexture(&texture); | ||||
|     shape.setPosition(getPosition()); | ||||
|     window.draw(shape); | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<sf::FloatRect> Ball::getAABB() { | ||||
|     return std::unique_ptr<sf::FloatRect>(new sf::FloatRect( | ||||
|         getPosition().x - 10, | ||||
|         getPosition().y - 10, | ||||
|         20, 20 | ||||
|     )); | ||||
| } | ||||
| 
 | ||||
| sf::Vector2f Ball::getForces(EngineState& state) { | ||||
|     sf::Vector2f forces = Object::getForces(state); | ||||
| 
 | ||||
|  | @ -73,3 +54,126 @@ sf::Vector2f Ball::getForces(EngineState& state) { | |||
| 
 | ||||
|     return forces; | ||||
| } | ||||
| 
 | ||||
| void Ball::draw(sf::RenderWindow& window) { | ||||
|     Object::draw(window); | ||||
| 
 | ||||
| 	// chargement de la texture de test
 | ||||
| 	if (!texture.loadFromFile("./res/ball.png")) { | ||||
|     	// erreur
 | ||||
| 	} | ||||
| 
 | ||||
| 	shape.setTexture(&texture); | ||||
|     shape.setPosition(getPosition()); | ||||
|     window.draw(shape); | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<sf::FloatRect> Ball::getAABB() { | ||||
|     return std::unique_ptr<sf::FloatRect>(new sf::FloatRect( | ||||
|         getPosition().x - 10, | ||||
|         getPosition().y - 10, | ||||
|         20, 20 | ||||
|     )); | ||||
| } | ||||
| 
 | ||||
| bool Ball::getNormal(Object& obj, sf::Vector2f& normal) { | ||||
|     return obj.getNormal(*this, normal); | ||||
| } | ||||
| 
 | ||||
| bool Ball::getNormal(Ball& obj, sf::Vector2f& normal) { | ||||
|     sf::Vector2f dir = obj.getPosition() - getPosition(); | ||||
|     float squaredLength = dir.x * dir.x + dir.y * dir.y; | ||||
| 
 | ||||
|     // TODO: supprimer les valeurs magiques
 | ||||
|     // si les deux balles sont à une distance supérieure
 | ||||
|     // à la somme de leurs deux rayons, il n'y a pas eu collision
 | ||||
|     if (squaredLength > 20 * 20) { | ||||
|         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) { | ||||
|         normal.x = 0; | ||||
|         normal.y = -1; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     // il y a eu collision
 | ||||
|     normal = dir / length; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool Ball::getNormal(Block& obj, sf::Vector2f& normal) { | ||||
|     // 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; | ||||
| 
 | ||||
|     // TODO: supprimer les valeurs magiques
 | ||||
|     // 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 > 20 * 20) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     normal = prenormal / std::sqrt(squaredLength); | ||||
| 
 | ||||
|     if (isInside) { | ||||
|         normal *= -1.f; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| #include "block.hpp" | ||||
| #include "ball.hpp" | ||||
| #include "constants.hpp" | ||||
| 
 | ||||
| Block::Block(float x, float y) : Object(x, y), | ||||
|  | @ -39,3 +40,17 @@ std::unique_ptr<sf::FloatRect> Block::getAABB() { | |||
|         Constants::GRID, Constants::GRID | ||||
|     )); | ||||
| } | ||||
| 
 | ||||
| bool Block::getNormal(Object& obj, sf::Vector2f& normal) { | ||||
|     return obj.getNormal(*this, normal); | ||||
| } | ||||
| 
 | ||||
| bool Block::getNormal(Ball& obj, sf::Vector2f& normal) { | ||||
|     // TODO: coder cette fonction
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool Block::getNormal(Block& obj, sf::Vector2f& normal) { | ||||
|     // TODO: coder cette fonction
 | ||||
|     return false; | ||||
| } | ||||
|  |  | |||
|  | @ -49,21 +49,62 @@ void Engine::update() { | |||
|         state.objects[i]->update(state); | ||||
|     } | ||||
| 
 | ||||
|     // collisions entre objets
 | ||||
|     // gère les collisions entre les objets
 | ||||
|     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]; | ||||
| 
 | ||||
|             // vérifie si on a un risque de collision
 | ||||
|             if (objA->getAABB()->intersects(*objB->getAABB())) { | ||||
|                 // TODO: gestion de la collision =>
 | ||||
|                 // calcul de la normale
 | ||||
|                 // calcul de la profondeur du choc
 | ||||
|                 // si profondeur > 0 :
 | ||||
|                 // résolution de la collision
 | ||||
|             // 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; | ||||
|             } | ||||
| 
 | ||||
|             float restitution = std::min(objA->getRestitution(), objB->getRestitution()); | ||||
| 
 | ||||
|             // calcul de l'inverse des masses de A et B. Pour rappel,
 | ||||
|             // une masse infinie est modélisée par 0, donc l'inverse
 | ||||
|             // d'une telle masse est nul
 | ||||
|             float invMassA = objA->getMass(); | ||||
|             float invMassB = objB->getMass(); | ||||
| 
 | ||||
|             if (invMassA != 0) { | ||||
|                 invMassA = 1.f / invMassA; | ||||
|             } | ||||
| 
 | ||||
|             if (invMassB != 0) { | ||||
|                 invMassB = 1.f / invMassB; | ||||
|             } | ||||
| 
 | ||||
|             // calcule et applique l'impulsion de résolution de la collision
 | ||||
|             float impulse = (-(1 + restitution) * dotnormal) / (invMassA + invMassB); | ||||
|             objA->setVelocity(objA->getVelocity() - invMassA * impulse * normal); | ||||
|             objB->setVelocity(objB->getVelocity() + invMassB * impulse * normal); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ Object::Object(float x, float y) : | |||
|     acceleration(0, 0), velocity(0, 0), position(x, y), | ||||
|     accelerationLine(sf::Lines, 2), | ||||
|     velocityLine(sf::Lines, 2), | ||||
|     mass(1.f), charge(0.f), layer(10) {} | ||||
|     mass(1.f), charge(0.f), restitution(0.f), layer(10) {} | ||||
| 
 | ||||
| sf::Vector2f Object::getForces(EngineState& state) { | ||||
|     sf::Vector2f forces(0, 0); | ||||
|  | @ -48,6 +48,10 @@ void Object::update(EngineState& state) { | |||
|     velocity += acceleration * state.delta; | ||||
| } | ||||
| 
 | ||||
| bool Object::getNormal(Object& obj, sf::Vector2f& normal) { | ||||
|     return obj.getNormal(*this, normal); | ||||
| } | ||||
| 
 | ||||
| sf::Vector2f Object::getAcceleration() { | ||||
|     return acceleration; | ||||
| } | ||||
|  | @ -80,6 +84,14 @@ void Object::setCharge(float set_charge) { | |||
|     charge = set_charge; | ||||
| } | ||||
| 
 | ||||
| float Object::getRestitution() { | ||||
|     return restitution; | ||||
| } | ||||
| 
 | ||||
| void Object::setRestitution(float set_restitution) { | ||||
|     restitution = set_restitution; | ||||
| } | ||||
| 
 | ||||
| unsigned int Object::getLayer() { | ||||
|     return layer; | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue