LibGDX Screen Shaking

Recently I was looking into options on how to shake screens using LibGDX. Initially I went looking for existing code samples by others and found several variations to attempt.

After trying them out, I did end up using a variation of one, but not entirely on its own. This article is a quick review on what each variation looked like in my game which hopefully will be of some help in your own LibGDX games.

My first attempt was using a simple algorithm that looks like this:


/**
 * Start the screen shaking with a given power and duration
 * @param intensity How much intensity should the shaking use.
 * @param duration Time in milliseconds the screen should shake.
 */
public void shake(float intensity, float duration) {
    this.elapsed = 0;
    this.duration = duration / 1000f;
    this.intensity = intensity;
}

/**
 * Updates the shake and the camera.
 * This must be called prior to camera.update()
 */
public void update(float delta, OrthographicCamera camera) {

    // Only shake when required.
    if(elapsed < duration) {

        // Calculate the amount of shake based on how long it has been shaking already
        float currentPower = intensity * camera.zoom * ((duration - elapsed) / duration);
        float x = (random.nextFloat() - 0.5f) * 2 * currentPower;
        float y = (random.nextFloat() - 0.5f) * 2 * currentPower;
        camera.translate(-x, -y);

        // Increase the elapsed time by the delta provided.
        elapsed += delta;
    }
}

In order to use the code above, you place it into a render() method for your map (or where ever you do rendering updates). For my camera, I also had to update the sprite batch prior to the camera.update() in order for the changes to be applied correctly. The final calling code looks like this in my render() method:

    screenShake.update(delta, camera);
    spriteBatch.setProjectionMatrix(camera.combined);
    camera.update();

Once the shake is completed, you then have to ensure you return to the original screen position, or your camera will be out of sync and things won’t look right anymore. I ended up saving the original position of the camera in a baseX and baseY variables and then when the shaking was completed, you simply assign them back to the camera and update it.

    camera.position.x = baseX;
    camera.position.y = baseY;
    spriteBatch.setProjectionMatrix(camera.combined);
    camera.update();

This worked as it suggested and causes the screen to shake pretty nicely. I didn’t quite like the result though because in my game the shaking seemed a bit too jagged and overly random for me. Also, due to the constant changing of position, you never know where the camera will be when you reach the end of the shake, so the “reset position” can quite often end up being a large jump on the screen when it returns to the original position.

Here’s a quick GIF of what the above code produced in my game. As you can see, it shakes pretty nicely, but things look a bit jagged and once completed there is a “jump” once it returns to the original position.

ShakingAttempt1

I decided to put this variation aside for a moment and wanted to try another one I found also. This one uses a radius algorithm to create a more “smooth” feel to shaking. Using their algorithm, I updated the shaking methods to be the following:


/**
 * Start the screen shaking with a given power and duration
 *
 * @param radius The starting radius for the shake. The larger the radius, the large the shaking effect.
 * @param duration Time in milliseconds the screen should shake.
 */
public void shake(float radius , float duration) {

    this.elapsed = 0;
    this.duration = duration / 1000f;
    this.radius = radius ;
    this.randomAngle = random.nextFloat() % 360f;
}

/**
 * Updates the shake and the camera.
 * This must be called prior to camera.update()
 */
public void update(float delta, OrthographicCamera camera) {

    // Only shake when required.
    if(elapsed < duration) {

        // Calculate the shake based on the remaining radius.
        radius *= 0.9f; // diminish radius each frame
        randomAngle += (150 + random.nextFloat() % 60f);
        float x = (float) (Math.sin(randomAngle) * radius);
        float y = (float) (Math.cos(randomAngle) * radius);
        camera.translate(-x, -y);

        // Increase the elapsed time by the delta provided.
        elapsed += delta;
    }
}

The code to call these methods did not change and we still call a “reset position”, however due to the reducing of the radius upon each call, when the shaking is completed we are really close (if not already) back to the original position. Thus there is no longer a “jump” at the end.

This creates a nice, smooth, shaking on the screen, but not really the one I was looking for. This produces something more like a “swirling” look rather than a shake. Here’s a GIF showing what the above code produced:

ShakingAttempt2

I decided to put this code aside since it wasn’t quite what I wanted, but I’m keeping it around for later in case I want to use a swirling effect later. I then returned back to the first attempt to see what I could to to try and reduce the jumping issue. I decided instead of allowing the camera to be adjusted from the previous shake position, I would make it start from the original position each time instead.

This creates less intensity between each shake, but allows us to make something far less jagged as a result. Once completed, like the radius version, we’re already close to or on the original position so calling the reset doesn’t create a visual jump.

Here is the final version of what I’ve ended up using for now:

public void update(float delta, OrthographicCamera camera) {

    // Only shake when required.
    if(elapsed < duration) {

        // Calculate the amount of shake based on how long it has been shaking already
        float currentPower = intensity * camera.zoom * ((duration - elapsed) / duration);
        float x = (random.nextFloat() - 0.5f) * currentPower;
        float y = (random.nextFloat() - 0.5f) * currentPower;
        camera.translate(-x, -y);

        // Increase the elapsed time by the delta provided.
        elapsed += delta;
    }
}

Then, when you go to call the update on shake, you reset back to the original position before updating. This results in the render() code looking like this:

    // Return back to the original position each time before calling shake update.
    // We don't update the batch here since we're only using the position for calculating shake.
    camera.position.x = baseX;
    camera.position.y = baseY;

    // Update the shake position, then the camera.
    screenShake.update(delta, camera);
    spriteBatch.setProjectionMatrix(camera.combined);
    camera.update();

This results in a more subtle shaking effect and closer to what I was aiming for. Here’s a couple GIF’s showing the final version in game:

FlurryArrowsShake

CaveExplosionShake

Hope this helps anyone wanting to try out screen shaking in their LibGDX project. If you have any questions, feel free to reach out.

Thanks for reading!

0