Animation tutorial

In this tutorial, we are going to implement sprite animation in Lua. There are two basic ways to store your animations. You can either have each frame saved as a separate image file or use sprite sheets which are sequences of frames saved in a single image. In this tutorial, we are going to look at the latter approach.

Extending the sprite object

There are many ways in which sprite animation can be implemented. In order to keep our code reusable, we are going to define an animation object. If you are unfamiliar with object-oriented programming (in Lua) it would be a good idea to first go over the official Lua documentation. AGen was binded using the toLua library. We will use toLua's capabilities to extend the sprite object so that it supports animations. The resulting animation object takes in a position, an image object of the sprite sheet and some additional information about the animation. Each object will stores its own timer which is responsible for updating the animation. During the update, we look at the elapsed time and determine which frame to draw.

AnimationMT = {}
AnimationMT.__index = AnimationMT

function Animation ( x, y, image, w, h, delay, frames )
  local child = {}
  setmetatable ( child, AnimationMT )

  -- create a new sprite object and inherit it
  local parent = Sprite ( x, y )
  tolua.setpeer ( parent, child )
  tolua.takeownership ( child )

  parent.image = image
  parent.w = w
  parent.h = h
  parent.tilesH = image.width / w
  parent.tilesV = image.height / h
  parent.delay = delay
  parent.frames = frames or ( parent.tilesH * parent.tilesV )

  parent.timer = Timer ( )
  parent.timer.on_tick = function ( timer )
    parent:Update ( timer:get_elapsed ( ) )

  parent:Update ( 0 )

  return parent

function AnimationMT:Update ( elapsed )
  -- calculate the current frame number
  local time = elapsed % ( self.frames * self.delay )
  local frame = math.floor ( time / self.delay )

  -- calculate the position of the current frame within the sprite sheet
  local x = ( frame % self.tilesH ) * self.w
  local y = math.floor ( frame / self.tilesV ) * self.h

  -- draw a clipped sub-section from the sprite sheet
  self.canvas:clear ( )
  self.canvas:set_source_subimage ( self.image, x, y, self.w, self.h )
  self.canvas:paint ( )

function AnimationMT:Play ( )
  self.timer:start ( 16, true )

function AnimationMT:Pause ( )
  self.timer:pause ( )

function AnimationMT:Stop ( )
  self.timer:stop ( )
  self:Update ( 0 )

Using the animation object

For this example, we are going to use an explosion animation saved in PNG format. The sprite sheet texture is 256 by 256 pixels where each frame is a 64 pixel square.

Using our animation object is not difficult. It works similarly to a sprite having its own position, rotation and scale properties. The animation object, however requires a few extra parameters. The first two are the X and Y coordinates or position of the animation. The third parameter is the image object with our sprite sheet. The fourth and fifth parameters are the width and height of one frame of animation. Next is the delay parameter or the amount of time each frame is to be displayed in milliseconds. The last parameter (which is optional) shows the number of frames of animation stored in the sprite sheet.

display:create ( "Animation Demo", 640, 480, 32, true )

image = Image ( "Tutorials/explosion.png" )

animation = Animation ( 0, 0, image, 64, 64, 64, 16 )
display.viewport:add_child ( animation )
animation:Play ( )
Download:  animation.lua