UndefinedDuck
UndefinedDuck

Reputation: 35

Move a QGraphicsPixmapItem through a function

I'm creating a chess game with Qt and now I'm going to add the AI to play against the computer, but I have a big problem... I used the mouseReleaseEvent() function to move the pieces by drag and drop, so now I don't know how to move them without it. Surely I have to create a function that takes the coordinates in which to move the piece, but then how to make it move? I suppose I need to explain you a bit more about the game, so here there is the essential code to let you understand how it works:

Piece.h

class Piece : public QGraphicsPixmapItem
{
public:
    Piece(QGraphicsItem *parent = nullptr);

    void setPieceScene(QGraphicsScene *graphicsScene) { pieceScene = graphicsScene; }
    QGraphicsScene *getPieceScene() { return pieceScene; }
    void setPieceName(QString pieceName) { name = pieceName; }
    QString getPieceName() { return name; }
    void setPrevPos(QPointF pos) { prevPos = pos; }
    QPointF getPrevPos() { return prevPos; }

    bool leftStart = false;      
protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
private:
    QString name;
    QGraphicsScene *pieceScene;
    QPointF prevPos;
 };

In MainWindow.cpp, when I start the game, I set the pieces

void MainWindow::start()
{
    scene.setSceneRect(-400, -250, 1024, 768);

    Piece* pieces = new Piece[32];
    ChessBoard::setBoard(scene, pieces);
}

In ChessBoard.cpp I draw the chessboard on the scene and add the pieces on it

void ChessBoard::setBoard(QGraphicsScene &scene, Piece piece[])
{
   setSquares(scene); //just drawing the squares by adding QGraphicsRectItem
   
   //...
   piece[8].setPixmap(QPixmap(":/images/rook_w.svg").scaled(80, 80));
   piece[8].setPos(-200, 360);
   piece[8].setPieceName("W_Rook");
   scene.addItem(&piece[8]);
   // and so on for the other pieces

}

Finally in Piece.cpp I move them by drag and drop

void Piece::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
        
        Piece::setPrevPos(Piece::pos()); 
}

void Piece::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
     QPointF lastPoint = mapToScene(event->lastPos());
     Piece::setPos(lastPoint.x() - 40, lastPoint.y() - 40);
}

void Piece::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    Game* game = Game::getInstance();
    game->setTurn();

    QList<QGraphicsItem*> colliders = Piece::collidingItems();
    int prevRow, prevCol, nextRow, nextCol;

    //...other checks for the capture before this, but it's not important
    if (colliders.length() == 1) //1 collider means it's a square
    {
      if ((this->name == "W_Pawn" && game->turn=="white") || (this->name == "B_Pawn" && game->turn=="black"))
        {
          
            if (Pawn::pawnMove(this, colliders[0], defaultBoardState))
              //here I check if the move is valide, then the drop is accepted
              //...same thing for the other pieces
        }
    }

    else 
    {
        this->setPos(this->prevPos);
    }
}

defaultBoardState is a simple global int matrix[8][8] with the values of the pieces and I update it when a move is done. So we are now at the problem: in Game.cpp I run the AI, let's say it decides to do this move

void Game::run()
{

    defaultBoardState[3][4] = -1;
    defaultBoardState[1][4] = 0;
        
    emit aiMoved();
    
}

The signal is connected to the MainWindow function that should move that piece (I wrote the function here cause the scene is a QGraphicsScene variable in the MainWindow class and I can easly access to it here)

connect(game, SIGNAL(aiMoved()), this, SLOT(aiMove()));

void MainWindow::aiMove()
{
    
    //here I don't know how to make it move

}

I was thinking about to make the pieces as global, so I can access to them in this function and do something like piece[1].setPos() in that position, but I can't make a QGraphicsPixmapItem as global. How can I create a function that moves the pieces only knowing the piece to move and the destination position?

Upvotes: 1

Views: 99

Answers (1)

You need to retain the pointers to the pieces in MainWindow. Something like:

class MainWindow : public (whatever, most likely should be QWidget not QMainWindow) {
  Piece *pieces[8*4] = {}; // so they are null be default

  ...
};

void MainWindow::start()
{
    scene.setSceneRect(-400, -250, 1024, 768);

    for (auto *& piece: m_pieces)
         piece = new Piece;

    ChessBoard::setBoard(scene, m_pieces);
}

And also: do not use global variables. They are unnecessary, they are problematic, and they hide coupling between modules in your program. You need to pass stuff explicitly, since that makes you actually think what goes where, and you have to do some design work, instead of just making everything available everywhere.

It is extremely hard to reason about such code. It's as if you would leave your car at a service station and give them the key to your house just in case they wanted to ask you a question. No, if they want to ask, they must go through a well defined interface: they call you, text you, or email you, or send a carrier pigeon. Whatever it is, it needs to be consciously chosen. You have to think about it and make a decision.

Upvotes: 1

Related Questions