【问题标题】:QT collision detection between 2 QGraphicsPixmapItems2个QGraphicsPixmapItems之间的QT碰撞检测
【发布时间】:2018-05-02 15:21:32
【问题描述】:

Link to project 有趣的部分应该在gameengine.cpp的“launchSplash”-function和splashanimation.cpp中

游戏在可接受的范围内随机创建气泡。玩家的工作是用水滴射出泡泡。水滴从游戏画面的中下部分发射。网格仅用于调试,稍后将消失,但它使区域的可视化更容易。

通过向气泡射击水滴来破坏气泡,但是当水滴击中气泡或游戏的上边界时会消失。水滴朝鼠标点击的方向射去。

我正在尝试为基本的泡泡射击游戏创建碰撞检测,但我不确定如何以简洁的方式检测碰撞。

游戏板看起来像这样game board,水滴从屏幕的中底部向光标的方向射出。

最终我会让水滴从墙上弹射出来,但目前我很不屑于弄清楚如何首先检测碰撞。

游戏板是 500x600 单位(宽 x 高),所以水滴的射击点是 (250, 600)。

水滴拍的时候,我用

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);
}

xDestinationyDestionation 是水滴在不受阻碍的情况下最终落下的地方。水滴最终会在 x=0 / x=500 和/或 y=0/y=600,但我认为这无关紧要。

气泡被添加到游戏板上

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);
}

gameengine.cpp 中绘制棋盘,水滴射向气泡。

水滴是用

绘制的
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));
}

我认为有两种方法可以做到这一点:内置 QT 碰撞检测或单独计算。我无法使用 QT Collision,并且我手动检测碰撞的尝试并没有真正奏效。

我已经有一个在某些单元格检测气泡对象的功能,但它在列/行而不是原始坐标 (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;
    }
}

编辑:目前我正在尝试做这样的事情,但我担心它对游戏来说会有点沉重,因为它迭代了很多(或没有?):

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);

我正在尝试将步骤分成小部分,并检查当前步骤中是否有气泡,直到水滴到达末端或找到气泡。

这在我的计算方面仅适用于我的一方,但动画偏离了标记,右侧 (x>250) 碰撞检测由于某种原因根本不起作用(它在右侧不可能的位置击中看似随机的气泡)。

Edit^2:为了配合 QT 的实际碰撞检测,我尝试了以下方法:

在 splashanimation.cpp 中,使用

绘制水滴
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());
}

其中场景是 QGraphicsScene,this 的父级包含气泡。

我在 gameboard.cpp(它是气泡和动画的父级)和 splash.cpp(它为水滴设置动画)上都试过这个,但都给了我相同的编译错误。

    QGraphicsItem::QGraphicsItem();

给了

错误:不能调用构造函数?QGraphicsItem::QGraphicsItem?直接[-fpermissive] QGraphicsItem::QGraphicsItem(); ^

QList<QGraphicsItem *> list = collidingItems() ;

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

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

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

我也尝试添加参数,但没有什么比我想尝试的效果更好的了。

【问题讨论】:

  • 为什么不用collidingItems来检测碰撞呢?
  • 我尝试寻找正确使用它的方法,但它不会以我尝试的任何方式为我编译。我不知道是不是因为 QT 更新和改变了它的使用方式还是什么,但我最初放弃了它。 QList&lt;QGraphicsItem *&gt; list = collidingItems() ; 无法编译,因为未定义“collidingItems()”,我无法获得 QList&lt;QGraphicsItem *&gt; list = QGraphicsItem::collidingItems() ; 的任何工作参数
  • 在我看来您没有正确使用它,您可以提供您项目的minimal reproducible example,如果您提供无法复制的代码,很难找到错误.
  • 我缺乏从头开始复制它的技能,但我可以更详细地描述我正在尝试的内容以及我面临的编译错误
  • 如果你分享你的项目会更容易提供帮助,你知道创建一个项目会耗费太多时间,很多人会劝阻我们吗?

标签: c++ qt qt5 qgraphicspixmapitem


【解决方案1】:

在这个答案中,我将为您提供一些用于实施解决方案的建议:

  • 避免使用以下指令,使用信号是 Qt 最强大的元素之一,并且事件循环可以完成工作。

    while (animations_.state() == QAbstractAnimation::Running)
    {
        QCoreApplication::processEvents(QEventLoop::AllEvents);
    }
    
  • QGraphicsScene 使用支持浮点的坐标,因此场景的坐标使用QPointF 处理。

  • 使用上述方法,如果您要发送点信息,请在信号中使用 QPointF 而不是 int, int

  • 使用 Qt 提供的方法,例如:

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

可以简化为:

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

这种实现的优点是它更具可读性

我不明白你为什么不能实现collidingItems,可能是你的.pro的配置,

使用以下添加gui模块、核心和小部件

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

还要实现动画使用我以前的answer

完整的功能代码可以在以下link找到。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-18
    • 2015-10-07
    • 2018-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多