Frames are the individual steps in an animation. Each frame represents a moment in time, and when played in sequence, frames create the illusion of movement.
local sprite = Sprite(32, 32)-- Access frames arrayprint(#sprite.frames) -- 1 (default)-- Get specific framelocal frame = sprite.frames[1]print(frame.frameNumber) -- 1-- Iterate through framesfor i, frame in ipairs(sprite.frames) do print("Frame", frame.frameNumber)end
-- Create new empty frame (no cels copied)local sprite = Sprite(32, 32)local newFrame = sprite:newEmptyFrame()print(newFrame.frameNumber) -- 2print(#sprite.frames) -- 2-- No cels in the new frameprint(sprite.layers[1]:cel(newFrame)) -- nil
local sprite = Sprite(32, 32)sprite:newFrame()sprite:newFrame()print(#sprite.frames) -- 3-- Delete specific framesprite:deleteFrame(2)print(#sprite.frames) -- 2-- Delete by frame objectlocal frame = sprite.frames[1]sprite:deleteFrame(frame)
Be careful when storing frame references - if frames are added or deleted, the frame numbers change, but old frame objects still reference their original frame number.
local sprite = Sprite(32, 32)for i = 1, 5 do sprite:newFrame()end-- Set duration for range of framessprite:setFrameRangeDuration(1, 3, 0.1) -- Frames 1-3: 100mssprite:setFrameRangeDuration(4, 6, 0.2) -- Frames 4-6: 200ms-- Set same duration for all framessprite:setDurationForAllFrames(0.15) -- All frames: 150ms
local sprite = Sprite(32, 32)local layer = sprite.layers[1]-- Get cel at specific framelocal cel = layer:cel(1) -- Frame 1if cel then print("Found cel at frame 1") print(cel.image.width) -- Access image print(cel.position) -- Cel positionend-- Create new celsprite:newEmptyFrame(2) -- Frame 2 has no celssprite:newCel(layer, 2) -- Create cel at frame 2
Multiple frames can share the same cel (image data):
local sprite = Sprite(32, 32)local layer = sprite.layers[1]-- Create linked celsprite:newEmptyFrame(2)local cel1 = layer:cel(1)local linkedCel = sprite:newCel(layer, 2, cel1.image)-- Check if cels are linkedprint(cel1.image == linkedCel.image) -- true (same image)-- Editing one affects the othercel1.image:clear(Color(255, 0, 0))-- linkedCel.image is also red now
Linked cels save memory and ensure consistency when you want multiple frames to show the same image.
local sprite = Sprite(32, 32)sprite:newFrame() -- Frame 2sprite:newFrame() -- Frame 3local frame2 = sprite.frames[2]-- Navigate to neighborslocal prev = frame2.previousprint(prev.frameNumber) -- 1local next = frame2.nextprint(next.frameNumber) -- 3-- First and last framesif frame2.previous ~= nil then print("Not the first frame")endif frame2.next ~= nil then print("Not the last frame")end
Before animating, decide how many frames your animation needs:
Simple animations: 2-4 frames
Walk cycles: 6-8 frames
Complex animations: 12+ frames
More frames create smoother animations but require more work.
Use Consistent Duration
Keep frame duration consistent within an animation sequence for smooth playback. Vary duration intentionally for timing effects.
-- Consistent timing for walk cyclesprite:setFrameRangeDuration(1, 8, 0.1)-- Special timing for impact framesprite.frames[5].duration = 0.2 -- Hold this frame longer
Use Linked Cels for Static Elements
If part of your sprite doesn’t change (like a background layer), use linked cels to save memory and ensure consistency.
Name Animation Sequences with Tags
Use tags to organize frames into named animations like “idle”, “walk”, “jump”. This makes it easier to manage and export animations.
function createFrameSequence(sprite, count) for i = 1, count do sprite:newFrame() endendlocal sprite = Sprite(32, 32)createFrameSequence(sprite, 7)print(#sprite.frames) -- 8 (1 original + 7 new)
function createPingPongFrames(sprite, startFrame, endFrame) -- Creates frames in reverse for ping-pong effect for i = endFrame - 1, startFrame + 1, -1 do local frame = sprite:newFrame() -- Copy cel from frame i for _, layer in ipairs(sprite.layers) do local cel = layer:cel(i) if cel then sprite:newCel(layer, frame.frameNumber, cel.image) end end endend
local sprite = Sprite(32, 32)for i = 1, 5 do sprite:newFrame()end-- Iterate with indexfor i, frame in ipairs(sprite.frames) do print("Frame", i, "duration:", frame.duration)end-- Process every other framefor i = 1, #sprite.frames, 2 do local frame = sprite.frames[i] frame.duration = 0.2end