187 lines
5.3 KiB
C++
187 lines
5.3 KiB
C++
#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));
|
|
}
|
|
|
|
sf::Vector2f Ball::getForces(EngineState& state) {
|
|
sf::Vector2f forces = Object::getForces(state);
|
|
|
|
// déplacement de la balle après appui sur les touches de direction
|
|
if (state.keys[sf::Keyboard::Left]) {
|
|
forces += sf::Vector2f(-Constants::MOVE, 0);
|
|
}
|
|
|
|
if (state.keys[sf::Keyboard::Right]) {
|
|
forces += sf::Vector2f(Constants::MOVE, 0);
|
|
}
|
|
|
|
// force d'attraction entre les balles et les blocs chargés
|
|
if (getCharge() != 0) {
|
|
for (unsigned int j = 0; j < state.objects.size(); j++) {
|
|
Object *attractive = state.objects[j];
|
|
|
|
if (attractive == this || attractive->getCharge() == 0) {
|
|
continue;
|
|
}
|
|
|
|
// vecteur allant de l'objet attirant vers l'objet considéré
|
|
sf::Vector2f attraction(getPosition() - attractive->getPosition());
|
|
|
|
// la norme de ce vecteur est la distance entre les objets
|
|
float distanceSquared = attraction.x * attraction.x +
|
|
attraction.y * attraction.y;
|
|
|
|
// éviter la division par zéro
|
|
if (distanceSquared == 0) {
|
|
continue;
|
|
}
|
|
|
|
// normalisation du vecteur direction qui porte
|
|
// la force d'attraction, puis application de la norme
|
|
attraction /= std::sqrt(distanceSquared);
|
|
attraction *= Constants::ATTRACTION * (
|
|
(getCharge() * attractive->getCharge()) /
|
|
distanceSquared
|
|
);
|
|
|
|
forces += attraction;
|
|
}
|
|
}
|
|
|
|
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.rotate(getVelocity().x * .1f);
|
|
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::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 = 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) {
|
|
// TODO: supprimer les valeurs magiques
|
|
depth = 10;
|
|
normal.x = 0;
|
|
normal.y = -1;
|
|
return true;
|
|
}
|
|
|
|
// TODO: supprimer les valeurs magiques
|
|
// il y a eu collision
|
|
depth = 10 - 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;
|
|
|
|
// 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 >= 10 * 10) {
|
|
return false;
|
|
}
|
|
|
|
float length = std::sqrt(squaredLength);
|
|
depth = 10 - length;
|
|
normal = prenormal / length;
|
|
|
|
if (isInside) {
|
|
normal *= -1.f;
|
|
}
|
|
|
|
return true;
|
|
}
|