I'm adapting the physics from the Sonic Physics Guide on Sonic Retro into PICO-8 and it's going pretty alright but my character keeps sinking into platforms?

Here's the code:
--platformer stuff --physics model taken from --sonicretro.org/sonic_physics_guide --todo! --wall collisions function sign(x) if(x<0) return -1 if(x==0) return 0 if(x>0) return 1 end function floor() local v=mget(flr((x+4)/8),flr((y+8)/8)) return fget(v,0) end function _init() --basic stuff x=58 --player x y=58 --player y d=false --direction:f=r,t=l --physics stuff xsp=0 --horizontal speed ysp=0 --vertical speed jsp=5 --jump speed acc=0.1 --accelerate dec=1 --decelerate frc=0.2 --friction top=2.5 --top speed grv=0.5 --gravity cj=true --can jump? (flag) j=false --jumping? (flag) --camera stuff cx=58 --camera x offset cy=78 --camera y offset dx=58 --default x offset dy=78 --default y offset csf=2 --camera speed while fall csl=4 --camera speed on landing end function _update() if floor() then --back on land ysp=0 --pans the camera back up cy+=csl if(cy>dy) cy=dy cj=true else --in the air! ysp+=grv --pans the camera downward cy-=csf if(cy<20) cy=20 cj=false end --if can jump,allow jump!!!! if cj then if btn(4) then ysp=-jsp end end --left and right movement if btn(0) then --left button if xsp>0 then xsp-=dec elseif xsp>-top then xsp-=acc end d=true elseif btn(1) then --right button if xsp<0 then xsp+=dec elseif xsp<top then xsp+=acc end d=false else xsp=xsp-min(abs(xsp),frc)*sign(xsp) end --adds restart button in pause --menu menuitem(1,"restart",function() _init() end) x%=128 x+=xsp y+=ysp end function _draw() cls() map(0,0) --camera(x-cx,y-cy) spr(0,x,y,1,1,d) end |



From what I can tell your code just stops the jump when the detection point is detected as being on a floor tile and sets y speed to zero. So if one frame the detection point is just above the tile, then if it moves say 4 pixels (as y speed increases by 0.5 per tick) it will be within the tile next frame.
Try adding some code that moves the player back "up" to the top of the tile when it lands ( eg FLR(y/8) * 8)



I added what you suggested here:
if floor() then --back on land ysp=0 --makes sure player stands --on top of tile instead of --inside of it y=flr(y/8)*8 --pans the camera back up cy+=csl if(cy>dy) cy=dy cj=true else --in the air! ysp+=grv --pans the camera downward cy-=csf if(cy<20) cy=20 cj=false end |
but the player still sinks into the floor for 1 frame before being pushed back to the top of the tile.




Maybe you can reorganize the code so that in the cases you would notice ground after you're inside it, the position correction is applied before it's had a chance to get drawn.
Maybe when you're falling fast enough you look a frame ahead (temporarily add an extra dose of dy and recheck) and turn on a flag that activates alternative handling for high-speed floor collisions.
Sometimes I like talking through my code in verbose comment style to see if there are obvious things I'm doing out of order or not accounting for. (in this case I would step through the main update loop assuming that I've already reached a ridiculous fall speed. Heck, just a quick max dy cap might be an easy fix depending.)



A few things I can think of going on here:
-
Your character seems to go up 8 px per frame until the peak of the jump, it goes 4 down ONCE on the way down, and then 8 px the entire rest of the jump. Remove the 4px frame and you're golden.
-
It's possible that once you've hit the ground, it doesn't immediately turn gravity "off," but a frame/step later, so your character "falls 4px" once they collide with the ground.
- Have each frame check for ground collision before landing - if it does, check for 4 px and move the last 4px there.



HOLY CRAP I ACTUALLY SOLVED IT
I added x and y arguments to my FLOOR function and when I called it in _update I added the characters y speed to the y argument.
function floor(x,y) local v=mget(flr((x+4)/8),flr((y+8)/8)) return fget(v,0) end ... function _update() if floor(x,(y+ysp)) then --back on land ysp=0 cy+=csl if(cy>dy) cy=dy cj=true else --in the air! ysp+=grv --pans the camera downward cy-=csf if(cy<20) cy=20 cj=false end |
(it still causes the player to pause 2 pixels above the floor for like one frame but I at least solved the sinking in platforms thing)
thanks for the help guys!
[Please log in to post a comment]