Home > Blockchain >  Preventing the timer from updating the counter variable for more than once
Preventing the timer from updating the counter variable for more than once

Time:05-09

I have a qt application with cpp. The application is Falling ball game, when the ball is catched by basket the score needs to be incremented. The application uses some timerEvent to update the score. The slot for timeout is called for every 10 msec. My problem is the score is updated more than once and is very random in its incrementation. How can I solve this problem.

MainWindow.cpp

  MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    qDebug() <<"mainWindow constructor";
    basket = new Basket(this);
    sprite = new Sprite(this);

     timer=new QTimer(this);
     connect(timer, &QTimer::timeout, this, &MainWindow::onTimer);
     timer->start(3000);
     scoreTimer=new QTimer(this);
     connect(scoreTimer, &QTimer::timeout, this, &MainWindow::onScoreTimer);
     scoreTimer->start(10);
     timerIdForScoreTimer=scoreTimer->timerId();
     score = 0;
}

MainWindow::~MainWindow()
{
    qDebug() <<"mainWindow destructor";
    delete ui;
}

void MainWindow::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.fillRect(rect(), QBrush(QColor(Qt::white)));
    painter.setPen(Qt::black);
    painter.setBrush(QBrush(QColor(Qt::darkBlue)));
    emit draw(painter);
}

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    if(event->key() == Qt::Key_Right)
    {
        emit move(1);
    }
    if(event->key() == Qt::Key_Left)
    {
        emit move(0);
    }
}


void MainWindow::on_actionStart_triggered()
{
    connect(this, &MainWindow::draw, sprite, &Sprite::drawBall);
    connect(this, &MainWindow::draw, basket, &Basket::drawBar);
    connect(this, &MainWindow::move, basket, &Basket::move);
}


void MainWindow::onTimer()
{
    std::cout << "Tick!-----------------------------------------------------------------------------" << std::endl;
    sprite = new Sprite(this);
    connect(this, &MainWindow::draw, sprite, &Sprite::drawBall);
}

void MainWindow::onScoreTimer()
{
//    qDebug() <<"Timer event called in Mainwindow.cpp";
    if( (((sprite->y - 15) >= 500)&& sprite->y <530 ) && ( (sprite->x <= (basket->x1   80)) && (sprite->x >= (basket->x1 - 80)) ) )
    {
//         qDebug("x and y position when condition is met and also x1 and y1");
//         qDebug()<<x;
//         qDebug()<<y;
//         qDebug()<<x1;
//         qDebug()<<y1;
         sprite->dy *= -1;
         sprite->dx *=  -1;
        qDebug() << "score update";
        qDebug() <<score;
        score   ;
//         ui->lcdNumber->setDigitCount(score);
        ui->score_label->setText(QVariant(score).toString());
    }
}

In MainWindow::onScoreTimer() the "score" variable is incremented randomly and more than once. How can I prevent this.

CodePudding user response:

So with all the debugging cruft removed, onScoreTimer() looks like this:

void MainWindow::onScoreTimer()
{
   if( (((sprite->y - 15) >= 500)&& sprite->y <530 ) && ( (sprite->x <= (basket->x1   80)) && (sprite->x >= (basket->x1 - 80)) ) )
   {
      sprite->dy *= -1;
      sprite->dx *= -1;
      score  ;
      ui->score_label->setText(QVariant(score).toString());
   }
}

It reverses the sprite's direction, increments the score, and updates the score_label. All well and good, but note that what it doesn't do is anything that would prevent that same if-test (at the top of the method) from succeeding again the next time onScoreTimer() is called.

Therefore, it's entirely likely that when the sprite reaches the target area, onScoreTimer() (which is called every 10mS) will do all of the above many times in a row, changing the sprite's direction every time, and increasing the score quite a bit as the sprite vibrates back-and-forth.

As for how to avoid that behavior, I can think of two possible solutions:

  1. Along with what you're doing inside the above block of code, also change sprite->x and sprite->y to some location outside of the target-area, so that the next call to onScoreTimer won't trigger the same routine again.

or

  1. Add a boolean member-variable to track the current in-target/not-in-target state of the sprite, and only run the routine when that variable's state changes from false to true

e.g.:

class MainWindow {
[...]
private:
   bool spriteWasInTarget = false;
};

void MainWindow::onScoreTimer()
{
   const bool spriteIsInTarget = ( (((sprite->y - 15) >= 500)&& sprite->y <530 ) && ( (sprite->x <= (basket->x1   80)) && (sprite->x >= (basket->x1 - 80)) ) );
   if ((spriteIsInTarget)&&(!spriteWasInTarget))
   {
      sprite->dy *= -1;
      sprite->dx *= -1;
      score  ;
      ui->score_label->setText(QVariant(score).toString());
   }
   spriteWasInTarget = spriteIsInTarget;
}

... so that way the routine only executes on the first call after the sprite (re)enters the target-area.

  • Related