Tutorial:Drawing Order

As LÖVE doesn't have a built-in z-ordering system, you may encounter some issues when trying to create a game that automatically draws a character in front or behind objects depending on their respective positions such as is customarily found in adventure games. This tutorial will show you how to do this in a very simplistic manner.

Note: In this tutorial, when I refer to an object being "in front" of the character I mean that it is between the character and the screen (ie: you). "Behind" means that the character is between the screen and the object.

First, we need some resources:

-- the background for our scene
scene = love.graphics.newImage("room.png")
-- the character we will be moving around
person = love.graphics.newImage("guy.png")
-- an object to move around
object = love.graphics.newImage("ball.png")

Alright, now that we have our resources we need some variables to contain the positions of our person and objects:

-- the character position
character = {400,400} -- x,y
 
-- a bunch of objects, each with a position
objects = {}
objects[1] = {550,370}
objects[2] = {220,390}
objects[3] = {600,410}
objects[4] = {300,450}
objects[5] = {400,530}

Make sure that your list of objects are ordered by their y-position. This becomes important when drawing them. For a general application, one can first use the Lua function table.sort.

function orderY(a,b)
  return a[2] < b[2]
end
table.sort(objects, orderY)

Although, You must be careful not to store the index to an object directly. As the objects index will change after the sort (You may wish to make a copy for drawing purposes only)

To draw these objects, we are going to use

love.graphics.draw(object, objects[i][1] - object:getWidth()/2, objects[i][2] -  object:getHeight())

The object parameter specifies the image we're going to draw and the next two arguments specify where the upper left corner of the image should be drawn on the screen. We subtract by half of the width and the height of the image to place the image's bottom horizontal center at object[i]'s specified x and y coordinate.

Ok, now we are ready to draw the whole shebang, assuming that you have created some way of moving your character around the screen (or else this is just pointless as you won't see the difference). If you don't know how to do that, I suggest looking at some of our other tutorials for assistance. For simplicity I am just going to show you the entire love.draw function and then explain what happens:

function love.draw() 
    love.graphics.draw(scene, love.graphics:getWidth() / 2 - scene:getWidth()/2,
        love.graphics:getHeight() / 2 - scene:getHeight() / 2) -- draw at the center of the screen
 
    local drawn = false -- true when the character has been drawn
 
    for i,v in ipairs(objects) do
        if not drawn and objects[i][2] > character[2] then
            love.graphics.draw(person, character[1] - person:getWidth()/2, character[2] - person:getHeight())
            drawn = true
        end
	love.graphics.draw(object, objects[i][1] - object:getWidth()/2, objects[i][2] - object:getHeight())
   end
 
   if not drawn then -- if the person is below all objects it won't be drawn within the for loop
      love.graphics.draw(person, character[1] - person:getWidth()/2, character[2] - person:getHeight())
   end
 
   -- any foreground objects go here
 
end

First of all we draw our scene as it normally is in the back (like any potential foreground objects will always be in the front). Then I create a variable called drawn. This will tell us if the character has been drawn to ensure that he isn't re-drawn multiple times which will cause him to be in front of objects where he shouldn't be. Next I go into a for loop and iterate through all of our objects (which are ordered from furthest away to closest). Then I check if the object is supposed to be in front of our character, if it is then you should draw the character (and set the drawn variable). The reason for this is because this for loop will go through and draw the objects (which are all the same image in this case, but that doesn't have to be so) until it comes to an object which should be in front of the chracter which is when it draws the character and then continues to draw the rest of the objects. If it comes to the end of the for loop and hasn't drawn the character yet it means that the character is in front of all the objects so it draws it at the end.

That's all. Download the demo and give it a go. It may be very simple but this tutorial is only about drawing order and if you want to know how to check if the character is colliding with the object or you want the character to go where you have clicked then you're on your own (until a tutorial is made for that). If this is not straightforward enough, please feel free to comment on it on our forum.

See Also

An alternative, scalable implementation : Skip list:Drawing Order



Other languages