Skip to content

Rotation plus motion causes jitter in pixel-perfect mode #57221

@Cerno-b

Description

@Cerno-b

Godot version

3.4.2.stable.official

System information

Win10, NVIDIA GeForce GTX 960

Issue description

I am working in pixel-perfect mode (viewport). When I rotate a sprite it follows the pixel grid perfectly, just as I want. Motion is also as expected, even if I use float velocities, the character is always fixed to the pixel grid, which is exactly what I would expect.

Now this:

If I move my rotated character with integer speed, this works fine (here: 1 px per frame):

ok

But if I move my character with float speeds (here: 1.01 px per frame), the rotation is not stable and I get jittery pixel shifts all across the sprite:

not_ok

This effect seems to stem from the interaction between the float position and the float angle.

This is a version with float speed (1.01 px per frame) and int (90°) rotation, which is fine:

ok_90_degree

It seems like Godot maps the float position and angle to the pixel grid in one go, which accumulates rounding errors between the two parameters into a single step. This could lead to the fractional part of the position influencing the outcome of the rotation, which should be independent from it.

If that is the case, a solution might lie in the direction of first rounding the position before applying the rotation.

EDIT

The same seems to be happing for scaling.
I was able to see a problem with the combination of float scale and float position.
But I was also able to see the problem with int position (px/frame) and a very specific scale (1.5 or 2.5), other float scalings seem to work with int positions.

Steps to reproduce

This is the minimal example code I used (also see Godot project below). It's pretty standard. Default settings except these two:

  • Display -> Window -> Size -> 320x180
  • Display -> Window -> Stretch -> viewport

Change the values in the code as the comment states to get the different behaviors:

func _physics_process(delta):
	
	var SPEED_INT = 1.0
	var SPEED_FLOAT = 1.01
	
	var ANGLE_90 = PI/2
	var ANGLE_float = 1

	# change these to reproduce:
	# SPEED_INT / ANGLE_float --> ok
	# SPEED_INT / ANGLE_90 --> ok
	# SPEED_FLOAT / ANGLE_90 --> ok
	# SPEED_FLOAT / ANGLE_float --> not ok
	var speed = SPEED_INT  
	var angle = ANGLE_float
			
	if Input.is_key_pressed(KEY_A):
		self.position.x -= speed
	if Input.is_key_pressed(KEY_D):
		self.position.x += speed
	if Input.is_key_pressed(KEY_W):
		self.position.y -= speed
	if Input.is_key_pressed(KEY_S):
		self.position.y += speed
	if Input.is_key_pressed(KEY_E):
		self.rotate(angle)
	if Input.is_key_pressed(KEY_Q):
		self.rotate(-angle)
	if Input.is_key_pressed(KEY_SPACE):
		self.rotation = 0

Minimal reproduction project

game.zip

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions