Lerping: 2D scrolling camera.

I wanted a nice smooth camera for a 2D top down RPG, you know, those ones that follow the player with a slight chase/lag behaviour.

I couldn’t really find a good clear example and a lot of the Google search results didn’t quite give me a full code example that I could figure things out how it should work and how it fits in to my game. As a result, I decided to write this brief post that captures what I have learnt on my voyage of discovery.

Follow that Player!!

Distance based approaches seemed to do what I wanted, but with too much player movement, the camera speed would continue to increase meaning the camera would shoot off in to the distance.  This is how you calculate the distance (in Pseudo code):

dx = player.x - camera.x 
dy = player.y - camera.y

This is how you might apply it to the camera:

camera.xScroll += dx * delta 
camera.yScroll += dy * delta

Now at first it appears to work, but after a while you’ll find that the camera will just shoot off at a crazy speed, and even if you try to reign it in by checking whether camera.xScroll == dx , it is likely that any tests will be missed due to the large increase in velocity of the camera.

Turns out what we actually need to do is a bit of lerping, otherwise known as LinEaR InterPolation.  This takes the directional velocity and smooths it with the camera position to find the current camera position at any given time.

I found a nice post here, which provided the basis for my camera code and was exactly what I needed:

Lerp my camera!!

To interpolate between two points you need the following formula:

(A * t) + (1 – t) * B;

In our case, A represents the directional velocity over a particular axis, i.e. the dx or dy (mentioned above), and B represents the current camera x or y-axis position (you could adapt this formula for vector multiplication).

Variable t represents the amount of interpolation we give to each of our parameters A and B, you can think of it as a percentage 0% – 100% (actually 0.0 – 1.0, because 0.0 = 0% and 1.0 = 100% in coding terms).

So, as the camera moves, A‘s influence will decrease in size (i.e. the directional velocity) and B‘s influence will increase, for example, as we increase the amount of interpolation t, each value A and B will receive a percentage of it.  So if A contributes 20% of its value to the final camera position, then to find B, we take :

100% – 20% (A‘s contribution) = 80%.

Therefore, B‘s contribution to the final camera position will be 80% at this particular time (whatever that may be).

The camera code

Here’s some example code, I’ve written it in Javascript, but you can probably convert it to any language you prefer.

Set up a camera object:

function Camera(Game){
    // The camera is set to the middle of the game window
    this.x = Game.canvas.width * 0.5;
    this.y = Game.canvas.height * 0.5;

    this.xScroll = 0;
    this.yScroll = 0;

    this.position = [this.xScroll, this.yScroll];
    this.prevPosition = [this.xScroll, this.yScroll];
    this.curPosition = [this.xScroll, this.yScroll];

    this.lerpAmount = 1.0; 
};

This is where the actual interpolation is done:

Camera.prototype.Lerp = function(A, B, t){
    return (A * t) + ((1.0 - t) * B);
};

The update method:

Camera.prototype.update = function(dt){
    // Get the distance from the player to the middle of the screen (our focal point)  
    this.dx = (Game.player.x - this.x);
    this.dy = (Game.player.y - this.y);
    // The camera is moving 
    this.position = [this.xScroll, this.yScroll];
    if(this.position != this.curPosition){
        this.lerpAmount = 0.0;
        this.curPosition = this.position;
    }
    // increase the speed of the camera if we are not at full camera speed
    if(this.lerpAmount < 1.0){
        this.lerpAmount += 0.05;
    } else {
        this.prevPosition = this.curPosition;
    }
    // Interpolate the  current position on the x-axis
    this.xScroll = this.Lerp(this.dx, this.curPosition[0], this.lerpAmount); 
    // Interpolate the current position on the y-axis
    this.yScroll = this.Lerp(this.dy, this.curPosition[1], this.lerpAmount); 
};

Don’t forget to deduct the camera scroll position from the player position as you would do with enemy entities and NPCs, when rendering to screen (here again I’m using Javascript and canvas), just deduct the camera scroll position from the player:

Player.prototype.draw = function(dt, context){
     context.fillStyle = "green";
        context.fillRect(
            this.x - Game.camera.xScroll,
            this.y - Game.camera.yScroll,
            this.width,
            this.height
    );
};

And there you have it, a nice smooth camera like that in Hyper Light Drifter (…to name a recent example. Can’t wait for it to be out it looks so good)!

Thanks for reading, and if you find anything that doesn’t make sense, or you could offer a better explanation, let me know via the comments.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s