Developing Multiplatform Game with LibGDX, part 14: screenshake and appearance

Screenshake

In our previous lesson, we made our character blink on hit. That feels better, but it’s not enough. Let’s make our screen shake to indicate how hurt the player is!

In our Character class, let’s make a public function that returns last time our character has been hurt:

public float getTimeOfDmgTaken()
{
    return timeOfDmgTaken;
}

We also need one getter to see time alive:

public float getTimeAlive()
{
    return timeAlive;
}

In our GameScreen.java, define two new constants:

private static final float SHAKE_TIME_ON_DMG = 0.3f;
private static final float SHAKE_DIST = 4.0f;

One is responsibe on how long the shake is going to happen, the other one for the intensity of shake (Shake Distance, amplitude). After we do that – we’re ready to start screenshake process! In our GameScreen render function, before DrawUi() call, we have to add the following lines:

gameStage.getCamera().position.set(gameStage.getWidth() / 2, gameStage.getHeight() / 2, 0);
if (player.getLives() > 0 &&
    player.getTimeAlive() - player.getTimeOfDmgTaken() < SHAKE_TIME_ON_DMG)

    gameStage.getCamera().translate(-(SHAKE_DIST/2) + MathUtils.random(SHAKE_DIST),
            -(SHAKE_DIST / 2) + MathUtils.random(SHAKE_DIST), 0);
}
gameStage.getCamera().update();

The sequence:

  1. Reset camera position
  2. If player has recently taken damage, move the camera. Note that player must be alive (otherwise we’ll get an infinite shake)
  3. Apply our camera movement

Run the game and take damage! The screen should be shaking after receiving damage now 🙂

Appearance at the start of the game

Right now our player and enemy both appear at the fixed position at the start of the game. Let’s make them appear from the side of the screen and then take the initial positions.

In our Player class, make a separate constant,

private static final float APPROACH_TIME = 0.5f;

Then, modify our draw method call.

public void draw(SpriteBatch batch, SizeEvaluator sizeEval)
{
    preDraw();

    if (timeAlive < APPROACH_TIME)
    {
        float t = timeAlive / APPROACH_TIME;
        setPosition((t * t) * sizeEval.getBaseScreenX(fieldX), sizeEval.getBaseScreenY(fieldY));
    }
    else
    {
        setPosition(sizeEval.getBaseScreenX(fieldX), sizeEval.getBaseScreenY(fieldY));
    }
    super.draw(batch);

    postDraw();
}

To explain: in the first 0.5 seconds, the player is going to seemingly move from left to right side.

Let’s do something different for our enemy. Why not make him increase in size (starting from very small)?

Modify our Enemy class, declare a constant:

private static float SCALE_TIME = 0.5f;

And modify our Enemy.draw method:

public void draw(SpriteBatch batch, SizeEvaluator sizeEval)
{
    preDraw();
    setPosition(sizeEval.getEnemyX(this), sizeEval.getEnemyY(this));
    if (timeAlive < SCALE_TIME)
    {
        float t = timeAlive / SCALE_TIME;
        t = t * t;
        setScale(t);
    }
    else
    {
        setScale(1);
    }
    super.draw(batch);
    postDraw();
}

Finally, make a small bugfix to prevent spawning on already existing places: in our gamelogic.java, in SpawnRandomBonus function, change for cycle from

for (int i = 0; i < bonuses.size() && targetNonEmpty; i++)

to

for (int i = 0; i < bonuses.size() && (targetNonEmpty == false); i++)

Run the game. You can see that both player and spider smoothly appear on the screen now.

Relevant Github commit: https://github.com/vladimirslav/dodginghero/commit/ab9d4e938bf8dde615bacbccdc87005672770ebc

Leave a Reply

Your email address will not be published. Required fields are marked *