182 lines
5.8 KiB
ReStructuredText
182 lines
5.8 KiB
ReStructuredText
.. _doc_singletons_autoload:
|
|
|
|
Singletons (AutoLoad)
|
|
=====================
|
|
|
|
Introduction
|
|
------------
|
|
|
|
Scene Singletons are very useful things, as they represent a very common
|
|
use case, but it's not clear at the beginning where their value is.
|
|
|
|
The scene system is very useful, but by itself it has a few drawbacks:
|
|
|
|
- There is no "common" place to store information (such as core, items
|
|
obtained, etc) between two scenes.
|
|
- It is possible to make a scene that loads other scenes as children
|
|
and frees them, while keeping that information, but then if that is
|
|
done, it's not possible to run a scene alone by itself and expect it
|
|
to work
|
|
- It is also possible to store persistent information to disk in
|
|
\`user://\` and have scenes always load it, but saving/loading that
|
|
while changing scenes is cumbersome.
|
|
|
|
So, after using Godot for a while, it becomes clear that it is necessary
|
|
to have parts of a scene that:
|
|
|
|
- Are always loaded, no matter which scene is opened from the editor.
|
|
- Can keep global variables, such as player information, items, money,
|
|
etc.
|
|
- Can handle switching of scenes and transitions.
|
|
- Just have something that acts like a singleton, since GDScript does
|
|
not support global variables by design.
|
|
|
|
For this, the option for auto-loading nodes and scripts exists.
|
|
|
|
AutoLoad
|
|
--------
|
|
|
|
AutoLoad can be a scene, or a script that inherits from Node (a Node
|
|
will be created and the script will be set to it). They are added to the
|
|
project in the Scene > Project Settings > AutoLoad tab.
|
|
|
|
Each autoload needs a name, this name will be the node name, and the
|
|
node will be always added to the root viewport before any scene is
|
|
loaded.
|
|
|
|
.. image:: /img/singleton.png
|
|
|
|
This means, that a for a singleton named "playervariables", any node can
|
|
access it by requesting:
|
|
|
|
::
|
|
|
|
var player_vars = get_node("/root/playervariables")
|
|
|
|
Custom scene switcher
|
|
---------------------
|
|
|
|
This short tutorial will explain how to make a scene switcher by using
|
|
autoload. For simple scene switching, the
|
|
:ref:`SceneTree.change_scene() <class_SceneTree_change_scene>`
|
|
method suffices (described in :ref:`doc_scene_tree`), so this method is for
|
|
more complex behaviors when switching scenes.
|
|
|
|
First download the template from here:
|
|
:download:`autoload.zip </files/autoload.zip>`, then open it.
|
|
|
|
Two scenes are present, scene_a.scn and scene_b.scn on an otherwise
|
|
empty project. Each are identical and contain a button connected to a
|
|
callback for going to the opposite scene. When the project runs, it
|
|
starts n scene_a.scn. However, this does nothing and pressing the
|
|
button does not work.
|
|
|
|
global.gd
|
|
---------
|
|
|
|
First of all, create a global.gd script. The easier way to create a
|
|
resource from scratch is from the resources tab:
|
|
|
|
.. image:: /img/newscript.png
|
|
|
|
Save the script to a file global.gd:
|
|
|
|
.. image:: /img/saveasscript.png
|
|
|
|
The script should be opened in the script editor. Next step will be
|
|
adding it to autoload, for this, go to: Scene [STRIKEOUT:> Project
|
|
Settings]> AutoLoad and add a new autoload with name "global" that
|
|
points to this file:
|
|
|
|
.. image:: /img/addglobal.png
|
|
|
|
Now, when any scene is run, the script will be always loaded.
|
|
|
|
So, going back to it, In the _ready() function, the current scene
|
|
will be fetched. Both the current scene and global.gd are children of
|
|
root, but the autoloaded nodes are always first. This means that the
|
|
last child of root is always the loaded scene.
|
|
|
|
Also, make sure that global.gd extends from Node, otherwise it won't be
|
|
loaded.
|
|
|
|
::
|
|
|
|
extends Node
|
|
|
|
var current_scene = null
|
|
|
|
func _ready():
|
|
var root = get_tree().get_root()
|
|
current_scene = root.get_child( root.get_child_count() -1 )
|
|
|
|
Next, is the function for changing scene. This function will erase the
|
|
current scene and replace it by the requested one.
|
|
|
|
::
|
|
|
|
func goto_scene(path):
|
|
|
|
# This function will usually be called from a signal callback,
|
|
# or some other function from the running scene.
|
|
# Deleting the current scene at this point might be
|
|
# a bad idea, because it may be inside of a callback or function of it.
|
|
# The worst case will be a crash or unexpected behavior.
|
|
|
|
# The way around this is deferring the load to a later time, when
|
|
# it is ensured that no code from the current scene is running:
|
|
|
|
call_deferred("_deferred_goto_scene",path)
|
|
|
|
|
|
func _deferred_goto_scene(path):
|
|
|
|
# Immediately free the current scene,
|
|
# there is no risk here.
|
|
current_scene.free()
|
|
|
|
# Load new scene
|
|
var s = ResourceLoader.load(path)
|
|
|
|
# Instance the new scene
|
|
current_scene = s.instance()
|
|
|
|
# Add it to the active scene, as child of root
|
|
get_tree().get_root().add_child(current_scene)
|
|
|
|
# optional, to make it compatible with the SceneTree.change_scene() API
|
|
get_tree().set_current_scene( current_scene )
|
|
|
|
As mentioned in the comments above, we really want to avoid the
|
|
situation of having the current scene being deleted while being used
|
|
(code from functions of it being run), so using
|
|
:ref:`Object.call_deferred() <class_Object_call_deferred>`
|
|
is desired at this point. The result is that execution of the commands
|
|
in the second function will happen at an immediate later time when no
|
|
code from the current scene is running.
|
|
|
|
Finally, all that is left is to fill the empty functions in scene_a.gd
|
|
and scene_b.gd:
|
|
|
|
::
|
|
|
|
#add to scene_a.gd
|
|
|
|
func _on_goto_scene_pressed():
|
|
get_node("/root/global").goto_scene("res://scene_b.scn")
|
|
|
|
and
|
|
|
|
::
|
|
|
|
#add to scene_b.gd
|
|
|
|
func _on_goto_scene_pressed():
|
|
get_node("/root/global").goto_scene("res://scene_a.scn")
|
|
|
|
Finally, by running the project it's possible to switch bewtween both
|
|
scenes y pressing the button!
|
|
|
|
(To load scenes with a progress bar, check out the next tutorial,
|
|
:ref:`doc_background_loading`)
|