I'm trying to make the enemy move towards either the player or a ball in my 3D football game, but I keep encountering an issue where the enemy will become faster or slower depending on the distance from the ball, and obviously I don't really want that as they can become unnaturally fast and cause lag.

Here is the code for finding the ball:

func _process():
                       if ball_detected:
				target = ball_identity
				mode = "chasing_ball"
				speed_multiplier = 0.5
				var pos = target.translation
				var self_pos = self.translation
				var look_at_me = Vector3(pos.x, 0, pos.z)
				#print(look_at_me)
				look_at(look_at_me, Vector3.UP)
			else:
				target = self
				speed_multiplier = 0
				mode = "idle"
		move_to_target(delta)

And then here is the code to move the enemy:

func move_to_target(delta):
	var direction = (target.transform.origin - transform.origin).normalized() #sets the direction
	direction.y = 0 #this is to prevent the enemy from floating, as I has encountered this before
	move_and_slide(direction * speed * speed_multiplier * delta, Vector3.UP) #moves the enemy

Any help is greatly appreciated 🙂

When writing code add ``` before and after it.

    Can we have more code for context? I also want to know what type of node you're using Kinematic/Rigidbody/Node3D

      Lethn here's everything before and including the move_to_target function:

      extends KinematicBody
      
      onready var raycast = $RayCast
      
      var speed = 500
      var target
      var speed_multiplier = 1
      var mode = "idle"
      
      var team
      
      var goal_attacking
      var goal_defending
      
      var starting_y_level = 1.5
      var starting_rotation = 0
      
      var stunned = false
      var ball_identity = null
      var on_ball = false
      var in_tackling_range = false
      var ball_detected = false
      var enemy_detected = false
      var enemy_detected_closer = false
      var refreshing = false
      var updating = false
      var looking_for_goal = false
      var direct_line_of_goal = false
      
      var identities_in_area = [null, null, null, null, null]
      var identities_in_closer_area = [null, null, null, null, null]
      var total_identities = [identities_in_area, identities_in_closer_area]
      
      var reaction_speed = 0.25
      var tackling = 50
      
      func _ready():
      	check()
      	closer_check()
      	yield(get_tree().create_timer(0.1),"timeout")
      	set_goal_targets()
      	#refresh_cycle()
      
      func check():
      	yield(get_tree().create_timer(reaction_speed), "timeout")
      	if Global.identity_on_ball != null:
      		enemy_detected = check_for_identity(Global.identity_on_ball, 0)
      		if enemy_detected:
      			target = Global.identity_on_ball.get_node("future_pos")
      	check()
      
      func closer_check():
      	yield(get_tree().create_timer(reaction_speed), "timeout")
      	if Global.identity_on_ball != null:
      		enemy_detected_closer = check_for_identity(Global.identity_on_ball, 1)
      	closer_check()
      
      
      
      func update_rotation(target):
      	if updating:
      		pass
      	else:
      		updating = true
      		yield(get_tree().create_timer(reaction_speed), "timeout")
      		look_at(target.global_transform.origin, Vector3.UP)
      		updating = false
      		
      
      func stun(length):
      	stunned = true
      	yield(get_tree().create_timer(length), "timeout")
      	stunned = false
      
      
      func _physics_process(delta):
      	translation.y = 0
      
      
      func _process(delta):
      	if !stunned:
      		if Global.identity_on_ball != null:
      			$ball_detection/CollisionShape.disabled = true #somebody is on the ball
      			if on_ball:
      				ball_identity.global_transform.origin = $feet_area.global_transform.origin
      				#$feet_area/CollisionShape.disabled = true
      			else:
      				ball_identity = null
      				#$feet_area/CollisionShape.disabled = false
      			#enemy_detected = check_for_identity(Global.identity_on_ball)
      			if enemy_detected_closer:
      				mode = "chasing"
      				target = Global.identity_on_ball
      				speed_multiplier = 1.5
      				var pos = target.translation
      				#var self_pos = self.translation
      				var look_at_me = Vector3(pos.x, pos.y, pos.z)
      				look_at(target.global_transform.origin, Vector3.UP)
      				rotation.x = 0
      			elif enemy_detected:
      				#print("enemy detected")
      				mode = "chasing"
      				#target = Global.identity_on_ball
      				speed_multiplier = 1.1
      				var pos = target.translation
      				#var self_pos = self.translation
      				var look_at_me = Vector3(pos.x, pos.y, pos.z)
      				look_at(target.global_transform.origin, Vector3.UP)
      				rotation.x = 0
      				#update_rotation(target)
      			elif Global.team_on_ball != team:
      				mode = "retreating"
      				speed_multiplier = 1
      				target = goal_defending
      				var pos = target.translation
      				#var self_pos = self.translation
      				var look_at_me = Vector3(pos.x, pos.y, pos.z)
      				look_at(target.global_transform.origin, Vector3.UP)
      				rotation.x = 0
      			elif on_ball:
      				check_collision()
      				mode = "attacking_on_ball"
      				target = goal_attacking
      				speed_multiplier = 0.5
      				var pos = target.translation
      				#var self_pos = self.translation
      				var look_at_me = Vector3(pos.x, pos.y, pos.z)
      				look_at(target.global_transform.origin, Vector3.UP)
      				rotation.x = 0
      			
      			else:
      				#print("target self")
      				target = self
      				speed_multiplier = 0
      				mode = "idle"
      			
      		else:
      			$ball_detection/CollisionShape.disabled = false #ball is free
      			if ball_detected:
      				target = ball_identity
      				mode = "chasing_ball"
      				speed_multiplier = 0.75
      				var pos = target.translation
      				#var self_pos = self.translation
      				var look_at_me = Vector3(pos.x, pos.y, pos.z)
      				look_at(target.global_transform.origin, Vector3.UP)
      				rotation.x = 0
      			else:
      				target = self
      				speed_multiplier = 0
      				mode = "idle"
      		move_to_target(delta)
      
      
      
      func move_to_target(delta):
      	#print(rotation.y)
      	var direction = (target.transform.origin - transform.origin).normalized() #sets the direction
      	direction.y = 0 #this is to prevent the enemy from floating, as I has encountered this before
      	move_and_slide(direction * speed * speed_multiplier * delta, Vector3.UP) #moves the enemy

      It uses a KinematicBody3D btw
      Thanks for your interest 🙂

      • xyz replied to this.

        I think it might be down to you using translation is the most immediate thing I can think of, I found this reddit thread that explains the differences well.

        Instead because you want to have more control over your node, I would potentially use lerp_interpolate() ( Godot 3 ) or lerp() ( Godot 4 ) or change the position constantly using += among other things for a more instantaneous approach like you do for standard sidescrollers or top down.

        Joe_r 3 things look problematic:

        Your code changes the velocity direction vector after normalizing it. It should be done before normalizing. Otherwise its length will always vary and will unlikely be 1

        move_and_slide() expects a velocity vector as the first argument. You're giving it velocity vector multiplied by delta. This will result in incorrect behavior.

        move_and_slide() internally works with default physics engine delta time. Because of that it should only be called from _physics_process(). You're calling it from _process().