diff --git a/docs/comptes-rendus/25-03-16.md b/docs/comptes-rendus/25-03-16.md new file mode 100644 index 0000000..4dd8029 --- /dev/null +++ b/docs/comptes-rendus/25-03-16.md @@ -0,0 +1,18 @@ +# Vendredi 25 Mars - Cours Projet CMI - + +## Compte-rendu de la semaine + +* Rémi : affinage des constantes, recherche de bugs du moteur +* Maëlle : mise en place de la caméra, conception initiale des textures, musiques +* Mattéo : correction des collisions et du moteur physique en général + +## Ce jour + +* Correction d'un bug de collisions. +* Recherche d'autres bugs. +* Distinction des deux joueurs. +* Conception (sur papier) des niveaux. + +## Répartition des tâches + +Cf. Gant. diff --git a/include/block.hpp b/include/block.hpp index cc9b39d..d742317 100644 --- a/include/block.hpp +++ b/include/block.hpp @@ -2,7 +2,6 @@ #define __PTF_BLOCK_HPP__ #include -#include #include "object.hpp" #include "engine_state.hpp" @@ -24,13 +23,10 @@ public: std::unique_ptr getAABB(); /** - * Calcule les informations sur une éventuelle collision de - * cet objet avec un autre : la normale et la profondeur + * Récupère l'identifiant de type de cet objet */ - virtual bool getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth); - virtual bool getCollisionInfo(Player& obj, sf::Vector2f& normal, float& depth); - virtual bool getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth); + static constexpr unsigned int TYPE_ID = 1; + unsigned int getTypeId(); }; #endif - diff --git a/include/collision.hpp b/include/collision.hpp new file mode 100644 index 0000000..7f17c37 --- /dev/null +++ b/include/collision.hpp @@ -0,0 +1,20 @@ +#ifndef __PTF_COLLISION_HPP__ +#define __PTF_COLLISION_HPP__ + +#include "object.hpp" +#include +#include +#include + +namespace Collision { + typedef bool (*collision_data)(Object&, Object&, sf::Vector2f&, float&); + typedef std::map, collision_data> collision_dispatcher; + extern collision_dispatcher dispatch; + + bool playerToBlock(Object& objA, Object& objB, sf::Vector2f& normal, float& depth); + bool blockToPlayer(Object& objA, Object& objB, sf::Vector2f& normal, float& depth); + bool playerToPlayer(Object& objA, Object& objB, sf::Vector2f& normal, float& depth); + bool blockToBlock(Object& objA, Object& objB, sf::Vector2f& normal, float& depth); +} + +#endif diff --git a/include/object.hpp b/include/object.hpp index 2adf11a..42ba13e 100644 --- a/include/object.hpp +++ b/include/object.hpp @@ -52,12 +52,9 @@ public: virtual std::unique_ptr getAABB() = 0; /** - * Calcule les informations sur une éventuelle collision de - * cet objet avec un autre : la normale et la profondeur + * Récupère l'identifiant de type de cet objet */ - virtual bool getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth); - virtual bool getCollisionInfo(Player& obj, sf::Vector2f& normal, float& depth) = 0; - virtual bool getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth) = 0; + virtual unsigned int getTypeId() = 0; /** * Détecte s'il y a collision entre cet objet @@ -169,4 +166,3 @@ struct ObjectCompare { }; #endif - diff --git a/include/player.hpp b/include/player.hpp index f1ad8d1..ac3a94e 100644 --- a/include/player.hpp +++ b/include/player.hpp @@ -31,18 +31,16 @@ public: std::unique_ptr getAABB(); /** - * Calcule les informations sur une éventuelle collision de - * cet objet avec un autre : la normale et la profondeur + * Récupère l'identifiant de type de cet objet */ - virtual bool getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth); - virtual bool getCollisionInfo(Player& obj, sf::Vector2f& normal, float& depth); - virtual bool getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth); + static constexpr unsigned int TYPE_ID = 0; + unsigned int getTypeId(); /** * Renvoie le rayon de la balle */ float getRadius(); - + /** * Renvoie le numéro du joueur */ @@ -50,7 +48,6 @@ public: /** * Modifie le numéro du joueur - * */ void setPlayerNumber(unsigned int set_number); }; diff --git a/res/ball.bmp b/res/ball.bmp deleted file mode 100644 index f0ce621..0000000 Binary files a/res/ball.bmp and /dev/null differ diff --git a/res/block.bmp b/res/block.bmp deleted file mode 100644 index e496717..0000000 Binary files a/res/block.bmp and /dev/null differ diff --git a/res/lvl1.wav b/res/lvl1.wav new file mode 100644 index 0000000..2e49972 Binary files /dev/null and b/res/lvl1.wav differ diff --git a/res/menu.wav b/res/menu.wav new file mode 100644 index 0000000..198f9c8 Binary files /dev/null and b/res/menu.wav differ diff --git a/src/block.cpp b/src/block.cpp index b8e08e5..ac5a8b5 100644 --- a/src/block.cpp +++ b/src/block.cpp @@ -39,52 +39,6 @@ std::unique_ptr Block::getAABB() { )); } -bool Block::getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth) { - return obj.getCollisionInfo(*this, normal, depth); +unsigned int Block::getTypeId() { + return Block::TYPE_ID; } - -bool Block::getCollisionInfo(Player& obj, sf::Vector2f& normal, float& depth) { - // la collision Block -> Player est la collision Player -> Block - // avec une normale de collision retournée - bool result = obj.getCollisionInfo(*this, normal, depth); - normal *= -1.f; - return result; -} - -bool Block::getCollisionInfo(Block& obj, sf::Vector2f& normal, float& depth) { - std::unique_ptr aabb = getAABB(); - std::unique_ptr obj_aabb = obj.getAABB(); - sf::Vector2f relpos = getPosition() - obj.getPosition(); - - float overlap_x = aabb->width / 2 + obj_aabb->width / 2 - std::abs(relpos.x); - float overlap_y = aabb->height / 2 + obj_aabb->height / 2 - std::abs(relpos.y); - - // si il n'y a pas de chauvauchement sur l'axe X et Y, pas de collision - if (overlap_x <= 0 || overlap_y <= 0) { - return false; - } - - // on choisit l'axe de pénétration maximale pour calculer la normale - if (overlap_x < overlap_y) { - if (relpos.x < 0) { - normal.x = -1; - } else { - normal.x = 1; - } - - normal.y = 0; - depth = overlap_x; - } else { - if (relpos.y < 0) { - normal.y = -1; - } else { - normal.y = 1; - } - - normal.x = 0; - depth = overlap_y; - } - - return true; -} - diff --git a/src/collision.cpp b/src/collision.cpp new file mode 100644 index 0000000..53090c2 --- /dev/null +++ b/src/collision.cpp @@ -0,0 +1,175 @@ +#include "collision.hpp" +#include "player.hpp" +#include "block.hpp" +#include "object.hpp" +#include +#include + +namespace Collision { + // initialisation du dictionnaire associant les types + // impliqués dans une collision à leur fonction de résolution + collision_dispatcher dispatch = { + {std::make_pair(Player::TYPE_ID, Block::TYPE_ID), &playerToBlock}, + {std::make_pair(Block::TYPE_ID, Player::TYPE_ID), &blockToPlayer}, + {std::make_pair(Player::TYPE_ID, Player::TYPE_ID), &playerToPlayer}, + {std::make_pair(Block::TYPE_ID, Block::TYPE_ID), &blockToBlock} + }; + + bool playerToBlock(Object& objA, Object& objB, sf::Vector2f& normal, float& depth) { + Player player = dynamic_cast(objA); + Block block = dynamic_cast(objB); + + // 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 aabb = block.getAABB(); + sf::Vector2f relpos = block.getPosition() - player.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 >= player.getRadius() * player.getRadius()) { + return false; + } + + float length = std::sqrt(squaredLength); + depth = player.getRadius() - length; + + if (length != 0) { + normal = prenormal / length; + } + + if (isInside) { + normal *= -1.f; + } + + return true; + } + + bool blockToPlayer(Object& objA, Object& objB, sf::Vector2f& normal, float& depth) { + // la collision Block -> Player est la collision Player -> Block + // avec une normale de collision retournée + bool result = playerToBlock(objB, objA, normal, depth); + normal *= -1.f; + return result; + } + + bool playerToPlayer(Object& objA, Object& objB, sf::Vector2f& normal, float& depth) { + Player playerA = dynamic_cast(objA); + Player playerB = dynamic_cast(objB); + + sf::Vector2f dir = playerB.getPosition() - playerA.getPosition(); + float squaredLength = dir.x * dir.x + dir.y * dir.y; + float totalRadius = playerB.getRadius() + playerA.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 blockToBlock(Object& objA, Object& objB, sf::Vector2f& normal, float& depth) { + Block blockA = dynamic_cast(objA); + Block blockB = dynamic_cast(objB); + + std::unique_ptr aabb = blockA.getAABB(); + std::unique_ptr obj_aabb = blockB.getAABB(); + sf::Vector2f relpos = blockB.getPosition() - blockA.getPosition(); + + float overlap_x = aabb->width / 2 + obj_aabb->width / 2 - std::abs(relpos.x); + float overlap_y = aabb->height / 2 + obj_aabb->height / 2 - std::abs(relpos.y); + + // si il n'y a pas de chauvauchement sur l'axe X et Y, pas de collision + if (overlap_x <= 0 || overlap_y <= 0) { + return false; + } + + // on choisit l'axe de pénétration maximale pour calculer la normale + if (overlap_x < overlap_y) { + if (relpos.x < 0) { + normal.x = -1; + } else { + normal.x = 1; + } + + normal.y = 0; + depth = overlap_x; + } else { + if (relpos.y < 0) { + normal.y = -1; + } else { + normal.y = 1; + } + + normal.x = 0; + depth = overlap_y; + } + + return true; + } +} diff --git a/src/engine.cpp b/src/engine.cpp index 5f2b181..2b6096e 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -43,12 +43,13 @@ void Engine::start() { if (event.type == sf::Event::KeyReleased) { state.keys[event.key.code] = false; } - // lorsque la fenêtre est redimensionnée par l'utilisateur - if (event.type == sf::Event::Resized){ - // mise à jour de la caméra en fonction de la taille de la fenêtre - sf::FloatRect visibleArea(0, 0, event.size.width, event.size.height); - window.setView(sf::View(visibleArea)); - } + + // lorsque la fenêtre est redimensionnée par l'utilisateur, + // mise à jour de la caméra en fonction de la taille de la fenêtre + if (event.type == sf::Event::Resized) { + sf::FloatRect visibleArea(0, 0, event.size.width, event.size.height); + window.setView(sf::View(visibleArea)); + } } float frame = clock.restart().asSeconds(); diff --git a/src/object.cpp b/src/object.cpp index c9d1481..8e879d3 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -1,6 +1,6 @@ - #include "object.hpp" #include "constants.hpp" +#include "collision.hpp" #include Object::Object(float x, float y) : @@ -75,16 +75,12 @@ void Object::draw(sf::RenderWindow& window, ResourceManager& resources) { } void Object::update(EngineState& state) { - // intégration de la vitesse dans la position - position += velocity * Constants::PHYSICS_TIME; - // intégration des forces appliquées sur l'objet dans la vitesse acceleration = getForces(state) * getMassInvert(); velocity += acceleration * Constants::PHYSICS_TIME; -} -bool Object::getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth) { - return obj.getCollisionInfo(*this, normal, depth); + // intégration de la vitesse dans la position + position += velocity * Constants::PHYSICS_TIME; } void Object::collide(Object& obj) { @@ -105,7 +101,7 @@ void Object::collide(Object& obj) { // vérifie plus finement s'il y a collision et récupère // les informations sur la collision (normale et profondeur) - if (!getCollisionInfo(obj, normal, depth)) { + if (!Collision::dispatch[std::make_pair(getTypeId(), obj.getTypeId())](*this, obj, normal, depth)) { return; } diff --git a/src/player.cpp b/src/player.cpp index f5933ad..92352b9 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -9,20 +9,11 @@ Player::Player(float x, float y) : Object(x, y) { sprite.setOrigin(sf::Vector2f(getRadius(), getRadius())); } -unsigned int Player::getPlayerNumber(){ - return player_number; -} - -void Player::setPlayerNumber(unsigned int set_number){ - player_number = set_number; -} - sf::Vector2f Player::getForces(EngineState& state) { sf::Vector2f forces = Object::getForces(state); - - //commandes du joueur 1 - if(player_number==1){ - // déplacement de la balle après appui sur les touches de direction + + // déplacement de la balle après appui sur les touches de direction + if (getPlayerNumber() == 1) { if (state.keys[sf::Keyboard::Left]) { forces += sf::Vector2f(-Constants::MOVE, 0); } @@ -31,12 +22,13 @@ sf::Vector2f Player::getForces(EngineState& state) { forces += sf::Vector2f(Constants::MOVE, 0); } } - else{ - if(state.keys[sf::Keyboard::Q]){ + + if (getPlayerNumber() == 2) { + if (state.keys[sf::Keyboard::Q]) { forces += sf::Vector2f(-Constants::MOVE, 0); } - - if(state.keys[sf::Keyboard::D]){ + + if (state.keys[sf::Keyboard::D]) { forces += sf::Vector2f(Constants::MOVE, 0); } } @@ -48,7 +40,7 @@ void Player::draw(sf::RenderWindow& window, ResourceManager& resources) { Object::draw(window, resources); // utilisation de la texture - sprite.setTexture(resources.getTexture("ball.bmp")); + sprite.setTexture(resources.getTexture("ball.png")); // déplacement du sprite à la position de la balle sprite.rotate(getVelocity().x * .1f); @@ -64,114 +56,18 @@ std::unique_ptr Player::getAABB() { )); } -bool Player::getCollisionInfo(Object& obj, sf::Vector2f& normal, float& depth) { - return obj.getCollisionInfo(*this, normal, depth); -} - -bool Player::getCollisionInfo(Player& obj, sf::Vector2f& normal, float& depth) { - 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 Player::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 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; +unsigned int Player::getTypeId() { + return Player::TYPE_ID; } float Player::getRadius() { return 10 * getMass(); } + +unsigned int Player::getPlayerNumber() { + return player_number; +} + +void Player::setPlayerNumber(unsigned int set_number) { + player_number = set_number; +}