godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
88.12k stars 19.94k forks source link

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

Open MrWolfyer opened 2 months ago

MrWolfyer commented 2 months ago

Tested versions

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

JekSun97 commented 2 months ago

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

Calinou commented 2 months ago

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:

Or you can do this:

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.

MrWolfyer commented 2 months ago

Followed your instructions and everything worked! :)

Thank you so much!

lankv2 commented 2 months ago

Using Godot v4.3.beta1.official [a4f2ea91a].

The solution did not work for me although, not sure if my issue is related.

Character is a little jittery, the game resolution is 810 x 1080 (I was trying to replicate an arcade aspect ratio, I know it's not the correct resolution.) I have snap 2d transforms and vertices to pixel on, otherwise sprite looks wonky.

https://github.com/godotengine/godot/assets/109240718/68f07877-b0ff-4925-8bb5-457f33d582ee

test.zip

MasonGuinn commented 2 months ago

@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.