scene_organization.rst 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. .. _doc_scene_organization:
  2. Scene organization
  3. ==================
  4. This article covers topics related to the effective organization of
  5. scene content. Which nodes should one use? Where should one place them?
  6. How should they interact?
  7. How to build relationships effectively
  8. --------------------------------------
  9. When Godot users begin crafting their own scenes, they often run into the
  10. following problem:
  11. They create their first scene and fill it with content before the creeping
  12. sense that they need to split it up into re-usable pieces haunts them. They
  13. save branches of their scene into their own scene. However, they then notice
  14. that the hard references they were able to rely on before are no longer
  15. possible. Re-using the scene in multiple places creates issues because the
  16. node paths do not find their targets. Signal connections established in the
  17. editor break.
  18. To fix these problems, one must instantiate the sub-scenes without them
  19. requiring details about their environment. One needs to be able to trust
  20. that the sub-scene will create itself without being picky about how one uses
  21. it.
  22. One of the biggest things to consider in OOP is maintaining
  23. focused, singular-purpose classes with
  24. `loose coupling <https://en.wikipedia.org/wiki/Loose_coupling>`_
  25. to other parts of the codebase. This keeps the size of objects small (for
  26. maintainability) and improves their reusability so that re-writing completed
  27. logic is unnecessary.
  28. These OOP best practices have *several* ramifications for the best practices
  29. in scene structure and script usage.
  30. **If at all possible, one should design scenes to have no dependencies.**
  31. That is, one should create scenes that keep everything they need within
  32. themselves.
  33. If a scene must interact with an external context, experienced developers
  34. recommend the use of
  35. `Dependency Injection <https://en.wikipedia.org/wiki/Dependency_injection>`_.
  36. This technique involves having a high-level API provide the dependencies of the
  37. low-level API. Why do this? Because classes which rely on their external
  38. environment can inadvertently trigger bugs and unexpected behavior.
  39. To do this, one must expose data and then rely on a parent context to
  40. initialize it:
  41. 1. Connect to a signal. Extremely safe, but should use only to "respond" to
  42. behavior, not start it. Note that signal names are usually past-tense verbs
  43. like "entered", "skill_activated", or "item_collected".
  44. .. tabs::
  45. .. code-tab:: gdscript GDScript
  46. # Parent
  47. $Child.connect("signal_name", object_with_method, "method_on_the_object")
  48. # Child
  49. emit_signal("signal_name") # Triggers parent-defined behavior.
  50. .. code-tab:: csharp
  51. // Parent
  52. GetNode("Child").Connect("SignalName", ObjectWithMethod, "MethodOnTheObject");
  53. // Child
  54. EmitSignal("SignalName"); // Triggers parent-defined behavior.
  55. 2. Call a method. Used to start behavior.
  56. .. tabs::
  57. .. code-tab:: gdscript GDScript
  58. # Parent
  59. $Child.method_name = "do"
  60. # Child, assuming it has String property 'method_name' and method 'do'.
  61. call(method_name) # Call parent-defined method (which child must own).
  62. .. code-tab:: csharp
  63. // Parent
  64. GetNode("Child").Set("MethodName", "Do");
  65. // Child
  66. Call(MethodName); // Call parent-defined method (which child must own).
  67. 3. Initialize a :ref:`FuncRef <class_FuncRef>` property. Safer than a method
  68. as ownership of the method is unnecessary. Used to start behavior.
  69. .. tabs::
  70. .. code-tab:: gdscript GDScript
  71. # Parent
  72. $Child.func_property = funcref(object_with_method, "method_on_the_object")
  73. # Child
  74. func_property.call_func() # Call parent-defined method (can come from anywhere).
  75. .. code-tab:: csharp
  76. // Parent
  77. GetNode("Child").Set("FuncProperty", GD.FuncRef(ObjectWithMethod, "MethodOnTheObject"));
  78. // Child
  79. FuncProperty.CallFunc(); // Call parent-defined method (can come from anywhere).
  80. 4. Initialize a Node or other Object reference.
  81. .. tabs::
  82. .. code-tab:: gdscript GDScript
  83. # Parent
  84. $Child.target = self
  85. # Child
  86. print(target) # Use parent-defined node.
  87. .. code-tab:: csharp
  88. // Parent
  89. GetNode("Child").Set("Target", this);
  90. // Child
  91. GD.Print(Target); // Use parent-defined node.
  92. 5. Initialize a NodePath.
  93. .. tabs::
  94. .. code-tab:: gdscript GDScript
  95. # Parent
  96. $Child.target_path = ".."
  97. # Child
  98. get_node(target_path) # Use parent-defined NodePath.
  99. .. code-tab:: csharp
  100. // Parent
  101. GetNode("Child").Set("TargetPath", NodePath(".."));
  102. // Child
  103. GetNode(TargetPath); // Use parent-defined NodePath.
  104. These options hide the source of accesses from the child node. This in turn
  105. keeps the child **loosely coupled** to its environment. One can re-use it
  106. in another context without any extra changes to its API.
  107. .. note::
  108. Although the examples above illustrate parent-child relationships,
  109. the same principles apply towards all object relations. Nodes which
  110. are siblings should only be aware of their hierarchies while an ancestor
  111. mediates their communications and references.
  112. .. tabs::
  113. .. code-tab:: gdscript GDScript
  114. # Parent
  115. $Left.target = $Right.get_node("Receiver")
  116. # Left
  117. var target: Node
  118. func execute():
  119. # Do something with 'target'.
  120. # Right
  121. func _init():
  122. var receiver = Receiver.new()
  123. add_child(receiver)
  124. .. code-tab:: csharp
  125. // Parent
  126. GetNode<Left>("Left").Target = GetNode("Right/Receiver");
  127. public class Left : Node
  128. {
  129. public Node Target = null;
  130. public void Execute()
  131. {
  132. // Do something with 'Target'.
  133. }
  134. }
  135. public class Right : Node
  136. {
  137. public Node Receiver = null;
  138. public Right()
  139. {
  140. Receiver = ResourceLoader.Load<Script>("Receiver.cs").New();
  141. AddChild(Receiver);
  142. }
  143. }
  144. The same principles also apply to non-Node objects that maintain dependencies
  145. on other objects. Whichever object actually owns the objects should manage
  146. the relationships between them.
  147. .. warning::
  148. One should favor keeping data in-house (internal to a scene) though as
  149. placing a dependency on an external context, even a loosely coupled one,
  150. still means that the node will expect something in its environment to be
  151. true. The project's design philosophies should prevent this from happening.
  152. If not, the code's inherent liabilities will force developers to use
  153. documentation to keep track of object relations on a microscopic scale; this
  154. is otherwise known as development hell. Writing code that relies on external
  155. documentation for one to use it safely is error-prone by default.
  156. To avoid creating and maintaining such documentation, one converts the
  157. dependent node ("child" above) into a tool script that implements
  158. :ref:`_get_configuration_warning() <class_Node_method__get_configuration_warning>`.
  159. Returning a non-empty string from it will make the Scene dock generate a
  160. warning icon with the string as a tooltip by the node. This is the same icon
  161. that appears for nodes such as the
  162. :ref:`Area2D <class_Area2D>` node when it has no child
  163. :ref:`CollisionShape2D <class_CollisionShape2D>` nodes defined. The editor
  164. then self-documents the scene through the script code. No content duplication
  165. via documentation is necessary.
  166. A GUI like this can better inform project users of critical information about
  167. a Node. Does it have external dependencies? Have those dependencies been
  168. satisfied? Other programmers, and especially designers and writers, will need
  169. clear instructions in the messages telling them what to do to configure it.
  170. So, why do all this complex switcharoo work? Well, because scenes operate
  171. best when they operate alone. If unable to work alone, then working with
  172. others anonymously (with minimal hard dependencies, i.e. loose coupling).
  173. If the inevitable changes made to a class cause it to interact with other
  174. scenes in unforeseen ways, then things break down. A change to one class could
  175. result in damaging effects to other classes.
  176. Scripts and scenes, as extensions of engine classes should abide
  177. by *all* OOP principles. Examples include...
  178. - `SOLID <https://en.wikipedia.org/wiki/SOLID>`_
  179. - `DRY <https://en.wikipedia.org/wiki/Don%27t_repeat_yourself>`_
  180. - `KISS <https://en.wikipedia.org/wiki/KISS_principle>`_
  181. - `YAGNI <https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it>`_
  182. Choosing a node tree structure
  183. ------------------------------
  184. So, a developer starts work on a game only to stop at the vast possibilities
  185. before them. They might know what they want to do, what systems they want to
  186. have, but *where* to put them all? Well, how one goes about making their game
  187. is always up to them. One can construct node trees in a myriad of ways.
  188. But, for those who are unsure, this helpful guide can give them a sample of
  189. a decent structure to start with.
  190. A game should always have a sort of "entry point"; somewhere the developer can
  191. definitively track where things begin so that they can follow the logic as it
  192. continues elsewhere. This place also serves as a bird's eye view to all of the
  193. other data and logic in the program. For traditional applications, this would
  194. be the "main" function. In this case, it would be a Main node.
  195. - Node "Main" (main.gd)
  196. The ``main.gd`` script would then serve as the primary controller of one's
  197. game.
  198. Then one has their actual in-game "World" (a 2D or 3D one). This can be a child
  199. of Main. In addition, one will need a primary GUI for their game that manages
  200. the various menus and widgets the project needs.
  201. - Node "Main" (main.gd)
  202. - Node2D/Spatial "World" (game_world.gd)
  203. - Control "GUI" (gui.gd)
  204. When changing levels, one can then swap out the children of the "World" node.
  205. :ref:`Changing scenes manually <doc_change_scenes_manually>` gives users full
  206. control over how their game world transitions.
  207. The next step is to consider what gameplay systems one's project requires.
  208. If one has a system that...
  209. 1. tracks all of its data internally
  210. 2. should be globally accessible
  211. 3. should exist in isolation
  212. ... then one should create an :ref:`autoload 'singleton' node <doc_singletons_autoload>`.
  213. .. note::
  214. For smaller games, a simpler alternative with less control would be to have
  215. a "Game" singleton that simply calls the
  216. :ref:`SceneTree.change_scene() <class_SceneTree_method_change_scene>` method
  217. to swap out the main scene's content. This structure more or less keeps
  218. the "World" as the main game node.
  219. Any GUI would need to also be a
  220. singleton, be transitory parts of the "World", or be manually added as a
  221. direct child of the root. Otherwise, the GUI nodes would also delete
  222. themselves during scene transitions.
  223. If one has systems that modify other systems' data, one should define those as
  224. their own scripts or scenes rather than autoloads. For more information on the
  225. reasons, please see the
  226. :ref:`'Autoloads vs. Internal Nodes' <doc_autoloads_versus_internal_nodes>`
  227. documentation.
  228. Each subsystem within one's game should have its own section within the
  229. SceneTree. One should use parent-child relationships only in cases where nodes
  230. are effectively elements of their parents. Does removing the parent reasonably
  231. mean that one should also remove the children? If not, then it should have its
  232. own place in the hierarchy as a sibling or some other relation.
  233. .. note::
  234. In some cases, one needs these separated nodes to *also* position themselves
  235. relative to each other. One can use the
  236. :ref:`RemoteTransform <class_RemoteTransform>` /
  237. :ref:`RemoteTransform2D <class_RemoteTransform2D>` nodes for this purpose.
  238. They will allow a target node to conditionally inherit selected transform
  239. elements from the Remote\* node. To assign the ``target``
  240. :ref:`NodePath <class_NodePath>`, use one of the following:
  241. 1. A reliable third party, likely a parent node, to mediate the assignment.
  242. 2. A group, to easily pull a reference to the desired node (assuming there
  243. will only ever be one of the targets).
  244. When should one do this? Well, it's up to them to decide. The dilemma
  245. arises when one must micro-manage when a node must move around the SceneTree
  246. to preserve itself. For example...
  247. - Add a "player" node to a "room".
  248. - Need to change rooms, so one must delete the current room.
  249. - Before the room can be deleted, one must preserve and/or move the player.
  250. Is memory a concern?
  251. - If not, one can just create the two rooms, move the player
  252. and delete the old one. No problem.
  253. If so, one will need to...
  254. - Move the player somewhere else in the tree.
  255. - Delete the room.
  256. - Instantiate and add the new room.
  257. - Re-add the player.
  258. The issue is that the player here is a "special case", one where the
  259. developers must *know* that they need to handle the player this way for the
  260. project. As such, the only way to reliably share this information as a team
  261. is to *document* it. Keeping implementation details in documentation however
  262. is dangerous. It's a maintenance burden, strains code readability, and bloats
  263. the intellectual content of a project unnecessarily.
  264. In a more complex game with larger assets, it can be a better idea to simply
  265. keep the player somewhere else in the SceneTree entirely. This involves...
  266. 1. More consistency.
  267. 2. No "special cases" that must be documented and maintained somewhere.
  268. 3. No opportunity for errors to occur because these details are not accounted
  269. for.
  270. In contrast, if one ever needs to have a child node that does *not* inherit
  271. the transform of their parent, one has the following options:
  272. 1. The **declarative** solution: place a :ref:`Node <class_Node>` in between
  273. them. As nodes with no transform, Nodes will not pass along such
  274. information to their children.
  275. 2. The **imperative** solution: Use the ``set_as_toplevel`` setter for the
  276. :ref:`CanvasItem <class_CanvasItem_method_set_as_toplevel>` or
  277. :ref:`Spatial <class_Spatial_method_set_as_toplevel>` node. This will make
  278. the node ignore its inherited transform.
  279. .. note::
  280. If building a networked game, keep in mind which nodes and gameplay systems
  281. are relevant to all players versus those just pertinent to the authoritative
  282. server. For example, users do not all need to have a copy of every players'
  283. "PlayerController" logic. Instead, they need only their own. As such, keeping
  284. these in a separate branch from the "world" can help simplify the management
  285. of game connections and the like.
  286. The key to scene organization is to consider the SceneTree in relational terms
  287. rather than spatial terms. Do the nodes need to be dependent on their parent's
  288. existence? If not, then they can thrive all by themselves somewhere else.
  289. If so, then it stands to reason they should be children of that parent (and
  290. likely part of that parent's scene if they aren't already).
  291. Does this mean nodes themselves are components? Not at all.
  292. Godot's node trees form an aggregation relationship, not one of composition.
  293. But while one still has the flexibility to move nodes around, it is still best
  294. when such moves are unnecessary by default.