singletons_autoload.rst 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. .. _doc_singletons_autoload:
  2. Singletons (AutoLoad)
  3. =====================
  4. Introduction
  5. ------------
  6. Scene Singletons are very useful things, as they represent a very common
  7. use case, but it's not clear at the begining where their value is.
  8. The scene system is very useful, but by itself it has a few drawbacks:
  9. - There is no "common" place to store information (such as core, items
  10. obtained, etc) between two scenes.
  11. - It is possible to make a scene that loads other scenes as children
  12. and frees them, while keeping that information, but then if that is
  13. done, it's not possible to run a scene alone by itself and expect it
  14. to work
  15. - It is also possible to store persistent information to disk in
  16. \`user://\` and have scenes always load it, but saving/loading that
  17. while changing scenes is cumbersome.
  18. So, after using Godot for a while, it becomes clear that it is necessary
  19. to have parts of a scene that:
  20. - Are always loaded, no matter which scene is opened from the editor.
  21. - Can keep global variables, such as player information, items, money,
  22. etc.
  23. - Can handle switching of scenes and transitions.
  24. - Just have something that acts like a singleton, since GDScript does
  25. not support global variables by design.
  26. For this, the option for auto-loading nodes and scripts exists.
  27. Autoload
  28. --------
  29. Autoload can be a scene, or a script that inherits from Node (a Node
  30. will be created and the script will be set to it). They are added to the
  31. project in the Scene [STRIKEOUT:> Project Settings]> AutoLoad tab.
  32. Each autoload needs a name, this name will be the node name, and the
  33. node will be always added to the root viewport before any scene is
  34. loaded.
  35. .. image:: /img/singleton.png
  36. This means, that a for a singleton named "playervariables", any node can
  37. access it by requesting:
  38. ::
  39. var player_vars = get_node("/root/playervariables")
  40. Custom Scene Switcher
  41. ---------------------
  42. This short tutorial will explain how to make a scene switcher by using
  43. autoload. For simple scene switching, the
  44. :ref:`SceneTree.change_scene() <class_SceneTree_change_scene>`
  45. method suffices (described in :ref:`doc_scene_tree`), so this method is for
  46. more complex behaviors when switching scenes.
  47. First download the template from here: attachment:autoload.zip, then
  48. open it.
  49. Two scenes are present, scene\_a.scn and scene\_b.scn on an otherwise
  50. empty project. Each are identical and contain a button connected to a
  51. callback for going to the opposite scene. When the project runs, it
  52. starts n scene\_a.scn. However, this does nothing and pressing the
  53. button does not work.
  54. global.gd
  55. ---------
  56. First of all, create a global.gd script. The easier way to create a
  57. resource from scratch is from the resources tab:
  58. .. image:: /img/newscript.png
  59. Save the script to a file global.gd:
  60. .. image:: /img/saveasscript.png
  61. The script should be opened in the script editor. Next step will be
  62. adding it to autoload, for this, go to: Scene [STRIKEOUT:> Project
  63. Settings]> AutoLoad and add a new autoload with name "global" that
  64. points to this file:
  65. .. image:: /img/addglobal.png
  66. Now, when any scene is run, the script will be always loaded.
  67. So, going back to it, In the \_ready() function, the current scene
  68. will be fetched. Both the current scene and global.gd are children of
  69. root, but the autoloaded nodes are always first. This means that the
  70. last child of root is always the loaded scene.
  71. Also, make sure that global.gd extends from Node, otherwise it won't be
  72. loaded.
  73. ::
  74. extends Node
  75. var current_scene = null
  76. func _ready():
  77. var root = get_tree().get_root()
  78. current_scene = root.get_child( root.get_child_count() -1 )
  79. Next, is the function for changing scene. This function will erase the
  80. current scene and replace it by the requested one.
  81. ::
  82. func goto_scene(path):
  83. # This function will usually be called from a signal callback,
  84. # or some other function from the running scene.
  85. # Deleting the current scene at this point might be
  86. # a bad idea, because it may be inside of a callback or function of it.
  87. # The worst case will be a crash or unexpected behavior.
  88. # The way around this is deferring the load to a later time, when
  89. # it is ensured that no code from the current scene is running:
  90. call_deferred("_deferred_goto_scene",path)
  91. func _deferred_goto_scene(path):
  92. # Immediately free the current scene,
  93. # there is no risk here.
  94. current_scene.free()
  95. # Load new scene
  96. var s = ResourceLoader.load(path)
  97. # Instance the new scene
  98. current_scene = s.instance()
  99. # Add it to the active scene, as child of root
  100. get_tree().get_root().add_child(current_scene)
  101. # optional, to make it compatible with the SceneTree.change_scene() API
  102. get_tree().set_current_scene( current_scene )
  103. As mentioned in the comments above, we really want to avoid the
  104. situation of having the current scene being deleted while being used
  105. (code from functions of it being run), so using
  106. :ref:`Object.call_deferred() <class_Object_call_deferred>`
  107. is desired at this point. The result is that execution of the commands
  108. in the second function will happen at an immediate later time when no
  109. code from the current scene is running.
  110. Finally, all that is left is to fill the empty functions in scene\_a.gd
  111. and scene\_b.gd:
  112. ::
  113. #add to scene_a.gd
  114. func _on_goto_scene_pressed():
  115. get_node("/root/global").goto_scene("res://scene_b.scn")
  116. and
  117. ::
  118. #add to scene_b.gd
  119. func _on_goto_scene_pressed():
  120. get_node("/root/global").goto_scene("res://scene_a.scn")
  121. Finally, by running the project it's possible to switch bewtween both
  122. scenes y pressing the button!
  123. (To load scenes with a progress bar, check out the next tutorial,
  124. [[Background Loading]])