You will be redirected to the new docs in a few seconds.
If you are certain you want to see the old docs, click cancel.
Cancel | Continue to new docs
Advanced Animation Using Sprites
- What are Sprite Sheets?
- Limitations
- Summary
- Sprite API
- sprite.newSpriteSheet
- sprite.newSpriteSheetFromData
- spriteSet = sprite.newSpriteSet(spriteSheet, startFrame, frameCount)
- sprite.add( spriteSet, "startFlying", startFrame, frameCount, time, [loopCount])
- spriteSheet:dispose()
- si = sprite.newSprite( spriteSet )
- si:prepare([sequence])
- si:play()
- si:pause()
- si:addEventListener("sprite", listener)
- Sprite properties
What are Sprite Sheets?
Sprite sheets are 2D animations packed as multiple frames into a single texture image. This allows a much more efficient use of texture memory, which is highly limited on mobile devices, and also minimizes loading time.
Corona supports two types of sprite sheets: those whose animation frames are all sized and positioned uniformly, and those whose frames have non-uniform sizes and positions. In the former case, you simply provide Corona with the width and height of each frame in the sprite sheet image file to create a new internal representation of the sprite sheet. The image below (from the JungleScene sample project that ships with Corona SDK) is an example of a sprite sheet with uniformly-sized frames.
In the other case, the positions and sizes of individual frames within the sprite sheet are described by an external data file that can be exported from several sprite sheet generator tools (such as Zwoptex or TexturePacker). Corona lets you easily create an internal sprite sheet object from a sprite sheet image and its accompanying data file. See below for examples of how to work with both types of sprite sheets. The sprite sheet below (from the HorseAnimation sample project) contains frames with non-uniform sizes and positions.
Corona also includes constructors for complex animated characters with multiple sequences, and sequence playback methods that are time-based rather than frame-based, so animation duration will remain consistent on slower devices. These time-based sequences also allow you to individually choose and tweak the frame rate for each sequence, rather than having the animation speed locked to the application’s global frame rate.
There may be more than one instance of each sprite on the stage at the same time, playing at different locations and with different durations. A sprite animation behaves like any other Corona display object -- it may be added to a group or transformed, or even used as a Box2D physics object.
The JungleScene sample project demonstrates using two different sprite sheets (both with uniformly-sized frames), to create animations running at different frame rates. To see an example of sprite sheet source images, view the files “greenman.png” and “runningcat.png” in that project folder.
The HorseAnimation sample project demonstrates how to use create a sprite sheet from a Lua data descriptor file and source image whose frames are non-uniformly sized and positioned in the sheet.
Limitations
If you attempt to use a sprite sheet whose width or height exceeds your device's maximum texture size, no image will be displayed.
To check the maximum texture size, use system.getInfo("maxTextureSize").
Summary
For each animation sequence, the developer specifies the following things:
- Source image (the sprite sheet)
- One of the following:
- The height and width of each frame within the sprite sheet (for sprite sheets with uniformly sized frames)
- A Lua data descriptor file that describes the size, position, and other attributes of each frame (for sprite sheets with non-uniformly sized frames). Typically this would be generated by a sprite sheet packing utility.
- Location of sprite frames -- these may not be contiguous!
When drawing an instance of the sprite animation, the developer specifies:
- Destination in the scene
- Optional transforms such as rotate/scale
- Length of animation (time-based)
- Looping behavior
Sprite API
This line makes the sprite features available under the “sprite” namespace:
require "sprite"
sprite.newSpriteSheet
sprite.newSpriteSheetFromData
These functions create new sprite sheets.
The following code creates a new sprite sheet from image containing uniformly-sized frames, but does not add anything to the display list.
spriteSheet = sprite.newSpriteSheet("image.png", frameWidth, frameHeight)
In this case, the number of frames in the sprite sheet is assumed to be floor(imageWidth/frameWidth) * floor(imageHeight/frameHeight). They are indexed in row major order:
1 2 3 4 5 6 7 8 9
spriteSheet = sprite.newSpriteSheetFromData( "image.png", spriteData )
This second form takes as its second parameter a table that describes the frame sizes and positions in the source sprite sheet image.
The code below is an example of a sprite sheet descriptor file named test.lua. It defines a single function named getSpriteSheetData() that returns a Lua table whose elements describe the size, position, and other properties of each frame in the sprite sheet.
-- test.lua module (...) function getSpriteSheetData() local sheet = { frames = { { name = "01.png", spriteColorRect = { x = 38, y = 38, width = 50, height = 50 }, textureRect = { x = 2, y = 70, width = 50, height = 50 }, spriteSourceSize = { width = 128, height = 128 }, spriteTrimmed = true, textureRotated = false }, { name = "02.png", spriteColorRect = { x = 38, y = 36, width = 50, height = 52 }, textureRect = { x = 2, y = 242, width = 50, height = 52 }, spriteSourceSize = { width = 128, height = 128 }, spriteTrimmed = true, textureRotated = false }, } } return sheet end
1 2 3 4 5 6 7 | local sprite = require("sprite") -- In this case, test.lua is exported from Zwoptex local test = require("test.lua") -- Method defined by test.lua that returns table data defining the sprites local spriteData = test.getSpriteSheetData() -- Load the sprite sheet in test.png using the sprite definitions from spriteData local spriteSheet = sprite.newSpriteSheetFromData( "test.png", spriteData ) |
In either case, creating a new sprite sheet object does not actually result in anything being added to the display list. To do that you need to create a sprite (and optionally sprite sets) from your sprite sheet object. These concepts are explained further below.
Tip: You can combine sprite sheet loading into one line of code:
local spriteSheet = sprite.newSpriteSheetFromData( "test.png", test.getSpriteSheetData() )
spriteSet = sprite.newSpriteSet(spriteSheet, startFrame, frameCount)
Creates a new sprite set from a sprite sheet. Since a single sprite sheet may contain images from unrelated game characters, a “sprite set” defines the collection of frames that belong to the same game character, which may then be subdivided into different animation sequences for playback. For example, a fighting game character would probably have different animation sequences for punching and for kicking, but these would be defined within the same sprite set.
startFrame is the index number of the first frame of the sprite, using the index defined in newSpriteSheet() above, and frameCount is the total number of frames in the set.
A sprite set is a Lua table containing keys to one or more animation sequences for a given character. Each sprite set is created with an initial sequence named "default", containing all frames in the set, so you can either use this default sequence immediately or define further animation sequences within the set.
sprite.add( spriteSet, "startFlying", startFrame, frameCount, time, [loopCount])
Adds a sequence named "startFlying" to the sprite set with the specified frames. The sequence has frameCount frames, and will play for time milliseconds. You can control the frame rate of each sequence individually by altering this time parameter.
The optional parameter loopParam controls the looping behavior:
- A value of 1 or greater sets the number of times the animation sequence will loop. When done it will stop on the last frame of the sequence.
- A loopParam of 0 (which is the default) means that the sequence will loop indefinitely.
- A loopParam of -1 means that the sequence will "bounce" back and forth exactly once (1, 2, 3, 2, 1).
- Finally, a loopParam of -2 means that the sequence will bounce back and forth forever. In our 3-frame example, this would play in the following order: 1, 2, 3, 2, 1, 2, 3, 2, 1 (...) and so on.
spriteSheet:dispose()
Disposes of a sprite sheet and releases its texture memory. It also calls removeSelf() on all sprite instances using the sheet, removing them from the stage. All sprites, sequences, and sets that belong to the removed sprite sheet are no longer valid after calling this method, and will be garbage collected when they are no longer referenced by your application's Lua code.
1 2 3 4 | local sprite = require("sprite") local spriteSheet = sprite.newSpriteSheet("test.png") -- Later dispose of the sprite sheet and all of its associated sprites, sequences and sets. spriteSheet:dispose() |
si = sprite.newSprite( spriteSet )
Create a new instance of a sprite. A sprite is a DisplayObject. Sprites play one animation sequence at a time.
si:prepare([sequence])
Stops any currently playing animation sequence, optionally sets the new current sequence, and also moves to the first frame of that sequence. This also resets the loop counter, if any. However, this does not start playing the current sequence.
si:play()
Play animation sequence, starting at the current frame. Does not reset looping.
si:pause()
Stop animation, but the frame remains on the last displayed frame. Playback can resume later with play().
si:addEventListener("sprite", listener)
Notify listener when the spriteInstance animation has an event.
The event, passed to the listener, has the following fields:
event.sprite
The sprite that fired the event; its current properties may also be accessed via the event: e.g., event.sprite.sequence.
event.phase
The phase is one of:
"end" - the sprite stops playing
"loop" - the sprite loops (from last to first, or reverses direction)
"next" - the sprite's next frame is played
Also see "Custom Events" in the Corona API Reference.
Sprite properties
animating = true/false. read only.
currentFrame = frame # of animation. read/write.
sequence = name of currently playing sequence
Replies
Try changing
sprite = require "sprite"
to
require "sprite"
Also, "si:addListener" does not exist, and "si:addEventListener" doesn't seem to be working. The following code runs fine, but KillSprite is never called.
local explosion = sprite.newSprite( explosionSpriteSet )
explosion:prepare( "explosion" )
explosion:play()
explosion:addEventListener( "animation", KillSprite )
#591 event.sprite isn't pushed for sprite listener
#594 sprite properties need implementation
Are fixed for next drop.
as is
#296: require "sprite" doesn't return anything?
The sprite.add syntax is currently
sprite.add( spriteSet, "startFlying", startFrame, endFrame, time, [loopCount])
not
sprite.add( spriteSet, "startFlying", startFrame, frameCount, time, [loopCount])
Note: Assert for maximum frame count is based on the second version listed above. ie. assert( startFrame + frameCount <= total frames )
This means that some animations near the end of the sheet may be impossible to set up.
Example: Using a 4x4 sprite sheet (16 frames), an animation that starts at 9 and ends at 14 cannot be set up since 9+14 > 16 frames.
NeoBlargg: I believe you're absolutely correct. The only way it works is by specifying the end frame, rather than framecount as in the docs. But as you say, an assertion is thrown based on assuming the second frame parameter is count rather than endframe.
Hopefully the Corona folks are aware of this now, as it makes the spritesheet a lot less useful.
NeoBlargg, it's supposed to be frameCount. There are some bugs in the current drop that are now fixed.
The example for using test.lua says "takes as it's second parameter, the name of the lua file that describes the frame sizes", however, the example shows passing an instance of the actual call to getSpriteSheetData() rather than a string.
Fixed.
spriteSheet.dispose() should be
spriteSheet:dispose()
Fixed, thanks.
Using Zwoptex to generate a texture and a coordinate map, when i drop the texture along with the map into the code, i get this error. anyone seen it before? I tried rebuilding the coordinate map and texture for a second time with more padding between the individual sprites but nothing.
ERROR: Invalid parameter passed to sprite.newSpriteSheetFromData(). Frame overlaps sprite sheet.
I think there is an issue instead of framecount it seems to be the endframe. This is using Zwoptex
Gives me this error before I even get to that point.
"Frame overlaps sprite sheet" occurs when a frame lies outside the sprite sheet, or a frame is marked as rotated. Please make sure that "Allow Rotation" is deselected in Zwoptex. If this isn't the problem, make sure the Width and Height are large enough to accomodate all the frames. If you still have problems, can you send the .lua file to support? You don't need to include the PNG, but please let us know what the PNG size is.
Hey Eric, I emailed you the PNG and the coordinate sheet last night, the allow rotation was deselected and nothing was rotated in the texture PNG.
Eric,
Is the problem I am having something related?
Can you describe the problem you are having in more detail?
In
sprite.add( spriteSet, "startFlying", startFrame, frameCount, time, [loopCount])
instead of frameCount it seems to be endFrame. So the final frame instead of the frameCount. This bug was present before beta 8 and seems to be back. Same exact issues as before.
@ Eric
You can check this post too. Look at the bottom. Same exact issue.
http://developer.anscamobile.com/forum/2010/08/09/manually-set-frame-sprite-sheet
I think there needs to be some clarification of the documentation. I have been in touch with Zwoptex to cure some bugs. He says that the frames can be put in any order but then there are no instructions on how to call them properly to execute the animation.
It seems to me that we still have to put them in order with the current system.
I am having the same "frame overlaps sprite sheet" problem, were you able to resolve this?
according to eric, its because i have one of the dimensions of my sprite sheet larger than 1024 (2048) which ipad can handle, but corona currently does not have the ability to. I think he fixed it for next revision.
Ah ok, thanks... I was trying to use a 2048 x 2048 sprite sheet.
[ added to bug tracker https://developer.anscamobile.com/issues/3491 ]
my square sprites from an irregular spritesheet (using SpriteGrabber) are all returning physics bodies as 60x60 which is the size of the 2nd entry in my list. if i swap my second entry round with the third then all my physics bodies are 90x90. basically physics body is coming from the 2nd entry. the sprites display fine but the physics body is not the right size. so in the case below my 3rd sprite is 90x90 but has a 60x60 physics body within it
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | function getSpriteSheetData() local sheet = { frames = { { name = "chuzzle.png", spriteColorRect = { x = 0, y = 0, width = 46, height = 48 }, textureRect = { x = 122, y = 198, width = 46, height = 48 }, spriteSourceSize = { width = 46, height = 48 }, spriteTrimmed = false, textureRotated = false }, { name = "crate.png", spriteColorRect = { x = 0, y = 0, width = 60, height = 60 }, textureRect = { x = 64, y = 90, width = 60, height = 60 }, spriteSourceSize = { width = 60, height = 60 }, spriteTrimmed = false, textureRotated = false }, { name = "xcrateB.png", spriteColorRect = { x = 0, y = 0, width = 90, height = 90 }, textureRect = { x = 0, y = 0, width = 90, height = 90 }, spriteSourceSize = { width = 90, height = 90 }, spriteTrimmed = false, -- etc |
So sprites with dimensions over 2048 are not supported?
@abinop - correct, it's a GPU hardware limitation. No single bitmaps larger than 2048 (the maximum on iPad and iPhone 4) or 1024 (the maximum on older iPhones and probably most Android devices, depending on the GPU).
There should be a section in this page devoted to how Sprite Sheets work with content scaling, if at all.
In case anyone is looking for a description of the parameters:
name
spriteColorRect
textureRect
spriteSourceSize
spriteTrimmed
textureRotated
Here is a link:
http://zwopple.com/2010/07/26/zwoptex-1-0-2-generic-coordinate-format/
@Pixelrevision,
many thanks for the link!
I wonder how to get access to the sprite-property "currentframe" to switch to another graphic inside the sheet, if all similar sprites are in a table. lets say
local markerSet = sprite.newSpriteSet(spriteSheet, 1, 2)
local marker = {}
-- now lets make 12 instances
for i = 1,12 do
marker[i] = sprite.newSprite(markerSet)
end
-- Now I want to define the currentframe of marker[i].
-- How can I do this?
si = marker[i]
si.currentframe = 2 -- doesnt work.
Solution:
I should insert one line of code
after setting the currentFrame-prop:
maker[i] = si
Now it works
I am having a issue with zooming and reference point. A running animation that seemed smooth get's really bumpy when I zoom out to .5 scale.
I think the reference point of the sprite doesn't scale or something funny is going on.
Can anyone give some advice on this?
Thanks
Sprites are complicated :S
I just get errors :L
instructions are hard
Spritetrimming will result a sprite to return a wrong width/height.
The width/height and contentWidth/Height are set to the trimmed values instead of the original.
Can someone at ansca reply?
This is indeed clearly advanced feature.
I wanted to use spritesheet for my individual png (not animations, just fixed ones, just because tons of people recommend that) but I have to give up.
Way too complicated...
why... do you use code examples not using the spritelog that is distributed with the SDK?
if you are using what Corona is supplying with its SDK.
than replace:
getSpriteSheetData()
with:
Load()
sprtiteloq only works with flash based spritesheet...
Creates a new sprite set from a sprite sheet. Since a single sprite sheet may contain images from unrelated game characters, a “sprite set” defines the collection of frames that belong to the same game character, which may then be subdivided into different animation sequences for playback. For example, a fighting game character would probably have different animation sequences for punching and for kicking, but these would be defined within the same sprite set.
could you show me a sample code for this?
http://developer.anscamobile.com/forum/2011/12/14/all-sprites-spritesheet-has-dimensions-first-frame
If i print the sprites dimensions in the initialization corona gives me width and height of the first frame in .lua doc but later, inside runtimeevents or timers, corona returns the correct values.
Please note, that in order to see event.phase == "end" you cannot "reuse" prepared sequence:
1 2 3 4 | sprite:prepare("something") sprite:play() --wait for it to finish sprite:play() |
if you have a sprite listener created, the second time your sequence finishes playing there will be no event.phase == "end" sent to the listener.
SpriteSet is not a table
When attempting to add a field to a spriteSet I get an error "attempt to index local 'p' (a userdata value)"
The docs above say a spriteSet is a lua table, but apparently it isn't (anymore?). Type(p) produces "userdata" and ipairs(p) produces bad argument #1 to 'pairs' (table expected, got userdata)
Am I correct in this?
event.phase == "end" don't work, correct name of this phase is "ended"




I have these codes:
sprite = require "sprite"
spriteSheet = sprite.newSpriteSheet("Sprite3.png", 240, 180)
I couldn't go past the second line. I get the error message:
"...main.lua 2: attempt to call field 'newSpriteSheet' (a nil value)"
"...stack traceback: (C): in function 'newSpriteSheet'
"...main.lua:2: in main chunk"
Am I missing anything?