For such precise timing requirements, my recommendation would be to use a tweening library to operate a mouse joint. That way you control the duration, position, and the acceleration profile.
Here's an example with a very basic tweening function (no library), save as main.lua and press space to activate:
Code: Select all
local orig_x, orig_y = 200, 300
local dest_x, dest_y = 600, 300
local radius = 80
local platform_w = radius * 4 + dest_x - orig_x
local platform_h = radius / 4
local time_str = ""
local tween_timer = false
local tween_max
local tween_func
local world = love.physics.newWorld(0, 98, false)
local platform_b = love.physics.newBody(world, (orig_x + dest_x) / 2, orig_y + radius + platform_h/2, "static")
local platform_s = love.physics.newRectangleShape(0, 0, platform_w, platform_h)
local platform_f = love.physics.newFixture(platform_b, platform_s)
local ball_b = love.physics.newBody(world, orig_x, orig_y, "dynamic");
local ball_s = love.physics.newCircleShape(0, 0, radius);
local ball_f = love.physics.newFixture(ball_b, ball_s)
local ball_j = love.physics.newMouseJoint(ball_b, orig_x, orig_y)
local function tween(time, func)
tween_timer = 0
tween_time = time
tween_func = func
end
-- Linear interpolation between a and b by fraction t (with t between 0 and 1)
local function lerp(a, b, t)
return t < 0.5 and a + (b - a) * t or b - (b - a) * (1 - t)
end
-- Easing function: apply a curve to 't' to make movement slower at both ends,
-- when calling lerp()
local function ease(a, b, t)
return lerp(a, b,
-- smoothstep https://en.wikipedia.org/wiki/Smoothstep
-- t * t * (3 - 2 * t)
-- smootherstep https://en.wikipedia.org/wiki/Smoothstep#Variations
t * t * t * (t * (t * 6 - 15) + 10)
-- linear
-- t
)
end
-- Move ball from origin to destination with easing. This function receives
-- the fraction of the time interval from 0 to 1 as the 't' parameter.
local function ltr(t)
ball_j:setTarget(ease(orig_x, dest_x, t), ease(orig_y, dest_y, t))
end
-- Move ball from destination to origin with easing
local function rtl(t)
ball_j:setTarget(ease(dest_x, orig_x, t), ease(dest_y, orig_y, t))
end
function love.update(dt)
-- Update tween timer
if tween_timer then
tween_timer = tween_timer + dt
-- calculate t from 0 to 1
local t = math.min(tween_timer / tween_time, 1)
-- call tween function (ltr or rtl), passing t to it
tween_func(t)
-- update time string with current tween duration
time_str = tostring(tween_time * t)
-- if t is 1, the tween has finished
if t == 1 then tween_timer = false end
end
world:update(dt)
end
function love.draw()
-- draw ball
love.graphics.setColor(1, 1, 1)
local x, y = ball_b:getPosition()
love.graphics.circle("fill", x, y, radius)
love.graphics.setColor(0, 0, 0)
local angle = ball_b:getAngle()
love.graphics.line(x, y, x + radius * math.cos(angle) * 0.9,
y + radius * math.sin(angle) * 0.9)
-- draw origin and destination
love.graphics.setColor(1, 0, 0)
love.graphics.circle("line", orig_x, orig_y, radius)
love.graphics.setColor(0, 1, 0)
love.graphics.circle("line", dest_x, dest_y, radius)
-- draw platform
love.graphics.setColor(1, 1, 1)
love.graphics.polygon("fill", platform_b:getWorldPoints(platform_s:getPoints()))
-- draw time
love.graphics.setColor(1, 1, 1)
love.graphics.print(time_str, 0, 0)
end
function love.keypressed(k)
if k == "escape" then return love.event.quit() end
if k == "space" then
-- if we're left of the middle point, go left-to-right, else right-to-left
if ball_j:getTarget() < (orig_x + dest_x) / 2 then
tween(2, ltr)
else
tween(2, rtl)
end
end
end