I need help making a enemy that follows you around

Godot Version

4.5.1

Question

I have tried 10 videos but it never works, i just need help please

Need more information, what methods have you tried? What about your current attempts do that doesn’t work? What does the feature your making trying to achieve?

I found this very simple video that works for 2D and should easily translate into 3D if you remove the Y axis

I’ve used that tutorial and word of advice, make a variable that is the subtracted positions then take that variable and set the velocity to that variable then use .normalize on it and multiply it by a speed variable
Eg:

var speed = 5

func process(delta) -> void:
    var direction = global_position - player.global_position  # you'd need a reference to the player
    velocity = direction.normalized * speed
    move_and_slide()

Global position removes some weirdness where it targets the player’s position with an offset and normalizing the vector makes it so that it isn’t faster the further away or is from the player.

1 Like

Ok i will try the one that you suggested.

Ive tried most of the ones when you search “how to make a enemy ai in godot 4 3d” as well as the one on here but it kept on coming up with erorrs and when i searched in online i couldnt find anything to do with what i was doing, however i did do the gd quest one but i really wanted to focus on my game instead of doing tutorials

ok so it does work but how do i remove the y axis so i don’t go up into the heavens

Can you post you current code implementation?

Sure one sec

1 Like
extends CharacterBody3D

var speed = 25
var player_chase = false
var player = null


func _physics_process(_delta: float) -> void:
	if player_chase:
		position += (player.position - position)

@warning_ignore("unused_parameter")
func _on_detection_area_body_entered(body: Node3D) -> void:
	player = body
	player_chase = true


@warning_ignore("unused_parameter")
func _on_detection_area_body_exited(body: Node3D) -> void:
	player = null
	player_chase = false

1 Like

Some things that will confuse you if you ever expand you code:

the name detection area for your area3D is a okay node name but a bad name for the function as there is also area_entered/area_exited and if you ever make a signal for those it will be named _on_detection_area_area_entered which is a bit confusing (maybe detection range would be better)

I also implemented the suggestion from @istubbdmytoe as so you don’t encounter any weirdness later on

extends CharacterBody3D

var speed: float = 5 #lowered speed to account for physics (adjust as needed)
var player_chase: bool #initializes as false
var player: Node3D #this is good to know how data is going through your code

func _physics_process(_delta: float) -> void:
	if player_chase:
		#removed position
		var direction: Vector3 = self.global_position - player.global_position #direction 
		self.velocity = -direction.normalized() * speed #calculate
		move_and_slide() #physics application

#suggestion to add a check in the _on_detection_area_body_entered/exited to see if 
#the body is the player either through class_name or checking to see if it is a CharacterBody3D
#like this- if body is CharacterBody3D:
#or this if you have a class_name on the player- if body is player:

#removed not needed @warning_ignore
func _on_detection_area_body_entered(body: Node3D) -> void:
	player = body
	player_chase = true

#removed @warning_ignore
func _on_detection_area_body_exited(_body: Node3D) -> void: #changed to _body
	player = null
	player_chase = false

Below is how to remove the Y axis (if still needed)

extends CharacterBody3D

var speed: float = 5
var player_chase: bool
var player: Node3D


func _physics_process(_delta: float) -> void:
	if player_chase:
		var direction_z: float = self.global_position.z - player.global_position.z#added
		var direction_x: float = self.global_position.z - player.global_position.z#added
		var direction: Vector3 = Vector3(direction_x,0,direction_z)#changed
		self.velocity = -direction.normalized() * speed#same
		move_and_slide()#same
		#removing the y axis does cause some weirdness when the player is above the enemy

func _on_detection_area_body_entered(body: Node3D) -> void:
	player = body
	player_chase = true

func _on_detection_area_body_exited(_body: Node3D) -> void:
	player = null
	player_chase = false

thank you so much

1 Like

How can i make it jump and stuff like that

You could think of it like this:

when the player is above my current y value I will apply the same jump mechanic the player has to myself to try to get to the player ( so if direction_y of player compared to me is different, jump )

I don’t have time right now to make a code block but I will later in my day, try to do it yourself I think I provided all the information needed in the code blocks above to be able to figure it out

ok i will, its fine that you don’t have time.

2 Likes

Have you got it figured out?

If you had time to get something somewhat working, you can post it and I can help with it.

If not it’s all good I have time now.

ive try to but it wasnt working so i took a brake and havent touched it since but heres what i was thinking

var y_axis = player.global_position

and

	if player.global_position = y_axis and is_on_floor():
		velocity.y = JUMP_VELOCITY

I went a bit crazy with adding jumping as its been a while since I coded in 3D but here is something that is an upgrade to before with jumping.

extends CharacterBody3D

var speed: float = 5.0 #(adjust as needed)
var acceleration: float = 10.0 #added acceleration for cooler movement (adjust as needed)
var friction: float = 10.0 #added friction for cooler movement (adjust as needed)
var jump_strength: float = 10.0 #changed your jump velocity from a CONST to a var (adjust as needed)

var player_chase: bool
var player: Node3D

var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity") #added the gravity value from godot project settings

func _physics_process(delta: float) -> void: #changed _delta to delta since we use it for gravty

	if not is_on_floor(): #gravity logic
		self.velocity.y -= gravity * delta

	#there is a bug in virtual input where the enemy resets to 0,0 if you leave the area while its jumping, you could change this value to target a return point for when the enemy is done chasing
	var virtual_input: Vector3 = Vector3.ZERO
	if player_chase and player: #added a saftey check (maybe when the player dies it could throw an error)
		var direction: Vector3 = player.global_position - self.global_position #redid calculation cleaner
		direction.y = 0#code cleanup
		if direction.length_squared() > 0.01: #added this for enemy looking
			virtual_input = direction.normalized() #for better movement
			var target_angle = atan2(direction.x, direction.z) 
			self.rotation.y = lerp_angle(self.rotation.y, target_angle, delta * 10.0)#added rotation to the enemy mesh (not needed)

		#this is for the enemy jumping added a +0.5 deadzone so the enemy doesn't jump to slight changes in elevation
		if is_on_floor() and player.global_position.y > self.global_position.y + 0.5: #jumping logic
			self.velocity.y = jump_strength

	#made the enemy movement more like what a player script would look like
	if virtual_input != Vector3.ZERO:
		self.velocity.x = move_toward(self.velocity.x, virtual_input.x * speed, acceleration * delta) #added acceleration (not needed)
		self.velocity.z = move_toward(self.velocity.z, virtual_input.z * speed, acceleration * delta)
	else:
		self.velocity.x = move_toward(self.velocity.x, 0, friction * delta) #added friction (not needed)
		self.velocity.z = move_toward(self.velocity.z, 0, friction * delta)

	move_and_slide() #physics application

func _on_detection_area_body_entered(body: Node3D) -> void:
	player = body
	player_chase = true
func _on_detection_area_body_exited(_body: Node3D) -> void:
	player = null
	player_chase = false




thanks man, i will make sure i your credited in script

1 Like

No problem!

No need to credit I was just trying to give you a good place to start and building from here will add a lot more than the bare bones I provided