{"id":701,"date":"2023-10-22T18:19:20","date_gmt":"2023-10-22T23:19:20","guid":{"rendered":"https:\/\/www.jasonsblog.place\/?p=701"},"modified":"2023-10-22T18:19:20","modified_gmt":"2023-10-22T23:19:20","slug":"tips-for-making-scenes-and-nodes-modular-in-the-godot-engine","status":"publish","type":"post","link":"https:\/\/www.jasonsblog.place\/index.php\/2023\/10\/22\/tips-for-making-scenes-and-nodes-modular-in-the-godot-engine\/","title":{"rendered":"Tips for making scenes and nodes modular in the Godot engine"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">When I\u2019m designing a game, I try to make the nodes and scenes modular. When they are modular, the scenes can be placed in many situations and are easier to debug. This is even recommended by the developers of the engine (see more examples from the developers in the Godot <a href=\"https:\/\/docs.godotengine.org\/en\/stable\/tutorials\/best_practices\/scene_organization.html\">documentation<\/a>). So please join me in a little article on making nodes and scenes modular.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Note<\/strong>: this is meant for intermediate to advanced designers of the Godot engine. The concepts and code discussed here may be unfamiliar to those who haven\u2019t been programming for a while, or who haven\u2019t made a few games, and have not read up on object oriented programming (OOP). Also note that some of the syntax described here works in Godot v3.4 and v3.5. It has not been tested in Godot v.4.0+.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Self containment<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A node or a scene should rely mainly on what&#8217;s in the scene or node. If a node <em>must<\/em> depend on something outside of it, then it should at least be given to the node (something I think is called &#8220;dependency injection&#8221;). I usually do it like this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Top of the script for the node or scene.\nextends Node2D\n# This is the node that will be \"injected\" into the node or scene.\nexport(NodePath) var foobar_node_path = @\"\"\nonready var foobar_node = get_node_or_null(foobar_node_path)<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">After that is written in the node&#8217;s script, I add it to the node via the editor:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"445\" height=\"113\" src=\"https:\/\/www.jasonsblog.place\/wp-content\/uploads\/2023\/10\/Foobar-node-path.png\" alt=\"\" class=\"wp-image-702\" srcset=\"https:\/\/www.jasonsblog.place\/wp-content\/uploads\/2023\/10\/Foobar-node-path.png 445w, https:\/\/www.jasonsblog.place\/wp-content\/uploads\/2023\/10\/Foobar-node-path-300x76.png 300w\" sizes=\"auto, (max-width: 445px) 100vw, 445px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Then all I need to do is instance this node in the modular scene:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># You can do this in the `_ready()` function. Or you could do it in other parts of the script.\nfunc _ready():\n    var foo = foobar_node.instance()<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Be careful when using <code>NodePath<\/code>s. They should not be used for nodes that will move around the scene tree (or will be freed at runtime). It is best to rely on the editor to generate the <code>NodePath<\/code>, or have them constructed programmatically. To get the path of a <code>Node<\/code>, use the <code>get_path()<\/code> method of the <code>Node<\/code> class:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">var my_path = foobar_node.get_path()<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Loose coupling<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">When you have a node that&#8217;s modular, it <em>still<\/em> has to perform its job: the node has to show the bad guy, the scene has to animate the main character, or the node just needs to keep track of things. So how do you make it usable yet also prevent it from wrecking the other parts of the game with bugs and stuff? Use this motto from the developers: <strong>call down, signal up<\/strong>. What does that mean? In the scene tree of your average Godot scene, all of the nodes are arranged in a tree, with one node as the root. The parent of nodes can call methods on their child nodes so as to get them to do something. When they need to call to sibling nodes or other parent nodes, they can use signals to get those other nodes to react. Here&#8217;s some examples of calling down and signaling up.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We have here a scene tree:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"285\" height=\"128\" src=\"https:\/\/www.jasonsblog.place\/wp-content\/uploads\/2023\/10\/Scene-tree-hierarchy-larger.png\" alt=\"\" class=\"wp-image-703\"\/><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">The <code>Mama_node<\/code> can call methods on the <code>Baby_node<\/code> so as to get it do something. Here&#8217;s something that the <code>Baby_node<\/code> may do:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">## In Baby_node\n# These variables are local to the Baby_node, and should only be used in this node, or its child nodes.\nvar health = 10\nvar move_distance = 10\nfunc move_player():\n    # Move the player here with something like move_and_slide().<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In this example, the <code>Baby_node<\/code> has the <code>Player_node<\/code> as a child. It can use its methods, variables, and what-have-you to perform actions on the <code>Player_node<\/code>. If the <code>Mama_node<\/code> needs to move the <code>Player_node<\/code>, it could <em>call down<\/em> to the <code>Baby_node<\/code>. When the <code>Mama_node<\/code> needs to have the <code>Player_node<\/code> do other things (like take damage in some cutscene), it can access the <code>health<\/code> variable on the <code>Baby_node<\/code>. All that the <code>Baby_node<\/code> needs to worry about is the <code>Player_node<\/code> (or any other child nodes in its custody). It doesn&#8217;t need to worry about the enemy nodes, or the scenes that need to interact with the <code>Player_node<\/code>; that&#8217;s something for the <code>Mama_node<\/code> (or higher parent nodes) to solve. So what if the <code>Baby_node<\/code> needs to tell the <code>Mama_node<\/code> (or other important nodes) about the <code>Player_node<\/code>? That&#8217;s where the node needs to <strong>signal up<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On the <code>Baby_node<\/code>, a signal may be defined:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># In the script attached to the Baby_node.\nsignal player_died<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When another node (such as the <code>root_node<\/code> or some &#8220;director&#8221; node) needs that information, all it needs to do is listen for this signal. It may be hooked up like this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># In a script attached to some \"director\" node.\nfunc _ready():\n    connect(\"player_died\", self, \"_on_player_death\")\n\n# Further down the script.\nfunc _on_player_death():\n    # Show player death animation, and fade to black.<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now these two nodes (or scenes) don&#8217;t need to rely on each other. The &#8220;director&#8221; scene can have its optional player death (either by directly animating it, or using an <code>AnimationPlayer<\/code> node). This also helps with debugging. When we encounter a bug where the player is <em>suppose<\/em> to die and doesn&#8217;t, we don&#8217;t have to go far to figure out where the error is. We could look in the <code>Mama_node<\/code> (which has indirect access to the <code>Player_node<\/code>), or we could look at the <code>Baby_node<\/code> (the most likely culprit) for our bug. When we reduce our nodes and scenes to separate steps in the possible states of what can happen, it makes debugging easier because we know where in the steps how things are <em>suppose<\/em> to happen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Hope you enjoy these couple of tips. I&#8217;m sure there are <em>many<\/em> other ways the scenes and the nodes (or various objects) of the Godot engine can be made modular. Just remember to watch out for code or designs which break <code>NodePath<\/code>s, mess up function calls, or make it difficult to use signals, and your development journey will be made easier.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Making modular scenes and nodes in the Godot engine will really help with moving around objects and pieces of a game. Here&#8217;s some tips on making your game more modular.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7,76,31],"tags":[55,106,91,54,23],"class_list":["post-701","post","type-post","status-publish","format-standard","hentry","category-information-technology","category-programming","category-video-games","tag-design","tag-game-development","tag-godot-engine","tag-programming-languages","tag-video-game"],"_links":{"self":[{"href":"https:\/\/www.jasonsblog.place\/index.php\/wp-json\/wp\/v2\/posts\/701","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.jasonsblog.place\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.jasonsblog.place\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.jasonsblog.place\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.jasonsblog.place\/index.php\/wp-json\/wp\/v2\/comments?post=701"}],"version-history":[{"count":2,"href":"https:\/\/www.jasonsblog.place\/index.php\/wp-json\/wp\/v2\/posts\/701\/revisions"}],"predecessor-version":[{"id":705,"href":"https:\/\/www.jasonsblog.place\/index.php\/wp-json\/wp\/v2\/posts\/701\/revisions\/705"}],"wp:attachment":[{"href":"https:\/\/www.jasonsblog.place\/index.php\/wp-json\/wp\/v2\/media?parent=701"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.jasonsblog.place\/index.php\/wp-json\/wp\/v2\/categories?post=701"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.jasonsblog.place\/index.php\/wp-json\/wp\/v2\/tags?post=701"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}