Partage
  • Partager sur Facebook
  • Partager sur Twitter

Pointeur d'une classe vers une autre

    4 mai 2022 à 17:10:14

    Bonjour à tous, je suis entrain de coder un petit jeu de casse brique. Pour l'instant j'ai principalement 2 objets en interaction :


    Brick.cpp

    #include <iostream>
    
    #include "Brick.hpp"
    
    Brick::Brick() : width_(1), 
                     height_(1), 
                     color_(0),
                     position_(0,0),
                     number_(0),
                     disappear_(false)
    {}
    
    Brick::Brick(Brick const& _brick) 
                   : width_(_brick.width_), 
                     height_(_brick.height_), 
                     color_(_brick.color_),
                     position_(_brick.position_),
                     number_(_brick.number_),
                     disappear_(false)
    {}
    
    Brick::Brick(double _width, double _height, int _color, Vector _position, int _number)
                   : width_(_width), 
                     height_(_height), 
                     color_(_color),
                     position_(_position),
                     number_(_number),
                     disappear_(false)
    {}
    
    Brick::Brick(double _width, double _height, int _color,  double _p_x, double _p_y, int _number)
                   : width_(_width), 
                     height_(_height), 
                     color_(_color),
                     position_(_p_x,_p_y),
                     number_(_number),
                     disappear_(false)
    {}
    
    std::ostream& operator<<(std::ostream &_flow, Brick _brick)
    {
        _flow << "Width = "    << _brick.getWidth()  << std::endl;
        _flow << "Height = "   << _brick.getHeight() << std::endl;
        _flow << "Color = "    << _brick.getColor()  << std::endl;
        _flow << "Position = " << _brick.getPosition();
        _flow << "Disappear = " << _brick.getDisapppear() << std::endl;
    
        return _flow;
    }

    et Ball.cpp

    #include <iostream>
    #include <cmath>
    
    #include "Vector.hpp"
    #include "Ball.hpp"
    #include "Brick.hpp"
    #include "Wall.hpp"
    
    Ball::Ball() 
              : mass_(1),
                radius_(1),
                color_(0),
                position_(0,0), 
                velocity_(0,0),
                dt_collision_(100000, DEFAULT)
                
    {
        brickColl_ = new Brick();
    }
    
    Ball::Ball(double _mass, double _radius, int _color, Vector _position, Vector _velocity) 
              : mass_(_mass),
                radius_(_radius),
                color_(_color),
                position_(_position),
                velocity_(_velocity),
                dt_collision_(100000, DEFAULT)
    {
        brickColl_ = new Brick();
    }
    
    Ball::Ball(double _mass, double _radius, int _color, double _p_x, double _p_y, double _v_x, double _v_y, double _dt)
              : mass_(_mass),
                radius_(_radius),
                color_(_color),
                position_(_p_x, _p_y), 
                velocity_(_v_x, _v_y),
                dt_collision_(_dt, DEFAULT)
                
    {
        brickColl_ = new Brick();
    }
    
    Ball::~Ball()
    {
        delete brickColl_;
    }
    
    
    void Ball::setMass(double _mass)
    {
        mass_ = _mass;
    }
    
    void Ball::setRadius(double _radius)
    {
        radius_ = _radius;
    }
    
    void Ball::setColor(int _color)
    {
        color_ = _color;
    }
    
    void Ball::setPosition(Vector _position)
    {
        position_.setVector(_position);
    }
    
    void Ball::setPosition(double _p_x, double _p_y)
    {
        position_.setVector(_p_x, _p_y);
    }
    
    void Ball::setVelocity(Vector _velocity)
    {
        velocity_.setVector(_velocity);
    }
    
    void Ball::setVelocity(double _v_x, double _v_y)
    {
        velocity_.setVector(_v_x, _v_y);
    }
    
    void Ball::setBrick(Brick *_brickColl)
    {
        brickColl_ = _brickColl;
    }
    
    void Ball::dtballBrickColllision(Brick _brick)
    {   
        double px = position_.getX();
        double py = position_.getY();
        double vx = velocity_.getX();
        double vy = velocity_.getY();
    
        double h_brick = _brick.getHeight();
        double w_brick = _brick.getWidth();
        double p_brick_x = _brick.getPosition().getX();
        double p_brick_y = _brick.getPosition().getY();
    
        double dtx, dty;
        bool x_coll = false;
        bool y_coll = false;
    
        dtx = vx > 0 ? (p_brick_x - px - w_brick/2 - radius_)/vx
                     : (p_brick_x - px + w_brick/2 + radius_)/vx;
    
        dty = vy > 0 ? (p_brick_y - py - h_brick/2 - radius_)/vy
                     : (p_brick_y - py + h_brick/2 + radius_)/vy;
        
    
        x_coll = (dtx > 0 && vy*dtx + py >= p_brick_y - h_brick/2 - radius_ && vy*dtx + py <= p_brick_y + h_brick/2 + radius_);
        y_coll = (dty > 0 && vx*dty + px >= p_brick_x - w_brick/2 - radius_ && vx*dty + px <= p_brick_x + w_brick/2 + radius_);
        
        dtx = x_coll ? dtx : 100000;
        dty = y_coll ? dty : 100000;
    
        bool vertical = dtx < dty;
    
        vertical ? this->updateDt(dtx, BRICKVERT, &_brick) : this->updateDt(dty, BRICKHORI, &_brick);
    }
    
    void Ball::dtballWallColllision(Wall _wall)
    {
        double px = position_.getX();
        double py = position_.getY();
        double vx = velocity_.getX();
        double vy = velocity_.getY();
    
        double h_wall = _wall.getHeight();
        double w_wall = _wall.getWidth();
    
    
        double dtx, dty;
        bool x_coll = false;
        bool y_coll = false;
    
        dtx = vx > 0 ? (w_wall - px - radius_)/vx : (radius_ - px)/vx;
        dty = vy > 0 ? (h_wall - py - radius_)/vy : (radius_ - py)/vy;
    
        x_coll = dtx > 0 ? true : false;
        y_coll = dty > 0 ? true : false;
    
        dtx = x_coll ? dtx : 10000;
        dty = y_coll ? dty : 10000;
    
        bool vertical = dtx < dty;
    
        vertical ? this->updateDt(dtx, WALLVERT, NULL) : this->updateDt(dty, WALLHORI, NULL);
    }
    
    void Ball::updateBall(double dt)
    {
        Vector vel = velocity_ * dt;
        position_ += vel;
    }
    
    void Ball::velocityPostBrick(bool vertical)
    {
        vertical ? velocity_.setX(-velocity_.getX()) : velocity_.setY(-velocity_.getY());
    }
    
    void Ball::updateDt(double _dt, int TYPE, Brick* _brick)
    {
        dt_collision_.second = dt_collision_.first > _dt ? TYPE : dt_collision_.second;
        dt_collision_.first = dt_collision_.first > _dt ? _dt : dt_collision_.first;
        this->setBrick(_brick);
        std::cout << *(this->getBrickColl()) << std::endl;
    }
    
    void Ball::forward()
    {
        int TYPE = dt_collision_.second;
    
        updateBall(dt_collision_.first);
    
        switch (TYPE)
        {
        case WALLVERT:
            this->velocityPostBrick(true);
            break;
        case WALLHORI:
            this->velocityPostBrick(false);
            break;
        case BRICKVERT:
            this->velocityPostBrick(true);
            this->brickColl_->disappearSwitch();
            break;
        case BRICKHORI:
            std::cout << this->brickColl_ << std::endl;
            this->velocityPostBrick(false);
            std::cout << this->brickColl_ << std::endl;
            break;
        case BALL:
            
            break;
        case PADDLE:
            
            break;
        
        default:
            break;
        }
    
        dt_collision_.second = DEFAULT;
        dt_collision_.first = 30;
    }
    
    
    std::ostream& operator<<(std::ostream &_flow, Ball _ball)
    {
        _flow << "Mass = "     << _ball.getMass()     << std::endl;
        _flow << "Radius = "   << _ball.getRadius()   << std::endl;
        _flow << "Color = "    << _ball.getColor()    << std::endl;
        _flow << "Position = " << _ball.getPosition();
        _flow << "Velocity = " << _ball.getVelocity();
        _flow << "Dt coll = "  << _ball.getDt().first << " Type : " << _ball.getDt().second << std::endl;
    
        return _flow;
    }

    La chose qui me pose problème est l'attribut brickColl_ de Ball qui est un pointeur vers l'objet brick. Cet attribut pointe vers la prochaine brick avec laquelle la balle va collisionner et est update à chaque instant t dans updateDt(). Cependant là ets le problème, après avoir update, j'essaye de visualiser la prochaine brick avec laquelle la balle va entrer en collision mais j'obtiens une segmentation fault, signe que j'accède à une zone mémoire non allouée. Je ne comprends malheureusement pas pourquoi ce pointeur brickColl_ serait vide ou alors mal initialisé. Si vous avez des pistes je suis preneur


    • Partager sur Facebook
    • Partager sur Twitter
    0100001001101001011011100110000101101001011100100110010100001010
      4 mai 2022 à 17:43:02

      Avec le code que tu donnes, un premier moyen pour que brickColl_ soit invalide dans un object Ball valide, c'est si tu appelles setBrick avec une valeur invalide. C'est facile de vérifier avec un assert dans setBrick.

      Plus généralement, tu as un gros problème de non vérification des contrats de tes fonctions. Ajoute des asserts, pour garantir ces contrats (par exemple brickColl_ non null, position non negative, etc.)

      (Hors sujet : ca manque aussi beaucoup de const)

      Je sais pas a quoi sert setBrick, mais potentiellement, peut etre que Ball devrait avoir l'ownership de brickColl_ et donc que tu devrais utiliser un unique_ptr.

      Une autre possibilité, c'est que ton setBrick affecte un objet valide, mais que celui-ci n'est plus valide ensuite. Si c'est le cas, ca montre un probleme plus general d'ownership (qui est propriétaire des objets Brick ?).

      Une autre possibilité que je vois, c'est que c'est l'objet Ball lui même qui devient invalide. (Et donc que brickColl_ est invalidé en même temps, mais que l'erreur n'apparaît que pour brickColl_ et pas Ball).

      Il faut lancer en mode debug et voir la call stack et les valeurs des variables lors du crash.

      Donc globalement, un problème de qualité du code concernant la validité de tes pointeurs. Et une conception assez étrange ! Pourquoi Brick est copiable ? (tres clairement, c'est une sémantique d'entité). Pourquoi Ball contient une Brick ? Pourquoi Ball est chargé de calcul la future collision avec Brick ?

      • Partager sur Facebook
      • Partager sur Twitter
        4 mai 2022 à 18:07:02

        La ligne 211 est un excellent candidat à la segfault.

        La recette pointeurs bruts (/nus) + copie oubliée est une excellente source de problèmes.

        donc =>

        - abandonne définitivement les pointeurs bruts. unique_ptr exclusivement, cela t’obligera à réfléchir aux responsabilités.

        - arrête de passer tes paramètres par valeur, ça duplique les objets -> références constantes

        - A propos le constructeur de copie de Brick est excessivement louche, vu qu'un champs est overridé, alors qu'il est ensuité affiché depuis une fonction qui prend une copie de la brique. Ca n'a pas de sens.

        - (EDIT) sécurise les copies d'objets qui ont des pointeurs

        - A propos les constructeurs des briques devraient être factorisés (C++11!, sorti il y a 11ans)

        - Si je comprends bien il s'agit d'un casse-briques. Et que la balle soit responsable d'une brique n'a aucun sens pour moi.

        -
        Edité par lmghs 4 mai 2022 à 18:16:21

        • Partager sur Facebook
        • Partager sur Twitter
        C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
          4 mai 2022 à 18:31:38

          Plutôt que d’avoir plein d’objets éparpillés qui essaient péniblement de communiquer entre eux et où on sait pas qui fait quoi, pourquoi ne pas avoir une classe principale qui représente le niveau et qui gère la balle, les briques, les collisions, etc ?
          • Partager sur Facebook
          • Partager sur Twitter
            4 mai 2022 à 20:02:06

            C'est sûre que dans un casse-brick, les briques et la balle sont totalement indépendant, et n'en ont rien a cirer de que fait l'un et l'autre.

            C'est clairement à une entité supérieur (le jeux) de décider qui fait quoi en fonction de la situation:
            - deplacer la balle.
            - Verifier les collisions.
            - Faire disparaitre une brique.
            - ect ect ...

            • Partager sur Facebook
            • Partager sur Twitter

            Pointeur d'une classe vers une autre

            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
            • Editeur
            • Markdown