Player Jittering with Camera Follow

Started by
9 comments, last by Magdev 10 years, 10 months ago

I'm making a 2D sidescroller in XNA and I'm having an issue with the way the camera follows the player. When the player walks left or right, and its walk speed is a decimal number, there's jitter.

Lpwkcxh.gif

This is what it looks like. I had to slow the game down to 50% speed for the gif to catch the jitter frames.

(Ignore the random vertical twitching, that's just some strange graphical bug that happens when I scale the backbuffer up too far.)

I believe the cause of this is the relative speed between the camera and the player.

In the gif, the player's speed is at 1.5, so it will alternate between moving one pixel and two pixels per frame. This causes a sort of desync between the player and camera movement which causes the jitter.

I need to be able to use decimal speeds because first of all, a speed of 1 is a bit slow for the player, and 2 is a bit fast, but also, if I add acceleration/deceleration the player's speed won't always be a whole number.

Some details:

- Nothing is rounded in-code. All positions and velocities are floats. Rounding isn't needed since nothing can move in sub-pixels. I reduced the backbuffer resolution so changing an object's position by 1 will move it one pixel. Everything is drawn at integer positions, of course.

- It's not the update order. The cycle is simple right now. The player moves, then the camera is updated. Switching the order doesn't fix the issue.

- I remembered that Cave Story has nice and smooth camera interpolation, and large pixels, so I fired it up to see what it looks like, and I was really surprised too see that it suffers the same problem. For roughly a second after the camera starts moving, the player jitters by one horizontal pixel.

My camera movement interpolation code is this:


if (camPos != playerPos) camPos += (playerPos - camPos) * strength;

Any ideas on how to fix this? I'm sure SOMEBODY has had this issue before other than me (and Pixel).

If I need to include any more relevant code snippets, just let me know. I can't really think of any that would help at the moment.

Advertisement

If everything is drawn at one pixel increments but the camera moves continuously then there is going to be aliasing between the 2 values over time when the values get truncated to pixel boundaries.

eg. Imagine your player moves at 0.7 pixels to the right per frame.

Frame 1: x=0.7 renderX=0

Frame 2: x=1.4 renderX=1

Frame 3: x=2.1 renderX=2

Frame 4: x=2.8 renderX=2

Frame 5: x=3.5 renderX=3

Between frame 3 and frame 4 the player doesn't move from pixel 2 at all, but the camera will have moved ahead, some way towards 2.8. The camera moves continuously but the player motion does not.

You need to make your camera track the on-screen render position, not the logical position before rendering.

You need to make your camera track the on-screen render position, not the logical position before rendering.

Ooh. That makes a lot of sense. I did what you said and it fixed the jittering during constant movement, but there's still jittering while the camera builds up to the player's speed.

JELQYJl.gif

I ran the game at 10% speed and output the camera's delta and position, holding a key to mark the jittering frames when I saw them.

sF53YpT.png

It's not super accurate because of my timing, but it seems like it only jitters when the camera moving more than one pixel per frame. The player moves at a constant speed of 1.5. When the camera is at 1.5 delta, it alternates between one pixel per frame and 2. Not sure how helpful this information is.

I could possibly clamp the camera speed below 1, but that's probably too slow, and just a workaround rather than a fix.

Any ideas?

It would be useful to see the code in which the camera position is updated.

The camera position code is in the first post.

if (camPos != playerPos) camPos += (playerPos - camPos) * strength;
This seems dodgy to me. What happens if the distance between player and camera is smaller than the length of (playerPos-camPos)*strength? This would happen if strength>1. You don't show how you calculate strength, so I assume it's tied to elapsed time somehow. If strength>1 then the camera will over-shoot the player's position which can possibly cause camera stutter. You might try clamping strength to 1 to keep it from overshooting.

I saw that code but assumed you have updated it to make the camera movement relative to the on-screen player position. The problem is in my opinion that the camera may sometimes need to move faster then the player to catch it. A possible solution may be to clamp the camera movement to the player movement on screen when the player is moving (i.e. its velocity is non zero).

It's not super accurate because of my timing, but it seems like it only jitters when the camera moving more than one pixel per frame. The player moves at a constant speed of 1.5. When the camera is at 1.5 delta, it alternates between one pixel per frame and 2. Not sure how helpful this information is.

To some degree this is an inevitable consequence of the system you have. You're essentially rounding a real number to an integer and so you have an effective error of +/- 0.5 each time. This means the movement speed in pixels varies by up to one whole pixel from frame to frame, which means it's going to jump ahead and behind a camera moving at a continuous rate. Making the camera track the pixel position like I suggested may reduce the problem but, now I think about it more clearly, it can't solve it.

I think the only real solution is to be able to move both the entity and the camera with subpixel precision, or to lock the camera to the entity pixel position (rather than attempting to smoothly track it after it moves).


if (camPos != playerPos) camPos += (playerPos - camPos) * strength;
This seems dodgy to me. What happens if the distance between player and camera is smaller than the length of (playerPos-camPos)*strength? This would happen if strength>1. You don't show how you calculate strength, so I assume it's tied to elapsed time somehow. If strength>1 then the camera will over-shoot the player's position which can possibly cause camera stutter. You might try clamping strength to 1 to keep it from overshooting.

Strength is just a constant between 0 and 1 that I define in-code which is just the strength that the camera follows the player. No time variable is needed.

The problem is in my opinion that the camera may sometimes need to move faster then the player to catch it.

That's not related to the jittering problem, but it can be a problem for some people. I don't see it as a problem though. I don't think it's even possible for the player to move fast enough to leave the camera's view, but if it is, it's not really a big deal. If it becomes an issue then I can just constrain it.

To some degree this is an inevitable consequence of the system you have. You're essentially rounding a real number to an integer and so you have an effective error of +/- 0.5 each time. This means the movement speed in pixels varies by up to one whole pixel from frame to frame, which means it's going to jump ahead and behind a camera moving at a continuous rate. Making the camera track the pixel position like I suggested may reduce the problem but, now I think about it more clearly, it can't solve it.

I think the only real solution is to be able to move both the entity and the camera with subpixel precision, or to lock the camera to the entity pixel position (rather than attempting to smoothly track it after it moves).

Yeah, it does seem like the nature of the system. I have a few workaround ideas in my head, but at the very least, the first fix did make it pretty acceptable and not very noticeable. The jittering how it is now is exactly like Cave Story's jittering, which is barely noticeable, which also reinforces how it's probably not worth fixing further. Thanks a lot for your help.

If I come across anything that makes it better, I'll post it in this thread.

You have misunderstood my suggestion. Let suppose the player is moving 1 pixel in some direction. Since (playerPos - camPos)*strength is not related to the player movement, it can be any value. In particular, it may rounds to 2 pixels in the same direction. The result is the player moving one pixel in the wrong direction. In the next frame the player will move of 2 pixels and the camera of some other amount and you now see a jitter. The jitter is caused by the difference between the camera position and the player position not being a monotonic function. My suggestion is thus to make it monotonic by clamping the camera position so that it does not move faster than the player. When the player is still you clearly have to catch the player if you do not want the player to exit from the screen.

This topic is closed to new replies.

Advertisement