Godot Version
3.5.3.stable
Movement Question!
So, I have been having this small issue with my movement. If I go from a fast movement state to a slower movement state it looks like the character jitters. So I’m trying to figure out something a little more smooth. Here is what I have at the moment:
if Input.is_action_pressed("Thrust"):
velocity += Vector2(0, -FORWARD_SPEED).rotated(rotation) * delta
velocity = velocity.limit_length(SLOW_MOVEMENT)
else:
velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
My thought is to remove velocity/limit_length()
and working the FRICTION
variable into the first equation to slow the character down naturally, but I’m not great with mathematics and can’t figure it out 
Any help would be greatly appreciated!
Edit: Here is a video for reference. It’s a tiny detail, but it’s been bothering me like no other.
Is there any other part of the code that edits velocity or SLOW_MOVEMENT? Seems like this should slowly reduce the speed, you are only limiting the length while thrust is active so it shouldn’t suddenly slow down like that.
I have it set up so that I can go from MEDIUM/FAST/BOOST_MOVEMENT to SLOW_MOVEMENT. The transition is a bit jarring.
Could you share some of that code? Sounds like it may be the culprit!
Sure! Here it is:
export (int) var FRICTION = 100
export (int) var SLOW_MOVEMENT = 200
export (int) var MEDIUM_MOVEMENT = 300
export (int) var FAST_MOVEMENT = 400
export (int) var BOOST_MOVEMENT = 500
export (int) var FORWARD_SPEED = 200
export (int) var BACKWARD_SPEED = 110
var moveState = SLOWMOVE
var velocity = Vector2.ZERO
enum {
SLOWMOVE,
MEDIUMMOVE,
FASTMOVE,
BOOSTMOVE1,
BOOSTMOVE2,
BOOSTMOVE3
}
func slowMove(delta):
if Input.is_action_pressed("Reverse"):
velocity += Vector2(0, BACKWARD_SPEED).rotated(rotation) * delta
velocity = velocity.limit_length(SLOW_MOVEMENT)
if Input.is_action_pressed("Thrust"):
velocity += Vector2(0, -FORWARD_SPEED).rotated(rotation) * delta
velocity = velocity.limit_length(SLOW_MOVEMENT)
animationState.travel("SlowMovement")
else:
animationState.travel("Idle")
velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
if Input.is_action_just_pressed("Speed_Up"):
moveState = MEDIUMMOVE
if Input.is_action_just_pressed("Boost") && cooldownTimer.is_stopped():
moveState = BOOSTMOVE1
func mediumMove(delta):
if Input.is_action_pressed("Reverse"):
velocity += Vector2(0, BACKWARD_SPEED).rotated(rotation) * delta
velocity = velocity.limit_length(SLOW_MOVEMENT)
if Input.is_action_pressed("Thrust"):
velocity += Vector2(0, -FORWARD_SPEED).rotated(rotation) * delta
velocity = velocity.limit_length(MEDIUM_MOVEMENT)
animationState.travel("MediumMovement")
else:
animationState.travel("Idle")
velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
if Input.is_action_just_pressed("Speed_Up"):
moveState = FASTMOVE
if Input.is_action_just_pressed("Speed_Down"):
moveState = SLOWMOVE
if Input.is_action_just_pressed("Boost") && cooldownTimer.is_stopped():
moveState = BOOSTMOVE2
func fastMove(delta):
if Input.is_action_pressed("Reverse"):
velocity += Vector2(0, BACKWARD_SPEED).rotated(rotation) * delta
velocity = velocity.limit_length(SLOW_MOVEMENT)
if Input.is_action_pressed("Thrust"):
velocity += Vector2(0, -FORWARD_SPEED).rotated(rotation) * delta
velocity = velocity.limit_length(FAST_MOVEMENT)
animationState.travel("FastMovement")
else:
animationState.travel("Idle")
velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
if Input.is_action_just_pressed("Speed_Down"):
moveState = MEDIUMMOVE
if Input.is_action_just_pressed("Boost") && cooldownTimer.is_stopped():
moveState = BOOSTMOVE3
func boostMove1(delta):
velocity += Vector2(0, -BOOST_MOVEMENT).rotated(rotation) * delta
velocity = velocity.limit_length(BOOST_MOVEMENT)
animationState.travel("Boost")
if boostLengthTimer.is_stopped():
moveState = SLOWMOVE
func boostMove2(delta):
velocity += Vector2(0, -BOOST_MOVEMENT).rotated(rotation) * delta
velocity = velocity.limit_length(BOOST_MOVEMENT)
animationState.travel("Boost")
if boostLengthTimer.is_stopped():
moveState = MEDIUMMOVE
func boostMove3(delta):
velocity += Vector2(0, -BOOST_MOVEMENT).rotated(rotation) * delta
velocity = velocity.limit_length(BOOST_MOVEMENT)
animationState.travel("Boost")
if boostLengthTimer.is_stopped():
moveState = FASTMOVE
func move():
velocity = move_and_slide(velocity)
func _physics_process(delta):
match moveState:
SLOWMOVE:
slowMove(delta)
MEDIUMMOVE:
mediumMove(delta)
FASTMOVE:
fastMove(delta)
BOOSTMOVE1:
boostMove1(delta)
BOOSTMOVE2:
boostMove2(delta)
BOOSTMOVE3:
boostMove3(delta)
move()
Seems like pressing the “Speed_Down” action will immediately change your state, thus immediately change how the velocity is limited. maybe you can use the same move_toward
with friction if the velocity is greater than the limit, instead of setting it directly? Assuming your FORWARD_SPEED is less than FRICTION.
velocity += Vector2(0, -FORWARD_SPEED).rotated(rotation) * delta
if velocity.length() > SLOW_MOVEMENT:
velocity = velocity.move_toward(velocity.limit_length(SLOW_MOVEMENT), FRICTION * delta)
There’s a lot of repeated code here, especially with boostMove1, 2, and 3, seems like a seperate “max speed” variable would perform better.
onready var max_speed: int = SLOW_MOVEMENT
var animation_prefix = "Slow"
enum {
MOVE,
BOOSTMOVE,
}
# Increasing or decreasing max_speed by set intervals, Could use
# `move_toward` on max_speed to smoothly transition to any target speed
func increaseSpeed() -> void:
if max_speed == SLOW_MOVEMENT:
max_speed = MEDIUM_MOVEMENT
animation_prefix = "Medium"
elif max_speed == MEDIUM_MOVEMENT:
max_speed = FAST_MOVEMENT
animation_prefix = "Fast"
func decreaseSpeed() -> void:
if max_speed == MEDIUM_MOVEMENT:
max_speed = SLOW_MOVEMENT
animation_prefix = "Slow"
elif max_speed == FAST_MOVEMENT:
max_speed = MEDIUM_MOVEMENT
animation_prefix = "Medium"
func normalMove(delta: float):
if Input.is_action_pressed("Reverse"):
velocity += Vector2(0, BACKWARD_SPEED).rotated(rotation) * delta
velocity = velocity.limit_length(SLOW_MOVEMENT)
if Input.is_action_pressed("Thrust"):
velocity += Vector2(0, -FORWARD_SPEED).rotated(rotation) * delta
# Using `max_speed` to dynamicly limit length, this does not
# fix sudden stops when using "Speed_Down" but reduces states
velocity = velocity.limit_length(max_speed)
animationState.travel(animation_prefix + "Movement")
else:
animationState.travel("Idle")
velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
if Input.is_action_just_pressed("Speed_Up"):
increaseSpeed()
elif Input.is_action_just_pressed("Speed_Down"):
decreaseSpeed()
if Input.is_action_just_pressed("Boost") && cooldownTimer.is_stopped():
moveState = BOOSTMOVE
func boostMove(delta):
velocity += Vector2(0, -BOOST_MOVEMENT).rotated(rotation) * delta
velocity = velocity.limit_length(BOOST_MOVEMENT)
animationState.travel("Boost")
if boostLengthTimer.is_stopped():
moveState = MOVE
func _physics_process(delta):
match moveState:
MOVE:
normalMove(delta)
BOOSTMOVE:
boostMove(delta)
move()
1 Like
This is exactly what I was looking for! With this is can remove velocity = velocity.limit_length(max_speed)
and have just have FRICTION
do all the work.
Thank you for optimizing the code! I will have to study this to refine my abilities 