how to make homing bullet detect new target

Godot Version

` godot 4.2.2

I want to make a bullet that always homing toward an enemy but when the enemy died, it crash. how to make the bullet always move toward the closest enemy and switch to new enemy when it dies

This guide might help you to build a nice homing bullet.

In your case, the game is probably crashing because the enemy has been freed (when it died).

You can use is_instance_valid() to ensure the enemy is valid before trying to access its position.

Here is some pseudo code :

if enemy_target.is_instance_valid():
    #code to follow the enemy_target
    ...
else:
    #code to find the closest enemy
    ...
1 Like

I copied the code and used a dummy target to test it but for some reason the bullets moved to 0,0. Then I still haven’t found a way to switch to another nearby enemy. Sorry, I’m still a beginner.

Can you share the code you have so far ?

You can paste it in the forum here, surrounding it with this :

```gdscript

your code goes here

```

extends Sprite2D

@export var speed = 300
@export var drag = 10
var acceleration = Vector2.ZERO
var velocity = Vector2.ZERO
var target = null
var enemy_in = []

func _on_visible_on_screen_enabler_2d_screen_exited():
	queue_free()

func _on_aim_area_area_entered(area):
	if area.is_in_group("enemy"):
		target = area


func _on_aim_area_area_exited(area):
	target = null


func seek():
	var steer = Vector2.ZERO
	if target != null:
		var desired = (target.position - position).normalized() * speed
		steer = (desired - velocity).normalized() * drag
	return steer
	

func _physics_process(delta):
	acceleration += seek()
	velocity += acceleration * delta
	velocity = velocity.limit_length(speed)
	rotation = velocity.angle()
	position += velocity * delta

func start(_transform, _target):
	global_transform = _transform
	rotation += randf_range(0.09,-0.09)
	velocity = transform.x * speed
	target = _target

the bullet still move to 0,0 even if the enemy haven’t died yet

You should use if is_instance_valid(target): rather than if target != null:

That will catch cases where target has been deleted but not nulled.

Do target and this object have the same parent? If not, you may need to use global_position.

I’m not sure I understand the logic for the movement. seek(), for example, is getting a normalized vector from the seeker towards the target, then multiplying that by speed, then subtracting velocity and normalizing it again? Then multiplying by drag, adding that to acceleration and multiplying by delta before adding it to velocity.

That seems… convoluted.

How do you want this to behave, ideally?

nope, target have different parent

well, i just copypast the code from tutorial :sweat_smile: I’m not really understand it either. I want the bullet (this object) to move towards the enemy (target) while slowly rotating towards the enemy. Drag is to slowdown the bullet’s rotation. I also want the bullet to search for new enemy if the bullet is still exist.

It’s hard to be sure what is causing the issue in your case. It might be because of the aim_area.

func _on_aim_area_area_entered(area):
	if area.is_in_group("enemy"):
		target = area 

This function is being called whenever an area enters the aim_area of your missile. Its checking if the area itself is in an group called “enemy”.

Could it be that its the owner of the area that is in the “enemy” group, and not the area itself ?

If so, this may fix your issue:

func _on_aim_area_area_entered(area):
	if area.owner.is_in_group("enemy"):
		target = area.owner

Notice that the target is also the area.owner in this version, and not the area itself. If you want to target the area itself, then you will need to do like @hexgrid said, and use the target.global_position instead of the target.position


To make sure the tutorial’s code is working (even if its a bit convoluted like @hexgrid pointed out), I tested it myself. I did have to modify 1-2 lines.

(the movement is smooth in the game, its just the gif that makes it looks slow)
missile

Code for the missile
extends Area2D

@export var speed = 350
@export var steer_force = 50.0

var velocity = Vector2.ZERO
var acceleration = Vector2.ZERO
var target:Node2D

func start(_transform, _target):
	global_transform = _transform
	rotation += randf_range(-0.09, 0.09)
	velocity = transform.x * speed
	target = _target

func seek():
	var steer = Vector2.ZERO
	# The target has been freed or is null
	if ! is_instance_valid(target):
		
		# Get the nodes in the group "enemies"
		var other_targets = get_tree().get_nodes_in_group("enemies")
		
		# If there are enemies, pick a random one
		if not other_targets.is_empty():
			target = other_targets.pick_random()
	if target:
		var desired = (target.position - position).normalized() * speed
		steer = (desired - velocity).normalized() * steer_force * 2
	return steer

func _physics_process(delta):
	acceleration += seek()
	velocity += acceleration * delta
	velocity = velocity.limit_length(speed)
	rotation = velocity.angle()
	position += velocity * delta