Hour 11. Game Flow
    What You’ll Learn in This Hour:
    Pausing the scene and stopping node processing
    Changing to another scene
    Loading resources while providing visual feedback
    Controlling game behavior when the quit button is pressed
    One can think of a video game as a show on stage, but interactive. It is “live,” i.e., things you see on the screen are being dynamically rendered
    at the time you are playing. There are several scenes, and you are going through them one at a time. There is also a “backstage,” where tasks
    are managed behind the scenes, such as setting up the scene, retrieving objects and resources, etc. However, most of the time, the player is
    in control and may choose which scene to play or to take a break.
    In this hour, our focus is on the “stage,” or the Scene Tree. You’ll learn how to use it to control game flow, selectively pause the game, and
    switch scenes. You’ll also learn about Resource Loader, a “backstage,” and use it to load large resources without the game freezing while it
    waits for them. Finally, you’ll learn how to handle “quit” requests from players and respond properly.
    After Launching the Game
    Have you ever wondered what actually happens when you launch the game executable?
    A game cannot start if there is no operating system. Thus, it begins with the module that speaks directly with the system—the OS singleton.
    OS
    OS is a singleton that abstracts interactions with the system and tries to make it consistent, regardless of the platform. It is also one of the first
    modules that are loaded, before other singletons and the scene system. A Main Loop must be given to keep the engine running. Without a
    Main Loop, the engine will quit, because there is nothing else to process, kind of like renting a theater with no plays to show. This is useful for
    stand-alone scripts, for example, which will have to inherit the Main Loop class.
    Main Loop
    Main Loop is a class capable of keeping the engine running after initialization. It will receive callbacks on each tick (“iteration”) throughout the
    duration since the last tick (“delta”). When launching a game, a subclass of Main Loop called Scene Tree is automatically created by the
    engine, and provides the interface to manage the current scene. Main Loop or Scene Tree can be accessed via
    Engine.get_main_loop().
    Scene Tree
    Scene Tree is a class derived from Main Loop. As mentioned previously, besides preventing the engine from quitting, Scene Tree manages
    the current scene. It has a viewport called “root”’ that acts as the root of the tree. Here are some of the functions regarding scene management:
    change_scene(String path): Loads the scene file pointed to by the provided path and change the current scene to it. An example of
    this path parameter is res://main.tscn.
    change_scene_to(PackedScene packed_scene): Changes the current scene to a loaded scene. PackedScene of a scene file can
    be acquired by load() or preload(). For example: var scene = preload(“res://main.tscn”) will load “main.tscn” as
    PackedScene “scene.” Then you can call change_scene_to(scene).
    get_current_scene(): Retrieves the root node of the current scene.
    is_paused(): Notifies whether the current Scene Tree state is paused.
    set_pause(bool paused): Sets the Scene Tree pause state.
    quit(): Closes the game.
    reload_current_scene(): Loads the current scene file from the disk.
    set_auto_accept_quit(bool enabled): If set to “true,” the game will exit immediately when the player presses the exit button or the quit
    key combination (Alt + F4).
    In addition to being the nodes manager, Scene Tree is also capable of calling multiple nodes of the same group, high-level networking, and
    more, but those are out of the scope of this hour.
    Scene Tree is always accessible via the mentioned Engine.get_main_loop() method. It is optional to check whether the returned Main
    Loop is a Scene Tree, as most of the time, that is what it will be. Another way is to call get_tree() from any nodes in the tree. If successful,
    get_tree() will return a Scene Tree. It is important to know that get_tree() will not work if called from nodes that are not in the tree.
    Once Scene Tree is created and loads your main scene, calling tree_entered signal and _ready method, your game will finally be
    running.
    Pausing the Game
    111111111
    22222222You can call get_tree().set_paused(true) from any nodes inside the tree to pause the game. However, your game will become
    unresponsive from that point on, because all nodes inside the tree stop processing. You’ll need to whitelist some nodes that you want
    processed regardless of the pause state. Nodes can be set to stop, continue to process, or inherit a parent’s setting via the pause_mode
    property. If set to inherit, scene root will stop processing when paused.
    Let’s see pausing in action. Create a new node, add a child accept dialog named PauseMenu, and attach the following GDScript to the node,
    as shown in Listing 11.1.
    LISTING11.1 Simple Counter with Pause Functionality
    Click here to view code image
    extends Node
    var label = Label.new()
    var counter = 0
    func _ready():
    label.text = str(0)
    $PauseMenu.dialog_text = “Paused”
    $PauseMenu.connect(“popup_hide”, self, “unpause”)
    $PauseMenu.popup_exclusive = true
    add_child(label)
    func _process(delta):
    counter += delta
    label.text = “%.1f” % counter
    func _input(event):
    if event is InputEventKey:
    if event.scancode == KEY_ESCAPE:
    $PauseMenu.popup()
    get_tree().set_pause(true)
    func unpause():
    get_tree().set_pause(false)
    The script above creates a label that tells time once the game is launched. When the escape key is pressed, it pops up an accept dialog that
    says “paused,” and pauses the game. When the accept dialog is closed, the game resumes. Try running the scene and press “escape.” You’ll
    notice that the timer stops when the Scene Tree is paused. This is the expected response (see Figure 11.1).
    FIGURE 11.1
    Simple game pause dialog.
    However, when you try to unpause the game by clicking the “OK” button, it doesn’t let you, because the dialog is also paused.
    Now try setting the pause mode of the pause menu to “process” and restart. Alternatively, you can add the following line in _ready, as
    shown in Listing 11.2.
    LISTING11.2 Setting the Pause Mode to Process
    Click here to view code image
    $PauseMenu.pause_mode = PAUSE_MODE_PROCESS
    The “OK” button should respond to input now, and the timer continues when you press it.
    Switching Scenes
    It is possible to never switch a scene by using one scene that loads and unloads its children. For simple games, this is overkill. It is advisable
    to use Scene Tree to switch scenes.
    Scene Tree is responsible for scene switching. You can call get_tree().change_scene(String path) to load a scene and set it as
    the current scene. If you want to load the scene beforehand, you can use preload() or load() and save the scene in a variable. Then, call
    get_tree().change_scene_to(PackedScene) with that variable as a function parameter.
    Let’s try switching scenes. Create two scenes. One has a Sprite with a default robot icon as texture for visualization. Save the Sprite scene as
    res://sprite.tscn. Another has a node as the root node with the following GDScript attached, as shown in Listing 11.3.
    LISTING11.3 Scene Switching
    Click here to view code image
    extends Node
    var label = Label.new()
    func _ready():
    label.text = “Press Start”
    add_child(label)
    111111111
    22222222func _input(event):
    if event is InputEventKey:
    get_tree().change_scene(“res://sprite.tscn”)
    Try running the node scene. The text saying “Press Start” should appear (see Figure 11.2).
    FIGURE 11.2
    Scene switching
    Pressing any key should bring you to the Sprite scene (see Figure 11.3).
    FIGURE 11.3
    Sprite scene
    To go back to the node scene, you can add another GDScript to the Sprite node that changes the scene back.
    Background Loading
    Sometimes, the resource is too large to be loaded in a couple of frames. The game will be unresponsive until the loading is complete. It is
    generally undesirable to not provide visual feedback of long operations.
    Resource Interactive Loader
    Resource Interactive Loader is a class capable of loading Resource in steps. It can be created by the Resource Loader singleton by calling
    ResourceLoader.load_interactive(String path).
    preload() loads resources at compile time. You may not use variables as the function’s argument, and the file must exist.
    load() loads resources at run time. The file does not have to exist at the time of loading, making it useful when loading user data.
    Example
    The following example will require a scene with many references. You can find it in the official demo projects. Download a project, and copy
    both scenes and resources into your project root. Rename the scene file name to “scene.tscn.” Create a new scene and node, then attach the
    GDScript shown in Listing 11.4.
    LISTING11.4 Background Loading
    Click here to view code image
    extends Node
    var label
    var loader
    var clock = 0
    func _ready():
    label = Label.new()
    add_child(label)
    loader = ResourceLoader.load_interactive(“res://scene.tscn”)
    func _process(delta):
    clock += delta
    var err = loader.poll()
    if err == ERR_FILE_EOF:
    print(loader.get_resource())
    get_tree().quit()
    label.text = “%d / %d loaded (%.1f s)” % [loader.get_stage(),
    loader.get_stage_count(), clock]
    The script above will load the scene interactively in stages and display the progress. Note that it’s greatly slowed down so you can easily see
    the progress. Each call to ResourceInteractiveLoader.poll() increases the progress. You might notice that the timer is
    continuously counting up. This means the game doesn’t freeze while the resource is being loaded (Figure 11.4).
    FIGURE 11.4
    Interactively loading a scene.
    If the loading is finished, it will return ERR_FILE_EOF. You can get the resource using
    ResourceInteractiveLoader.get_resource(). If you want the resource instantly, calling
    ResourceInteractiveLoader.wait() will halt the game until the resource is loaded.
    Handling a Quit Request
    111111111
    22222222When a game receives an exit signal, there is a chance the player did not mean to quit, and it can be infuriating if he has not saved for a while.
    Loss of game data can adversely affect the reputation of your game. It is a good idea to have a dialog that pops up asking if the player wants
    to save first or auto-save when quitting. Any quit requests should be properly handled.
    Let’s see how this is made possible with the notification system. Create a new node and attach the GDScript shown in Listing 11.5.
    LISTING11.5 Manually Handling a Quit Request
    Click here to view code image
    extends Node
    var dialog = ConfirmationDialog.new()
    func _ready():
    dialog.dialog_text = “Are you sure?”
    dialog.get_ok().text = “Yes”
    dialog.get_cancel().text = “No”
    dialog.connect(“confirmed”, self, “end”)
    add_child(dialog)
    get_tree().set_auto_accept_quit(false)
    func _notification(what):
    if what == MainLoop.NOTIFICATION_WM_QUIT_REQUEST:
    dialog.popup()
    func end():
    get_tree().quit()
    The script above will intercept NOTIFICATION_WM_QUIT_REQUEST of Main Loop and respond by popping up the dialog that says, “Are you
    sure?” The game will quit when you press the button that says, “Yes” (see Figure 11.5). Otherwise, the game will continue to run.
    FIGURE 11.5
    Manually handling a “quit” request.
    This gives you a chance to save the game before quitting.
    Summary
    In this hour, you’ve learned about Main Loop and its importance of keeping the engine running. You’ve also learned about a type of Main Loop
    called Scene Tree that also manages the scene that is currently playing. You should be able to query Scene Tree to pause the game and
    switch the scene. When there’s a large resource that takes time to load, you’ve learned that the Resource Interactive Loader can help load it in
    stages. Finally, you know how to intercept a quit request and do things that are necessary before closing.
    Q&A
    Q. How do you switch between scenes with a transition effect such as fading?
    A. The simplest way is to make both scenes children of another scene that applies effect. If that is not applicable, there are several ways to
    achieve this. One way is to apply a fade effect to both scenes. Fade out the old scene before switching, and fade in the new scene when
    it’s ready. Another way is to add a fade effect on top of both scenes by adding it as a root viewport’s child and making it render on top. If
    the effect requires images from the old scene, such as a crossfading effect, you can capture the viewport before switching.
    Q. Can you skip the interactive loading and get resources immediately?
    A. Yes, by calling ResourceInteractiveLoader.wait().
    Q. Can you handle quit requests fromkilling the process?
    A. No. The game will exit immediately.
    Workshop
    Answer the following questions to make sure you understand the content of this hour.
    Quiz
    1. What is the class that manages the current scene and its nodes?
    2. What property of a node should be modified if you want to pause the Scene Tree without the game becoming unresponsive?
    3. What is the difference between SceneTree.change_scene() and SceneTree.change_scene_to()?
    4. What is returned by ResourceInteractiveLoader.poll() when the resource is done loading?
    5. To what class does NOTIFICATION_WM_QUIT_REQUEST belong?
    Answers
    111111111
    222222221. Scene Tree.
    2. pause_mode.
    3. change_scene() must be given a scene file path; change_scene_to() must be given a PackedScene.
    4. ERR_FILE_EOF.
    5. Main Loop.
    Exercises
    Try to execute the following exercises to get more acquainted with Scene Tree:
    1. Add a pause dialog to your game or a demo project.
    2. Display the pause dialog when a player makes a quit request.
    3. Choose two demo projects, merge them into one, and make a main menu with options to choose which to run.