1

Link to project The interesting parts should be in gameengine.cpp's "launchSplash" -function and splashanimation.cpp

The game creates the bubbles randomly in the acceptable are. The player's job is to shoot the bubbles with water drops. The water drops are launched from the middle bottom part of the game screen. The grids are only used for debugging, and will later on be gone, but it makes visualizing the areas easier.

The bubbles are destroyed by shooting the water drop at them, but the water drop disappears when it hits a bubble or the upper boundary of the game. The water drop shoots to the direction of a mouse click.

I'm trying to create a collision detection for a basic bubble shooter game, but I'm not sure how I can detect the collision in a neat way.

The game board looks something like this game board, the water drops are shot from the middle bottom part of the screen to the direction of the cursor.

Eventually I'll have the water drop ricochet from the walls, but at the moment I'm contempt with figuring out how to detect collisions in the first place.

The game board is 500x600 units (width x height), so the point the water drop is shot at is (250, 600).

When the water drop is shot, I use

void GameEngine::launchSplash(int clickX, int clickY){
// <my long method of calculating the coordinates for the water drop's path>

    graphicalGameBoard_.animateSplash(graphicalGameBoard_.width()/2, graphicalGameBoard_.height(), xDestination, yDestination);
}

where xDestination and yDestionation are the place the water drop will end up if it travels unhindered. The water drop will either end up at x=0 / x=500 and/or y=0/y=600, but I don't think that's relevant.

The bubbles are added to the game board with

board_.clear();

for(int y = 0; y < HEIGHT; ++y)
{
    std::vector< std::shared_ptr<Bubble> > row;
    for(int x = 0; x < WIDTH; ++x)
    {
        std::shared_ptr<Bubble> newBubble = nullptr;

        // There will be bubbles only in the top 3/4 of the board only in the middle
        // (i.e. not in the first two and last two columns).
        if (y < HEIGHT*3/4 && x > 1 && x < WIDTH-2)
        {
            // Generate random numbers using the enumearation type defined in
            // file bubble.hh. The value COLOR_COUNT is used to represent no bubble.
            std::uniform_int_distribution<int> distribution(RED,COLOR_COUNT);

            // If you want no empty squares, change the initialization to:
            // std::uniform_int_distribution<int> distribution(RED,BLUE);

            Color color = static_cast<Color>(distribution(randomEngine_));
            if(color != COLOR_COUNT) {
                newBubble = std::make_shared<Bubble>(x, y, color);
            }
        }
        row.push_back(newBubble);
    }
    board_.push_back(row);
}

in the gameengine.cpp which draws the board and the water drop being shot at the bubbles.

The water drops are drawn using

SplashAnimation::SplashAnimation(GameBoard* scene, QPointF startPoint, QPointF endPoint):
    QVariantAnimation(0),
    scene_(scene),
    item_()
{
    scene_->addItem(&item_);

    // The animation runs for the given duration and moves the splash
    // smoothly from startpoint to endpoint.
    setDuration(2000);
    setKeyValueAt(0, QPointF(startPoint));
    setKeyValueAt(1, QPointF(endPoint));
}

I figured there are two ways to do this: either the inbuilt QT Collision detection or doing a separate calculation. I was not able to work with QT Collision and my attempts at detecting the collision manually did not really work out too well.

I already have a function for detecting bubble objects at certain cells, but it's in column/row instead of raw coordinates (500x600).

std::shared_ptr<Bubble> GameEngine::bubbleAt(int x, int y) const
{
    if (0 <= x and x < WIDTH and 0 <= y and y < HEIGHT){
        return board_.at(y).at(x);
    }
    else{
        return nullptr;
    }
}

Edit: Currently I'm trying to work on something like this, but I'm afraid it's going to be a bit heavy for the game since it iterates so much (or not?):

for (int i = 0; i<600;++i)
{
     xfract = (xDestination+250.0)/600.0;
     yfract = (600.0-yDestination)/600.0;
     xStep = xfract*i;
     yStep = yfract*i;
     if (xStep >= 50){
        thisX = xStep/50-5;
     }else{
         thisX=5;
     }
     if (yStep >= 50){
         thisY = 11-yStep/50 + 1;
     }else{
         thisY = 11;
     }
     thisX = abs(thisX);
    if (bubbleAt(thisX, thisY)!=nullptr){
        endX = xfract*i;
        endY = yfract*i;
        i = 600;
       std::cout << "collision at x: "<<thisX<< " y: "<<thisY<<std::endl;
       std::cout << "collision at x: "<<xStep<< " y: "<<yStep<<std::endl;
       std::cout << graphicalGameBoard_.width() << " " << graphicalGameBoard_.height()<<std::endl;
       removeBubble(thisX, thisY);
       graphicalGameBoard_.removeBubble(thisX, thisY);
       endY = 600-endY;
    }
}


graphicalGameBoard_.animateSplash(graphicalGameBoard_.width()/2, graphicalGameBoard_.height(), endX, endY);

I'm trying to split the steps into small fractions and check if the current step has a bubble in it until the water drop reaches the end or finds a bubble.

This works on my only one of my sides in terms of calculating, but the animation is off the mark and right side (x>250) collision detection doesn't work at all for some reason (it hits seemingly random bubbles at impossible locations on the right side).

Edit^2: Here are things I've tried in order to work with the actual collision detection of QT:

Within splashanimation.cpp, where the water drop is drawn using

SplashAnimation::SplashAnimation(GameBoard* scene, QPointF startPoint, QPointF endPoint):
    QVariantAnimation(0),
    scene_(scene),
    item_()
{
    scene_->addItem(&item_);


    // The animation runs for the given duration and moves the splash
    // smoothly from startpoint to endpoint.
    setDuration(2000);
    setKeyValueAt(0, QPointF(startPoint));
    setKeyValueAt(1, QPointF(endPoint));   
}

SplashAnimation::~SplashAnimation()
{
    scene_->removeItem(&item_);
}

void SplashAnimation::updateCurrentValue(QVariant const& value)
{
    item_.setPos(value.toPointF());
}

where scene is QGraphicsScene, and the parent of this contains the bubbles.

I've tried this on both the gameboard.cpp (which is parent for bubbles and the animation) and splash.cpp (which animates the water drop), but both give me the same compilation errors.

    QGraphicsItem::QGraphicsItem();

gives

error: cannot call constructor ?QGraphicsItem::QGraphicsItem? directly [-fpermissive] QGraphicsItem::QGraphicsItem(); ^

QList<QGraphicsItem *> list = collidingItems() ;

gives error: ?collidingItems? was not declared in this scope QList<QGraphicsItem *> list = collidingItems() ; ^

    QList<QGraphicsItem *> list = QGraphicsItem::collidingItems() ;

gives error: cannot call member function ?QList<QGraphicsItem*> QGraphicsItem::collidingItems(Qt::ItemSelectionMode) const? without object QList<QGraphicsItem *> list = QGraphicsItem::collidingItems() ; ^

I also tried adding arguments, but nothing I had the mind to try worked any better.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Grak
  • 27
  • 6
  • Why do not you use collidingItems to detect the collision? – eyllanesc May 02 '18 at 17:21
  • I tried looking for the way to properly use it, but it would not compile for me in any of the ways I tried. I don't know if it was because of QT updating and changing the way it's used or what, but I gave up on it initially. ```QList list = collidingItems() ;``` does not compile because "collidingItems()" is not defined, and I was not able to get any working arguments for ```QList list = QGraphicsItem::collidingItems() ;``` – Grak May 02 '18 at 17:29
  • It seems to me that you are not using it correctly, you could provide a [mcve] of your project, it is very difficult to find the error if you provide pieces of code that can not be reproduced. – eyllanesc May 02 '18 at 17:34
  • I lack the skills to reproduce it from scratch, but I can put a more detailed description of what I'm trying and what compilation errors I'm facing – Grak May 02 '18 at 17:36
  • If you share your project it would be easier to help, do you understand that creating a project consumes too much time and many discourage us? – eyllanesc May 02 '18 at 17:40
  • Yes, though I am 99% sure that the problem is that I'm just being a dumb ass at something regarding this. I edited the original post to include a bit of what I tried regarding the actual Qt method. And I can share the project with no problems, I just wanted to try figuring it out in a "neat way" – Grak May 02 '18 at 17:51
  • If you can not provide a [mcve] then you will never be ordered, you tell me when you share the link to your project. :D – eyllanesc May 02 '18 at 17:54
  • Added the link to the project now. I'm not avoiding the Minimal, Complete and Verifiable example on purpose, but because I've not been able to figure out how to either smoothly pour all the code into a single main when it's spread as wide as this or, how I should pick only the relevant parts while still maintaining the (non)functionality – Grak May 02 '18 at 18:04
  • you could explain the dynamics of the game, I was thinking is that you press a square and a bubble should appear below and go to the point where you press, and eliminate the circles that are on the way, am I right ?, but instead of I see that it removes a circle without touching it and goes to a different route to the point that it presses. – eyllanesc May 02 '18 at 18:32
  • I'll add this to the main post as well, but I'll explain it here as well: The game creates the bubbles randomly in the acceptable are. The player's job is to shoot the bubbles with water drops. The water drops are launched from the middle bottom part of the game screen. The grids are only used for debugging, and will later on be gone, but it makes visualizing the areas easier. The bubbles are destroyed by shooting the water drop at them, but the water drop disappears when it hits a bubble or the upper boundary of the game. The water drop shoots to the direction of a mouse click. – Grak May 02 '18 at 18:36
  • Okay, I understand, but the part of shooting in the direction of the point you press does not work correctly. – eyllanesc May 02 '18 at 18:38
  • Um, I'll reupload the file. I commented a part of the code in charge of it when I was trying to swap into Qt's own collision method. Edit: if you change the ``` graphicalGameBoard_.animateSplash(graphicalGameBoard_.width()/2, graphicalGameBoard_.height(), endX, 600-endY);``` into ``` graphicalGameBoard_.animateSplash(graphicalGameBoard_.width()/2, graphicalGameBoard_.height(), xDestination, yDestination);```, it should work, but I re-uploaded the project anyway – Grak May 02 '18 at 18:41
  • I was able to make the non-ricochet collision detection "work", but it's pretty ugly and does not use QT's inbuilt methodology, making the ricochet functionality a lot harder to implement (plus the non-ricochet functionality has some border cases where it detects it incorrectly) – Grak May 02 '18 at 20:54

1 Answers1

1

In this answer I am going to give you some recommendations that you use to implement the solution:

  • Avoid using the following instruction, use the signals that is one of the most powerful elements of Qt and that the event loop does the work.

    while (animations_.state() == QAbstractAnimation::Running)
    {
        QCoreApplication::processEvents(QEventLoop::AllEvents);
    }
    
  • QGraphicsScene works with coordinates that support the floating point, so the coordinates of the scene are handled with QPointF.

  • Using the above, if you are going to send point information use QPointF instead of int, int in the signals.

  • Use the methods that Qt provides, for example the following:

    if (0 <= clickPosition.x() and clickPosition.x() <= GRID_SIDE*WIDTH and
        0 <= clickPosition.y() and clickPosition.y() <= GRID_SIDE*HEIGHT)
    

It can be reduced to the following:

if(sceneRect().contains(event->scenePos()))

The advantage of that implementation is that it is more readable

I do not understand why you can not implement collidingItems, maybe it's the configuration of your .pro,

Use the following to add the gui module, core and widgets

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

Also to implement the animation use my previous answer

The complete and functional code can be found at the following link.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241