Skip to content

146. Playing with (tween) functions

January 24, 2014

It takes a while to realise how you can play with functions in Codea (or rather Lua, which is the language behind Codea), and I am still surprised with the things you can do.

I found a nice example on the forum, which I explain below. If you are still learning Codea, you should pick up a few tips and be less nervous of some of Codea’s features.I’ve tried to make this very simple.

I learned something, too, so even if you are familiar with Codea, you may find something useful.

Someone posted that they were using tweens to animate things in their project, but they didn’t know how to pause and restart them.

A quick note on tweens

Now, tweens are not scary. All they do is interpolate a number(s) from one value to another, over a given number of seconds. They can do fancy stuff like starting off fast and slowing down, or interpolating back and forwards forever between two numbers – but all they do is change a number value.

You can use them for all sorts of things, whether it’s to make a robot guard patrol back and forwards, or a balloon drift up into the sky – anything that requires something to go from A to B. But all the tween does is to change numbers – you have to use those numbers to place your robot guard, or whatever.

I can never remember how to write a tween, so whenever I need one, I sneak into the Animation demo project built into Codea, and copy what I need.

For more – I’ve written about tweens before, here.

Some demo code

So let’s set up some demo code to play with. The numbered lines are explained underneath.

function setup()
   --initial height of images [1]
    p1={y=200}
    p2={y=600}
    --set up tweens [2]
    t1=tween( 3, p1, { y = 600 }, { easing = tween.easing.linear,
     loop = tween.loop.pingpong } )
    t2=tween( 7, p2, { y = 100 }, { easing = tween.easing.linear,
     loop = tween.loop.pingpong } )
    img=readImage("Planet Cute:Gem Orange")
    parameter.boolean("Tweens_On",true,Toggle)  --[3]
end
function Toggle()  --[4]
   if Tweens_On then tween.resumeAll() else tween.pauseAll() end
end
function draw()
    background(0)
    sprite(img,200,p1.y)  --[5]
    sprite(img,400,p2.y)
end

[1] I’m going to move two images up and down the screen, using tweens. First I set the starting y position. I have to put it in a table because that’s what tweens need.

[2] I set up a tween for each image, setting a time, the ending y position, and telling the tween to go back and forwards (ping pong).

[3] I create a parameter that we can change, to pause and restart the tweens. When changed, it runs a function called Toggle. (You can do this for any parameter, ie include a function name to be run if the parameter changes).

[4] The Toggle function will pause and restart our tweens. It won’t work yet, because the functions tween.resumeAll and tween.pauseAll don’t exist.

[5] We draw the two images, using the two y positions being interpolated.

The clever stuff

There isn’t a tween command to pause or restart all tweens, but there is a function tween.update, which is used behind the scenes to do all the interpolation every time Codea draws.

So if we could disable this function when we want to pause, and re-enable it when we want to restart, this would do what we want.

And here is what forum user toffer came up with. Again, I’ve numbered the lines, with explanations underneath.

local update = tween.update  [1]

local noop = function() end  [2]

tween.pauseAll = function()  [3]
    tween.update = noop
end

tween.resumeAll = function() [4]
    tween.update = update
end

First, the spoiler. What toffer is going to do is put something in place of the tween.update function, when he wants to pause the tweens, so they don’t get updated. When we restart, he will put back the original tween.update.

[1] First, we let update equal tween.update. What does this mean? Remember that a function name doesn’t store the actual function, but the address where the function can be found (rather like keeping a friend’s address on a piece of paper). More here.

So we make a copy of the address stored in tween.update and put it in update. If we now write update(), it will be the same as tween.update().

If this still isn’t clear, imagine that we are arranging courier deliveries, and we put delivery addresses in a stack of trays. When a driver comes in, we might say “take the address from tray 3”, and away he goes. Similarly, we say to Codea, run the function update. Codea takes the address stored in that variable, goes to that address in memory, and runs whatever is there. So Codea doesn’t care if you have 6 variables all containing the same function address.

We are making a copy, because we’re going to change tween.update (to prevent it updating), and so we need to keep a copy of (the address of) the original function, for when we restart the tweens again.

[2] We define a function noop (techie shorthand for “no operations”, ie does nothing), which is a function that is empty and does absolutely nothing. We could also write it as

function noop() end

If you want to understand how you can write functions in different ways like this, see here.

[3] The new function tween.pauseAll makes tween.update = noop, which copies the address of the noop function into tween.update, so when tween.update is run, it will now run the noop function – which does nothing!

As with the noop function, we could write the new function instead as

function tween.pauseAll()
    tween.update = noop
end

[4] The tween.resumeAll function simply copies the original address for the tween updating function, back into tween.update, so it starts animating normally again. That’s why we made a copy of it to start with.

So this is a very elegant way of disabling and enabling tween updating.

You can use this renaming approach for all sorts of things. For example, if you are using a lot of math.sin and math.cos functions in your code, you can make your code read more simply by writing

    local sin=math.sin
    local cos=math.cos

and now you can just use sin and cos in your code. It doesn’t make your code slower, because your new function names are pointing to exactly the same places as the old ones.

You can also enhance functions. For example, I find it a nuisance that math.sin needs radians, not degrees, so I have to convert from degrees first. I could write a sin function to do the conversion for me, like so

local function sin(a)
   local r = math.rad(a) --convert to radians
   return math.sin(r) --calculate sin using normal function
end

So Codea/Lua is exceptionally powerful, if you know how to use it well. I am still learning….

One Comment

Trackbacks & Pingbacks

  1. Index of posts | coolcodea

Leave a comment