Log In  


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

Cart #gejonopaye-0 | 2024-11-28 | Code ▽ | Embed ▽ | No License


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]