godot icon indicating copy to clipboard operation
godot copied to clipboard

Camera2D pixel position all over the place (jittery) in scaled subviewport with pixel snapping

Open mrwolfyer opened this issue 1 year ago • 4 comments

Tested versions

  • Reproducible in: v4.2.2.stable.official [15073afe3]

System information

Godot v4.2.2.stable - Windows 10.0.22631 - GLES3 (Compatibility) - AMD Radeon(TM) Graphics (Advanced Micro Devices, Inc.; 31.0.21912.14) - AMD Ryzen 5 5500U with Radeon Graphics (12 Threads)

Issue description

When I make my camera follow a CharacterBody2D, even without position smoothing, the character's position keeps jittering back and forth. When analyzing the footage frame by frame, I was able to see that the character's position sometimes, and only sometimes, snaps one pixel off-center.

https://github.com/godotengine/godot/assets/82157205/0ab6cd9d-21a7-4825-86b0-984ea7aebff6

Also, I'm using the move_and_slide() function inside the _process() function instead of the _physics_process() function, but there doesn't seem to make a difference (I'm talking about the jitter, I know the difference between these two I think.). The jitter still persists, although it does seem be a little bit better.

It's weird because I've never encountered an issue like this when using Monogame or GameMaker. Is there a workaround for this? Am I just being stupid?

Thanks!

Steps to reproduce

-Create a scene with Camera2D that follows a CharacterBody2D -Put the scene inside a subviewport container with a very low-res, then scale it to fit the window. -Enable pixel snapping in the subviewport container.

When you move your CharacterBody2D, its position will sometimes snap to an adjacent pixel, making it jittery...

Minimal reproduction project (MRP)

BugReportPixelCamera.zip

mrwolfyer avatar Jun 11 '24 17:06 mrwolfyer

I can confirm that this actually happens on 4.2.2 and 4.3 beta1. In all rendering modes

JekSun97 avatar Jun 11 '24 21:06 JekSun97

This is because the player is moving at a speed that is not a multiple (or divider) of the physics step (60 Hz by default). It's mathematically impossible to achieve smooth movement if this condition is not obeyed in pixel art games. This is due to jitter: one frame, the player will be moving one pixel, but another frame, the player will be moving two pixels. This alternates in a rapid succession, which causes movement to look uneven over time.

If this is a problem for your player's movement speed, you can change the physics tick rate in the Project Settings (Physics > Common > Physics Ticks per Second) to something more suitable to your project. Note that you should enable physics interpolation in the project settings as well if using 4.3 or later, as physics ticks per second values that are not multipliers of 60 will cause visible jitter on 60 Hz displays.

If you do the following things, movement will become perfectly smooth:

  • Change player movement method to be _physics_process() instead of _process().
  • Change the Camera2D's update mode from Idle to Physics.
  • Change const SPEED = 70 to const SPEED = 60 in the player script.

Or you can do this:

  • Change player movement method to be _physics_process() instead of _process().
  • Change the Camera2D's update mode from Idle to Physics.
  • Change Physics Ticks per Second from 60 to 70 in the Project Settings.

This is also why you want to avoid normalizing diagonal movement speed in 2D, as it'll easily become jittery since multiplying this speed by ~0.707 almost never results in a speed that is perfectly aligned to the physics tick rate. This project doesn't appear to be doing this, so diagonal movement looks fine.

Calinou avatar Jun 13 '24 10:06 Calinou

Followed your instructions and everything worked! :)

Thank you so much!

mrwolfyer avatar Jun 13 '24 17:06 mrwolfyer

@Calinou Thanks so much for this insightful explanation and solution. How do other game engines manage this problem? It would be beneficial if there was a way to automatically detect when these discrepancies occur and correct them in the background. This could help ensure consistently smooth movement without requiring manual adjustments. Being new to Godot, I spent way too long trying to fix this issue. I did google it, but nothing helped. I'm definitely going to be more active on GitHub. I would love to help with the project, but that's definitely a long-term goal.

MasonGuinn avatar Jun 20 '24 08:06 MasonGuinn