singletons_autoload.rst 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. .. _doc_singletons_autoload:
  2. Singletons (AutoLoad)
  3. =====================
  4. Introduction
  5. ------------
  6. Scene singletons are useful, catering to a common use case where you need
  7. to store persistent information between scenes.
  8. Albeit powerful, the scene system by itself has a few drawbacks:
  9. - There is no common place to store information (e.g. a player's items etc.)
  10. required by more than one scene.
  11. - While it is possible for a scene that loads and unloads other scenes as
  12. its children to store information common to these child scenes, it is no
  13. longer possible to run these scenes by themselves and expect them to work
  14. correctly.
  15. - While information can be stored to disk in \`user://\` and this information
  16. can be loaded by scenes that require it, continuously saving and loading this
  17. data when changing scenes is cumbersome and may be slow.
  18. However there is still a need in Godot to create parts of a scene that:
  19. - Are always loaded, no matter which scene is opened from the editor
  20. - Can store global variables, such as player information, items, money
  21. etc. and share information between scenes
  22. - Can handle switching scenes and transitions
  23. - Acts like a singleton, since GDScript does not support global variables by design.
  24. Auto-loading nodes and scripts caters to this need.
  25. AutoLoad
  26. --------
  27. You can use AutoLoad to load a scene, or a script that inherits from Node (a node
  28. will be created and the script will be set to it).
  29. To autoload a scene or script, select Project > Project Settings from the menu and switch
  30. to the AutoLoad tab. Each entry in the list requires a name, which is used as the name
  31. of the node, and the node is always added to the root viewport before any other scenes
  32. are loaded.
  33. .. image:: img/singleton.png
  34. This means that any node can access a singleton named "playervariables" with:
  35. .. tabs::
  36. .. code-tab:: gdscript GDScript
  37. var player_vars = get_node("/root/playervariables")
  38. player_vars.health
  39. .. code-tab:: csharp
  40. var playerVariables = (PlayerVariables)GetNode("/root/PlayerVariables");
  41. playerVariables.Health -= 10; // Instance field.
  42. Or even simpler using the name directly:
  43. .. tabs::
  44. .. code-tab:: gdscript GDScript
  45. playervariables.health
  46. .. code-tab:: csharp
  47. // Static members can be accessed by using the class name.
  48. PlayerVariables.Health -= 10;
  49. Custom scene switcher
  50. ---------------------
  51. This short tutorial will explain how to make a scene switcher using
  52. autoload. For simple scene switching, the
  53. :ref:`SceneTree.change_scene() <class_SceneTree_change_scene>`
  54. method suffices (described in :ref:`doc_scene_tree`), so this method is for
  55. more complex behavior when switching between scenes.
  56. First download the template from here:
  57. :download:`autoload.zip <files/autoload.zip>`, then open it.
  58. Two scenes are present, scene_a.tscn and scene_b.tscn on an otherwise
  59. empty project. Each are identical and contain a button connected to a
  60. callback for switching to the other scene. When the project runs, it
  61. starts in scene_a.tscn. However, this currently does nothing and pressing the
  62. button does not work.
  63. global.gd
  64. ---------
  65. First of all, create a global.gd script. The easy way to create a
  66. resource from scratch is from the new resource button in the inspector tab:
  67. .. image:: img/newscript.png
  68. Save the script as `global.gd`:
  69. .. image:: img/saveasscript.png
  70. The script should open in the script editor. The next step is to add
  71. it to AutoLoad list. Select Project > Project Settings from the menu,
  72. switch to the AutoLoad tab and add a new entry with name "global" that
  73. points to this file:
  74. .. image:: img/addglobal.png
  75. Now, whenever you run any of your scenes, the script is always loaded.
  76. Returning to our script, the current scene needs to be fetched in the
  77. `_ready()` function. Both the current scene and `global.gd` are children of
  78. root, but the autoloaded nodes are always first. This means that the
  79. last child of root is always the loaded scene.
  80. Note: Make sure that global.gd extends Node, otherwise it won't be
  81. loaded!
  82. .. tabs::
  83. .. code-tab:: gdscript GDScript
  84. extends Node
  85. var current_scene = null
  86. func _ready():
  87. var root = get_tree().get_root()
  88. current_scene = root.get_child(root.get_child_count() -1)
  89. .. code-tab:: csharp
  90. using Godot;
  91. using System;
  92. public class Global : Godot.Node
  93. {
  94. public Node CurrentScene { get; set; }
  95. public override void _Ready()
  96. {
  97. Viewport root = GetTree().GetRoot();
  98. CurrentScene = root.GetChild(root.GetChildCount() - 1);
  99. }
  100. }
  101. Next up is the function for changing the scene. This function frees the
  102. current scene and replaces it with the requested one.
  103. .. tabs::
  104. .. code-tab:: gdscript GDScript
  105. func goto_scene(path):
  106. # This function will usually be called from a signal callback,
  107. # or some other function from the running scene.
  108. # Deleting the current scene at this point might be
  109. # a bad idea, because it may be inside of a callback or function of it.
  110. # The worst case will be a crash or unexpected behavior.
  111. # The way around this is deferring the load to a later time, when
  112. # it is ensured that no code from the current scene is running:
  113. call_deferred("_deferred_goto_scene", path)
  114. func _deferred_goto_scene(path):
  115. # Immediately free the current scene,
  116. # there is no risk here.
  117. current_scene.free()
  118. # Load new scene.
  119. var s = ResourceLoader.load(path)
  120. # Instance the new scene.
  121. current_scene = s.instance()
  122. # Add it to the active scene, as child of root.
  123. get_tree().get_root().add_child(current_scene)
  124. # Optional, to make it compatible with the SceneTree.change_scene() API.
  125. get_tree().set_current_scene(current_scene)
  126. .. code-tab:: csharp
  127. public void GotoScene(string path)
  128. {
  129. // This function will usually be called from a signal callback,
  130. // or some other function from the running scene.
  131. // Deleting the current scene at this point might be
  132. // a bad idea, because it may be inside of a callback or function of it.
  133. // The worst case will be a crash or unexpected behavior.
  134. // The way around this is deferring the load to a later time, when
  135. // it is ensured that no code from the current scene is running:
  136. CallDeferred(nameof(DeferredGotoScene), path);
  137. }
  138. public void DeferredGotoScene(string path)
  139. {
  140. // Immediately free the current scene, there is no risk here.
  141. CurrentScene.Free();
  142. // Load a new scene.
  143. var nextScene = (PackedScene)GD.Load(path);
  144. // Instance the new scene.
  145. CurrentScene = nextScene.Instance();
  146. // Add it to the active scene, as child of root.
  147. GetTree().GetRoot().AddChild(CurrentScene);
  148. // Optional, to make it compatible with the SceneTree.change_scene() API.
  149. GetTree().SetCurrentScene(CurrentScene);
  150. }
  151. As mentioned in the comments above, we want to avoid the
  152. situation of having the current scene being deleted while being used
  153. (code from functions of it being run), so using
  154. :ref:`Object.call_deferred() <class_Object_call_deferred>`
  155. is desired at this point. The result is that execution of the commands
  156. in the second function will happen at a later time when no code from
  157. the current scene is running.
  158. Finally, all that is left is to fill the empty functions in scene_a.gd
  159. and scene_b.gd:
  160. .. tabs::
  161. .. code-tab:: gdscript GDScript
  162. # Add to 'scene_a.gd'.
  163. func _on_goto_scene_pressed():
  164. get_node("/root/global").goto_scene("res://scene_b.tscn")
  165. .. code-tab:: csharp
  166. // Add to 'SceneA.cs'.
  167. public void OnGotoScenePressed()
  168. {
  169. var global = (Global)GetNode("/root/Global");
  170. global.GotoScene("res://scene_b.tscn");
  171. }
  172. and
  173. .. tabs::
  174. .. code-tab:: gdscript GDScript
  175. # Add to 'scene_b.gd'.
  176. func _on_goto_scene_pressed():
  177. get_node("/root/global").goto_scene("res://scene_a.tscn")
  178. .. code-tab:: csharp
  179. // Add to 'SceneB.cs'.
  180. public void OnGotoScenePressed()
  181. {
  182. var global = (Global)GetNode("/root/Global");
  183. global.GotoScene("res://scene_a.tscn");
  184. }
  185. Now if you run the project, you can switch between both scenes by pressing
  186. the button!
  187. To load scenes with a progress bar, check out the next tutorial,
  188. :ref:`doc_background_loading`