So today, after staring at the problem for another half an hour, I finally managed to fix it. Turns out there were two separate problems, I'll try to explain both of them here.
The first problem was the "camera" scrolling way faster than the player was moving. This was caused by my failure to understand how exactly the translate()
function works. The offSetY variable, which tracked how much the player had moved, was getting its value calculated as an absolute value of how much the player had moved from its original position. This meant that for the first pixel moved, the camera was also moved one pixel, but for the second one the camera was moved two pixels and so on. The cumulative effect caused the camera to scroll up/down uncontrollably with extended periods of movement. I fixed this by adding a variable int offSetHelper
, the value of which is always the value of offSetY on the previous iteration of the game loop. The translate()
function is then called with the parameters (0, -offSetY + offSetHelper)
, meaning that its parameters can only ever be either (0,0)
or (0,1)
, depending on whether the players absolute position (a float) has changed one full pixels worth or not.
The second problem was objects outside the initially visible screen area (0,0)
to (480,800)
getting drawn multiple times in the same image, aka the offscreen area where the paint()
method runs not getting cleared properly. The fix to this was simple: instead of d.clearRect(0, 0, 480, 800);
I used d.clearRect(0, -100000, 480, 100800);
, which covers the entire levels area. It's likely there is a more elegant solution which only clears the area that will actually get drawn, but this solution works for me.
Thank you to the commenters, I hope I didn't waste too much of your time. I'd already been staring at this problem about three evenings before posting here, just happened to finally look at it the right way today.