In this lesson, we will create spawner nodes.
Spawners are little utility nodes whose function is to create other nodes. For example, it could be a pit or a portal that periodically makes new mobs appear in a game level.
But let’s first talk about why we would need these spawners.
You’ve spawned random scenes before. In the Random Rocks series, you wrote this code:
# We use the `ROCKS` array to pick a random scene to vary the visuals that we
# place on the grid.
const ROCKS := [
preload("rocks/Rock1.tscn"),
preload("rocks/Rock2.tscn"),
preload("rocks/Rock3.tscn"),
]# ...
# Creates and returns a new random rock instance.
func get_random_rock() -> Sprite:
# We first calculate a random index in the ROCKS array.
var rock_random_index := randi() % ROCKS.size()
# Then, we get the preloaded scene in the array and create a new instance of
# it.
return ROCKS[rock_random_index].instance()
And it works perfectly fine. But what if we wanted to reuse this code to spawn different things, like mobs or pickups?
One thing we could do is export the array of scenes to spawn. Then, it’d be possible to edit it without code, directly from the Inspector:
# Notice the export keyword.
export var rock_scenes := [
preload("rocks/Rock1.tscn"),
preload("rocks/Rock2.tscn"),
preload("rocks/Rock3.tscn"),
]
This is what a spawner is! A generic, reusable node that will spawn anything from a list of scenes you give it.
Our spawner will differ a bit in behavior from that code you wrote before:
In this lesson, we will:
Let’s start!
Open the directory res://rooms/spawners/
. You will find
several .png
image files there. Each of these represents a
different spawner we’ll set up. They will all share the same code.
We made basic images on purpose because the spawners themselves will not be visible in the running game.
The images only help us visualize the spawning point while designing levels.
Create a new script in the spawners/
directory.
Call it Spawner.gd
and make it inherit the class.
Then double-click the file to start editing it.
Note: If you did not specify the Inherits
property, you can just replace the text at the top from
extends Node
to extends Sprite
.
We give the script a class_name
to make it convenient to
refer to in the project. At the top, write:
class_name Spawner
extends Sprite
Then let’s export a variable to edit the list of spawn options in the Inspector.
export(Array, PackedScene) var list := []
The export hint in parentheses tells Godot that we want an array of scene resources rather than an array of anything.
It will make the array easier to edit in the Inspector as it’ll only accept scene files.
Let’s write our spawn()
function now:
func spawn() -> void:
if not list:
return
# Get a random scene resource from the list array.
var random_scene_index := randi() % list.size()
var scene: PackedScene = list[random_scene_index]
We have an additional check to make.
With the rocks, we knew the array was entirely filled with scenes. In this case, it’s not guaranteed.
For example, an exported array could look like this:
So, let’s check for holes:
func spawn() -> void:
# ...
# The array might have holes. We need to make sure we got an item.
if not scene:
return
var node: Node2D = scene.instance()
Finally, we instance the node and add it as a child of the spawner’s parent node. Then, we move the node to match the spawner’s position.
The role of our spawner is just to make one scene instance. After doing this, we can free it.
But if we added the spawned instance as a child of the spawner, it would get destroyed along with the spawner.
func spawn() -> void:
# ...
get_parent().add_child(node)
= global_position node.global_position
That’s the whole code wrapped up. Let’s now test our spawner.
Open res://rooms/TestRoom.tscn
. If you had added things
there, such as a mob, or items, you can remove them so the scene is
clean.
Add a node. In the dialog, start writing “spawner.” You can directly create your new node from here!
Notice how it appears under
in the node inheritance tree because it extends .Once you’ve added it, add a texture to it. For example, drag the
spawner_mob.svg
image to it. And then move it towards the
center of the scene.
With the spawner still selected, go to the Inspector. Click
on the 1
.
Then, drag a scene onto it.
We picked Dummy.tscn
in this example, but you could pick
anything! Why not try Shield.tscn
or
Robot.tscn
?
All that’s left to do is trigger the spawning behavior.
Add a script to the room. Keep the default TestRoom.gd
name.
In the room’s script, write:
onready var spawner := $Spawner
func _ready() -> void:
spawn() spawner.
Once that’s done, run the scene with F6. If everything works, you should see the scene you picked spawn!
Only one thing left to do: hide the spawner itself.
In Spawner.gd
, write this:
func _ready() -> void:
= null texture
Here’s the entire code for the spawner:
class_name Spawner
extends Sprite
export(Array, PackedScene) var list := []
func _ready() -> void:
# we hide the spawner in the running game
= null
texture
func spawn() -> void:
if not list:
return
# Get a random scene resource from the list array.
var random_scene_index := randi() % list.size()
var scene: PackedScene = list[random_scene_index]
# The array might have holes. We need to make sure we got an item.
if not scene:
return
var node: Node2D = scene.instance()
get_parent().add_child(node)
= global_position node.global_position
Challenge: We want to add a
spawning_chance
variable, between 0
and
100
, which will dictate if the spawner spawns or not. Can
you think of a way to do that?
Every time we want to create a spawner for mobs, we have to add a node, set scenes in its List property, and set a texture. It’s a bit too much repetition.
Instead, we will create four preset scenes for spawners:
You can then use these spawners in the rooms we create. If we still want a custom spawner, we can always add it manually, as we did for our test.
We will create the mob spawner first.
Create a scene inside the spawners/
directory. Call it
SpawnerMob, and select a Spawner as the root node.
Drag the spawner_mob.svg
file as a texture, and then add
the Dummy.tscn
mob and the Shield.tscn
to the
List array.
If you made the bomb and the sword, you can also add them.
Don’t forget to name the node SpawnerMob too. Save it.
That’s our first Spawner! Do the same with the three others.
The SpawnerRobot and SpawnerTeleporter should have only one scene in their list.
We’re ready to use these!