Hello,
I am simulating a ball being dragged around by a player. I'd like to create some animation to simulate that the ball is rotating (top-down view). I am trying to do that with a light source on top of the ball but I can't quite get it working correctly.
Looking for advice or some pointers to good tutorials on this subject.
Thanks in advance
function _init()
player = {x=64, y=64}
end
function _update()
local move_x, move_y = 0, 0
if btn(0) then move_x = -1 end
if btn(1) then move_x = 1 end
if btn(2) then move_y = -1 end
if btn(3) then move_y = 1 end
-- update player position and animation
if move_x != 0 or move_y != 0 then
player.x += move_x
player.y += move_y
end
update_ball()
end
function _draw()
cls()
rectfill(player.x,
player.y,
player.x+8,
player.y+8,
8)
draw_ball()
end
--ball
ball = {x=80, y=80, vx=0, vy=0, length=24, friction=0.9, mass=5, pull_force=0.2}
ball.hitbox = {2,2,6,6}
ball.name = "ball"
ball.radius = 5
-- in ball initialization
ball.light_pos = {x = ball.x, y = ball.y}
function distance(x1, y1, x2, y2)
return sqrt((x2-x1)^2 + (y2-y1)^2)
end
function update_ball()
local dx = ball.x - player.x
local dy = ball.y - player.y
local dist = distance(ball.x, ball.y, player.x, player.y)
if dist > ball.length then
local angle = atan2(dx, dy)
local pull_strength = (dist - ball.length) * ball.pull_force
ball.vx -= cos(angle) * pull_strength
ball.vy -= sin(angle) * pull_strength
end
ball.vx *= ball.friction
ball.vy *= ball.friction
ball.x += ball.vx
ball.y += ball.vy
if ball.vx != 0 or ball.vy != 0 then
local move_dir_x = ball.vx > 0 and 1 or (ball.vx < 0 and -1 or 0)
local move_dir_y = ball.vy > 0 and 1 or (ball.vy < 0 and -1 or 0)
ball.light_pos.x += move_dir_x * 0.5
ball.light_pos.y += move_dir_y * 0.5
-- check if light has reached ball edge
local light_dist = distance(ball.x, ball.y, ball.light_pos.x, ball.light_pos.y)
if light_dist >= ball.radius+1 then
-- move light along the direction of movement
ball.rolled_under = true
ball.roll_under_timer = ball.roll_under_frames
-- reposition light on opposite side of ball and continue moving
ball.light_pos.x = ball.x - move_dir_x * ball.radius
ball.light_pos.y = ball.y - move_dir_y * ball.radius
-- immediately continue moving in the same direction
ball.light_pos.x += move_dir_x * 0.5
ball.light_pos.y += move_dir_y * 0.5
end
end
end
function draw_ball()
line(player.x+4, player.y+4, ball.x, ball.y, 13)
circfill(ball.x, ball.y, 5, 1)
-- light spot
circfill(ball.light_pos.x, ball.light_pos.y, 1, 7)
end |
Closer, but still quite wonky. Also realised that the light source wouldn't rotate (duh), so just putting a dot on the ball instead.
--ball
ball = {x=80, y=80, vx=0, vy=0, length=24, friction=0.75, mass=5, pull_force=0.2}
ball.hitbox = {2,2,6,6}
ball.name = "ball"
ball.radius = 5
ball.light_travel =0
-- in ball initialization
ball.light_pos = {x = ball.x, y = ball.y}
function distance(x1, y1, x2, y2)
return sqrt((x2-x1)^2 + (y2-y1)^2)
end
function update_ball()
local dx = ball.x - player.x
local dy = ball.y - player.y
local dist = distance(ball.x, ball.y, player.x, player.y)
if dist > ball.length then
local angle = atan2(dx, dy)
local pull_strength = (dist - ball.length) * ball.pull_force
ball.vx -= cos(angle) * pull_strength
ball.vy -= sin(angle) * pull_strength
end
ball.vx *= ball.friction
ball.vy *= ball.friction
ball.x += ball.vx
ball.y += ball.vy
-- calculate movement angle
if ball.vx != 0 or ball.vy != 0 then
local angle = atan2(ball.vy, ball.vx)
-- move light along the ball's movement trajectory
ball.light_pos.x = ball.x + cos(angle) * ball.light_travel
ball.light_pos.y = ball.y + sin(angle) * ball.light_travel
-- when light reaches edge, reset travel
ball.light_travel += 0.1
if ball.light_travel >= ball.radius then
ball.light_travel = -ball.radius
end
end
end
function draw_ball()
line(player.x+4, player.y+4, ball.x, ball.y, 13)
circfill(ball.x, ball.y, 5, 7)
-- light spot
--circfill(ball.light_pos.x, ball.light_pos.y, 1, 7)
pset(ball.light_pos.x, ball.light_pos.y,1)
end |
Could you post a demo cart ? It would help visualizing what you are trying to do.
A word of warning : your distance function is likely to give wrong results due to integer overflow.
distance(0,0,128,128) will give you 0 for example.
(128*128+128*128)=-32768 , sqrt(-32768)=0
Your ball is not too big. A great way to sell the rotation could be to keep track of the three angles of rotation of the ball based on rolling, and color the ball accordingly. You could treat your ball as a transparent sphere with an embedded D20 , and color each pixel with the color of the visible face. Treating the ball as two halves of different colors also works great.
Here is the catridge
I realised I have my atan the wrong way around, it gets better with that fixed but doesn't really look like rolling to me.
[Please log in to post a comment]




