Create Array to reference enemy scenes placed in level

Godot Version

v4.2.2.stable.official [15073afe3]

Question

Hi there!
I want to have a door that only opens when you have defeated a list of enemies before it. I would like the door to have a list or array of the enemies that need to be defeated.

In my head, I should be able to declare an array for the door scene, and then have an array of some type like “Node3D” or “StaticBody3D” and drag/drop the enemies I have placed in the level within the editor. But I can’t seem to either get the type right or figure out how to make this array accept the scenes I have instantiated into the level.

How can I do this?
Thanks for any help! I provided a screenshot to illustrate further what I am trying to accomplish/

Try a node path, these will work better when outside of the SceneTree like in Resources or Object type scripts.

@export_node_path("StaticBody3D") var enemy: NodePath

Thanks for the reply - that can give me a node path, but I still am having a hard time constructing the array.

It appears I can do this:

@export var enemy_array : Array[Node3D] = []

And I can add it the editor as I wanted, but when calling on the array members on _ready(), it doesn’t have the array initialized yet

I would still use node paths,

@export_node_path("StaticBody3D") var enemy_array: Array[NodePath]

However, keep in mind a parent node is only ready when all of it’s children are ready. If your node is referencing a lot of parent or sibling nodes, some of them will not be ready, you may have to wait a frame await get_tree().process_frame or get the nodes as they are needed.

1 Like

It looks like one enemy is already inside the array? When you click add element in the export array you should be able to reference more.

1 Like

Okay, I think this ended up being an issue with signals. I was able to add some objects to the array in the editor using the @export variable.

Here is the gist:

  • My BaseDoor.tscn holds an Array of Node3D, which I add to from the viewport in the editor. Then, in the _ready function, I loop through the list and connect to a signal on the enemy nodes I added to the array. That signal goes off if an enemy is destroyed:
extends StaticBody3D
class_name BaseDoor

@export var enemy_array : Array[Node3D] = []

func _ready():
	for enemy in enemy_array:
		assert(enemy is Seeker || enemy is Tower)
		if enemy is Seeker:
			enemy._alert_parent_door_node.connect(_remove_enemy_from_array)

func _remove_enemy_from_array(enemy : Node3D):
	print("ERASING!")
	enemy_array.erase(enemy)
	if enemy_array.is_empty():
		_open_door()
		
func _open_door():
	# TODO: I should animate this 
	queue_free()

I am still unsure why I was unable to act upon the Nodes I added from the editor. For what it’s worth, I was adding them into the scene using this button here (The instantiate child button):
image

Noted - thank you for the tip about waiting a frame to get the nodes.

I made it work using an Array of Node3D (wasn’t sure how else to group different variants of enemies that have different class names), which is going to force me to require some type checking.

Forgive me for not understanding, but I have been reading about NodePath here: NodePath — Godot Engine (stable) documentation in English.

Keeping in mind that the enemies are not children of the object referencing them in a member array (the door scene), are you saying that it would be better to have it be an array of NodePaths, since it is more efficient/flexible?

Also, perhaps I misunderstood but I have a GD script error using that line of code

  res://Scripts/BaseDoor.gd:5 - Parse Error: "@export_node_path" annotation requires a variable of type "NodePath" but type "Array[NodePath]" was given instead.
1 Like

Maybe a 4.3 update, seems like you might have to omit the type, or the specific annotation

@export_node_path("StaticBody3D") var node_path_array

@export var node_path_array: Array[NodePath]
1 Like

Thank you!

Darn, can’t declare the same identifier twice. There must be a way of having an array of specific variants of NodePath. I will keep digging.

Oh I mean one of those two options apologies

1 Like

Oh PFFFF I am so dumb. The second one works, but I wish we could make an array that only accepts set of types.

Marking that answer as the solution. Thanks again for your help, friend!

NodePath would work better within the engine. All scene files and exports use and save as NodePath under the hood, I remember bugs with using nodes directly in non-scene tree objects like resources, so I recommend against nodes directly as an export.

I believe the nitty gritty is that @export uses get_node on the script, but your resource doesn’t have get_node, as it’s not a Node object itself, so it will fail. Instead you need to pass a Node into what ever resource function you want to use and call get_node on that with the appropriate path.

Writing that out I’ve noticed your door script which is exporting is a Node type, not a Resource so the NodePath thing might be unecessary; I must’ve been tripped up by the Seeker icon, it shows a resource icon instead of the CharacterBody3D icon. Sorry about the round trip

1 Like

All good - thank you for the context anyways, I am really happy to learn something. If anything, this is a good reminder for me to read up about resources again. Thanks again for the help!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.