Partage
  • Partager sur Facebook
  • Partager sur Twitter

[SFML] TileMap collisions

25 mai 2020 à 20:03:13

Bonjour à tous,

Je créé actuellement un petit jeu de plateforme basique type mario.

J'ai un problème avec la map. Je n'arrive pas à faire de collisions entre la map et mon perso.

Je précise que mon perso est censé tombé mais j'ai fait des modifs pour faire des tests.

Quelqu'un aurait-il une solution ?

Voici le code :

Le main:

int main()
{
    vector<vector<Vector2i>> map1;
    vector<vector<int>> colMap;
    Texture tileTexture;

    Platform cmap(tileTexture, map1, colMap);

    RenderWindow window(VideoMode(1000,650), "Super Game");
    Texture mario;
    Clock clock;
    View view(Vector2f(0,0), Vector2f(1000,650));

    mario.loadFromFile("/home/pouce/Programmation/SFML_test/images/mario_walk_sprite.png");

    Player player(mario, Vector2f(3.0f,3.9f), 0.1f, 150.0f, 175.0f);


    float deltaTime = 0.0f;

    cmap.loadMap("/home/pouce/Programmation/SFML_test/images/map.txt");
    cmap.loadColMap("/home/pouce/Programmation/SFML_test/images/colMap.txt");

    while (window.isOpen())
    {
        deltaTime = clock.restart().asSeconds();
        if(deltaTime > 1.0f/20.0f)
            deltaTime = 1.0f/20.0f;

        Event evnt;
        while (window.pollEvent(evnt))
        {
            if(evnt.type == Event::Closed)
                window.close();
            if(evnt.type == Event::Resized)
                resize(window, view);
            if((evnt.type == Event::KeyPressed) && (evnt.key.code == Keyboard::A))
            {
                std::cout << player.getPosition().x << " , " << player.getPosition().y << std::endl;
            }
        }

        Vector2f direction;

        player.update(deltaTime);

        cmap.colliding(player, direction);

        Vector2f position(window.getSize().x/2, window.getSize().y/2);
        position.x =  player.getPosition().x + mario.getSize().x/2 - window.getSize().x/2.5;
        position.y = player.getPosition().y + mario.getSize().y/2 - window.getSize().y/1.2;
        if(position.x < 0)
            position.x = 0;
        if(position.y < 0)
            position.y = 0;
        view.reset(FloatRect(position.x, position.y, window.getSize().x, window.getSize().y));

        window.clear(Color(146,215,244));
        window.setView(view);

        cmap.draw(window);
        player.draw(window);
        window.display();
    }

    return 0;
}

La classe du joueur :

#include "player.h"
#include "collision.h"
#include "math.h"

Player::Player(sf::Texture& texture, sf::Vector2f imageCount, float switchTime, float speed, float jumpHeight) :
    animation(texture, imageCount, switchTime)
{
    this->speed = speed;
    this->jumpHeight = jumpHeight;
    row = 0;

    body.setOrigin(body.getLocalBounds().width / 2, body.getLocalBounds().height/2);
    body.setPosition(60.0f, 400.0f);
    body.setTexture(texture);
    body.setScale(2,2);
}

Player::~Player()
{

}

void Player::update(float deltaTime)
{
    velocity.x = 0.0f;
    sf::Vector2f direction;
    velocity.y = 0.0f;

    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
    {
        velocity.x += speed;
        currentRow = 0;
    }

    else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
    {
        velocity.x -= speed;
        currentRow = 1;
        if(body.getPosition().x <= body.getLocalBounds().width/2)
            velocity.x = 0;
    }

    else if(sf::Keyboard::isKeyPressed((sf::Keyboard::Down)))
    {
        velocity.y = speed;
    }

    else if(sf::Keyboard::isKeyPressed((sf::Keyboard::Up)))
    {
        velocity.y = -speed;
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Space) && canJump)
    {
        canJump = false;
        velocity.y = -std::sqrt(2.0f * 981.0f * jumpHeight);
    }


    //velocity.y += 981.0f * deltaTime;

    if(velocity.x < 0)
    {
        row = 1;
        animation.update(row, deltaTime);
        if(canJump == false)
        {
            row = 3;
            animation.update(row, deltaTime);
        }
    }
    else if(velocity.x > 0)
    {
        row = 0;
        animation.update(row, deltaTime);
        if(canJump == false)
        {
            row = 2;
            animation.update(row, deltaTime);
        }
    }
    else if(canJump == false)
    {
        if(currentRow == 0)
        {
            row = 2;
            animation.update(row, deltaTime);
        }
        else if(currentRow == 1)
        {
            row = 3;
            animation.update(row, deltaTime);
        }
    }

    body.setTextureRect(animation.xyRect);
    body.move(velocity * deltaTime);
}

void Player::draw(sf::RenderWindow& window)
{
    window.draw(body);
}

void Player::onCollision(sf::Vector2f &direction)
{
    if(direction.x < 0.0f)
    {
        velocity.x = 0.0f;
    }
    else if(direction.x > 0.0f)
    {
        velocity.x = 0.0f;
    }
    else if(direction.y > 0.0f)
    {
        velocity.y = 0.0f;
        canJump = true;
    }
    else if(direction.y < 0.0f)
    {
        velocity.y = 0.0f;
    }
}   

La classe de la map :

#include "platform.h"

Platform::Platform(sf::Texture &tileTexture, std::vector<std::vector<sf::Vector2i> > &map1, std::vector<std::vector<int> > &colMap)
{
    this->tileTexture = tileTexture;
    this->map1 = map1;
    this->colMap = colMap;
}

Platform::~Platform()
{

}


void Platform::loadMap(const char *filename)
{
    std::ifstream openfile(filename);
    std::vector<sf::Vector2i> tempMap;
    map1.clear();

    if(openfile.is_open())
    {
        std::string tileLocation;
        openfile >> tileLocation;
        tileTexture.loadFromFile(tileLocation);
        body.setTexture(tileTexture);

        while(!openfile.eof())
        {
            std::string str, value;
            std::getline(openfile, str);
            std::stringstream stream(str);

            while(std::getline(stream, value, ' '))
            {
                if(value.length() > 0)
                {
                    std::string xx = value.substr(0, value.find(','));
                    std::string yy = value.substr(value.find(',') + 1);

                    unsigned int x, y, i, j;

                    for(i=0; i < xx.length(); i++)
                    {
                        if(!isdigit(xx[i]))
                            break;
                    }
                    for(j=0; j < yy.length(); j++)
                    {
                        if(!isdigit(yy[j]))
                            break;
                    }

                    x = (i == xx.length()) ? atoi(xx.c_str()) : -1;
                    y = (j == yy.length()) ? atoi(yy.c_str()) : -1;

                    tempMap.push_back(sf::Vector2i(x, y));
                }
            }
            map1.push_back(tempMap);
            tempMap.clear();
        }
    }
}

void Platform::loadColMap(const char *filename)
{
    std::ifstream openfile(filename);
    std::vector<int> tempMap;
    colMap.clear();

    if(openfile.is_open())
    {
        while(!openfile.eof())
        {
            std::string str, value;
            std::getline(openfile, str);
            std::stringstream stream(str);

            while(std::getline(stream, value, ' '))
            {
                if(value.length() > 0)
                {
                    int a = atoi(value.c_str());
                    tempMap.push_back(a);
                }
            }
                colMap.push_back(tempMap);
                tempMap.clear();
        }
    }
}

void Platform::colliding(Player &player, Vector2f &direction)
{
    for(unsigned int i = 0 ; i < colMap.size() ; i++)
    {
        for(unsigned int j = 0 ; j < colMap[i].size() ; j++)
        {
            if(colMap[i][j] == 1)
            {
                player.onCollision(direction);
            }
        }
    }
}

void Platform::draw(RenderWindow &window)
{
    for(unsigned int i = 0 ; i < map1.size() ; i++)
    {
        for(unsigned int j = 0 ; j < map1[i].size() ; j++)
        {
            if(map1[i][j].x != -1 && map1[i][j].y != -1)
            {
                body.setPosition(j * 29, i * 29);
                body.setTextureRect(sf::IntRect(map1[i][j].x * 31.5, map1[i][j].y * 32, 28, 28));
                window.draw(body);
            }
        }
    }    
}

 Et enfin la classe collision :

Collision::Collision(sf::Sprite& body) :
    body(body)
{

}

Collision::~Collision()
{

}

bool Collision::checkCollision(Collision&& other, sf::Vector2f& direction)
{
    sf::Vector2f otherPosition = other.getPosition();
    float otherHalfSizeX = other.getHalfSizeX();
    float otherHalfSizeY = other.getHalfSizeY();
    sf::Vector2f thisPosition = getPosition();
    float thisHalfSizeX = getHalfSizeX();
    float thisHalfSizeY = getHalfSizeY();

    float deltaX = otherPosition.x - thisPosition.x;
    float deltaY = otherPosition.y - thisPosition.y;

    float intersectX = std::abs(deltaX) - (otherHalfSizeX + thisHalfSizeX);
    float intersectY = std::abs(deltaY) - (otherHalfSizeY + thisHalfSizeY);

    if(intersectX < 0.0f && intersectY < 0.0f)
    {
        if(intersectX > intersectY)
        {
            if(deltaX > 0.0f)
            {
                move(0.0f, 0.0f);
                other.move(-intersectX,0.0f);
                direction.x = 1.0f;
                direction.y = 0.0f;
            }
            else
            {
                move(0.0f, 0.0f);
                other.move(intersectX,0.0f);
                direction.x = -1.0f;
                direction.y = 0.0f;
            }
        }
        else
        {
            if(deltaY > 0.0f)
            {
                move(0.0f, 0.0f);
                other.move(0.0f,-intersectY);
                direction.x = 0.0f;
                direction.y = -1.0f;
            }
            else
            {
                move(0.0f, 0.0f);
                other.move(0.0f,intersectY);
                direction.x = 0.0f;
                direction.y = 1.0f;
            }
        }
        return true;
    }


    return false;
}

Merci d'avance !





  • Partager sur Facebook
  • Partager sur Twitter
26 mai 2020 à 11:48:29

Salut,

Premier point, ce n'est pas à ta classe Player, ni à ta classe Plateform de verifier si elle collide avec quelque chose, c'est le rôle d'un algorithme "a part".
Second point, l'algorithme de detection des collisions n'est qu'un ensemble de fonctions, je ne suis pas conaincu que les encapsuler dans une classe soit nécessaire.

Le principe est relativement simple:
- Tu référence dans un conteneur toutes les entités "collidables", et il ne te reste plus qu'a les tester par pair (2 boucles imbriquées feront le job).
Attention toutefois a ne pas tester une entité avec elle même.

Je te conseille de jeter un coup d'œil sur le livre SFML Game Developpement  (https://www.sfml-dev.org/learn.php), il n'a peut être pas la meilleur approche, mais les algorithmes de base sont la (boucle de jeux, evennements, input, output, collisions ect ect …).

  • Partager sur Facebook
  • Partager sur Twitter
26 mai 2020 à 18:10:53

Ok merci pour le tuyau de ressource.

Donc si je te suis bien, je garde mes classes player et platform mais je vire la classe collision.

Quand tu dis que je place dans un conteneur les entités collidables, je ne vois pas bien.. ?

Pour la boucle de test des collisions, je vois bien comment faire pas de soucis.

Merci de ton aide.

  • Partager sur Facebook
  • Partager sur Twitter
27 mai 2020 à 12:07:54

Bonjour,

Ce qu'il entend par "Placer dans un conteneur les collidables", c'est qu'il faut regrouper le Player, les plateformes, les ennemis s'il y'en a plus tard, etc.

Ca permettra de tester d'un seul coup toutes les collisions, et de pouvoir rajouter d'autres entités sans avoir à recoder tout l'algo.

J'espère que j'ai été assez clair ^^

Bon courage ^^

  • Partager sur Facebook
  • Partager sur Twitter

Vous ne pouvez pas comprendre la récursivité sans d’abord avoir compris la récursivité