123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- Singletons (AutoLoad)
- =====================
- Introduction
- ------------
- Scene Singletons are very useful things, as they represent a very common
- use case, but it's not clear at the begining 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 [STRIKEOUT:> 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
- `SceneTree.change\_scene <https://github.com/okamstudio/godot/wiki/class_scenetree#change_scene>`__
- method suffices (described [[Scene Main Loop]] ), so this method is for
- more complex behaviors when switching scenes.
- First download the template from here: attachment: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
- `Object.call\_deferred <https://github.com/okamstudio/godot/wiki/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,
- [[Background Loading]])
- *Juan Linietsky, Ariel Manzur, Distributed under the terms of the `CC
- By <https://creativecommons.org/licenses/by/3.0/legalcode>`__ license.*
|