Game Programming using Qt 5 Beginner's Guide
上QQ阅读APP看书,第一时间看更新

What just happened?

First, we call the scene's QGraphicsScene::collidingItems() function, which takes the item for which colliding items should be detected as a first argument. With the second, optional argument, you can define how the collision should be detected. The type of that argument is Qt::ItemSelectionMode, which was explained earlier. By default, an item will be considered colliding with m_player if the shapes of the two items intersect.

Next, we loop through the list of found items and check whether the current item is a Coin object. This is done by trying to cast the pointer to Coin. If it is successful, we explode the coin by calling explode(). Calling the explode() function multiple times is no problem, since it will not allow more than one explosion. This is important since checkColliding() will be called after each movement of the player. So the first time the player hits a coin, the coin will explode, but this takes time. During this explosion, the player will most likely be moved again and thus collides with the coin once more. In such a case, explode() may be called multiple times.

The qgraphicsitem_cast<>() is a faster alternative to dynamic_cast<>(). However, it will properly work for custom types only if they implement type() properly. This virtual function must return a different value for each custom item class in the application.

The collidingItems() function will always return the background items as well, since the player item is above all of them most of the time. To avoid the continuous check if they actually are coins, we use a trick. Instead of using QGraphicsPixmapItem directly, we subclass it and reimplement its virtual shape() function, as follows:

QPainterPath BackgroundItem::shape() const {
  return QPainterPath();
} 

We already used the QPainterPath class in the previous chapter. This function just returns an empty QPainterPath. Since the collision detection is done with the item's shape, the background items can't collide with any other item since their shape is permanently empty. Don't try this trick with boundingRect() though, because it must always be valid.

Had we done the jumping logic inside Player, we could have implemented the item collision detection from within the item itself. QGraphicsItem also offers a collidingItems() function that checks against colliding items with itself. So scene->collidingItems(item) is equivalent to item->collidingItems().

If you are only interested in whether an item collides with another item, you can call collidesWithItem() on the item, passing the other item as an argument.