Utilisation du type std::function plutôt que pointeur sur callback
This commit is contained in:
		
							parent
							
								
									03cae0eb2e
								
							
						
					
					
						commit
						b7eb450225
					
				| 
						 | 
					@ -5,11 +5,13 @@
 | 
				
			||||||
#include "collision_data.hpp"
 | 
					#include "collision_data.hpp"
 | 
				
			||||||
#include <SFML/Graphics.hpp>
 | 
					#include <SFML/Graphics.hpp>
 | 
				
			||||||
#include <utility>
 | 
					#include <utility>
 | 
				
			||||||
 | 
					#include <functional>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Collision {
 | 
					namespace Collision {
 | 
				
			||||||
    typedef bool (*collision_detect)(CollisionData&);
 | 
					    extern std::map<
 | 
				
			||||||
    typedef std::map<std::pair<unsigned int, unsigned int>, collision_detect> collision_dispatcher;
 | 
					        std::pair<unsigned int, unsigned int>,
 | 
				
			||||||
    extern collision_dispatcher dispatch;
 | 
					        std::function<bool(CollisionData&)>
 | 
				
			||||||
 | 
					    > dispatch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool playerToBlock(CollisionData& data);
 | 
					    bool playerToBlock(CollisionData& data);
 | 
				
			||||||
    bool blockToPlayer(CollisionData& data);
 | 
					    bool blockToPlayer(CollisionData& data);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,172 +5,173 @@
 | 
				
			||||||
#include "object.hpp"
 | 
					#include "object.hpp"
 | 
				
			||||||
#include <utility>
 | 
					#include <utility>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Collision {
 | 
					// initialisation du dictionnaire associant les types
 | 
				
			||||||
    // initialisation du dictionnaire associant les types
 | 
					// impliqués dans une collision à leur fonction de résolution
 | 
				
			||||||
    // impliqués dans une collision à leur fonction de résolution
 | 
					std::map<
 | 
				
			||||||
    collision_dispatcher dispatch = {
 | 
					    std::pair<unsigned int, unsigned int>,
 | 
				
			||||||
        {std::make_pair(Player::TYPE_ID, Block::TYPE_ID), &playerToBlock},
 | 
					    std::function<bool(CollisionData&)>
 | 
				
			||||||
        {std::make_pair(Block::TYPE_ID, Player::TYPE_ID), &blockToPlayer},
 | 
					> Collision::dispatch = {
 | 
				
			||||||
        {std::make_pair(Player::TYPE_ID, Player::TYPE_ID), &playerToPlayer},
 | 
					    {std::make_pair(Player::TYPE_ID, Block::TYPE_ID), Collision::playerToBlock},
 | 
				
			||||||
        {std::make_pair(Block::TYPE_ID, Block::TYPE_ID), &blockToBlock}
 | 
					    {std::make_pair(Block::TYPE_ID, Player::TYPE_ID), Collision::blockToPlayer},
 | 
				
			||||||
    };
 | 
					    {std::make_pair(Player::TYPE_ID, Player::TYPE_ID), Collision::playerToPlayer},
 | 
				
			||||||
 | 
					    {std::make_pair(Block::TYPE_ID, Block::TYPE_ID), Collision::blockToBlock}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool playerToBlock(CollisionData& data) {
 | 
					bool Collision::playerToBlock(CollisionData& data) {
 | 
				
			||||||
        Player player = dynamic_cast<Player&>(data.objA);
 | 
					    Player player = dynamic_cast<Player&>(data.objA);
 | 
				
			||||||
        Block block = dynamic_cast<Block&>(data.objB);
 | 
					    Block block = dynamic_cast<Block&>(data.objB);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // recherche du point le plus proche du centre de la
 | 
					    // recherche du point le plus proche du centre de la
 | 
				
			||||||
        // balle sur le bloc. On regarde la position relative
 | 
					    // balle sur le bloc. On regarde la position relative
 | 
				
			||||||
        // du cercle par rapport au bloc
 | 
					    // du cercle par rapport au bloc
 | 
				
			||||||
        std::unique_ptr<sf::FloatRect> aabb = block.getAABB();
 | 
					    std::unique_ptr<sf::FloatRect> aabb = block.getAABB();
 | 
				
			||||||
        sf::Vector2f relpos = block.getPosition() - player.getPosition();
 | 
					    sf::Vector2f relpos = block.getPosition() - player.getPosition();
 | 
				
			||||||
        sf::Vector2f closest = relpos;
 | 
					    sf::Vector2f closest = relpos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // on restreint la position relative pour rester
 | 
					    // on restreint la position relative pour rester
 | 
				
			||||||
        // à l'intérieur du bloc
 | 
					    // à l'intérieur du bloc
 | 
				
			||||||
        if (closest.x < -aabb->width / 2) {
 | 
					    if (closest.x < -aabb->width / 2) {
 | 
				
			||||||
            closest.x = -aabb->width / 2;
 | 
					        closest.x = -aabb->width / 2;
 | 
				
			||||||
        }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (closest.x > aabb->width / 2) {
 | 
					    if (closest.x > aabb->width / 2) {
 | 
				
			||||||
            closest.x = aabb->width / 2;
 | 
					        closest.x = aabb->width / 2;
 | 
				
			||||||
        }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (closest.y < -aabb->height / 2) {
 | 
					    if (closest.y < -aabb->height / 2) {
 | 
				
			||||||
            closest.y = -aabb->height / 2;
 | 
					        closest.y = -aabb->height / 2;
 | 
				
			||||||
        }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (closest.y > aabb->height / 2) {
 | 
					    if (closest.y > aabb->height / 2) {
 | 
				
			||||||
            closest.y = aabb->height / 2;
 | 
					        closest.y = aabb->height / 2;
 | 
				
			||||||
        }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // si la position n'a pas été changée, elle
 | 
					    // si la position n'a pas été changée, elle
 | 
				
			||||||
        // était déjà à l'intérieur du cercle : le cercle
 | 
					    // était déjà à l'intérieur du cercle : le cercle
 | 
				
			||||||
        // est dans le bloc
 | 
					    // est dans le bloc
 | 
				
			||||||
        float isInside = false;
 | 
					    float isInside = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (relpos == closest) {
 | 
					    if (relpos == closest) {
 | 
				
			||||||
            isInside = true;
 | 
					        isInside = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // on se colle au bord le plus proche du bloc
 | 
					        // on se colle au bord le plus proche du bloc
 | 
				
			||||||
            if (std::abs(relpos.x) > std::abs(relpos.y)) {
 | 
					        if (std::abs(relpos.x) > std::abs(relpos.y)) {
 | 
				
			||||||
                if (closest.x > 0) {
 | 
					            if (closest.x > 0) {
 | 
				
			||||||
                    closest.x = aabb->width / 2;
 | 
					                closest.x = aabb->width / 2;
 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    closest.x = -aabb->width / 2;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                if (closest.y > 0) {
 | 
					                closest.x = -aabb->width / 2;
 | 
				
			||||||
                    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);
 | 
					 | 
				
			||||||
        data.depth = player.getRadius() - length;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (length != 0) {
 | 
					 | 
				
			||||||
            data.normal = prenormal / length;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (isInside) {
 | 
					 | 
				
			||||||
            data.normal *= -1.f;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bool blockToPlayer(CollisionData& data) {
 | 
					 | 
				
			||||||
        // la collision Block -> Player est la collision Player -> Block
 | 
					 | 
				
			||||||
        Object& objT = data.objB;
 | 
					 | 
				
			||||||
        data.objB = data.objA;
 | 
					 | 
				
			||||||
        data.objA = objT;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return playerToBlock(data);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bool playerToPlayer(CollisionData& data) {
 | 
					 | 
				
			||||||
        Player playerA = dynamic_cast<Player&>(data.objA);
 | 
					 | 
				
			||||||
        Player playerB = dynamic_cast<Player&>(data.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) {
 | 
					 | 
				
			||||||
            data.depth = totalRadius;
 | 
					 | 
				
			||||||
            data.normal.x = 0;
 | 
					 | 
				
			||||||
            data.normal.y = -1;
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // il y a eu collision
 | 
					 | 
				
			||||||
        data.depth = totalRadius - length;
 | 
					 | 
				
			||||||
        data.normal = dir / length;
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bool blockToBlock(CollisionData& data) {
 | 
					 | 
				
			||||||
        Block blockA = dynamic_cast<Block&>(data.objA);
 | 
					 | 
				
			||||||
        Block blockB = dynamic_cast<Block&>(data.objB);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        std::unique_ptr<sf::FloatRect> aabb = blockA.getAABB();
 | 
					 | 
				
			||||||
        std::unique_ptr<sf::FloatRect> 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) {
 | 
					 | 
				
			||||||
                data.normal.x = -1;
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                data.normal.x = 1;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            data.normal.y = 0;
 | 
					 | 
				
			||||||
            data.depth = overlap_x;
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            if (relpos.y < 0) {
 | 
					            if (closest.y > 0) {
 | 
				
			||||||
                data.normal.y = -1;
 | 
					                closest.y = aabb->height / 2;
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                data.normal.y = 1;
 | 
					                closest.y = -aabb->height / 2;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					 | 
				
			||||||
            data.normal.x = 0;
 | 
					 | 
				
			||||||
            data.depth = overlap_y;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 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);
 | 
				
			||||||
 | 
					    data.depth = player.getRadius() - length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (length != 0) {
 | 
				
			||||||
 | 
					        data.normal = prenormal / length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isInside) {
 | 
				
			||||||
 | 
					        data.normal *= -1.f;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Collision::blockToPlayer(CollisionData& data) {
 | 
				
			||||||
 | 
					    // la collision Block -> Player est la collision Player -> Block
 | 
				
			||||||
 | 
					    Object& objT = data.objB;
 | 
				
			||||||
 | 
					    data.objB = data.objA;
 | 
				
			||||||
 | 
					    data.objA = objT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return playerToBlock(data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Collision::playerToPlayer(CollisionData& data) {
 | 
				
			||||||
 | 
					    Player playerA = dynamic_cast<Player&>(data.objA);
 | 
				
			||||||
 | 
					    Player playerB = dynamic_cast<Player&>(data.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) {
 | 
				
			||||||
 | 
					        data.depth = totalRadius;
 | 
				
			||||||
 | 
					        data.normal.x = 0;
 | 
				
			||||||
 | 
					        data.normal.y = -1;
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // il y a eu collision
 | 
				
			||||||
 | 
					    data.depth = totalRadius - length;
 | 
				
			||||||
 | 
					    data.normal = dir / length;
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Collision::blockToBlock(CollisionData& data) {
 | 
				
			||||||
 | 
					    Block blockA = dynamic_cast<Block&>(data.objA);
 | 
				
			||||||
 | 
					    Block blockB = dynamic_cast<Block&>(data.objB);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::unique_ptr<sf::FloatRect> aabb = blockA.getAABB();
 | 
				
			||||||
 | 
					    std::unique_ptr<sf::FloatRect> 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) {
 | 
				
			||||||
 | 
					            data.normal.x = -1;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            data.normal.x = 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data.normal.y = 0;
 | 
				
			||||||
 | 
					        data.depth = overlap_x;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        if (relpos.y < 0) {
 | 
				
			||||||
 | 
					            data.normal.y = -1;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            data.normal.y = 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data.normal.x = 0;
 | 
				
			||||||
 | 
					        data.depth = overlap_y;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue