Hello guys, I'm working on a short article for fanzine #5, and I'm going to talk about game feel. So I wrote this tiny library that is super helpful and allows you to simply tween any value in your objects with some cool math functions. Here is a small example:

function _init() -- the object that has your value p={ x=32,y=32 } -- the tween function, takes args: -- p - your object -- table - table with the target values you want -- 1 (optional) - time (in seconds) in what your value should end with your target value, default is 1 -- "quad_in_out" - easing function, you can look up all defined functions in the tab 1, I've implemented a few examples from https://easings.net/ local v=tween(p,{x=96,y=96},1,"quad_in_out") -- you can also define a function that will run at the end of tweening v.onend=function() stop() end -- and you can even set the delay before tweening you object! v.delay=0.2 end function _update60() -- just update the library, if you use _update, update value to 1/30 tween_update(1/60) end function _draw() cls() -- draw our circle! circfill(p.x,p.y,4,8) end |

**The minimum source code ~340 tokens**

-- tween engine -- by @egordorichev local back=1.70158 -- feel free to remove unused functions to save up some space functions={ ["linear"]=function(t) return t end, ["quad_out"]=function(t) return -t*(t-2) end, ["quad_in"]=function(t) return t*t end, ["quad_in_out"]=function(t) t=t*2 if(t<1) return 0.5*t*t return -0.5*((t-1)*(t-3)-1) end, ["back_in"]=function(t) local s=back return t*t*((s+1)*t-s) end, ["back_out"]=function(t) local s=back t-=1 return t*t*((s+1)*t+s)+1 end, ["back_in_out"]=function(t) local s=back t*=2 if (t<1) s*=1.525 return 0.5*(t*t*((s+1)*t-s)) t-=2 s*=1.525 return 0.5*(t*t*((s+1)*t+s)+2) end } local tasks={} function tween(o,vl,t,fn) local task={ vl={}, rate=1/(t or 1), o=o, progress=0, delay=0, fn=functions[fn or "quad_out"] } for k,v in pairs(vl) do local x=o[k] task.vl[k]={start=x,diff=v-x} end add(tasks,task) return task end function tween_update(dt) for t in all(tasks) do if t.delay>0 then t.delay-=dt else t.progress+=dt local p=t.progress*t.rate local x=t.fn(p>=1 and 1 or p) for k,v in pairs(t.vl) do t.o[k]=v.start+v.diff*x end if p>=1 then del(tasks,t) if (t.onend) t.onend() end end end end |

*update, fixed tasks not looking onto t var

I have a lua tip. It's probably not really for you, Egor, since you already know your stuff, but this is a good place to note it or remind people of it:

table = { ["key"] = value } v = table["key"] |

Is functionally identical to:

table = { key = value } v = table.key |

I know you're actually intending for your functions table to be keyed by strings for the sake of simplicity of API use, rather than to have syntactically-sugared members as with an object, but this could save tokens in declaring the table inside of your library.

Also handy is that, if you're using picotool, one could require() this code and then, if so desired, refer to tween.functions.linear directly, rather than tween.functions["linear"]. The former feels a little more familiar/standard.

Nice functionality btw. :) I'm putting this on my little shelf of useful code.

Hmm, good point, btw. Thanks ;) If you need any other function implementations just tell me. But those 7 are the most used by myself, at least.

I've found this library extremely useful in trying to make some paths for my shooter. However, I can't figure out how to adjust the speed of an object moving between points.

I see the third parameter of tween() but no matter what I change it to, they always move at the same speed. Or am I misunderstanding the purpose of that param?

I thought that parameter was the time the object took to get from A to B. So 1 meant 1 second...2 means 2 seconds...and so on. But maybe not?

Thanks for putting this together. I understand it and so far can better integrate with what I'm trying to do better than some other tween code I've tried before.

Looks like egor missed multiplying dt by the t.rate attribute in the update

As an aside, I think it would be better to store the time argument and divide by that, rather than creating rate=1/time in the constructor, since I'm wary of precision issues with PICO-8's fixed-point number format.

Drop this in and try adjusting that param again:

function tween(o,vl,t,fn) local task={ vl={}, time=t or 1, -- <=== changed o=o, progress=0, delay=0, fn=functions[fn or "quad_out"] } for k,v in pairs(vl) do local x=o[k] task.vl[k]={start=x,diff=v-x} end add(tasks,task) return task end function tween_update(dt) for t in all(tasks) do if t.delay>0 then t.delay-=dt else t.progress+=dt/t.time -- <=== changed local p=t.progress local x=t.fn(p>=1 and 1 or p) for k,v in pairs(t.vl) do t.o[k]=v.start+v.diff*x end if p>=1 then del(tasks,t) if (t.onend) t.onend() end end end end |

Works like a charm! Thanks for the hookup.

[Please log in to post a comment]