Confused about sprite animation in Codea...

Hey all!

I’m making a random endless runner game where the main character stays in the same x coordinate and just jumps when tapping the screen. The obstacles are what moves toward the main character. I’m confused about how to make a running animation if I have a 6 piece animation. Like how do I make Codea keep switching the sprites in order and loop them?

Any insight would be awesome!

Thanks in advance

@Crumble - how about this

--setup
anim={readImage(sprite1),readImage(sprite2), ETC}
animNo=1
animTimer=0
animDelay=0.1

--then in draw
animTimer=animTimer+DeltaTime
if animTimer>animDelay then
    animNo=animNo+1
    if animNo>#anim then animNo=1
    animTimer=0
end
sprite(anim[animno],x,y)
--# Main
function setup()
    braid = animation("Dropbox:braid", 130, 150)
end

function draw()
    background(136, 188, 56, 255)
    braid:play()
end
--# Animation
animation = class()

function animation:init(spritesheet, width, height, ...)
    self.atlas  = mesh()
    self.mask   = self.atlas:addRect(0, 0, 0, 0)
    self.img    = type(spritesheet) == "string" and readImage(spritesheet) or spritesheet
    self.atlas.texture = self.img
    self.width  = width
    self.height = height
    self.cols   = self.img.width  / self.width
    self.rows   = self.img.height / self.height
    self.fps    = type(arg[1]) == "number" and arg[1] or 24
    self.frames = type(arg[1]) == "table"  and arg[1] or (type(arg[2]) == "table" and arg[2] or {})
    self.mode = { -- spriteMode() equivalent
        [0] = { -- CORNER
            pos  = vec2(self.width/2, self.height/2),
            size = vec2(self.width,   self.height)
        },
        [1] = { -- CORNERS
            pos  = vec2(self.width/2, self.height/2),
            size = vec2(self.width,   self.height)
        },
        [2] = { -- CENTER
            pos  = vec2(0, 0),
            size = vec2(self.width, self.height)
        },
        [3] = { -- RADIUS
            pos  = vec2(0, 0),
            size = vec2(self.width*2, self.height*2)
        }
    }
    
    local frameset = {}
    for row = 1, self.rows do
        for col = 1, self.cols do
            table.insert(frameset, vec2(col, row))
        end
    end
    if #self.frames == 0 then
        self.frames =  frameset
    else
        local cache = self.frames
        self.frames = {}
        for i = 1, #cache do
            local x, y = cache[i].x, cache[i].y
            if x < 1 or y < 1 then
                local fillin = false
                for n, vec in ipairs(frameset) do
                    if fillin == true then
                        table.insert(self.frames, vec)
                    end
                    if not self.frames[#self.frames] then
                        table.insert(self.frames, vec2(1,1))
                    end
                    if vec == self.frames[#self.frames] then fillin = true end
                    if vec == vec2(math.abs(x), math.abs(y)) then fillin = false end
                end
            else
                table.insert(self.frames, cache[i])
            end
        end
    end
end

function animation:play()
    self.timer = self.timer or ElapsedTime
    self.frame = self.frame or 1
    local u, v = 1/self.cols, 1/self.rows
    local mode = spriteMode()
    
    if (ElapsedTime-self.timer) > (1/self.fps) then
        self.frame = self.frames[self.frame+1] and (self.frame+1) or 1
        self.timer = nil
    end
    
    self.atlas:setRect(self.mask, self.mode[mode].pos.x, self.mode[mode].pos.y, self.mode[mode].size.x, self.mode[mode].size.y)
    self.atlas:setRectTex(self.mask, u*(self.frames[self.frame].x-1), v*(self.rows-self.frames[self.frame].y), u, v)
    self.atlas:draw()
end

braid spritesheet: https://www.dropbox.com/s/5xb1oe5r8x54qfb/braid_130x150.png?dl=0

Here’s another version using the above braid sprite sheet. This won’t run unless you have the sprite sheet in Dropbox.


supportedOrientations(PORTRAIT_ANY)

function setup()
    count=200
    parameter.integer("delay",1,120,60)
    spriteMode(CORNER)
    pic=readImage("Dropbox:braid")
    img=image(pic.width,pic.height)
    setContext(img)
    sprite(pic)
    setContext()
    x=-1
    y=3
end

function draw()
    background(40, 40, 50)
    sprite(img,0,0)
    count=count+1
    if count>=delay then
        count=0
        x=x+1
        if (x==6 and y==0) or x>6 then
            x=0
            y=y-1
            if y<0 then
                y=3
            end
        end
    end
    img1=img:copy(65*x,75*y,65,75)
    stroke(255,0,0)
    strokeWidth(4)
    noFill()
    rect(65*x,75*y,65,75)
    noStroke()
    fill(255)
    rect(0,340,520,600)
    sprite(img1,1,350,500)
end

Thanks guys!

Hmm I will try to wrap my head around this stuff, they don’t make animation easy do they…

Ignatz’s example seems like the easiest to understand so i’ll try to figure that one out first.

Side question: I see that you all used readImage() in your code in setup, why not just use sprite() and then select the location?

@Crumble - if you want to read the images from disk over and over again, 60 times a second, while your program runs, feel free.

But it’s better to read them from disk just once, in setup, then sprite them from memory after that. :wink:

@Ignatz - Good to know, I’ll have to change up my other projects to read image. So I take it that it should be done with all graphics? Including the background?

Try to only read images from disk once, and store them in variables. So if the background is an image, yes, definitely.

Modified my code above to adjust the speed of the animation to show which image in the sprite sheet is being used.

The user and all related content has been deleted.