Untarget dead enemy

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By OnceFilo

So I want to set my _target_nodeto null when an enemy dies.

Here how I target an enemy:
in my player.gd I have:

var _target_node: Node = null
    
func handle_target_lock():
    if Input.is_action_just_pressed("target_lock"):
    	if _target_node:
    		_target_node = null
    	else:
    		var enemies: Array = get_tree().get_nodes_in_group("Enemy")
    
    		if enemies.size() > 0:
    			_target_node = enemies[0]
    
    if _target_node:
    	look_at(_target_node.global_transform.origin, Vector3.UP)

Problem is, when an enemy dies, _target_node is now referencing a deleted instance.

My enemy node emits a signal died when its hp is 0, but I don’t know how to use this information to notify my player.

How do I notify the player that the targeted enemy is dead and _target_node should be set to null?

:bust_in_silhouette: Reply From: SnapCracklins

You have the answer, you just need to plug it in and stick the logic.
You have a signal? You need to connect it first (in code, at ready). Once it’s connected to where it needs to go, create a function at died() to reset the _target_node to null.

:bust_in_silhouette: Reply From: DigitalDrako

SnapCracklins 100% Right! You have everything you need, you just need to piece it all together.

Solving with Signals

The “died” signal is incredibly useful, and what we’ll be using to untarget an enemy node. First things first, we need to connect the enemy “died” signal, to the player. We can do this using the connect function.

Typically it would go like this:

func _ready():
 enemy.connect("died", self, "_untarget_node")

func _untarget_node():
 _target_node = null

However, depending on your project structure, doing this for every enemy can be quite tricky and messy. Instead, I would probably connect the signal whenever you target the enemy:

        var enemies: Array = get_tree().get_nodes_in_group("Enemy")

        if enemies.size() > 0:
            _target_node = enemies[0]
            _target_node.connect("died", self, "_untarget_node")

or, if youre feeling fancy, even using a setget function

var _target_node: Node = null setget set_target_node

func set_target_node(value):
 _target_node = value
 if _target_node != null: 
   _target_node.connect("died", self, "_untarget_node")

Alternative Solution

Now finally, there is ONE more solution you can use that is unrelated to signals. Whether or not you prefer it, is up to you!

if not is_instance_valid(_target_node): _target_node = null

Whether or not this is good practice completely depends on the coder, but technically it does completely decouple the player from the enemy! Consider also using a getter function, (note, you would always have to access _target_node with self._target_node for the getter function to work) but that way, whenever you fetch the _target_node, the getter function will check if the instance_is_valid before returning anything to you!

Hope this helps, I tried providing a variety of solutions so you can pick what works best for you!