Part 3 – the Main scene
The Main
scene is what ties all the pieces of the game together. It will manage the player, the coins, the clock, and all the other pieces of the game.
Node setup
Create a new scene and add a Node
named Main
. The simplest type of node is Node
– it doesn’t do much at all on its own, but you’ll use it as the parent for all the game objects and add a script that will give it the functionality you need. Save the scene.
Add the player as a child of Main
by clicking the Instantiate Child Scene button and choosing your saved player.tscn
:
Figure 2.23: Instantiating a scene
Add the following nodes as children of Main
:
- A
TextureRect
node namedBackground
– for the background image - A
Timer
node namedGameTimer
– for the countdown timer
Make sure Background
is the first child node by dragging it above the player in the node list. Nodes are drawn in the order shown in the tree, so if Background
is first, that ensures it’s drawn behind the player. Add an image to the Background
node by dragging the grass.png
image from the assets
folder into the Texture property. Change Stretch Mode to Tile, and then set the size to Full Rect by clicking the layout button at the top of the editor window:
Figure 2.24: Layout options
Main script
Add a script to the Main
node and add the following variables:
extends Node @export var coin_scene : PackedScene @export var playtime = 30 var level = 1 var score = 0 var time_left = 0 var screensize = Vector2.ZERO var playing = false
The Coin Scene and Playtime properties now appear in the Inspector window when you select the Main
node. Drag coin.tscn
from the FileSystem panel and drop it into the Coin Scene property.
Initializing
To start things off, add the _ready()
function:
func _ready(): screensize = get_viewport().get_visible_rect().size $Player.screensize = screensize $Player.hide()
Godot automatically calls _ready()
on every node when it’s added. This is a good place to put code that you want to happen at the beginning of a node’s lifetime.
Note that you’re referring to the Player
node by name using the $
syntax, allowing you to find the size of the game screen and set the player’s screensize
variable. hide()
makes a node invisible, so you won’t see the player before the game starts.
Starting a new game
The new_game()
function will initialize everything for a new game:
func new_game(): playing = true level = 1 score = 0 time_left = playtime $Player.start() $Player.show() $GameTimer.start() spawn_coins()
In addition to setting the variables to their starting values, this function calls the player’s start()
function that you wrote earlier. Starting GameTimer
will start counting down the remaining time in the game.
You also need a function that will create a number of coins based on the current level:
func spawn_coins(): for i in level + 4: var c = coin_scene.instantiate() add_child(c) c.screensize = screensize c.position = Vector2(randi_range(0, screensize.x), randi_range(0, screensize.y))
In this function, you create multiple instances of the Coin
object and add them as children of Main
(in code this time, rather than by manually clicking on the Instantiate Child Scene button). Whenever you instantiate a new node, it must be added to the scene tree using add_child()
. Lastly, you choose a random position for the coin, using the screensize
variable so that they won’t appear off screen. You’ll call this function at the start of every level, generating more coins each time.
Eventually, you’ll want new_game()
to be called when the player clicks the start button on the menu. For now, to test that everything is working, add new_game()
to the end of your _ready()
function and click Run Project (F5). When you are prompted to choose a main scene, select main.tscn
. Now, whenever you play the project, the Main
scene will be started.
At this point, you should see your player and five coins appear on the screen. When the player touches a coin, it disappears.
Once you’re done testing, remove new_game()
from the _ready()
function.
Checking for remaining coins
The main
script needs to detect whether the player has picked up all the coins. Since the coins are all in the coins
group, you can check the size of the group to see how many remain. Since it needs to be checked continuously, put it in the _process()
function:
func _process(delta): if playing and get_tree().get_nodes_in_group("coins").size() == 0: level += 1 time_left += 5 spawn_coins()
If no more coins remain, then the player advances to the next level.
This completes the main scene. The most important thing you learned in this step was how to dynamically create new objects in code using instantiate()
. This is something that you will use again and again in building many types of game systems. In the last step, you’ll create one more scene to handle displaying game information, such as the player’s score and the time remaining.