Search code examples
cameralibgdxparallaxsmoothingjitter

Parallax effect jitter in libGDX


Basically I'm using this ParallaxCamera class to create the effect in my game, but upon movement, the layers "wiggle". This is especially noticeable when the camera is moving slowly.

I have fixed the timestep and use interpolated smoothing. I use scaled up pixel art. Camera centered on player, updated after movement. Disabling the effect makes moving the camera smooth.

What I guess the issues might be:

  • the layers move at different paces which means they move at different times

  • rounding to display makes the layers assume slightly different positions each frame when moving the camera

Thanks for any help


Solution

  • For low-resolution pixel art, this is the strategy I've used. I draw to a small FrameBuffer at 1:1 resolution and then draw that to the screen. That should take care of jittering.

    If your Stage is also at the same resolution, then you have to use a bit of a hack to get input to be processed properly. The one I've used it to use a StretchViewport, but I manually calculate the world width and height to not stretch the world, so I'm basically doing the same calculation that ExtendViewport does behind the scenes. You also have to round to an integer for the width and height. You should do this in resize and apply the width and height using viewport.setWorldWidth() and setWorldHeight(). So in this case it doesn't matter what world size you give to the constructor since it will be changed in update().

    When you call update on the viewport in resize, you need to do it within the context of the FrameBuffer you are drawing to. Otherwise it will mess up the screen's frame buffer dimensions.

    public void resize(int width, int height) {
    
        int worldWidth = Math.round((float)WORLD_HEIGHT / (float)height * (float)width);
        viewport.setWorldWidth(worldWidth);
        viewport.setWorldHeight(worldHeight);
        frameBuffer.begin();
        viewport.update(width, height, true); // the actual screen dimensions
        frameBuffer.end();
    
    }
    

    You can look up examples of using FrameBuffer in LibGDX. You should do all your game drawing in between frameBuffer.begin() and end(), and then draw the frameBuffer's color buffer to the screen like this:

    stage.act();
    frameBuffer.begin();
    //Draw game
    stage.draw();
    frameBuffer.end();
    spriteBatch.setProjectionMatrix(spriteBatch.getProjectionMatrix().idt());
    spriteBatch.begin();
    spriteBatch.draw(frameBuffer.getColorBufferTexture(), -1, 1, 2, -2);
    spriteBatch.end();
    

    In my case, I do a more complicated calculation of world width and world height such that they are a whole number factor of the actual screen dimensions. This prevents the big pixels from being different sizes on the screen, which might look bad. Alternatively, you can change the filtering of the FrameBuffer's texture to linear and use an upscaling shader when drawing it.