Player running at different speeds based on direction...sometimes

Godot Version



I took a long break from game dev and am trying to come back, starting with moving my project over to Godot 4.2 from 3.5. I’ve been mostly successful so far, but I’m running into this strange issue where my player suddenly runs faster in the rightward and downward directions than in the leftward and upward directions. The strangest part about it is that it wears off after about a minute or so, more or less. In the video below, it’s at around the 42-second mark that it all starts going at the same speed. This is just using digital arrow keys on a keyboard, no analog control.

The recording above is using _process() for movement, running V-Sync, holding a max 60FPS, and using a 75FPS monitor. The output is showing velocity.length() each frame and FPS every second. As you can hopefully see, it’s clearly a magnitude of 90 units (while running non-diagonal) and 60FPS, consistently.

In addition to restarting Godot, I’ve tried messing with combinations of switching between _physics_process() and _process(), turning V-Sync on and off, using different monitors with different FPS limits (60 and 75), setting my max FPS project setting to 0, 60, and 75, and doing the same for the Force FPS project setting…until…(even weirder yet) all of a sudden that setting just completely disappeared. I swear to God, I was using it one second, then the next time I went into project settings, Force FPS is just not there anymore. Upon looking that up, apparently Force FPS isn’t supposed to be in Godot 4 at all anymore, so it’s kinda creepy that it was there in the first place, then just made like a ghost. I’m fairly certain it was set to force 60FPS prior to this issue starting, and I had set it to 0FPS right before disappearing. So now it’s stuck in a different value.

None of these combinations solved it, only made things worse in some cases (turning off V-Sync made it so my player wouldn’t move at all, besides rotating the direction he was facing, for instance). In some cases however, while Force FPS was still available, I found that running on the 75 FPS monitor resulted in no issues at all, but dragging the window over to the 60FPS monitor had the results shown in the video. Now, it is only ever how it is in the video, no matter the monitor FPS.

Anyway, here’s all the code that should be affecting it:

extends CharacterBody2D

var state = MOVE
var input_vector = Vector2.ZERO
var roll_vector = Vector2.DOWN

@export var ACCELERATION = 500
@export var MAX_SPEED = 90
@export var FRICTION = 500

enum {

func _ready(): = true

func _process(delta):
	match state:


func move():
	input_vector.x = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
	input_vector.y = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
	input_vector = input_vector.normalized()
	velocity = velocity
	position.x = round(position.x)
	position.y = round(position.y)

func move_state(delta):

	if input_vector != Vector2.ZERO:
		animationTree.set("parameters/Idle/blend_position", input_vector)
		animationTree.set("parameters/Run/blend_position", input_vector)"Run")
		velocity = velocity.move_toward(input_vector * MAX_SPEED, ACCELERATION * delta)
		velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)

Has anyone ever dealt with this strange behavior? Any tips?

Do like this:

func move():
	input_vector = Input.get_vector("left", "right", "up", "down")
	velocity = input_vector * speed if input_vector != Vector2.ZERO else velocity.move_towards(Vector.ZERO, delta*acceleration)
	#position.x = round(position.x)
	#position.y = round(position.y)
1 Like

Hi, thank you for your fast response!!

Some questions, if you don’t mind…

  1. Do you think I should be using _physics_process() instead of _process()? _process() has always worked for me here but during my research on this issue, I saw some say that _physics_process() should be used for all moving things over _process() when possible.
  2. By “speed” did you mean “MAX_SPEED?”
  3. Do you think I should take out the set_velocity(velocity) line as you did? Before the move to Godot 4, this and the move_and_slide() lines were one, simply:
    velocity = move_and_slide(velocity)
    Godot 4’s transfer tool from Godot 3 changed this to the two lines, move_and_slide() and set_velocity(velocity) as shown in my post.
  4. You’ve commented out my position rounding lines, I put those in before because if the player ever ended up at a sub-pixel position, everything moving on-screen would appear jittery. Those lines solved that to make it so the player was never at a sub-pixel position. Do you not think those are needed now?
  5. I’ve never seen someone use if and else statements in the same line like that. Is that the same as saying the following?
if input_vector != Vector2.ZERO:
	velocity = input_vector * speed
	velocity.move_towards(Vector.ZERO, delta*acceleration)

I’ve assumed the answers above are yes, yes, yes, no, and yes, and this seems to have worked. So thank you!!

However, I would like to hear your thoughts on my questions above/below as I don’t want to just assume, if you don’t mind. Even though it seems to be working at the moment, issues could arrive later on if I act naive. I can’t see a difference in my demo when switching your answers for 1, 3, 4, and 5, but I just want to check.

  1. Please confirm if what I saw was correct and if I should do that.
  2. Please confirm.
  3. I wouldn’t want to blindly change what Godot set for the set_velocity thing unless it’s backed up by your confidence, so please confirm.
  4. Please confirm.
  5. Please confirm.


That all being said, that Input.get_vector() is awesome! Looking at the documentation for it, it is made exactly for this situation just like you used it:

Vector2 get_vector(negative_x: StringName, positive_x: StringName, negative_y: StringName, positive_y: StringName, deadzone: float = -1.0) const

Gets an input vector by specifying four actions for the positive and negative X and Y axes.
This method is useful when getting vector input, such as from a joystick, directional pad, arrows, or WASD. The vector has its length limited to 1 and has a circular deadzone, which is useful for using vector input as movement.

So, with that, I was able to take out my 3 lines for input_vector.x, y, and normalized() in its place, but I did have to add in delta as an argument for the move() function and update that every time its referenced as it was not called before. That shouldn’t raise any additional issues later on though, I don’t think.

Thank you again!

  1. _process(delta) is called every frame and its frequency can vary depending on the frame rate. _physics_process(delta) is called at a fixed frequency, matching the physics tick of the engine, which by default is 60 times per second. I think _physics_process(delta) is correct
  2. Yeah
  3. you not need to use set_velocity()
  4. Ok, you can use round()
  5. Yes, its same, but for short, you can use my way that I used if else, it is called Ternary operation, for more information, you can visit this page
1 Like

Dude, you rock. You really are the king! Thanks so much!

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.