Skip to content

Cameras in Love2D Part 3: Movement Bounds

Posted on:May 9, 2011

Because there was some interest in a part 3, of this series, I’ve written it, and in this part we’ll cover creating bounds that the camera can’t move beyond. Make sure you’ve read part 1 and part 2 before continuing.

In case you’re wondering what I mean by this, I mean restricting the movement of the camera to a “box”, as in, having minimum and maximum x/y coordinates for the camera. This comes in handy when you’re following players, for example, and you don’t want the camera to show any of the stuff beyond the level (usually blackness) when the player comes to an edge. Now of course, movement bounding can get much more complicated than a simple rectangle, you restrict it to certain paths and the like, but we’re going to keep it simple here.


We’re going to achieve this by clamping the x/y coordinates of the camera to the minimum and maximum x/y values given (the bounds). We’ll do this using a handy little clamping function, which I’ve named math.clamp:

function math.clamp(x, min, max)
  return x < min and min or (x > max and max or x)

That’s a very compact version however, you may want to use this version for more clarity, if you’re still getting to grips with some of the subtleties of Lua:

function math.clamp(x, min, max)
  if x < min then
    return min
  elseif x > max then
    return max
    return x

With that out of the way, we need to find a way to restrict the x/y values. We could do this only when they’re changed, by using a setter, or we could clamp them every single frame inside of camera:set. The latter way is much less efficient, so I’m going with the former. Because the new module is rather large, I’ve put the code in a Gist; click the link and take a look at camera.lua to get it. Take note that math.clamp needs to be defined somewhere.

Camera Module Code

Here’s what has changed from part 1’s camera module:

  • x/y values are now stored in camera._x/camera._y instead of camera.x/camera.y. Reference to those variables in the camera module’s methods have been changed accordingly.
  • We now use setX/setY to set the x/y positions.
  • setPosition now just calls setX and setY.
  • getBounds/setBounds has been added.

Now, I would much rather be using my metatable approach to setters, but that would needlessly complicate things; you can convert the camera to that if you want.

Anyway, how is this working? Well, setX and setY check if there is camera._bounds exists, and if so, they use the respective values to make sure the x/y position of the camera never exceeds the maximum values provided. We can setup the bounds using setBounds, which will create the camera._bounds table.

An Example

With that out of the way, I’m going to give you a quick example of how this might be used. In the same Gist I’ve put main.lua which contains the example code.

Example Code

love.load sets up tables for the player, the bounding walls (which is just box), and some random white boxes (used to see that you’re moving). It’s pretty obvious what love.update is doing, allowing movement of the player via the arrow keys and making the camera centre on the player. love.draw is just drawing the information contained in the tables.

Now for the important part. In love.load we call setBounds passing in a minimum x/y position of 0, 0, and a maximum of the width and height of the screen. 0, 0 comes from the x/y position of the box, but where does width, height come from? Well the bottom-right corner of the box is width * 2, height * 2. We only want the camera to as far as the bottom-right corner, and since the camera’s “width” and “height” is equal to the width and height of the screen, the maximum x/y position is equal to width * 2 - width, height * 2 - height, or boxWidth - cameraWidth, boxHeight - cameraHeight, which of course equals, width, height.


Well, that’s all folks. I hope you’ve learnt something, and thanks for reading. If you’ve got any thoughts/comments/suggestions, please leave them in the comments section. Also remember the camera module and example code is a available at gist #961685.