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

Time for action - Creating an item for Benjamin

Let's create a new Qt Widgets project and start making our game. Since the project will become more complex than our previous projects, we will not be giving you precise instructions for editing the code. If at any time you are unsure about the changes you make, you can look at the reference implementation provided with the book. It also contains the image files you can use to implement the game.

Let's now look at how we can mobilize Benjamin. First, we need a custom item class for him. We call the Player class and choose QGraphicsPixmapItem as the base class, because Benjamin is a PNG image. In the item's Player class, we further create a private field of integer type and call it m_direction. Its value signifies in which direction Benjamin walks—left or right—or if he stands still. Next, we implement the constructor:

Player::Player(QGraphicsItem *parent)
    : QGraphicsPixmapItem(parent)
    , m_direction(0)
{
    QPixmap pixmap(":/elephant");
    setPixmap(pixmap);
    setOffset(-pixmap.width() / 2, -pixmap.height() / 2);
}

In the constructor, we set m_direction to 0, which means that Benjamin isn't moving at all. If m_direction is 1, Benjamin moves right, and if the value is -1, he moves left. In the body of the constructor, we set the image for the item by calling setPixmap(). The image of Benjamin is stored in the Qt Resource system; thus, we access it through QPixmap(":/elephant"), with elephant as the given alias for the actual image of Benjamin. Finally, we use the setOffset() function to change how the pixmap is positioned in the item's coordinate system. By default, the origin point corresponds to the top-left corner of the pixmap, but we prefer to have it at the center of the pixmap so that applying transformations is easier.

When you are unsure of how to specify the path to your resource, you can ask Qt Creator about it. To do that, expand the  Resources branch in the project tree, locate the resource, and select the  Copy Path... entry in its context menu.

Next, we create a getter and setter function for the m_direction field:

int Player::direction() const {
    return m_direction;
}

void Player::setDirection(int direction)
{
    m_direction = direction;
    if (m_direction != 0) {
        QTransform transform;
        if (m_direction < 0) {
            transform.scale(-1, 1);
        }
        setTransform(transform);
    }
}

The direction() function is a standard getter function for m_direction returning its value. The setDirection() setter function additionally checks in which direction Benjamin is moving. If he is moving left, we need to flip his image so that Benjamin looks to the left, the direction in which he is moving. If he is moving toward the right, we restore the normal state by assigning an empty QTransform object, which is an identity matrix.

We cannot use QGraphicsItem::setScale here, because it only supports the same scale factors for x and y axes. Fortunately, setTransform() enables us to set any affine or perspective transformation.

So, we now have our item of the Player class for the game's character, which shows the image of Benjamin. The item also stores the current moving direction, and based on that information, the image is flipped vertically if needed.