skizzle/src/ball.cpp

160 lines
4.3 KiB
C++
Raw Normal View History

2016-03-04 15:29:31 +00:00
#include "ball.hpp"
#include "block.hpp"
#include "constants.hpp"
#include <array>
2016-03-19 14:33:31 +00:00
#include <iostream>
2016-03-04 15:29:31 +00:00
Ball::Ball(float x, float y) : Object(x, y),
shape(sf::Vector2f(2 * getRadius(), 2 * getRadius())) {
shape.setOrigin(sf::Vector2f(getRadius(), getRadius()));
}
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);
}
return forces;
2016-03-04 15:29:31 +00:00
}
void Ball::draw(sf::RenderWindow& window) {
Object::draw(window);
// chargement de la texture de test
if (!texture.loadFromFile("./res/ball.png")) {
// erreur
}
2016-03-18 17:27:24 +00:00
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 - getRadius(),
getPosition().y - getRadius(),
2 * getRadius(), 2 * getRadius()
));
}
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) {
2016-03-19 14:33:31 +00:00
sf::Vector2f dir = getPosition() - obj.getPosition();
float squaredLength = dir.x * dir.x + dir.y * dir.y;
float totalRadius = getRadius() + obj.getRadius();
// si les deux balles sont à une distance supérieure
// à la somme de leurs deux rayons, il n'y a pas eu collision
if (squaredLength > totalRadius * totalRadius) {
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) {
depth = totalRadius;
normal.x = 0;
normal.y = -1;
return true;
}
// il y a eu collision
depth = totalRadius - 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;
// 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 >= getRadius() * getRadius()) {
return false;
}
float length = std::sqrt(squaredLength);
depth = getRadius() - length;
if (length != 0) {
normal = prenormal / length;
}
if (isInside) {
normal *= -1.f;
}
return true;
}
float Ball::getRadius() {
return 10 * getMass();
}