Godot Version
4.4
Question
I am facing a lot of stuttering and glitches involving my character’s movement. I am using CharacterBody2D and I am using deltatime.
4.4
I am facing a lot of stuttering and glitches involving my character’s movement. I am using CharacterBody2D and I am using deltatime.
The problem could be anything. Can you post your code (as pre-formatted text, e.g. by placing three backticks (```) before your first and after your last line of code)? If possible, also please record and share a short video of the problem.
extends CharacterBody2D
@onready var animated_sprite_2d = $AnimatedSprite2D
@export var starting_position:Vector2
var current_lv: String
const GRAVITY = 1000
#const SPEED = 12500
const SPEED = 250
const JUMP = -350
const PUSH_FORCE = 125
const MAX_VELOCITY = 125
var double_jump = -300
const MIN_JUMP = -150
var jumps: int = 2
var can_control = false
enum State { Idle , Run , Jump , Fall , Death }
var current_state
signal player_bounce(bounce: int)
signal disable_movement
func _ready():
current_state = State.Idle
position = starting_position
SceneTransition.fade_in()
$StartTimer.start()
current_lv = "res://areas/area1/level" + str($"../Objects/Finish".current_lv) + ".tscn"
func _on_start_timer_timeout():
can_control = true
func _process(delta):
if current_state != State.Death:
player_fall(delta, GRAVITY)
player_idle(delta)
if can_control:
player_run(delta)
player_jump(delta)
player_push()
move_and_slide()
player_animation()
elif current_state == State.Death:
player_fall(delta, GRAVITY / 2.0)
move_and_slide()
func player_fall(delta, gravity):
if !is_on_floor():
velocity.y += gravity * delta
if current_state != State.Death:
if velocity.y > 35:
current_state = State.Fall
elif velocity.y < -20:
current_state = State.Jump
if current_state == State.Death:
$AnimatedSprite2D.rotate(.1)
func player_idle(_delta):
if is_on_floor() and current_state != State.Death:
current_state = State.Idle
jumps = 2
func player_run(delta):
var direction = Input.get_axis("move_left", "move_right")
if direction:
#velocity.x = direction * SPEED #* delta
velocity.x = lerp(velocity.x, direction * SPEED, delta*(20.0 if direction else 14.0))
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
if direction != 0:
if is_on_floor():
current_state = State.Run
elif !is_on_floor():
if velocity.y < 0:
current_state = State.Jump
elif velocity.y > 0:
current_state = State.Fall
animated_sprite_2d.flip_h = false if direction > 0 else true
func player_jump(_delta):
if Input.is_action_just_released("jump") and velocity.y < MIN_JUMP:
if jumps > 1:
@warning_ignore("integer_division")
double_jump = JUMP / 2
velocity.y = double_jump
else:
@warning_ignore("integer_division")
velocity.y = JUMP / 3
if Input.is_action_just_pressed("jump"):
if is_on_floor() and jumps > 0:
velocity.y = 0
jumps -= 1
velocity.y = JUMP
current_state = State.Jump
if !is_on_floor() and jumps > 0:
jumps = 0
velocity.y = double_jump
current_state = State.Jump
func player_push():
for i in get_slide_collision_count():
var collision = get_slide_collision(i)
var crate = collision.get_collider()
if crate.is_in_group("Crates") and abs(crate.get_linear_velocity().x) < MAX_VELOCITY:
crate.apply_central_impulse(collision.get_normal() * -PUSH_FORCE)
func player_animation():
if current_state == State.Idle:
animated_sprite_2d.play("idle")
elif current_state == State.Run:
animated_sprite_2d.play("run")
elif current_state == State.Jump:
animated_sprite_2d.play("jump")
elif current_state == State.Fall:
animated_sprite_2d.play("fall")
elif current_state == State.Death:
animated_sprite_2d.play("death")
func _on_player_bounce(BOUNCE):
jumps = 1
velocity.y = BOUNCE
current_state = State.Jump
func _on_hitbox_body_entered(_body):
if current_state != State.Death:
$AnimatedSprite2D.play("death")
%PlayerCamera.apply_shake.emit(2.0)
current_state = State.Death
velocity.y = JUMP/1.5
velocity.x *= 0.5
$CollisionPolygon2D.set_deferred("disabled", true)
$Hitbox/CollisionPolygon2D.set_deferred("disabled", true)
SceneTransition.change_scene(current_lv)
func _on_disable_movement():
velocity = Vector2.ZERO
current_state = State.Idle
can_control = false
func _on_button_button_activated(value):
if value == "boss":
disable_movement.emit()
#position.x = xpos
func _on_timer_timeout():
can_control = true
I do have a video, but im not sure where I can share it.
Maybe it’s me, but other than the character seemingly hovering above the platform (which I imagine has more to do with the collision shape), I don’t see anything obviously wrong about the animations. Can you describe one of these glitches?
Oooh this could be a subpixel movement issue. Try rounding up everything (especially movement) to integer values.
That is an issue that I didn’t notice. On the godot docs, there are examples of stuttering. Every few seconds, stuttering occurs with my character (not the animations) and the character moves a few frames back.
Sorry I don’t understand what you mean. In my code, everything is an integer value.
The way move_toward
and any movement code in Godot works is by using floating point values (with decimals). I believe you scaled up your game window and used nearest interpolation to get the pixel art effect. This causes the snapping of the pixels to the pixel grid be very visible, thus the stuttering. To prevent this, I round all the positions of moving objects, and even the camera to whole integer values.
So should I round the movetoward and lerp functions or should I round the positions in a process function?
Use the round when rendering the object movement in the physics_process
(use this instead of _process
). Try adding a position.round()
at the end of the process block. Also, you can clean up the process block to use only one move_and_slide
. Like this maybe:
func _physics_process(delta):
if current_state != State.Death:
player_fall(delta, GRAVITY)
player_idle(delta)
if can_control:
player_run(delta)
player_jump(delta)
player_push()
player_animation()
elif current_state == State.Death:
player_fall(delta, GRAVITY / 2.0)
move_and_slide()
position.round()
Also, for future reference, consider using a Finite State Machine (very tedious to do though) for smoother transitions and state management.
Thank you! There is still very little stutter, but it’s almost unnoticeable. You mentioned a ‘Finite State Machine’, but I think I am using one. Is there a difference between the one im using and the one you mentioned?
You’re welcome! The Finite State Machine contains separate nodes of each character state, and a script for each one of them. It looks like this in my projects:
Thanks. Maybe in a later version of my game, ill use a finite state machine. Thanks again!
You’re welcome!