Parallax tutorial

Parallax, in 2D gaming, is the illusion of depth produced by scrolling multiple layers of sprites at different speeds. In this tutorial we'll be looking at the layer object and how it can be used to create parallax effects.

Using layers

Sprites can be grouped together using the layer object. This allows you to transform and apply visual effects to several sprites simultaneously. The contents of a layer can be rendered by the engine in a couple of ways. When we add some layer as a child to a specific viewport, the layer's contents are rendered within that viewport. It's also possible to render a layer or a section of it using cameras. In this tutorial, we'll stick to the former method.

display:create ( "Layers example", 800, 600, 32, true )

scene = Layer ( )
display.viewport:add_child ( scene )

-- creates two sprites, square and a circle
a = Sprite ( -100, 0 )
a.canvas:square ( 50 )
a.canvas:set_line_style ( 5, RED )
a.canvas:stroke ( )

b = Sprite ( 100, 0 )
b.canvas:circle ( 50 )
b.canvas:set_fill_style ( GREEN )
b.canvas:fill ( )

-- creates a layer and rotates it
l = Layer ( 0, 0 )
l.rotation = 45

-- adds the two sprites to the layer
l:add_child ( a )
l:add_child ( b )

scene:add_child ( l )
Download:  layers.lua

Each layer is a local coordinate system that can be transformed in different ways. Sprites added to a layer will have positions relative to the center or origin of that layer. In the example above, the two sprites are positioned in a horizontal line. However, they will appear at an angle because their parent layer is rotated by 45 degrees.

Parallax scrolling

How do we make a parallax scroller with multiple layers? Typically, each layer should be generated from a predefined tilemap. For the purposes of this tutorial however, we'll use a simpler approach. The "CreateLayer" function creates a new layer and fills it with a repeating sprite. We will be using four fairly large textures (512 by 512 pixels) in .png format. The .png textures we are loading have an alpha channel so we don't need to bother with transparency color keys. Transparency is important since you usually want one parallax layer to be rendered on top of another.


display:create ( "Parallax example", 640, 480, 32, true )

function CreateLayer ( image, depth )
  local layer = Layer ( )
  layer.depth = depth
  for x = -16, 16, 1 do
    local s = Sprite ( x * image.width, 0 )
    s.canvas:set_source_image ( image )
    s.canvas:paint ( )
    layer:add_child ( s )
  end
  display.viewport:add_child ( layer )
  return layer
end

desert = Image ( "Tutorials/tiles/desert.png" )
clouds = Image ( "Tutorials/tiles/clouds.png" )
sky = Image ( "Tutorials/tiles/sky.png" )
moon = Image ( "Tutorials/tiles/moon.png" )

foreground = CreateLayer ( desert, 0 )
middleground = CreateLayer ( clouds, 1 )
background1 = CreateLayer ( sky, 3 )
background2 = CreateLayer ( moon, 2 )

timer = Timer ( )
timer:start ( 32, true )

timer.on_tick = function ( timer )
  -- scroll the four layers at different speeds
  local delta = timer:get_delta ( )
  if keyboard:is_down ( KEY_LEFT ) then
    background2.x = background2.x + delta / 4
    background1.x = background1.x + delta
    middleground.x = middleground.x + delta * 2
    foreground.x = foreground.x + delta * 4
  elseif keyboard:is_down ( KEY_RIGHT ) then
    background2.x = background2.x - delta / 5
    background1.x = background1.x - delta
    middleground.x = middleground.x - delta * 2
    foreground.x = foreground.x - delta * 4
  end
end
Download:  parallax.lua
moon.png
sky.png
clouds.png
desert.png

Essentially, the parallax effect is produced by moving each layer at a different pace. Layers containing more distant objects are scrolled slower than those in the foreground. Each layer has a depth property which affects the rendering order. In this example, the "moon" layer is scrolling at the slowest pace yet it has a depth of 2 which renders it on top of the "sky" layer.