The last part of this chapter is dedicated to two enhancements of the game screen's GUI. Firstly, we will add a small animation that gives visual feedback to the player when a life is lost. Secondly, a counting-up animation for the player's score will be implemented.
We want to play a small animation in the event when the player has just lost a life. The extra lives are shown as bunny heads in the top-right corner of the game screen. These icons become dark one after another as soon as another life has been lost. The animation we are aiming for is a temporary bunny head icon on top of the just lost extra life. The temporary icon is going to be scaled up, rotated, and will have a slightly red tint.
The following screenshot is an example of the animation:
Add the following line to the WorldController
class:
public float livesVisual;
After this, make the following changes to the same class:
private void init () { Gdx.input.setInputProcessor(this); cameraHelper = new CameraHelper(); lives = Constants.LIVES_START; livesVisual = lives; timeLeftGameOverDelay = 0; initLevel(); } public void update (float deltaTime) { handleDebugInput(deltaTime); if (isGameOver()) { timeLeftGameOverDelay -= deltaTime; if (timeLeftGameOverDelay< 0) backToMenu(); } else { handleInputGame(deltaTime); } level.update(deltaTime); testCollisions(); cameraHelper.update(deltaTime); if (!isGameOver() &&isPlayerInWater()) { lives--; if (isGameOver()) timeLeftGameOverDelay = Constants.TIME_DELAY_GAME_OVER; else initLevel(); } level.mountains.updateScrollPosition(cameraHelper.getPosition()); if (livesVisual> lives) livesVisual = Math.max(lives, livesVisual - 1 * deltaTime); }
We have introduced a new variable livesVisual
that will contain pretty much the same information as lives. However, livesVisual
will only decrease slowly over time whenever the lives are decreased. This enables us to play an animation as long as livesVisual
has not yet reached the current value of lives.
Additionally, add the following import line to the WorldRenderer
class:
import com.badlogic.gdx.math.MathUtils;
Next, make the following changes to the same class:
private void renderGuiExtraLive (SpriteBatch batch) { float x = cameraGUI.viewportWidth – 50 Constants.LIVES_START * 50; float y = -15; for (int i = 0; i<Constants.LIVES_START; i++) { if (worldController.lives<= i) batch.setColor(0.5f, 0.5f, 0.5f, 0.5f); batch.draw(Assets.instance.bunny.head, x + i * 50, y, 50, 50, 120, 100, 0.35f, -0.35f, 0); batch.setColor(1, 1, 1, 1); } if (worldController.lives>= 0 &&worldController.livesVisual>worldController.lives) { int i = worldController.lives; float alphaColor = Math.max(0, worldController.livesVisual - worldController.lives - 0.5f); float alphaScale = 0.35f * (2 + worldController.lives - worldController.livesVisual) * 2; float alphaRotate = -45 * alphaColor; batch.setColor(1.0f, 0.7f, 0.7f, alphaColor); batch.draw(Assets.instance.bunny.head, x + i * 50, y, 50, 50, 120, 100, alphaScale, -alphaScale, alphaRotate); batch.setColor(1, 1, 1, 1); } }
The added code will draw a temporary bunny head icon that is changed in its alpha color, scale, and rotation over time to create the animation. The progress of the animation is controlled by the current value in livesVisual
.
Every time the player collects an item, a reward is given that is added to the overall game score. The current score and a gold coin icon are shown in the top-left corner of the game screen. We want to add two subtle effects that begin to play when an increased score is detected. Firstly, we want the score to slowly add up to the new score. Secondly, the gold coin icon will shake a bit while the score is still adding up.
Here is a screenshot of the combined animation in five steps where some items have been collected:
Add the following line to the WorldController
class:
public float scoreVisual;
Next, make the following changes to the same class:
private void initLevel () { score = 0; scoreVisual = score; level = new Level(Constants.LEVEL_01); cameraHelper.setTarget(level.bunnyHead); } public void update (float deltaTime) { ... level.mountains.updateScrollPosition(cameraHelper.getPosition()); if (livesVisual> lives) livesVisual = Math.max(lives, livesVisual - 1 * deltaTime); if (scoreVisual< score) scoreVisual = Math.min(score, scoreVisual + 250 * deltaTime); }
We introduced the new variable scoreVisual
, which serves the same purpose as livesVisual
does to control the progress of the score animation.
Additionally, make the following changes to the WorldRenderer
class:
private void renderGuiScore (SpriteBatch batch) { float x = -15; float y = -15; float offsetX = 50; float offsetY = 50; if (worldController.scoreVisual<worldController.score) { long shakeAlpha = System.currentTimeMillis() % 360; float shakeDist = 1.5f; offsetX += MathUtils.sinDeg(shakeAlpha * 2.2f) * shakeDist; offsetY += MathUtils.sinDeg(shakeAlpha * 2.9f) * shakeDist; } batch.draw(Assets.instance.goldCoin.goldCoin, x, y, offsetX, offsetY, 100, 100, 0.35f, -0.35f, 0); Assets.instance.fonts.defaultBig.draw(batch, "" + (int)worldController.scoreVisual, x + 75, y + 37); }
The value in scoreVisual
is cast to an integer value to cut off the fraction. The resulting intermediate value will be the score that is shown in the GUI for the counting-up animation. To let the coin icon shake, we use a sine function with different factors as input angles to find the offset for the temporary displacement of the icon.