singletons_autoload.rst 5.9 KB

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