How to tween a parabola?

General discussion about LÖVE, Lua, game development, puns, and unicorns.
User avatar
Gunroar:Cannon()
Party member
Posts: 1085
Joined: Thu Dec 10, 2020 1:57 am

How to tween a parabola?

Post by Gunroar:Cannon() »

I'm just wondering if there's anyway to make an object reach a target, in a specific time, using tweening/a tweening lib(like Chrono) but in an upward curve.

The way I do it involves making it tween in 3 different steps. Up a little+a little x, straight+x, down + a little x. Is there a formula for this or something simpler?
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
User avatar
darkfrei
Party member
Posts: 1168
Joined: Sat Feb 08, 2020 11:09 pm

Re: How to tween a parabola?

Post by darkfrei »

,You can use quadratic bezier curve, it's true parabola.
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
pgimeno
Party member
Posts: 3541
Joined: Sun Oct 18, 2015 2:58 pm

Re: How to tween a parabola?

Post by pgimeno »

Code: Select all

local duration = 2  -- in seconds
local t = -1

local function lerp(a, b, t)
  return t < 0.5 and a + (b - a) * t or b - (b - a) * (1 - t)
end

local function tween_parabola(a, b, t)
  return lerp(a, b, 1-(t*2-1)^2)
end

local dt

function love.update(_dt)
  dt = _dt
end

function love.draw()
  if t >= 0 then
    love.graphics.circle("fill", lerp(100, 700, t), tween_parabola(550, 50, t), 5)

    t = t + dt / duration
    if t > 1 then t = 1 end
  end
end

function love.keypressed(k)
  if k == "space" then t = 0 end
  if k == "escape" then return love.event.quit() end
end
The x coordinate is linear. The y coordinate is parabolic. The horizontal lerp takes starting and ending x, and the vertical tween_parabola takes initial y point and top y point (or bottom if you're using inverted gravity). `duration` tells how many seconds the movement should last. The `t` received by lerp and tweenParabola should always be between 0 and 1 inclusive. For this demo, use space to trigger.
User avatar
Gunroar:Cannon()
Party member
Posts: 1085
Joined: Thu Dec 10, 2020 1:57 am

Re: How to tween a parabola?

Post by Gunroar:Cannon() »

Thanks for the formula, pg!
I used it fine (for a bomb toss, heheh), though I have a problem which I want to fix up now because I had a time limit for a project so I managed before.

My timer lib, I kind of understand it's tweening functions, but I can't figure out how to add that parabola into one of it's functions (e.g. quad,sine,bounce)

Code: Select all


function Timer:tween(delay, subject, target, method, after, tag, ...)
    if type(after) == 'string' then tag, after = after, nil
    elseif type(after) == 'function' then tag = UUID()
    else tag = tag or UUID() end
    self:cancel(tag)
    self.timers[tag] = {type = 'tween', time = 0, delay = self:__getResolvedDelay(delay), subject = subject, target = target, method = method or "linear", after = after or null, 
                        args = {...}, last_s = 0, payload = self:__tweenCollectPayload(subject, target, {})}
    return tag
end

local t = {}
t.out = function(f) return function(s, ...) return 1 - f(1-s, ...) end end
t.chain = function(f1, f2) return function(s, ...) return (s < 0.5 and f1(2*s, ...) or 1 + f2(2*s-1, ...))*0.5 end end
t.linear = function(s) return s end
t.quad = function(s) return s*s end
t.cubic = function(s) return s*s*s end
t.quart = function(s) return s*s*s*s end
t.quint = function(s) return s*s*s*s*s end
t.sine = function(s) return 1-math.cos(s*math.pi/2) end
t.expo = function(s) return 2^(10*(s-1)) end
t.circ = function(s) return 1-math.sqrt(1-s*s) end
t.back = function(s, bounciness) bounciness = bounciness or 1.70158; return s*s*((bounciness+1)*s - bounciness) end
t.bounce = function(s) local a, b = 7.5625, 1/2.75; return math.min(a*s^2, a*(s-1.5*b)^2 + 0.75, a*(s-2.25*b)^2 + 0.9375, a*(s-2.625*b)^2 + 0.984375) end
t.elastic = function(s, amp, period) amp, period = amp and math.max(1, amp) or 1, period or 0.3; return (-amp*math.sin(2*math.pi/period*(s-1) - math.asin(1/amp)))*2^(10*(s-1)) end

function Timer:__tween(method, ...)
    if method == 'linear' then return t.linear(...)
    elseif method:find('in%-out%-') then return t.chain(t[method:sub(8, -1)], t.out(t[method:sub(8, -1)]))(...)
    elseif method:find('out%-in%-') then return t.chain(t.out(t[method:sub(8, -1)]), t[method:sub(8, -1)])(...)
    elseif method:find('out%-') then return t.out(t[method:sub(5, -1)])(...)
    elseif method:find('in%-') then return t[method:sub(4, -1)](...) end
end

function Timer:__tweenCollectPayload(subject, target, out)
    for k, v in pairs(target) do
        local ref = subject[k]
        if ref==nil then
        else
        assert(type(v) == type(ref), 'Type mismatch in field "' .. k .. '".')
        if type(v) == 'table' then self:__tweenCollectPayload(ref, v, out)
        else
            local ok, delta = pcall(function() return (v-ref)*1 end)
            assert(ok, 'Field "' .. k .. '" does not support arithmetic operations.')
            out[#out+1] = {subject, k, delta}
        end
        end
    end
    return out
end


function Timer:update(dt)
    for tag, timer in pairs(self.timers) do
        ...

        elseif timer.type == 'tween' then
            local s = self:__tween(timer.method, math.min(1, timer.time/timer.delay), unpack(timer.args or {}))
            local ds = s - timer.last_s
            timer.last_s = s
            for _, info in ipairs(timer.payload) do
                local ref, key, delta = unpack(info)
                ref[key] = ref[key] + delta*ds
            end
            ...
        end
    end
end
Its list of functions look like

t.linear = function(s) return s end
t.quad = function(s) return s*s end
t.cubic = function(s) return s*s*s end
t.quart = function(s) return s*s*s*s end
t.quint = function(s) return s*s*s*s*s end
t.sine = function(s) return 1-math.cos(s*math.pi/2) end
t.expo = function(s) return 2^(10*(s-1)) end
t.circ = function(s) return 1-math.sqrt(1-s*s) end
t.back = function(s, bounciness) bounciness = bounciness or 1.70158; return s*s*((bounciness+1)*s - bounciness) end
t.bounce = function(s) local a, b = 7.5625, 1/2.75; return math.min(a*s^2, a*(s-1.5*b)^2 + 0.75, a*(s-2.25*b)^2 + 0.9375, a*(s-2.625*b)^2 + 0.984375) end
t.elastic = function(s, amp, period) amp, period = amp and math.max(1, amp) or 1, period or 0.3; return (-amp*math.sin(2*math.pi/period*(s-1) - math.asin(1/amp)))*2^(10*(s-1)) end


And only have one compulsory argument, s.
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
User avatar
darkfrei
Party member
Posts: 1168
Joined: Sat Feb 08, 2020 11:09 pm

Re: How to tween a parabola?

Post by darkfrei »

I don't know what the tweening function is, but

Code: Select all

t.parab = function (x, a, b, c) return a*x*x+b*x+c end
Where a is acceleration (gravity), b is initial velocity and c is initial position.
X is some changing value, for example time or horizontal length from starting position.
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
Gunroar:Cannon()
Party member
Posts: 1085
Joined: Thu Dec 10, 2020 1:57 am

Re: How to tween a parabola?

Post by Gunroar:Cannon() »

darkfrei wrote: Tue Aug 30, 2022 1:12 pm

Code: Select all

t.parab = function (x, a, b, c) return a*x*x+b*x+c end
Where a is acceleration (gravity), b is initial velocity and c is initial position.
X is some changing value, for example time or horizontal length from starting position.
Oh, and I just make a,b and c have a default value (so the equivalent of "s" in my timer lib is x here, nice). Hopefully, his explains it more. But what's c, a constant?
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
User avatar
darkfrei
Party member
Posts: 1168
Joined: Sat Feb 08, 2020 11:09 pm

Re: How to tween a parabola?

Post by darkfrei »

Gunroar:Cannon() wrote: Tue Aug 30, 2022 1:21 pm
darkfrei wrote: Tue Aug 30, 2022 1:12 pm

Code: Select all

t.parab = function (x, a, b, c) return a*x*x+b*x+c end
Where a is acceleration (gravity), b is initial velocity and c is initial position.
X is some changing value, for example time or horizontal length from starting position.
Oh, and I just make a,b and c have a default value (so the equivalent of "s" in my timer lib is x here, nice). Hopefully, his explains it more. But what's c, a constant?
It'value of initial high. The value, that will be returned by x = 0.
https://www.desmos.com/calculator/zukjgk9iry
And
https://www.desmos.com/calculator/8keeh1pt5l
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
pgimeno
Party member
Posts: 3541
Joined: Sun Oct 18, 2015 2:58 pm

Re: How to tween a parabola?

Post by pgimeno »

See the formula I used in tween_parabola:

1-(t*2-1)^2

So:

Code: Select all

t.parab = function(s) return 1-(s*2-1)^2 end
User avatar
Gunroar:Cannon()
Party member
Posts: 1085
Joined: Thu Dec 10, 2020 1:57 am

Re: How to tween a parabola?

Post by Gunroar:Cannon() »

Yes, thanks too!!
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
User avatar
Gunroar:Cannon()
Party member
Posts: 1085
Joined: Thu Dec 10, 2020 1:57 am

Re: How to tween a parabola?

Post by Gunroar:Cannon() »

pgimeno wrote: Tue Aug 30, 2022 8:02 pm See the formula I used in tween_parabola:

1-(t*2-1)^2

So:

Code: Select all

t.parab = function(s) return 1-(s*2-1)^2 end
I think when the (Y) value of the goal is bigger than the (Y) value of the origin it has an opposite arc and reversing the at. just makes it wobble. Anyway to always make the arc one that goes up and back down no matter what (while still using tweening)?
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 19 guests