What just happened?

For the instance of QPropertyAnimation created here, we define the item as a parent; thus, the animation will get deleted when the scene deletes the item, and we don't have to worry about freeing the used memory. Then, we define the target of the animation—our MyScene class—and the property that should be animated, jumpFactor, in this case. Then, we define the start and the end value of that property; in addition to that, we also define a value in between, by setting setKeyValueAt(). The first argument of the qreal type defines time inside the animation, where 0 is the beginning and 1 the end, and the second argument defines the value that the animation should have at that time. So your jumpFactor element will get animated from 0 to 1 and back to 0 in 800 milliseconds. This was defined by setDuration(). Finally, we define how the interpolation between the start and end value should be done and call setEasingCurve(), with QEasingCurve::OutInQuad as an argument.

Qt defines up to 41 different easing curves for linear, quadratic, cubic, quartic, quintic, sinusoidal, exponential, circular, elastic, back easing, and bounce functions. These are too many to describe here. Instead, take a look at the documentation; simply search for QEasingCurve::Type.

In our case, QEasingCurve::OutInQuad ensures that the jump speed of Benjamin looks like an actual jump: fast in the beginning, slow at the top, and fast at the end again. We start this animation with the jump function:

void MyScene::jump()
{
if (QAbstractAnimation::Stopped == m_jumpAnimation->state()) {
m_jumpAnimation->start();
}
}

We only start the animation by calling start() when the animation isn't running. Therefore, we check the animation's state to see whether it has been stopped. Other states could be Paused or Running. We want this jump action to be activated whenever the player presses the Space key on their keyboard. Therefore, we expand the switch statement inside the key press event handler using this code:

case Qt::Key_Space:
    jump();
    break; 

Now the property gets animated, but Benjamin will still not jump. Therefore, we handle the changes of the jumpFactor value at the end of the setJumpFactor function:

void MyScene::setJumpFactor(const qreal &jumpFactor)
{
//...
qreal groundY = (m_groundLevel - m_player->boundingRect().height()
/ 2);
qreal y = groundY - m_jumpAnimation->currentValue().toReal() *
m_jumpHeight;
m_player->setY(y);
//...
}

When our QPropertyAnimation is running, it will call our setJumpFactor() function to update the property's value. Inside that function, we calculate the y coordinate of the player item to respect the ground level defined by m_groundLevel. This is done by subtracting half of the item's height from the ground level's value since the item's origin point is in its center. Then, we subtract the maximum jump height, defined by m_jumpHeight, which is multiplied by the actual jump factor. Since the factor is in the range of 0 and 1, the new y coordinate stays inside the allowed jump height. Then, we alter the player item's y position by calling setY(), leaving the x coordinate as the same. Et voilà, Benjamin is jumping!