## The Question

Hello everyone,

I am ludicrously close to finishing the engine for my game, but some physics equations have me stuck. Here is what I'm trying to achieve:

• The players are effectively nodes that can switch between being pendulum bobs and fulcrums. One can be anchored to serve as a fulcrum while the other swings. If both are anchored, neither moves. If both try to un-anchor themselves, they fall.
• In order to let the players navigate the playfield, I want the swing of a pendulum bob to increase bit by bit. (This will let players climb the playfield by swinging upward instead of just descending.)
• Once a bob makes a full loop around the fulcrum, I want it to continue in a circle at a more-or-less consistent speed.

With the help of this math writeup, I managed to assemble a simplified set of physics variables and equations that do an okay job of simulating momentum and creating a non-degrading swing (i.e., the swing distance never shrinks due to friction). But I haven't figured out how to make the desired increase in swing distance happen. I'm sure it's a single value or sign that I haven't identified.

I suspect I need to do something to increment the value of `p.a` in the function `move_player(p)`, but I am not sure how to approach it -- adding a value, multiplying by a value, etc.

Could someone better at physics/math please offer suggestions?

Thank you for any and all pointers!

The annotated code is below. It's 200 lines in total, but the relevant parts are the first ~90.

Cart #zafebaraza-0 | 2024-06-14 | Code ▽ | No License
1

## The Code

 ```function _init() screen_center=63 player_size=5 tether_length=25 g=20 --gravity/momentum. more=stronger pull. negative values flip the swing's arc. k=-(g/tether_length) default_v=0 --negative=swing left, positive=swing right default_dt=0.01 --period/speed of swing (screwy if >=1) dt_cap=0.1 dt_boost=dt_cap/(g*10) red={ controller=0, button=5, control_mode="toggle", --"hold" or "toggle" x=screen_center-tether_length/2, y=screen_center, colors={2,8,2}, r=player_size, a=0, v=default_v, dt=default_dt, glyph=nil, anchor=nil } blue={ controller=0, button=4, control_mode="toggle", x=screen_center+tether_length/2, y=screen_center, colors={1,12,1}, r=player_size, a=0, v=default_v, dt=default_dt, glyph=nil, anchor=nil } red.partner=blue blue.partner=red players={red,blue} for p in all(players) do set_glyph(p) end end -->8 --updates function _update() for p in all(players) do control_player(p) check_angle(p) move_player(p) end end function move_player(p) if p.anchor==false and p.partner.anchor==true then p.v+=k*cos(p.a)*p.dt --to do: steadily increase swing length --to do: circular movement at consistent speed after looping --what i really want to do is boost dt and angle at the end of each swing p.a+=p.v*p.dt --dt should start small, increase, and cap around 0.1 p.dt+=dt_boost if p.dt>dt_cap then p.dt=dt_cap end p.x=p.partner.x+tether_length*cos(p.a) p.y=p.partner.y+tether_length*sin(p.a) end if p.anchor==false and p.partner.anchor==false then --to-do: let swinging players finish their swing p.y+=g/5 end end --everything below this line works, and is included in case anyone wants to paste the full code into a cart function control_player(p) if p.control_mode=="toggle" then if p.anchor==nil then p.anchor=true end if btnp(p.button,p.controller) then if not out_of_bounds(p) then p.anchor=not p.anchor p.dt=default_dt p.v=default_v end end if p.anchor==true then p.colors[3]=7 else p.colors[3]=p.colors[1] end elseif p.control_mode=="hold" then if p.anchor==nil then p.anchor=false end if btn(p.button,p.controller) then if not out_of_bounds(p) then p.anchor=true end else p.anchor=false end if p.anchor==true then p.dt=default_dt p.v=default_v p.colors[3]=7 elseif p.anchor==false then p.colors[3]=p.colors[1] end end end function check_angle(p) local angle=atan2(p.x-p.partner.x,p.y-p.partner.y) p.a=angle end function out_of_bounds(p) if p.x>128 or p.x<0 or p.y>128 or p.y<0 then return true end end function set_glyph(p) local glyphs={"⬅️","➡️","⬆️","⬇️","🅾️","❎"} p.glyph=glyphs[p.button+1] end -->8 --draws function _draw() cls() draw_debug(red,1) draw_debug(blue,85) draw_tether(red,blue) for p in all(players) do draw_player(p) end end function draw_player(p) circfill(p.x,p.y,p.r,p.colors[1]) circfill(p.x,p.y,p.r-1,p.colors[2]) circfill(p.x,p.y,p.r-3,p.colors[3]) print(p.glyph,p.x-3,p.y-2,p.colors[2]) for i=1,2 do pset(p.x+1+i,p.y-5+i,7) end end function draw_tether(p1,p2) line(p1.x,p1.y,p2.x,p2.y,10) end function draw_debug(p,spot) local bottom=123 local col=p.colors[2] if p.anchor==true then print("anchored",spot,bottom-21,col) else print("unanchored",spot,bottom-21,col) end print("v: "..p.v,spot,bottom-14,col) print("a: "..p.a,spot,bottom-7,col) print("dt: "..p.dt,spot,bottom,col) end```

2

Try this: don't change `dt`, and instead do something like:

 ` p.v+=(k*cos(p.a)+0.01*sgn(p.v))*p.dt`

This adds a small angular acceleration in the direction of the current velocity, and should increase the angle of swing over time.

This works beautifully! I'd never heard of `sgn()` before, either, but I can see it being quite useful.

All I need to do now is figure out how to speed up the swing from here.

Thank you, @luchak. You've helped me solve the last major obstacle in my game. Now for all the stuff I actually know how to do!

I actually did end up keeping the `dt` updates, because they made for a more pleasing swing speed. With a simple acceleration cap included, everything works nicely.

Thanks again, Luchak!

Cart #fawanefida-0 | 2024-06-14 | Code ▽ | License: CC4-BY-NC-SA