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
|
* Récupère la boîte englobante de l'objet
|
||||||
*/
|
*/
|
||||||
std::unique_ptr<sf::FloatRect> getAABB();
|
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
|
#endif
|
||||||
|
|
|
@ -23,6 +23,13 @@ public:
|
||||||
* Récupère la boîte englobante de l'objet
|
* Récupère la boîte englobante de l'objet
|
||||||
*/
|
*/
|
||||||
std::unique_ptr<sf::FloatRect> getAABB();
|
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
|
#endif
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "engine_state.hpp"
|
#include "engine_state.hpp"
|
||||||
|
|
||||||
|
class Block;
|
||||||
|
class Ball;
|
||||||
|
|
||||||
class Object {
|
class Object {
|
||||||
private:
|
private:
|
||||||
sf::Vector2f acceleration;
|
sf::Vector2f acceleration;
|
||||||
|
@ -16,6 +19,7 @@ private:
|
||||||
|
|
||||||
float mass;
|
float mass;
|
||||||
float charge;
|
float charge;
|
||||||
|
float restitution;
|
||||||
int layer;
|
int layer;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -43,6 +47,13 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual std::unique_ptr<sf::FloatRect> getAABB() = 0;
|
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
|
* Récupère l'accélération de l'objet
|
||||||
*/
|
*/
|
||||||
|
@ -84,6 +95,16 @@ public:
|
||||||
*/
|
*/
|
||||||
void setCharge(float set_charge);
|
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
|
* 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 "ball.hpp"
|
||||||
|
#include "block.hpp"
|
||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
|
#include <array>
|
||||||
|
|
||||||
Ball::Ball(float x, float y) : Object(x, y), shape(10) {
|
Ball::Ball(float x, float y) : Object(x, y), shape(10) {
|
||||||
shape.setOrigin(sf::Vector2f(10, 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 Ball::getForces(EngineState& state) {
|
||||||
sf::Vector2f forces = Object::getForces(state);
|
sf::Vector2f forces = Object::getForces(state);
|
||||||
|
|
||||||
|
@ -73,3 +54,126 @@ sf::Vector2f Ball::getForces(EngineState& state) {
|
||||||
|
|
||||||
return forces;
|
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 "block.hpp"
|
||||||
|
#include "ball.hpp"
|
||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
|
|
||||||
Block::Block(float x, float y) : Object(x, y),
|
Block::Block(float x, float y) : Object(x, y),
|
||||||
|
@ -39,3 +40,17 @@ std::unique_ptr<sf::FloatRect> Block::getAABB() {
|
||||||
Constants::GRID, Constants::GRID
|
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);
|
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++) {
|
for (unsigned int i = 0; i < state.objects.size(); i++) {
|
||||||
Object* objA = state.objects[i];
|
Object* objA = state.objects[i];
|
||||||
|
|
||||||
for (unsigned int j = i + 1; j < state.objects.size(); j++) {
|
for (unsigned int j = i + 1; j < state.objects.size(); j++) {
|
||||||
Object* objB = state.objects[j];
|
Object* objB = state.objects[j];
|
||||||
|
|
||||||
// vérifie si on a un risque de collision
|
// si les objets ne sont pas sur la même couche,
|
||||||
if (objA->getAABB()->intersects(*objB->getAABB())) {
|
// ils ne peuvent pas entrer en collision
|
||||||
// TODO: gestion de la collision =>
|
if (objA->getLayer() != objB->getLayer()) {
|
||||||
// calcul de la normale
|
continue;
|
||||||
// calcul de la profondeur du choc
|
|
||||||
// si profondeur > 0 :
|
|
||||||
// résolution de la collision
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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),
|
acceleration(0, 0), velocity(0, 0), position(x, y),
|
||||||
accelerationLine(sf::Lines, 2),
|
accelerationLine(sf::Lines, 2),
|
||||||
velocityLine(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 Object::getForces(EngineState& state) {
|
||||||
sf::Vector2f forces(0, 0);
|
sf::Vector2f forces(0, 0);
|
||||||
|
@ -48,6 +48,10 @@ void Object::update(EngineState& state) {
|
||||||
velocity += acceleration * state.delta;
|
velocity += acceleration * state.delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Object::getNormal(Object& obj, sf::Vector2f& normal) {
|
||||||
|
return obj.getNormal(*this, normal);
|
||||||
|
}
|
||||||
|
|
||||||
sf::Vector2f Object::getAcceleration() {
|
sf::Vector2f Object::getAcceleration() {
|
||||||
return acceleration;
|
return acceleration;
|
||||||
}
|
}
|
||||||
|
@ -80,6 +84,14 @@ void Object::setCharge(float set_charge) {
|
||||||
charge = set_charge;
|
charge = set_charge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float Object::getRestitution() {
|
||||||
|
return restitution;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Object::setRestitution(float set_restitution) {
|
||||||
|
restitution = set_restitution;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int Object::getLayer() {
|
unsigned int Object::getLayer() {
|
||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue