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