So, I'm making a top down shooter, and I want to infinitely instantiate enemies to chase the player. When I instantiate the enemy, it just appears on the screen and does none of the code i put in the enemy script. here is the main lvl script: the enemy is it's child.
extends Node2D
var enemy_scene = preload("res://level/enemies/zombie.tscn")
func _ready():
pass
func _on_timer_timeout():
spawn_enemy()
func spawn_enemy():
var enemy_instance = enemy_scene.instantiate() as CharacterBody2D
enemy_instance.position = get_enemy_position()
enemy_instance.visible = true
add_child(enemy_instance)
func get_enemy_position():
var spawn_x = randf_range($player.position.x + 100.0, $player.position.x - 100.0)
var spawn_y = randf_range($player.position.y + 100.0, $player.position.y - 100.0)
return Vector2(spawn_x, spawn_y)
there is a 3 second timer which instantiates a enemy, but it doesn't do ANYTHING i've tried coding it to do. I can use the variable "enemy instance" to do basic stuff, like setting visability to true, but if I want to do something every frame, thats a problem.
I've tried to watch youtube toturials, which for some reason, they did the same thing as me, and the instantiations still worked. I even asked GPT, who was of no use.
Create a Zombie Class
It is either a good idea or it is necessary to give a script to the root of the
zombie.tscnclass. So let us start there...Create an script, I'll call it
zombie.gd, which you attach to the root of thezombie.tscnscene.That
zombie.gdmust extendCharacterBody2Dsince that is the type of node the root of thezombie.tscnscene is:zombie.gdYou can also give it a class name so we can use that as a type globally:
zombie.gdMake the Zombies move on their own
In that script you can define
_physics_processwhich will run every physics frame, and_processwhich will rune every graphics frame (Thedeltaargument will have the time in seconds since the last frame of its kind):zombie.gdSo you can use these to have your
Zombiedo something every frame.Notably, being a
CharacterBody2D, you probably want to callmove_and_slideon_physics_process.I'll also assume you want them to chase the player character. This is what comes to mind:
You can add an
Area2Das a child to theZombiewith a collision shape that defines the area within which theZombiewill detect the player character.For that detection you can connect the
body_enteredsignal to the root of thezombie.tscnscene, adding a new method in the thezombie.gdscript that looks somthing like this:zombie.gdSo you can get a reference to the body it detected:
zombie.gdYou might want to confirm it is the player character. We might do this by giving also a script with a class name to the player character scene (similar to how we gave
Zombieto thezombie.tscnscene. For example if that class isPlayerCharacterwe can do this:zombie.gdI'm going to defer you to How do I detect collisions in Godot? for details
Then using
global_positionof theZombieand the player character you can pick the direction to move, and export a variable for the speed (you set it in the Inspector dock):zombie.gdThe behavior of the zombies could be more complex, of course, but with this you have zombies that do something.
Presumably you also want to handle the case of the zombies colliding with the player character. Again please refer to How do I detect collisions in Godot?. And presumably you also want some kind of damage/health/lives... While that is beyond the scope of this question, here is a hint: you can add a
healthproperty to thePlayerCharacterclass.A Director telling the Zombies what to do
You seem to want your spawner script to not only spawn the enemies, but also to control them. I'll continue to call it spawner for the sake of this answer, but know what you would be making is a director.
Let us start by using the
Zombieclass in your spawner:spawner
So if you define additional properties or methods in your
zombie.gd, you can access them from your spawner. For example:zombie.gdspawner
Or:
zombie.gdspawner
To be clear, you could call any method, including
move_and_slide, not just the ones you defined.You can add paramters to the method, for example:
zombie.gdspawner
This can be a way to give information to the zombies, such as the position of the player, or tell them to stop chasing, etc.
If you need to call something on the zombies after you created them, you are going to need to keep a reference to them. One way would be to keep a list of the instances you created (which you also need to maintain to remove any no longer valid instances, such as zombies the player destroyed)...
But since you are adding the instances as children:
We could use the list of children to do this instead.
So if you want to call something on all of them, you could iterate over all the children, for example:
spawner
Another option could be to put the root of the
zombie.tscnscene on a node group (You find this in the Groups tabs of the Node dock), for example, let us say they are in the"zombie"group, so you can do this:spawner
Node communication is an iceberg. There are other approaches for telling your nodes to do something, notably using signals. But without more context of what are you trying to do, the above ones are the ones I'm willing to suggest.