Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
991 views
in Technique[技术] by (71.8m points)

c++ - Qt - circles for collision detection

I've been working on a physics simulation with circles in Qt. Thus far the easiest way to define circles I found is to make a QRect object and then draw the ellipse with that rectangle as a "blueprint". Now I've just got the problem that it paints a circle but the hit box for the hit detection is still a square, which looks rather awkward. I've not been able to find a solution for it thus far and hope to find some help here.

QRectF Ball::boundingRect() const
{    
  return QRect(0,0,20,20);
}
void Ball::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QRectF rec = boundingRect();
    QBrush Brush(Qt::gray);

    //basic Collision Detection

    if(scene()->collidingItems(this).isEmpty())
    {
        //no collision
        Brush.setColor(Qt::green);
    }
    else
    {
        //collision!!!!!
        Brush.setColor(Qt::red);

        //Set position
        DoCollision();
    }

    //painter->fillEllipse(rec,Brush);
    painter->drawEllipse(rec);
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
QPainterPath QGraphicsItem::shape() const

Returns the shape of this item as a QPainterPath in local coordinates. The shape is used for many things, including collision detection, hit tests, and for the QGraphicsScene::items() functions.

The default implementation calls boundingRect() to return a simple rectangular shape, but subclasses can reimplement this function to return a more accurate shape for non-rectangular items.

For fine collision detection you have to use:

bool QGraphicsItem::collidesWithItem(const QGraphicsItem * other, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const

which you can also reimplement, because checking for collision between circles is faster than checking painterpaths for intersection.

I haven't used that myself, but it seems like the function you use will only give you "coarse detection" so then you must manually check if any of those actually intersect with the fine grained method. This saves on performance, you use the rough check to isolate potential collision candidates, and then check only those items using the slower method. In your case it is not a convenience, because a circle collision test would be as fast, if not faster than the bounding box test, but that's how Qt is designed. Ideally, you should be able to pass your own collision detection function to collidingItems().

Also last but not least, once you get the collidingItems list you can easily check for circle collisions on the spot, without using shape() and collidesWithItem()... It will actually save you some CPU time from not having to call extra virtual functions, plus the time to reimplement those...

So you can use something like this:

inline bool circCollide(QGraphicsItem * item, QList<QGraphicsItem *> items) {
    QPointF c1 = item->boundingRect().center();
    foreach (QGraphicsItem * t, items) {
        qreal distance = QLineF(c1, t->boundingRect().center()).length();
        qreal radii = (item->boundingRect().width() + t->boundingRect().width()) / 2;
        if ( distance <= radii ) return true; 
    }
    return false;
}

... and do it on the spot:

if (circCollide(this, collidingItems())) ... we have a collision

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...