Enemy will speed up when trasitioning to idle state

Godot Version

4.5

Question

I’m making a 2D platformer game, and I have an enemy that will chase the player when they get in range. But when the player leaves the area for some reason, the enemy will launch itself forward, which makes no sense to me cause I’m using the move_to() function to slow them down, so I have no idea why they get a speed boost.

If need be, I can send a video of what I’m talking about, but right now I’m unable to.

Code

Idle State code

extends NodeState

#the export vars
@export var charcterBody2D: CharacterBody2D
@export var animatedSprite2D: AnimatedSprite2D
@export var friction: int = 500

@warning_ignore("unused_parameter")
func on_process(delta: float) -> void:
	pass

func on_phyisics_process(delta: float) -> void:
	#slows down the dino until the stop
	charcterBody2D.velocity.x = move_toward(charcterBody2D.velocity.x, 0, friction * delta)
	#plays the idle animation
	animatedSprite2D.play("idle")
	#calls the move and slide function
	charcterBody2D.move_and_slide()

func enter() -> void:
	pass

func exit() -> void:
	pass

Attack State

extends NodeState

#the export vars
@export var charcterBody2D: CharacterBody2D
@export var animatedSprite2D: AnimatedSprite2D
@export var speed: int = 200
var maxSpeed: int

var player: CharacterBody2D

func enter() -> void:
	#sets player to the player by looking in the player group and taking the first one
	player = get_tree().get_nodes_in_group("Player")[0] as CharacterBody2D
	#sets the max speed
	maxSpeed = speed + 20

@warning_ignore("unused_parameter")
func on_process(delta: float) -> void:
	pass

func on_phyisics_process(delta: float) -> void:
	var direction: int
	
	#if the player is on right side it flips the sprite and switches direction
	if charcterBody2D.global_position > player.global_position:
		animatedSprite2D.flip_h = false
		direction = -1
	#if the player is on left side it flips the sprite and switches direction
	elif charcterBody2D.global_position < player.global_position:
		animatedSprite2D.flip_h = true
		direction = 1
	
	#plays the attack animation
	animatedSprite2D.play("attack")
	
	#sets the velocity to move the parent node
	charcterBody2D.velocity.x += direction * speed * delta
	#clamps the speed so it doesn't go too fast
	charcterBody2D.velocity.x = clamp(charcterBody2D.velocity.x, -maxSpeed, maxSpeed )

func exit() -> void:
	pass

State Machine Controller

extends Node

@export var nodeStateMachine: NodeStateMachine

var player: Player
@onready var ray_cast_2d: RayCast2D = $"../RayCast2D"
@onready var attack_area: Area2D = $"../AttackArea"

@warning_ignore("unused_parameter")
func _physics_process(delta: float) -> void:
	
	if !attack_area.has_overlapping_areas() and ray_cast_2d.is_colliding():
			player = null
	
	if !player:
		nodeStateMachine.transition_to("idle")
		return
	
	var targetPostion: Vector2 = ray_cast_2d.to_local(player.center.global_position)
	ray_cast_2d.target_position = targetPostion
	
	
	if ray_cast_2d.is_colliding():
		nodeStateMachine.transition_to("idle")
	else:
		nodeStateMachine.transition_to("attack")

func _on_attack_area_body_entered(body: Node2D) -> void:
	#sets the state to attack when the player is in range
	if body.is_in_group("Player"):
		player = get_tree().get_nodes_in_group("Player")[0] as CharacterBody2D
		nodeStateMachine.transition_to("attack")
		var targetPostion: Vector2 = ray_cast_2d.to_local(player.center.global_position)
		ray_cast_2d.target_position = targetPostion
		

func _on_hurt_box_area_entered(area: Area2D) -> void:
	if !area.get_parent().get_parent().has_method("get_damage_amount"):
		return
	player = get_tree().get_nodes_in_group("Player")[0] as CharacterBody2D
	var targetPostion: Vector2 = ray_cast_2d.to_local(player.center.global_position)
	ray_cast_2d.target_position = targetPostion
	nodeStateMachine.transition_to("attack")

The Enemy’s code

extends Enemy

func _ready() -> void:
	set_floor_constant_speed_enabled(true)

@warning_ignore("unused_parameter")
func _physics_process(delta: float) -> void:
	if global_position.y > 200:
		death()

func _on_hurt_box_area_entered(area: Area2D) -> void:
	Util.damage(area, self)

Hi,

That line seems to work fine for me. I’m no move_toward expert but I just tried with some values and the results are fine.

charcterBody2D.velocity.x = move_toward(charcterBody2D.velocity.x, 0, friction * delta)

You can try to clamp the velocity afterward, as you’re done already in the attack state, but I’m really not sure the issue comes from that code.

Are there any other place where the velocity is updated during state change, or maybe another call to move_and_slide that could be related to such a bug?

1 Like

in your enter state functions for attack and idle add a:

print("entering attack")
or 
print("entering idle")

Or in your NodeStateMachine add a similar print statement for your transition_to function.

Either way, I suspect you are entering idle and then re-entering attack again giving you the boost in velocity. If that is so, it will be the raycast checking. Not sure as I cannot see the NodeStateMachine code.

1 Like

I did check that, and that wasn’t the issue. It enters only once as intended

1 Like

ok after a bit of debugging i relized it only happend when the enemy was starting to run left but was still going right and yet to start moving the correct way.

i think it was a move_and_slide bug. i move the function to be in the dino code instead of each state and that fixed it. no idea how but it did.

1 Like

You may have an issue with multiple states calling the function at the same time. Anyway, glad it’s fixed.

2 Likes