Implémentation initiale des collisions

This commit is contained in:
Mattéo Delabre 2016-03-15 22:00:03 +01:00
parent 6a34c31ae9
commit a99f1ccd0a
7 changed files with 237 additions and 30 deletions

View File

@ -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

View File

@ -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

View File

@ -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
*/

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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;
}