Tweens

All, does anyone have a good example/tutorial of using tweens to animate a series of sprites. Like a walking man across the screen. Just can not seem to get my head round them and they seem to be very useful.

@Bri_G Here’s a simple tween. You create a variable with the starting position, then you set up the tween with the tween time, the start variable, the end position, and the tween info of how you want the tween to move. There’s also a function you can execute when the tween completes it move.

function setup()
    sp={x=0,y=HEIGHT/2} -- start position and variable to use
    tween(5,sp,{x=WIDTH,y=HEIGHT/2},    -- time, start variable, end position
        {easing=tween.easing.linear,loop=tween.loop.once},done) -- tween info
end

function draw()
    background(40, 40, 50)
    sprite("Platformer Art:Guy Standing",sp.x,sp.y)
end

function done()
    print("done")
end

@dave1707 - thanks for the example and notes, noe beginning to understand - the focus is on the location of the sprite. With animation in mind I am looking at several spritesused in sequence.

Taking your example I managed to achieve this by using a table containing sprite data - but I had to use counters (my own timer) to switch sprite. Here is my main code, builder() is a routine for loading a sprite sheet into an image then stripping out the sprites into a table adv[].


displayMode(OVERLAY)
function setup()
    defaults()
    builder()
    sp={x=400,y=HEIGHT/2} -- start position and variable to use
    tween(100,sp,{x=400,y=HEIGHT/2},    -- time, start variable, end position
    {easing=tween.easing.sine,loop=tween.loop.once},done) -- tween info
    count = 1
    cno = 1
    cnl = 129
end

function draw()
    background(40, 40, 50)
-- sprite("Platformer Art:Guy Standing",sp.x,sp.y)
    sprite(adv[cno],sp.x,sp.y)
    sprite(adv[cnl],sp.x,sp.y+40)
    count = count+1
    if count > 5 then 
        count = 1 
        cno = cno+1
        if cno > 16 then cno = 1 end
    
        cnl = cnl+1
        if cnl > 144 then cnl = 129 end
    end
end

function done()
    print("done")
end

Thanks again.

@Bri_G You can add a counter within the tween code. That way as the tween runs, the counter will increment. Here’s the above code with count added. You set a starting and ending value to count and it will increment for the time of the tween.

function setup()
    sp={x=0,y=HEIGHT/2,count=0} -- start position and variable to use
    tween(5,sp,{x=WIDTH,y=HEIGHT/2,count=50},    -- time, start, end
        {easing=tween.easing.linear,loop=tween.loop.once},done) -- tween type
end

function draw()
    background(40, 40, 50)
    fill(255)
    sprite("Platformer Art:Guy Standing",sp.x,sp.y)
    text("count "..sp.count//1,WIDTH/2,HEIGHT-50)
end

function done()
    print("done")
end

@dave1707 - interesting, but your code doesn’t actually cycle through the sprite sheet - you have only used one sprite. Using an array with adv[count] as the array and count also doesn’t cycle through the sprites.

Count isn’t a counter it’s a decimal timer so you need to floor it count//1. But that doesn’t work either until I realised counter wasn’t addressed properly and what you need is sp.counter//1.

Bingo moving animated sprites.

Thanks for your help.

@Bri_G How about defining a callback function that increments the counter? Or is it that the sprites continually change during the motion?

@dave1707, @LoopSpace - update with the code, I managed to get an example written where you load up the image, set up arrays, extract the sprites to an array and set up the Tween.

Note image details given in code by !!! I had to load the image into Gimp, export the image so that the image was not compressed (should be 128x128 then) and rename so that the file type is lower case ie .png not .PNG. Then the code runs OK.

Try switching sp.count//1 to sp.count or even count!!!


-- AnimationCM
-- using IMG_0273.PNG from https://opengameart.org/content/2d-complete-characters
-- see notes for modifications to the sheet

function setup()
    -- load image, set up array, images to sprite array and set up tween
    cm = readImage("Dropbox:img0273")
    tsz = 32.  — sprite size
    obh, obw = 128,128. — image width and height
    manS = {}
    builder() -- routine to strip sprites
    sp={x=WIDTH/2,y=HEIGHT/2-128,count=1} -- start position and variable to use
    tween(1,sp,{x=WIDTH/2,y=HEIGHT/2-128,count=16},    -- time, start, end
        {easing=tween.easing.linear,loop=tween.loop.forever},done) -- tween type
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)
    sprite(cm,WIDTH/2,HEIGHT/2).          -- sprite sheet
    sprite(manS[sp.count//1],sp.x,sp.y)   -- animated sprite
end

function builder()
    --
    spm = 0
    for j = obh-tsz,0,-tsz do
        for i = 1,obw,tsz do
            spm = spm+1
            manS[spm] = cm:copy(i,j+1,tsz,tsz)
        end
    end
end

@LoopSpace it’s meant to be an animation, the above shows running on the spot but if you change the start and finish position you should see the caveman run across the screen. There are 16 sprites in the image covering the animation itself so they cycle forever, kinda like Mo Farrah !! As long as the cycle runs.

I think that Tween.sequence may be the proper way of doing this, but I am still learning.

Tried to include the amended image, hope I’ve added the correct one.

@Bri_G Here’s your above code modified so the caveman runs across the screen from right to left. When I loaded your img0273.png, it ended up being 64x64 pixels, so I had to change some of your values. Since I don’t have hard coded values, it might work OK with a size of 128x128.

function setup()
    cm = readImage("Dropbox:cave")
    obw, obh = cm.width,cm.height   -- image width and height    
    tsz = cm.width/4 -- sprite size
    manS = {}
    builder() -- routine to strip sprites
    sp={x=WIDTH,y=HEIGHT/2,count=1} -- start position and variables to use
    tween(10,sp,{x=0,y=HEIGHT/2,count=128},    -- time, start, end
        {easing=tween.easing.linear,loop=tween.loop.forever},done) -- tween type
end

function draw()
    background(40, 40, 50)
    sprite(manS[(sp.count//1)%tsz+1],sp.x,sp.y,50)
end

function builder()
    spm = 0
    for j = obh-tsz,0,-tsz do
        for i = 1,obw,tsz do
            spm = spm+1
            manS[spm] = cm:copy(i,j+1,tsz,tsz)
        end
    end
end

@dave1707 - thanks for that, made a few mods cm.width and cm.height give the size decimal notation, spriteSize() returns the integer. As printed above my caveman made it part way across before crashing with an error. Code below goes right across the screen.


displayMode(OVERLAY)
function setup()
    cm = readImage("Dropbox:img0273") 
    obw, obh = spriteSize(cm)
    print(obw,obh)
    tsz = obw//4 -- sprite size
    manS = {}
    builder() -- routine to strip sprites
    sp={x=WIDTH,y=HEIGHT/2,count=1} -- start position and variables to use
    tween(4,sp,{x=0,y=HEIGHT/2,count=16},    -- time, start, end
        {easing=tween.easing.linear,loop=tween.loop.forever},done) -- tween type
end

function draw()
    background(40, 40, 50)
    sprite(manS[(sp.count//1)],sp.x,sp.y,64)
end

function builder()
    spm = 0
    for j = obh-tsz,0,-tsz do
        for i = 1,obw,tsz do
            spm = spm+1
            manS[spm] = cm:copy(i,j+1,tsz,tsz)
        end
    end
end

Must be a relationship between image cycles and time.

@Bri_G Not sure why mine didn’t work. Even if your image was 128x128, the divide should have resulted in an integer. When I run yours, the moving feet don’t match the movement across the screen. The image moves too fast across the screen. The sp.count should be a higher number so you go thru more table iterations as the image moves. That’s why I was using a sp.count of 128 and doing a % divide. I was looping thru the image table 8 times to cross the screen.

@Bri_G I just figured out the problem. The % divide should be % 16 + 1 instead of % tsz + 1. Your table has 16 entries and the value of tsz using 128x128 would be 32, that’s why it crashed. It was indexing past the end of the table.

@dave1707 - well spotted. Must admit I didn’t do the math. Changed to your suggestion (except I used 64 instead of 50 for sprite size) and the caveman jogs along great. Thanks again, eventually I’ll get to know all about this app!!!

@Bri_G Glad everything worked OK. The more you play around with Tweens, the more comfortable you’ll get using them. But then that works for everything. The more you do something, the better you get at it.

Getting into Tweens now, does anyone know if you can move Tweens in Parallel (same time slot), as I’d like to animate a sprite against a scrolling background - so action sprite appears to move against a moving background?

@Bri_G Not sure if this is what you’re after. You can create a table of Tweens and set each one at a different position and time.

displayMode(FULLSCREEN)

function setup()
    tw={}
    for z=1,25 do
        x1=math.random(-200,0)
        y1=math.random(100,HEIGHT-50)
        s=math.random(5,60)
        table.insert(tw,tween(s, {x=x1,y=y1}, {x=WIDTH+200,y=y1}))
    end
end

function draw()
    background(40, 40, 50)
    for a,b in pairs(tw) do
        sprite("SpaceCute:Beetle Ship",b.subject.x,b.subject.y,50)
    end
end

@dave1707 - thanks for the demo illustrated the point well. I think I know where I was going wrong.

But, eye opener - shows my ignorance with Lua - what is subject? Don’t answer figured it out. How generic can you get!!!

@Bri_G subject is not pure Lua, it is really private code within the tween library. Without Simeon or John’s say-so, it should be regarded as not safe to use as the internals of a library can change. They could change the variable name to something else without changing the functionality.

Without having tested it, here’s what I would do:

displayMode(FULLSCREEN)

function setup()
    objs={}
    local obj, x1, y1, s
    for z=1,25 do
        x1=math.random(-200,0)
        y1=math.random(100,HEIGHT-50)
        s=math.random(5,60)
        obj = {x=x1, y=y1}
        tween(s, obj, {x=WIDTH+200,y=y1})
        table.insert(objs, obj)
    end
end

function draw()
    background(40, 40, 50)
    for a,b in ipairs(objs) do
        sprite("SpaceCute:Beetle Ship",b.x,b.y,50)
    end
end

(Note that I’ve switched to ipairs instead of pairs since the table is enumerated. Also, despite despairing of @dave1707 ever using local, I’ve used it so that I avoid polluting the global namespace.)

@LoopSpace I very seldom use locals in my examples. I try to make my examples small, mostly to show how to do something. I also don’t spend a lot of time writing them. I hope if someone uses my code, they add locals where or if needed. Since I don’t write anything that goes beyond this forum, the only time I use locals is when I want the most speed out of the code.