<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Christine Coomans on Medium]]></title>
        <description><![CDATA[Stories by Christine Coomans on Medium]]></description>
        <link>https://medium.com/@christinec-dev?source=rss-3d2c08006851------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*Yrnc-mRAB6O3IYavuFzWWw.jpeg</url>
            <title>Stories by Christine Coomans on Medium</title>
            <link>https://medium.com/@christinec-dev?source=rss-3d2c08006851------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 23 Jun 2026 00:46:32 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@christinec-dev/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Godot 4: The Book of Code]]></title>
            <link>https://christinec-dev.medium.com/godot-4-the-book-of-code-148ebdb60a3e?source=rss-3d2c08006851------2</link>
            <guid isPermaLink="false">https://medium.com/p/148ebdb60a3e</guid>
            <category><![CDATA[fundamentals]]></category>
            <category><![CDATA[godot]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[game-development]]></category>
            <category><![CDATA[godot-engine]]></category>
            <dc:creator><![CDATA[Christine Coomans]]></dc:creator>
            <pubDate>Sun, 26 Oct 2025 17:26:59 GMT</pubDate>
            <atom:updated>2025-10-26T17:48:37.932Z</atom:updated>
            <content:encoded><![CDATA[<p>If you’re eager to dive into the exciting world of Godot development, it’s important to get a handle on the basics of programming! You don’t need to be a coding wizard to make a game, you just need to have a solid understanding the key concepts like variables, functions, loops, and arrays.</p><p>I previously put together the <a href="https://oops-i-devd.gitbook.io/christinec-dev/free-resources/godot-book-of-nodes"><strong>Book of Nodes</strong></a> (which I’m currently updating), covering the common nodes you’ll encounter on your game development journey. I thought it would be super helpful to create a companion resource — a friendly coding guide!</p><p>This guide covers the fundamentals of coding in Godot. We won’t delve into all the aspects of coding or GDScript, but we will cover the core structures necessary to begin coding.</p><h3>What is GDScript?</h3><p>GDScript is Godot’s own programming language, made <em>just for games</em>. It’s easy to read, beginner-friendly, and designed to integrate tightly with the Godot Editor.</p><p><strong>Why use GDScript?</strong></p><ul><li>It’s simple, fast, and built for Godot.</li><li>It’s similar to Python (clean syntax, no extra symbols).</li><li>You can write code directly on any node to control how it behaves<strong>.</strong></li></ul><p>A lot of developers switch over from Unity and therefore codes in Godot using <strong>C#</strong>. For that reason, we will be covering both GDScript and C# in this resource.</p><h3>Navigation</h3><p>This resource is broken down into the following sections:</p><h3>1. Fundamentals</h3><ul><li>Variables</li><li>Data Types</li><li>Constants</li><li>Functions</li><li>Comments</li><li>Scope</li></ul><h3>2. Logic &amp; Control</h3><ul><li>If / Else</li><li>Match (Switch)</li><li>Loops (For / While)</li><li>Break, Continue, &amp; Return</li></ul><h3>3. Collections</h3><ul><li>Arrays</li><li>Dictionaries</li><li>Resources</li></ul><h3>4. Gameplay Mechanics</h3><ul><li>Signals (Events)</li><li>Input</li><li>Timers</li><li>Physics Process vs. Process</li><li>Random Numbers</li><li>The Ready Function</li></ul><h3>5. Object &amp; Scene Control</h3><ul><li>Instancing Scenes</li><li>Accessing Nodes</li><li>Inheritance</li><li>Export Variables</li><li>Groups</li></ul><h3>6. Bonus: Polish &amp; Organization</h3><ul><li>Code Style Tips</li><li>Debugging</li><li>Autoloads (Singletons)</li></ul><p>To make it easier to read, you can download the (free) <strong>Offline PDF</strong> version of this post <a href="https://ko-fi.com/s/e90ca02be7">here</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jdKsatIgHMMewt6w7TNung.png" /></figure><h3>Part I: Fundamentals</h3><p>Before embarking on a complex project, it’s crucial to grasp the foundational elements of coding. The Fundamentals section covers the core concepts that you will frequently use in nearly every Godot script. These principles form the basis of all programming, not just within Godot.</p><p><strong>In this section, we will cover the following topics:</strong></p><ul><li><a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#variables"><strong>Variables</strong> </a>— storing and reusing data</li><li><a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#built-in-types"><strong>Data Types</strong></a> — understanding different kinds of information</li><li><a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#constants"><strong>Constants</strong> </a>— setting fixed, unchanging values</li><li><a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#functions"><strong>Functions</strong> </a>— organizing your logic into reusable actions</li><li><a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#comments"><strong>Comments</strong></a> — explaining your code for yourself and others</li><li><a href="https://school.gdquest.com/glossary/scope"><strong>Scope </strong></a>— understanding where and how your data exists</li></ul><h3>1. Variables</h3><p>Variables are containers used to store reusable pieces of data such as numbers, text, or even entire nodes. You can think of them as your code’s “memory slots,” holding information like the player’s name, health, or the sound that plays when they take damage.</p><p>Variables can be defined globally, making them accessible in every piece of the script (and sometimes other scripts), or locally, within functions, where they exist only while that function runs.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_M1hmlPy4-SyyTCqIqEQjg.png" /></figure><h3>Why Variables are Needed</h3><p><strong>Games constantly track information:</strong></p><ul><li>Player health, score, or ammo</li><li>Enemy positions</li><li>Whether a door is open or not</li><li>The name of the NPC you’re talking to</li></ul><p>Without variables, a game wouldn’t know what happened before or what should happen next. It couldn’t reference or change values , making it impossible to keep track of states. Variables are the backbone of all programming.</p><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre>var variable_name = value<br>var player_health = 100</pre><p><strong>C#:</strong></p><pre>type variableName = value;<br>int playerHealth = 100</pre><h3>Best Practices</h3><ul><li><strong>Use clear, descriptive names</strong> — for example, player_health or enemy_speed, so it’s easy to understand what each variable represents.</li><li><strong>Follow snake_case naming in GDScript</strong> — write variable names in lowercase with underscores, like player_score or door_open.</li><li><strong>Follow camelCase naming in C#</strong> — write variable names in camel case, like playerScore or doorOpen.</li><li><strong>Avoid reserved keywords</strong> — don’t use words that Godot or GDScript already reserve for internal use (e.g. name, class, print), as this can cause errors or unexpected behavior.</li></ul><h3>Example</h3><p>Here’s how you might define variables throughout your game.</p><p>The below code creates a player with the name “Bob”, who has 100 health and a sword.</p><p><strong>GDScript:</strong></p><pre>var player_health = 100<br>var player_name = &quot;Bob&quot;<br>var has_sword = true</pre><p><strong>C#:</strong></p><pre>int playerHealth = 100;<br>string playerName = &quot;Bob&quot;;<br>bool hasSword = true;</pre><h3>2. Data Types</h3><p>Data types define the kind of information that a variable can store, such as numbers, text, boolean values (true/false), or even complex objects like nodes or images. They are essential for ensuring that your game can correctly utilize and process data while maintaining a standardized approach to handling and processing that data.</p><p>For example, by specifying the data type, we can prevent the addition of a string to a numeric data type. This means that you cannot assign “Bob” as a value for your health; it must always be a number or a float!</p><h3>Why Data Types Are Needed</h3><p><strong>Every game needs to handle different kinds of information:</strong></p><ul><li><strong>Numbers:</strong> Used for health, speed, or score. These can be either <em>integers</em> (whole numbers like 1 or 99) or <em>floats</em> (decimal values, such as 1.42 or 9.87).</li><li><strong>Text:</strong> Refers to player names, dialogues, or menus. Text is defined as <em>strings</em>.</li><li><strong>Boolean Values:</strong> Represent true/false states, such as “Is the door open?” or “Is the player jumping?”</li><li><strong>Complex Types:</strong> Include structures like <em>Vector2</em>, <em>Color</em>, or <em>Node</em>, which are used for movement, visual representation, and object references.</li></ul><p>Without data types, your game wouldn’t know how to compare, process, calculate, or display information properly. This could potentially lead to to confusing bugs and broken logic.</p><h3>Types of Data</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZKphLCGOfFvswIorm6QOlw.png" /></figure><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre>var variable_name: Type = value<br>var player_speed: float = 4.5</pre><p><strong>C#:</strong></p><pre>Type variableName = value;<br>float playerSpeed = 4.5f;</pre><h3>Best Practices</h3><ul><li><strong>Always use the correct type for what you’re storing — </strong>use integers (int) for whole numbers, floats (float) for decimals, and strings (String) for text.</li><li><strong>Be consistent — </strong>don’t change a variable’s type mid-script (e.g., storing a number and later replacing it with text).</li><li><strong>Use type hints</strong> (GDScript 2.0+) <strong>or explicit types</strong> (C#) to make your code safer and easier to read.</li><li><strong>Learn Godot’s built-in types</strong> — like Vector2, Vector3, Color, Node, and Array, as they’re core to building any game.</li></ul><h3>Example</h3><p>Here’s how you might define different data types for a player’s stats and position.</p><p>The below code creates a player with a name of “Bob” (value of string), a health of 100 and speed of 4.5 (numeric values), a sword (boolean), and position (Vector2).</p><p><strong>GDScript:</strong></p><pre>var player_name: String = &quot;Bob&quot; #string<br>var player_health: int = 100 #integer<br>var player_speed: float = 4.5 #float<br>var has_sword: bool = true #boolean<br>var player_position: Vector2 = Vector2(200, 100) # complex type</pre><p><strong>C#:</strong></p><pre>string playerName = &quot;Bob&quot;; //string<br>int playerHealth = 100; //integer<br>float playerSpeed = 4.5f; //float<br>bool hasSword = true; //boolean<br>Vector2 playerPosition = new Vector2(200, 100); //complex type</pre><h3>3. Constants</h3><p>Constants are values that never change while your game runs. They’re like permanent markers in your code — once defined, they stay the same no matter what happens.</p><p>You might use constants for things like maximum health, gravity, default speed, or level names. These are all values that should stay fixed and not be altered during gameplay.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cbi0Q5-fMEzLgZ1zecd5_Q.png" /></figure><h3>Why Constants Are Needed</h3><p>Constants make your code more organized, predictable, and easier to maintain.</p><p><strong>They’re especially useful for:</strong></p><ul><li>Fixed values that should never change, like MAX_HEALTH = 100 (we want our health to change, but never go over 100%)</li><li>Game settings like gravity, jump force, or map size</li><li>Avoiding “magic numbers” — instead of random hard-coded numbers, you give them clear names</li><li>Preventing accidental changes to important values during gameplay</li></ul><p>Without constants, you might end up reusing the same number in multiple places, and if you ever need to change it, you’ll have to hunt it down everywhere in your code.</p><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre>const CONSTANT_NAME = value<br>const MAX_HEALTH = 100</pre><p><strong>C#:</strong></p><pre>const Type CONSTANT_NAME = value;<br>const int MAX_HEALTH = 100;</pre><h3>Best Practices</h3><ul><li><strong>Use constants for fixed or shared values</strong> — especially ones that appear multiple times in your code.</li><li><strong>Name constants in ALL_CAPS</strong> to make them stand out (e.g., MAX_SPEED, GRAVITY).</li><li><strong>Group related constants together</strong> at the top of your script for better organization.</li><li><strong>Never modify a constant</strong> <strong>after it’s defined</strong> in your code. That defeats its purpose, so if you define it at the beginning of your code, avoid modifying its value later in a function.</li></ul><h3>Example</h3><p>Here’s how you might define constants for player attributes and physics.</p><p>The code below creates constants that ensure the maximum health never surpasses 100%, sets a gravity force of 9.8, assigns the final player name as Bob, and establishes a spawn position that will never change.</p><p><strong>GDScript:</strong></p><pre>const MAX_HEALTH = 100<br>const GRAVITY = 9.8<br>const PLAYER_NAME = &quot;Bob&quot;<br>const START_POSITION = Vector2(100, 200)</pre><p><strong>C#:</strong></p><pre>const int MAX_HEALTH = 100;<br>const float GRAVITY = 9.8f;<br>const string PLAYER_NAME = &quot;Bob&quot;;<br>readonly Vector2 START_POSITION = new Vector2(100, 200);</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/921/1*NodV0EqdMvly6J7bjR0b8g.png" /></figure><h3>4. Functions</h3><p>Functions are reusable blocks of code that perform specific tasks. They let you organize your logic into smaller, manageable pieces, almost like instructions you can call at anytime.</p><p>Think of a function as a mini-program inside your script: instead of writing the same code over and over, you define it once and <em>call</em> it whenever needed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*O20Y3C1h6Cykx8RKgOb4aQ.png" /></figure><h3>Why Functions Are Needed</h3><p>Games rely on repeated actions and logic. For example, an NPC should know when to walk and when to stop. Functions make that process clean and efficient.</p><p><strong>For example:</strong></p><ul><li>Moving a character</li><li>Playing a sound when the player takes damage</li><li>Spawning an enemy or item</li><li>Saving or loading game data</li></ul><p>Without functions, you’d need to duplicate the same lines of code in multiple places — making your game harder to read, debug, and maintain. Functions promote clean, modular, and reusable code.</p><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre>func function_name(parameters):<br>    # code to run<br>    return value<br><br>func add(a, b):<br>   return a + b</pre><p><strong>C#:</strong></p><pre>ReturnType FunctionName(parameters)<br>{<br>    // code to run<br>    return value;<br>}<br><br>int Add(int a, int b) { <br>  return a + b; <br>}</pre><h3>Best Practices</h3><ul><li><strong>Use clear, action-based names</strong> (e.g., move_player(), take_damage(), play_music()), so it’s obvious what the function does.</li><li><strong>Keep functions focused</strong> — each one should do <em>one thing well</em>.</li><li><strong>Use parameters</strong> to pass data into a function, and returns to get data back.</li><li><strong>Avoid long functions</strong> — break them into smaller pieces for readability and reuse.</li><li>In GDScript, function names use snake_case; in C#, use PascalCase.</li></ul><h3>Example</h3><p>The code defines a variable which stores the player’s health, and in the function, whenever the player takes damage, the health value changes. Thus, the function controls the logic for when the player takes damage.</p><p><strong>GDScript:</strong></p><pre># variable<br>var player_health = 100<br><br># function to change variable<br>func take_damage(amount):<br>    player_health -= amount<br>    print(&quot;Player health:&quot;, player_health)</pre><p><strong>C#:</strong></p><pre>// variable<br>int playerHealth = 100;<br><br>// function to change variable<br>void TakeDamage(int amount)<br>{<br>    playerHealth -= amount;<br>    GD.Print(&quot;Player health: &quot; + playerHealth);<br>}</pre><h3>5. Comments</h3><p>Comments are notes that you leave inside your code that the computer completely ignores. They’re meant for <em>you</em> and other developers to explain what your code does, why you wrote it that way, or to temporarily disable certain lines during testing.</p><p>Think of comments as sticky notes for your future self. They help make your code readable, teachable, and easier to maintain.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/559/0*yIMF6luQxOP6vduc.jpg" /></figure><h3>Why Comments Are Needed</h3><p>As you grow your game, your code may become confusing after a few weeks (or to someone new reading it). Comments help you remember the <em>reasoning</em> behind your decisions, and take notes on what you need to change or refer back to.</p><p><strong>You might use comments to:</strong></p><ul><li>Explain what a function or variable does</li><li>Clarify complex logic or math</li><li>Leave notes for future improvements</li><li>Temporarily disable code without deleting it</li></ul><p>Without comments, your codebase can quickly become a maze of unexplained logic — especially in large projects with multiple scripts and systems.</p><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre># GDScript<br># Single-line comment<br># This explains a line of code</pre><p><strong>C#:</strong></p><pre>// CSharp <br>// Single-line comment<br>/* Multi-line<br>   comment */<br>// This explains a line of code</pre><h3>Best Practices</h3><ul><li><strong>Write comments for clarity, not decoration.</strong> Use them to explain <em>why</em> something exists, not just <em>what</em> it does.</li><li><strong>Keep comments up to date.</strong> Outdated comments can be more confusing than no comments at all.</li><li><strong>Use comments to break your code into sections.</strong> It helps you navigate long scripts easily.</li><li>In <strong>GDScript</strong>, <strong>comments</strong> start with #.</li><li>In <strong>C#</strong>, <strong>comments</strong> start with //.</li></ul><h3>Example</h3><p>Here’s how you might use comments to explain your code in both languages.</p><p><strong>GDScript:</strong></p><pre># Player starts with 100 health<br>var player_health = 100  <br><br># Function to apply damage to the player<br>func take_damage(amount):<br>    player_health -= amount<br>    print(&quot;Player health:&quot;, player_health)  # Debug print to console</pre><p><strong>C#:</strong></p><pre>// Player starts with 100 health<br>int playerHealth = 100;<br><br>/* Function to apply damage<br>   and display remaining health */<br>void TakeDamage(int amount)<br>{<br>    playerHealth -= amount;<br>    GD.Print(&quot;Player health: &quot; + playerHealth); // Debug print<br>}</pre><h3>6. Scope</h3><p>Scope determines where a variable or function can be accessed in your code. It’s like defining whether a certain piece of logic is available everywhere or only inside a specific block of code.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qzUFa2I7rRbb3SHmQnuMRg.png" /></figure><h3>Why Scope Is Needed</h3><p>Scope keeps your code clean, predictable, and safe from unwanted changes.</p><p>In games, you might want some data to be accessible everywhere (like the player’s score), while other data should only exist temporarily inside a function (like a loop counter or a temporary position).</p><p>Without scope rules, variables could overwrite each other, cause bugs, or leak data across scripts unintentionally.</p><p><strong>Here’s the most common scopes:</strong></p><ul><li><strong>Global scope:</strong> Variables and functions that can be accessed from anywhere in the script, and sometimes by other scripts.</li><li><strong>Local scope:</strong> Variables that only exist inside a specific function or block. They’re created when the function runs and destroyed when it ends.</li><li><strong>Access modifiers (C# only):</strong> Define who can access a variable or function from other scripts.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*O6MPQVU0ePGBZDa12YPf7Q.png" /></figure><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre># Global variable<br>var global_var = value<br><br>func some_function():<br>    var local_var = value  # Local to this function - you cannot call it outside of here</pre><p><strong>C#:</strong></p><pre>// Global variable<br>int globalVar = 100;<br><br>void SomeFunction()<br>{<br>    int localVar = 50; // Only accessible inside this function<br>}<br>// Access modifiers (C# only)<br>public int localVar = 100;    // Accessible by all scripts<br>private int localVar = 30;       // Accessible only in this class<br>protected int localVar = 10;         // Accessible in this class and subclasses<br>internal int localVar = 0;           // Accessible within the same assembly</pre><h3>Best Practices</h3><ul><li><strong>Use local scope whenever possible</strong> — it keeps variables self-contained and prevents accidental changes elsewhere.</li><li><strong>Use global scope only for shared data</strong> like settings, player stats, or managers that must be accessed by multiple scripts.</li><li><strong>Avoid naming conflicts</strong> — two variables with the same name in different scopes can cause confusion.</li><li><strong>In GDScript</strong>, define globals outside of functions (like at the top of your script), and locals inside.</li><li><strong>In C#</strong>, use access modifiers like public, private, and protected to control visibility.</li></ul><h3>Example</h3><p>Here’s a simple example showing the difference between global and local scope.</p><p><strong>GDScript:</strong></p><pre># Global variable - any function can access this, even other scripts<br>var player_health = 100<br><br>func take_damage(amount):<br>    # Local variable (only exists while this function runs)<br>    var new_health = player_health - amount<br>    print(&quot;After damage:&quot;, new_health)</pre><p><strong>C#:</strong></p><pre>// Global variable - any function can access this, even other scripts<br>public int playerHealth = 100;    // Accessible by all scripts<br>private int ammoCount = 30;       // Accessible only in this class<br>protected int armor = 10;         // Accessible in this class and subclasses<br>internal int score = 0;           // Accessible within the same assembly<br><br>void TakeDamage(int amount)<br>{<br>    // Local variable (only exists within this function)<br>    int newHealth = playerHealth - amount;<br>    GD.Print(&quot;After damage: &quot; + newHealth);<br>}</pre><h3>Part II: Logic &amp; Control</h3><p>Games and programs are filled with decisions — should the player take damage? Should the door open? Should the enemy chase or retreat?</p><p>Logic and control structures enable your game to think, react, and adapt to various situations. These tools instruct your code on what actions to take and when to take them. They serve as the brain behind your game’s behavior, controlling the flow of the game, responding to player input, and determining outcomes.</p><p><strong>In this section, we will explore the following concepts:</strong></p><ul><li><a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#if-else-elif"><strong>If / Else</strong></a><strong>:</strong> Making choices</li><li><a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#match"><strong>Match (Switch)</strong></a><strong>:</strong> Handling multiple possibilities</li><li><a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#match"><strong>Loops (For / While)</strong></a><strong>:</strong> Repeating actions efficiently</li><li><a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html"><strong>Break, Return, &amp; Continue</strong></a><strong>:</strong> Controlling loop &amp; function behavior</li></ul><h3>1. If / Else</h3><p>If / Else statements are the foundation of decision-making in programming. They let your game choose different paths depending on whether certain conditions are true or false.</p><p>You can think of them like branching roads — your game checks a condition, and depending on the result, it takes one route or another.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wNd2dUCHy9hYoLDhOM2aig.png" /></figure><h3>Why If / Else Is Needed</h3><p><strong>Every game relies on conditions, for example:</strong></p><ul><li>If the player’s health reaches zero → trigger game over</li><li>If the score is high enough → unlock a new level</li><li>If a key is collected → open a door</li><li>If the player presses jump → play jump animation</li></ul><p>Without conditional logic, your game would run in a straight line with no reaction to what happens — no decisions, no consequences, and no interactivity.</p><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre>if condition:<br>    # code<br>elif other_condition:<br>    # code<br>else:<br>    # code</pre><p><strong>C#:</strong></p><pre>if (condition)<br>{<br>    // code<br>}<br>else if (otherCondition)<br>{<br>    // code<br>}<br>else<br>{<br>    // code<br>}</pre><h3>Best Practices</h3><ul><li><strong>Keep conditions clear and readable</strong> — use descriptive variable names so your logic reads like a sentence (if player_health &lt;= 0:).</li><li><strong>Avoid deep nesting</strong> — too many nested if statements make code hard to follow. Consider using match or early returns instead.</li><li><strong>Use elif for multiple related conditions</strong> instead of chaining separate ifs.</li><li><strong>Test edge cases</strong> — ensure your logic works at boundaries (e.g., 0 health, max score).</li></ul><h3>Example</h3><p>Here’s a simple block of code that uses if/else statements to determine whether the player is alive, low on health, or defeated.</p><p><strong>GDScript:</strong></p><pre>var player_health = 50<br><br># If health is more than zero, the player is alive.<br>if player_health &gt; 50:<br>    print(&quot;Player is alive!&quot;)<br># If health is between 1 and 50, warn that it&#39;s low.<br>elif player_health &gt; 0 and player_health &lt;= 50:<br>    print(&quot;Player health low&quot;)<br># Otherwise, the player is defeated.<br>else:<br>    print(&quot;Player is defeated.&quot;)</pre><p><strong>C#:</strong></p><pre>int playerHealth = 50;<br><br>// If health is more than zero, the player is alive.<br>if (playerHealth &gt; 50)<br>{<br>    GD.Print(&quot;Player is alive!&quot;);<br>}<br>// If health is between 1 and 50, warn that it&#39;s low.<br>else if (playerHealth &gt; 0 &amp;&amp; playerHealth &lt;= 50)<br>{<br>    GD.Print(&quot;Player health low&quot;);<br>}<br>// Otherwise, the player is defeated.<br>else<br>{<br>    GD.Print(&quot;Player is defeated.&quot;);<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/920/1*z0KJekaK42WIgRsALedNFQ.png" /></figure><h3>2. Match (Switch)</h3><p>The Match statement (called <strong>Switch</strong> in many other languages) is a cleaner, more organized way to handle multiple possible outcomes for a single value.</p><p>Instead of writing a long chain of if and elif statements, match lets your code check one variable against several conditions, thus keeping your logic simple and easy to read.</p><p>Think of it like a menu of possibilities: the program picks the one that matches and runs the code for that case.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wVunj8mBzo-PhgQs0f0d0w.png" /></figure><h3>Why Match Is Needed</h3><p>When your game needs to react differently to multiple options — such as player input, item types, or enemy states — match keeps your logic tidy.</p><p><strong>For example:</strong></p><ul><li>Checking which key was pressed</li><li>Determining what item the player picked up</li><li>Reacting to an enemy’s state (idle, chasing, attacking)</li><li>Handling dialog choices or menu selections</li></ul><p>Without match, you’d end up with repetitive if/elif blocks that are harder to maintain.</p><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre>match value:<br>    pattern1:<br>        # code<br>    pattern2:<br>        # code<br>    _:<br>        # default case</pre><p><strong>C#:</strong></p><pre>switch (value)<br>{<br>    case pattern1:<br>        // code<br>        break;<br>    case pattern2:<br>        // code<br>        break;<br>    default:<br>        // default case<br>        break;<br>}</pre><h3>Best Practices</h3><ul><li><strong>Use match for clear, single-variable comparisons</strong> — it’s cleaner than stacking if/elif.</li><li><strong>Include a _ (default) case</strong> to catch unexpected values or errors.</li><li><strong>Keep cases short and direct</strong> — avoid nesting too much logic inside each one.</li><li><strong>In C#</strong>, use the switch statement (and consider switch expressions in newer C# versions for concise code).</li></ul><h3>Example</h3><p>Here’s how you might use match to handle player actions. The logic of the game will change depending on the state of the player.</p><p><strong>GDScript:</strong></p><pre># Player state<br>var action = &quot;jump&quot;<br><br># Change state based on action<br>match action:<br>    &quot;attack&quot;:<br>        print(&quot;Player attacks!&quot;)<br>    &quot;jump&quot;:<br>        print(&quot;Player jumps!&quot;)<br>    &quot;block&quot;:<br>        print(&quot;Player blocks!&quot;)<br>    _:<br>        print(&quot;Unknown action!&quot;)</pre><p><strong>C#:</strong></p><pre>// Player state<br>string action = &quot;jump&quot;;<br><br>// Change state based on action<br>switch (action)<br>{<br>    case &quot;attack&quot;:<br>        GD.Print(&quot;Player attacks!&quot;);<br>        break;<br>    case &quot;jump&quot;:<br>        GD.Print(&quot;Player jumps!&quot;);<br>        break;<br>    case &quot;block&quot;:<br>        GD.Print(&quot;Player blocks!&quot;);<br>        break;<br>    default:<br>        GD.Print(&quot;Unknown action!&quot;);<br>        break;<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/921/1*d_sdCwbrDOb3UFOG3jW-Mw.png" /></figure><h3>3. Loops (For / While)</h3><p>Loops allow your code to repeat actions automatically without writing the same line over and over. They’re essential for anything that needs to run multiple times — like moving enemies, checking objects, or counting through a list.</p><p>Think of loops as automated cycles: you define <em>what</em> should happen and <em>how long</em> it should continue</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oeRChQOd5Nmox7yT_J94gw.png" /></figure><h3>Why Loops Are Needed</h3><p>Games rely on repetition — from drawing frames to checking collisions. Loops make that process simple and efficient.</p><p><strong>You might use them to:</strong></p><ul><li>Move every enemy in a list</li><li>Spawn several coins or projectiles</li><li>Update player stats each frame</li><li>Repeat something until a condition is met</li></ul><p>Without loops, you’d have to manually duplicate code for every instance — wasting time and creating room for errors.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JXjVUV_9fi9E1bf5DRjsNA.png" /></figure><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre>for element in collection:<br>    # repeated code<br><br>while condition:<br>    # repeated code</pre><p><strong>C#:</strong></p><pre>foreach (var element in collection)<br>{<br>    // repeated code<br>}<br>while (condition)<br>{<br>    // repeated code<br>}</pre><h3>Best Practices</h3><ul><li><strong>Use for loops</strong> when you know how many times something should run (e.g., counting or iterating through arrays).</li><li><strong>Use while loops</strong> when you don’t know how long something will continue (e.g., waiting for an event or condition).</li><li><strong>Avoid infinite loops</strong> — always include a clear condition or break.</li><li><strong>Use descriptive loop variables</strong> (for enemy in enemies: instead of for i in range(…)).</li><li><strong>Keep loop bodies small</strong> — too much logic inside can hurt performance.</li></ul><h3>Example</h3><p>Here’s how you might use both types of loops to manage enemies in a scene.</p><p>The <strong>for loop</strong> will iterate over a list of enemies (Goblin, Orc, and Troll), and for each enemy, it will print a message indicating that they are ready to attack.</p><p>The <strong>while loop</strong> will decrease the player’s health as long as their current health is more than 0. Once the player’s health reaches 0, the loop will stop.</p><p><strong>GDScript:</strong></p><pre># Array of enemies<br>var enemies = [&quot;Goblin&quot;, &quot;Orc&quot;, &quot;Troll&quot;]<br><br># FOR LOOP: Go through a list of enemies<br>for enemy in enemies:<br>    print(enemy, &quot;is ready to attack!&quot;)<br><br># WHILE LOOP: Reduce player health over time<br>var player_health = 100<br>while player_health &gt; 0:<br>    player_health -= 10<br>    print(&quot;Player health:&quot;, player_health)</pre><p><strong>C#:</strong></p><pre>// Array of enemies<br>string[] enemies = { &quot;Goblin&quot;, &quot;Orc&quot;, &quot;Troll&quot; };<br><br>// FOR LOOP: Go through a list of enemies<br>foreach (string enemy in enemies)<br>{<br>    GD.Print(enemy + &quot; is ready to attack!&quot;);<br>}<br><br>// WHILE LOOP: Reduce player health over time<br>int playerHealth = 100;<br>while (playerHealth &gt; 0)<br>{<br>    playerHealth -= 10;<br>    GD.Print(&quot;Player health: &quot; + playerHealth);<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/928/1*cLzadhCaTR5zwXiqZPW15Q.png" /></figure><h3>4. Break, Continue, &amp; Return</h3><p>Inside loops and functions, <strong>break, continue</strong>, and <strong>return</strong> are special keywords that give you precise control over how and when your code stops or skips certain actions.</p><ul><li><strong>break</strong> — immediately stops a <strong>loop</strong>, even if it hasn’t finished all its cycles.</li><li><strong>continue</strong> — skips the rest of the <strong>current</strong> loop cycle and jumps to the next one.</li><li><strong>return</strong> — exits a <strong>function</strong> immediately and optionally sends a value back.</li></ul><p>Think of them as <strong>traffic signs</strong> for your logic flow: “Stop here,” “Skip ahead,” or “Exit and report back.”</p><h3>Why They’re Needed</h3><p>Games constantly run logic loops and functions that depend on changing conditions. These keywords give you fine-grained control over <em>when</em> that logic should stop, skip, or exit.</p><p><strong>You might use them to:</strong></p><ul><li><strong>break</strong> when you’ve found the first matching item in a list</li><li><strong>continue</strong> to skip inactive enemies or irrelevant data</li><li><strong>return</strong> to exit a function early (like when the player is already dead)</li><li><strong>Save performance</strong> by stopping unnecessary calculations</li></ul><p>Without these control statements, loops and functions would run to completion every time — wasting cycles and cluttering logic.</p><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre>break       # Exit loop immediately<br>continue    # Skip to next loop iteration<br>return value  # Exit function and return value</pre><p><strong>C#:</strong></p><pre>break;       // Exit loop immediately<br>continue;    // Skip to next loop iteration<br>return value; // Exit function and return value</pre><h3>Best Practices</h3><ul><li><strong>Use break only when needed</strong> to exit loops early.</li><li><strong>Use continue for filtering logic</strong>, such as skipping invalid data.</li><li><strong>Use return for early exits in functions</strong>, especially for edge-case checks.</li><li><strong>Avoid multiple returns</strong> in long functions — too many can make code flow harder to follow.</li><li><strong>Keep conditions clear and simple</strong> to prevent confusion when jumping out of loops or functions.</li></ul><h3>Example</h3><p>Here’s how you might combine all three in a single gameplay function.</p><p>In the code below, we loop through our list of enemies. If we encounter an invalid enemy, we use the continue statement to skip to the next iteration. If we find an Orc in the list, we print a message and continue with the remaining logic (checking for dragon). If we come across a dragon, we print a message and exit the loop using the break statement. Once the loop is complete, we return, which stops the function from executing further.</p><p><strong>GDScript:</strong></p><pre>func find_enemy(enemies):<br>    for enemy in enemies:<br>        if enemy == null:<br>            continue  # Skip missing entries<br>        if enemy == &quot;Orc&quot;:<br>            print(&quot;Ignoring Orc.&quot;)<br>            continue # Skip missing entries<br>        if enemy == &quot;Dragon&quot;:<br>            print(&quot;Boss found! Stopping search.&quot;)<br>            break # Stop loop<br>        print(&quot;Spotted:&quot;, enemy)<br>    return &quot;Search complete.&quot;  # Exit function and return a message</pre><p><strong>C#:</strong></p><pre>string FindEnemy(string[] enemies)<br>{<br>    foreach (string enemy in enemies)<br>    {<br>        if (enemy == null)<br>            continue; // Skip missing entries<br>        if (enemy == &quot;Orc&quot;)<br>        {<br>            GD.Print(&quot;Ignoring Orc.&quot;);<br>            continue; // Skip missing entries<br>        }<br>        if (enemy == &quot;Dragon&quot;)<br>        {<br>            GD.Print(&quot;Boss found! Stopping search.&quot;);<br>            break; // Stop loop<br>        }<br>        GD.Print(&quot;Spotted: &quot; + enemy);<br>    }<br>    return &quot;Search complete.&quot;; // Exit function and return a message<br>}</pre><h3>Part III: Collections</h3><p>As your games develop, you’ll soon realize that single variables, like player_name and enemy, aren’t sufficient. You will need ways to store, organize, and manage groups of data, such as multiple enemies, items, or lines of dialogue.</p><p>Collections are special data structures that enable you to handle many values simultaneously, making your scripts more dynamic, efficient, and powerful.</p><p><strong>In this section, we’ll cover:</strong></p><ul><li><a href="https://docs.godotengine.org/en/stable/classes/class_array.html"><strong>Arrays</strong> </a>— ordered lists of values</li><li><a href="https://docs.godotengine.org/en/stable/classes/class_dictionary.html"><strong>Dictionaries</strong> </a>— key-value pairs for labelled data</li><li><a href="https://docs.godotengine.org/en/stable/classes/class_resource.html#description"><strong>Resources</strong></a> — saved or reusable data objects</li></ul><h3>1. Arrays</h3><p>Arrays are ordered lists that can store multiple values in a single variable.<br>You can think of them as containers or shelves, where each slot holds an item — like a list of enemies, levels, or collected items.</p><p>Each item in an array is stored at a numbered position called an <strong>index</strong>, starting at <strong>0</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ANls9DZn0XdHYQauxGkZpQ.png" /></figure><h3>Why Arrays Are Needed</h3><p>Arrays are essential whenever you need to handle more than one piece of related data.</p><p><strong>For example:</strong></p><ul><li>A list of enemies in a level</li><li>All items in the player’s inventory</li><li>A sequence of checkpoints or spawn points</li><li>A queue of dialog lines or sound effects</li></ul><p>Without arrays, you’d need a separate variable for every element (enemy1, enemy2, enemy3…), which quickly becomes unmanageable and performance heavy.</p><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre>var array_name = [value1, value2, value3]<br>array_name.append(value)<br>array_name[index]</pre><p><strong>C#:</strong></p><pre>Type[] arrayName = { value1, value2, value3 };<br>arrayName[index];<br>var listName = new List&lt;Type&gt;() { value1, value2, value3 };<br>listName.Add(value);</pre><h3>GDScript Methods</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aahpxpyJC7tyd6SuM11m-Q.png" /></figure><h3>C# Methods</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jyyDpsycV3cLSsDz_ej_cg.png" /></figure><h3>Best Practices</h3><ul><li><strong>Use arrays for ordered data</strong> where position matters.</li><li><strong>Access elements by index</strong> — array[0] gets the first item.</li><li><strong>Use loops</strong> to efficiently process all items.</li><li><strong>Be careful with indices</strong> — trying to access an index that doesn’t exist causes an error.</li><li><strong>Use methods like append(), erase(), or size()</strong> to manage array contents.</li></ul><h3>Example</h3><p>Here’s how you might use arrays to handle multiple enemies.</p><p>We start by defining an array named enemies, which holds a list of enemy names. Next, we print the first element in the array (at index 0), which is “Goblin”. After that, we add a new enemy “Dragon” to the list. When we print the array again, it will then display the updated list: [“Goblin”, “Orc”, “Troll”, “Dragon”]</p><p><strong>GDScript:</strong></p><pre>var enemies = [&quot;Goblin&quot;, &quot;Orc&quot;, &quot;Troll&quot;]<br><br># Access the first element<br>print(enemies[0])  # Output: Goblin<br># Add a new enemy<br>enemies.append(&quot;Dragon&quot;)<br># Loop through the list<br>for enemy in enemies:<br>    print(enemy)</pre><p><strong>C#:</strong></p><pre>// Create an array of enemies<br>string[] enemies = { &quot;Goblin&quot;, &quot;Orc&quot;, &quot;Troll&quot; };<br><br>// Access the first element<br>GD.Print(enemies[0]);  // Output: Goblin<br>// Convert to a dynamic list to add new items<br>var enemyList = new System.Collections.Generic.List&lt;string&gt;(enemies);<br>enemyList.Add(&quot;Dragon&quot;);<br>// Loop through the list<br>foreach (string enemy in enemyList)<br>{<br>    GD.Print(enemy);<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/925/1*0yLNgT93H1wFPwDneSh0vQ.png" /></figure><h3>2. Dictionaries</h3><p>Dictionaries store data as key–value pairs, allowing you to label each piece of information instead of relying on numerical positions.</p><p>Think of a dictionary as a filing cabinet — the key is the label on the drawer, and the value is what’s inside. This makes your data easier to understand and access, especially when order isn’t important but <em>meaning</em> is.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BXf9TypolmCZrccD2KqwBg.png" /></figure><h3>Why Dictionaries Are Needed</h3><p>Dictionaries are perfect for data that needs names instead of numbers.</p><p><strong>For example:</strong></p><ul><li>Storing player stats like {“health”: 100, “mana”: 50}</li><li>Tracking inventory items and their quantities</li><li>Keeping NPC dialog by character name</li><li>Mapping key bindings or configuration settings</li></ul><p>Without dictionaries, you’d need separate variables or parallel arrays for every related value — which quickly gets messy.</p><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre>var dict_name = {<br>    &quot;key1&quot;: value1,<br>    &quot;key2&quot;: value2<br>}<br>dict_name[&quot;key3&quot;] = value3</pre><p><strong>C#:</strong></p><pre>var dictName = new Dictionary&lt;string, Type&gt;()<br>{<br>    { &quot;key1&quot;, value1 },<br>    { &quot;key2&quot;, value2 }<br>};<br>dictName[&quot;key3&quot;] = value3;</pre><h3>GDScript Methods</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*51H6eBkarxxohbhmn2r8xg.png" /></figure><h3>C# Methods</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*J69dT0Bw9EddbSKE236tTQ.png" /></figure><h3>Best Practices</h3><ul><li><strong>Use dictionaries when labels make data clearer</strong> — e.g., player[&quot;health&quot;] is more readable than player[0].</li><li><strong>Access values by key</strong>, not index — use dict[&quot;key_name&quot;].</li><li><strong>Check for existence</strong> before accessing a key to avoid errors (if “health” in player:).</li><li><strong>Use nested dictionaries</strong> for structured data (like characters, stats, or items).</li><li><strong>In C#</strong>, use Dictionary&lt;TKey, TValue&gt; for type-safe collections.</li></ul><h3>Example</h3><p>Here’s how you might use dictionaries to store player stats and inventory.</p><p>In the code below, we create a new dictionary for our players values — storing their name, health, and mana. We then add a new value to the dictionary, which will store the amount of gold that they have. Finally, we iterate over the dictionary to return the updated dictionary, which will be: {“name”: “Bob”, “health”: 100, “mana”: 50, “gold”: 250}</p><p><strong>GDScript:</strong></p><pre># Create a dictionary of player stats<br>var player = {<br>    &quot;name&quot;: &quot;Bob&quot;,<br>    &quot;health&quot;: 100,<br>    &quot;mana&quot;: 50<br>}<br><br># Access a value by key<br>print(player[&quot;health&quot;])  # Output: 100<br><br># Add a new key-value pair<br>player[&quot;gold&quot;] = 250<br><br># Loop through keys and values<br>for key in player:<br>    print(key, &quot;:&quot;, player[key])</pre><p><strong>C#:</strong></p><pre>using System.Collections.Generic;<br><br>// Create a dictionary of player stats<br>Dictionary&lt;string, object&gt; player = new Dictionary&lt;string, object&gt;()<br>{<br>    { &quot;name&quot;, &quot;Bob&quot; },<br>    { &quot;health&quot;, 100 },<br>    { &quot;mana&quot;, 50 }<br>};<br><br>// Access a value by key<br>GD.Print(player[&quot;health&quot;]);  // Output: 100<br><br>// Add a new key-value pair<br>player[&quot;gold&quot;] = 250;<br><br>// Loop through all keys and values<br>foreach (var stat in player)<br>{<br>    GD.Print(stat.Key + &quot;: &quot; + stat.Value);<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/928/1*yTwBACV2fp74TJEb24T5kg.png" /></figure><h3>3. Resources</h3><p>Resources in Godot are data objects that can be saved, loaded, and reused across your project. They’re stored as .tres (text-based) or .res (binary) files, and can contain variables, configurations, or even scripts — anything from item stats and materials to player data or configuration settings.</p><p>Think of them as data containers that live outside your scripts, allowing you to manage information in a clean, modular way.</p><h3>Why Resources Are Needed</h3><p>Resources make your game more modular, reusable, and data-driven. Instead of hardcoding every item and managing a list of arrays or dictionaries, you can store that information in <strong>Resource files</strong> and reuse them across multiple scenes or objects.</p><p><strong>You’ll typically use Resources to:</strong></p><ul><li>Store <strong>item stats</strong> (like damage, rarity, or price).</li><li>Define <strong>enemy attributes</strong> or <strong>character abilities</strong>.</li><li>Create <strong>configuration files</strong> for tuning and game balance.</li><li>Create <strong>NPC, enemy, or character data</strong> which can be loaded into scenes.</li><li>Save and load <strong>player progress or inventory data</strong>.</li><li>Define <strong>materials, audio effects, shaders, and particle settings</strong>.</li></ul><p>Without resources, you’d have to duplicate data across scripts or scenes, making your project harder to update, maintain, and balance.</p><h3>Custom Resource Example</h3><p>You can <strong>create your own resource classes</strong> to store structured data, such as an RPG item database or character stats.</p><h4>GDScript</h4><pre># ItemData.gd<br>extends Resource<br>@export var name: String<br>@export var damage: int<br>@export var price: int</pre><p>Once you’ve created your custom Resource script (for example, <a href="http://itemdata.gd/">ItemData.gd</a>), you can easily create Resource files directly inside the Godot Editor — no code needed.</p><p><strong>To create a Resource file:</strong></p><ol><li>In the <strong>FileSystem</strong> panel, right-click anywhere in your data/ folder (or wherever you want to store it).</li><li>Select <strong>New Resource</strong> → <strong>Create Resource</strong>.</li><li>In the window that appears, scroll down and select your custom class (e.g., ItemData).</li><li>Click <strong>Create</strong>, then assign your script (<a href="http://itemdata.gd/">ItemData.gd</a>) if it’s not already linked.</li><li>You’ll now see your custom exported variables (like name, damage, and price) in the <strong>Inspector</strong>.</li><li>Fill in their values — e.g. name = &quot;Sword&quot;, damage = 10, price = 100.</li><li>Save it as sword.tres.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/445/0*9tuvy6SYwXtBFUgJ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/523/0*9_oYmnIqB6fdxEZl.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*r_exQ7eHJRDdeUjK.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/519/0*I5ni2AcbBCB_rbfh.png" /></figure><p>For example, you could create multiple item resources:</p><pre>data/<br>  sword.tres<br>  axe.tres<br>  potion.tres</pre><p>Then load them in-game:</p><pre>var sword = load(&quot;res://data/sword.tres&quot;)<br>print(sword.name, &quot;does&quot;, sword.damage, &quot;damage&quot;)</pre><h4>C#</h4><pre>// ItemData.cs<br>using Godot;<br><br>[GlobalClass]<br>public partial class ItemData : Resource<br>{<br>    [Export] public string Name { get; set; }<br>    [Export] public int Damage { get; set; }<br>    [Export] public int Price { get; set; }<br>}</pre><p>You can now create .tres resources from this script directly in the editor — each one representing an item.</p><h3>Best Practices</h3><ul><li>Use .tres for text-based resources (readable and version control friendly).</li><li>Keep reusable data in res://data/ or a similar dedicated folder.</li><li>Use <strong>custom Resource scripts</strong> for structured data (like items, quests, or NPCs).</li><li>Don’t hardcode data — load and reference Resource files instead.</li><li>Reuse Resources between multiple nodes or scenes whenever possible.</li><li>Avoid circular references (when Resources reference each other in a loop).</li></ul><h3>Example</h3><p>Here’s how you can create, load, and use Resources dynamically to power your game’s item system.</p><h4>GDScript</h4><pre># Game.gd<br>extends Node<br><br>func _ready():<br>    var sword = load(&quot;res://data/sword.tres&quot;)<br>    print(&quot;Picked up:&quot;, sword.name)<br>    print(&quot;Damage:&quot;, sword.damage)</pre><h4>C#</h4><pre>using Godot;<br><br>public partial class Game : Node<br>{<br>    public override void _Ready()<br>    {<br>        var sword = (ItemData)GD.Load(&quot;res://data/sword.tres&quot;);<br>        GD.Print($&quot;Picked up: {sword.Name}&quot;);<br>        GD.Print($&quot;Damage: {sword.Damage}&quot;);<br>    }<br>}</pre><h3>Part IV: Gameplay Mechanics</h3><p>Now that you understand how to store data and control logic, it’s time to explore the coding features that can help make your game <em>interactive.</em> Gameplay mechanics are what bring your project to life by connecting player actions, objects, and events in real time. They’re what make doors open, characters jump, and music change when you take damage.</p><p><strong>In this section, we’ll explore:</strong></p><ul><li><a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#signals"><strong>Signals (Events)</strong></a> — letting nodes communicate with each other</li><li><a href="https://docs.godotengine.org/en/stable/tutorials/inputs/inputevent.html"><strong>Input</strong> </a>— handling player actions and controls</li><li><a href="https://docs.godotengine.org/en/stable/classes/class_timer.html"><strong>Timers</strong> </a>— triggering delayed or repeated actions</li><li><a href="https://docs.godotengine.org/en/stable/tutorials/scripting/idle_and_physics_processing.html"><strong>Physics Process vs. Process</strong> </a>— controlling frame updates and physics logic</li><li><a href="https://docs.godotengine.org/en/stable/classes/class_randomnumbergenerator.html"><strong>Random Numbers</strong></a> — adding unpredictability and variety</li></ul><h3>1. Signals (Events)</h3><p>Signals are Godot’s way of letting nodes communicate without being directly connected. Think of them as events, where one node or part of the code “emits” a signal, and another “listens” and reacts to it. This makes your game more modular and organized, since nodes don’t need to directly reference each other to interact.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Kibs9_SViT8YULY9_3vZOA.png" /></figure><p><strong>You can use signals for all sorts of gameplay interactions:</strong></p><ul><li>Notify the game that the Start button was pressed → start the game</li><li>Have the UI flash red when the player takes damage</li><li>When an action is pressed, tell the door to open</li><li>When an animation is complete, switch to the next one</li></ul><p>Signals turn isolated nodes into a living system that reacts to events in real time, which is a key concept for dynamic, responsive gameplay.</p><h3>Why Signals Are Needed</h3><p>Signals allow for clean, modular communication between game elements.</p><p><strong>You’ll use them to:</strong></p><ul><li>Detect button presses in UI</li><li>React when a player’s health changes</li><li>Trigger events when a collision shape is entered or exited</li><li>Notify scripts when timers finish or animations end</li></ul><p>Without signals, nodes would need to constantly “poll” each other, ultimately creating messy code.</p><h3>Syntax</h3><p>I like to think that signals are comprised of three main parts: <strong>Event → Listener → Response</strong></p><p><strong>GDScript:</strong></p><pre># Emitting a signal (Event)<br>signal health_changed(new_health)<br>health_changed.emit_signal(80)<br><br># Connecting a signal (Listener)<br>button.pressed.connect(_on_button_pressed)<br><br># Defining the action (Response)<br>func _on_button_pressed():<br>    print(&quot;Button was pressed!&quot;)</pre><p><strong>C#:</strong></p><pre>// Emitting a signal (Event)<br>[Signal]<br>public delegate void HealthChangedEventHandler(int newHealth);<br>EmitSignal(nameof(HealthChanged), 80);<br><br>// Connecting a signal (Listener)<br>button.Pressed += OnButtonPressed;<br><br>// Response method (Response)<br>private void OnButtonPressed()<br>{<br>    GD.Print(&quot;Button was pressed!&quot;);<br>}</pre><h3>Best Practices</h3><ul><li><strong>Use signals to decouple logic</strong> — nodes shouldn’t depend directly on each other</li><li><strong>Connect signals in</strong> _ready() or in the editor for clarity.</li><li><strong>Name custom signals descriptively</strong>, like player_died or door_opened.</li><li><strong>Disconnect unused signals</strong> to prevent unexpected behavior.</li></ul><h3>Example</h3><p>Here’s a simple setup where pressing a button updates the player’s health bar.</p><p>In the code below, we <strong>define a signal</strong> called health_changed in the Player script. When our player takes damage, we <strong>emit this signal</strong> to notify the other parts in our game that our health has changed.</p><p>When the signal is emitted, the other parts of our code which are connected to it, should execute their logic. In this case, we have another script called UI, which <strong>connects to this signal</strong> in the ready() function. We are connecting this signal to a new function in this script called on_health_changed. This tells the game our <strong>UI</strong> script is <strong>listening for events</strong> (emits) from the <strong>Player</strong> script, and whenever that event occurs, the logic within the function will execute - which in this case is a simple print statement notifying us of our new health value.</p><p><strong>GDScript:</strong></p><pre># Player.gd<br><br># Signal creation<br>signal health_changed(new_health)<br>var health = 100<br><br># Signal emission (Event)<br>func take_damage(amount):<br>    health -= amount<br>    health_changed.emit(health)</pre><pre># UI.gd<br><br># Signal connection (Listener)<br>func _ready():<br>    var player = get_node(&quot;../Player&quot;)<br>    player.health_changed.connect(_on_health_changed)<br><br># Signal action (Response)<br>func _on_health_changed(new_health):<br>    print(&quot;Player health:&quot;, new_health)</pre><p><strong>C#:</strong></p><pre>// Player.cs<br><br>// Signal creation<br>[Signal]<br>public delegate void HealthChangedEventHandler(int newHealth);<br>int health = 100;<br><br>// Signal emission (Event)<br>public void TakeDamage(int amount)<br>{<br>    health -= amount;<br>    EmitSignal(nameof(HealthChanged), health);<br>}</pre><pre>// UI.cs<br><br>// Signal connection (Listener)<br>public override void _Ready()<br>{<br>    var player = GetNode(&quot;../Player&quot;);<br>    player.Connect(&quot;HealthChanged&quot;, this, nameof(OnHealthChanged));<br>}<br><br>// Signal action (Response)<br>private void OnHealthChanged(int newHealth)<br>{<br>    GD.Print(&quot;Player health: &quot; + newHealth);<br>}</pre><h3>2. Input</h3><p>Input is how players interact with your game. This can be via pressing keys, clicking, or touching the screen to control characters and trigger actions. Godot’s input system makes it easy to detect and respond to player actions through code or the Input Map (found in <em>Project → Project Settings → Input Map</em>).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*C0sJtfhC0XrugV6t.png" /></figure><p>You can think of Input as the player’s choices, and your game listens and reacts accordingly.</p><h4>Why Input Is Needed</h4><p><strong>Every interactive game depends on Input:</strong></p><ul><li>Moving the player character</li><li>Jumping, attacking, or using abilities</li><li>Navigating menus or UI</li><li>Detecting gamepad or touchscreen actions</li></ul><p>Without Input handling, your game wouldn’t know what the player wants to do, and the player won’t have any way of interacting with the game, they would just sit idle!</p><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre># Detecting input actions continuously (we&#39;ll move right as long as we hold key)<br>func _process(delta):<br>    if Input.is_action_pressed(&quot;move_right&quot;):<br>        # logic<br><br># Detecting input actions once-off (we&#39;ll jump once and have to press key again)<br>func _input(event):<br>    if event.is_action_pressed(&quot;jump&quot;):<br>        # logic</pre><p><strong>C#:</strong></p><pre>// Detecting input actions continuously (we&#39;ll move right as long as we hold key)<br>public override void _Process(double delta)<br>{<br>    if (Input.IsActionPressed(&quot;move_right&quot;))<br>       // logic<br>}<br><br>// Detecting input actions once-off (we&#39;ll jump once and have to press key again)<br>public override void _Input(InputEvent @event)<br>{<br>    if (@event.IsActionPressed(&quot;jump&quot;))<br>        // logic<br>}</pre><h3>Input Methods</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3B04fGIG6IoCkLKB_t8IpA.png" /></figure><h3>Best Practices</h3><ul><li><strong>Use the Input Map</strong> instead of hardcoding keys — this lets players rebind controls easily.</li><li><strong>Check for actions</strong> (like &quot;ui_accept&quot; or &quot;move_left&quot;) rather than specific keys.</li><li><strong>Use</strong> _process() for continuous input (movement), and _input() for one-time actions (button presses).</li><li><strong>Support multiple devices</strong> (keyboard, controller, touchscreen) whenever possible.</li><li><strong>Keep input logic separate</strong> from gameplay logic — use signals or dedicated functions to handle actions cleanly.</li></ul><h3>Example</h3><p>Here’s a simple movement script that lets a player move left and right, and jump when the spacebar is pressed.</p><p>In the code below, the move_right and move_left keys have been assigned to the ← → keys in the <strong>Project Settings</strong>, and the jump key to the spacebar.</p><p><strong>GDScript:</strong></p><pre>extpends CharacterBody2D<br><br>const SPEED = 200<br>const JUMP_FORCE = -400<br><br>func _physics_process(delta):<br>    var velocity = Vector2.ZERO<br><br>    # Move player left and right<br>    if Input.is_action_pressed(&quot;move_right&quot;):<br>        velocity.x += SPEED<br>    elif Input.is_action_pressed(&quot;move_left&quot;):<br>        velocity.x -= SPEED<br><br>    # Move player up (jump)<br>    if is_on_floor() and Input.is_action_just_pressed(&quot;jump&quot;):<br>        velocity.y = JUMP_FORCE<br>    velocity = move_and_slide(velocity, Vector2.UP)</pre><p><strong>C#:</strong></p><pre>using Godot;<br><br>public partial class Player : CharacterBody2D<br>{<br>    const float Speed = 200f;<br>    const float JumpForce = -400f;<br>    public override void _PhysicsProcess(double delta)<br>    {<br>        Vector2 velocity = Velocity;<br><br>        // Move player left and right<br>        if (Input.IsActionPressed(&quot;move_right&quot;))<br>            velocity.X = Speed;<br>        else if (Input.IsActionPressed(&quot;move_left&quot;))<br>            velocity.X = -Speed;<br>        else<br>            velocity.X = 0;<br><br>        // Move player up (jump) <br>        if (IsOnFloor() &amp;&amp; Input.IsActionJustPressed(&quot;jump&quot;))<br>            velocity.Y = JumpForce;<br>        Velocity = velocity;<br>        MoveAndSlide();<br>    }<br>}</pre><h3>3. Timers</h3><p>Timers are nodes that let you run code after a delay or repeatedly at fixed intervals. They’re essential for creating time-based events such as cooldowns, countdowns, enemy spawns, or temporary effects.</p><p>Think of a timer as an alarm clock inside your game: you set it, wait, and when it rings, something happens.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*W88LmSXdsi3eK2SI6Vi8WA.png" /></figure><h3>Why Timers Are Needed</h3><p><strong>Games constantly rely on timing to feel dynamic and balanced:</strong></p><ul><li>Countdown before starting a match</li><li>Enemy attack intervals</li><li>Power-up durations or cooldowns</li><li>Auto-saving or periodic events</li></ul><p>Without timers, you’d need to manually track time using variables, which quickly becomes messy and unreliable.</p><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre># Creating and starting a timer<br>var timer = Timer.new()<br>add_child(timer)<br>timer.wait_time = 2.0 # how long it will execute for (2 seconds)<br>timer.one_shot = true # will execute once, set to false for continuous execution<br>timer.start() # start the timer<br><br># Connecting timeout signal<br>timer.timeout.connect(_on_timer_timeout)<br><br># When timer times out, do this logic<br>func _on_timer_timeout():<br>    print(&quot;Time&#39;s up!&quot;)</pre><p><strong>C#:</strong></p><pre>// Creating and starting a timer<br>var timer = new Timer();<br>AddChild(timer);<br>timer.WaitTime = 2.0f; // how long it will execute for (2 seconds)<br>timer.OneShot = true; // will execute once, set to false for continuous execution<br>timer.Start(); // start the timer<br>timer.Timeout += OnTimerTimeout; <br><br>// When timer times out, do this logic<br>private void OnTimerTimeout()<br>{<br>    GD.Print(&quot;Time&#39;s up!&quot;);<br>}</pre><h3>Best Practices</h3><ul><li><strong>Use Timer nodes</strong> for clarity instead of manually counting seconds in _process().</li><li><strong>Connect the</strong> timeout signal to trigger actions cleanly when the timer finishes.</li><li><strong>Set</strong> one_shot to true for single-use timers, or false to make them loop.</li><li><strong>Use</strong> start() and stop() to control timers in code.</li><li><strong>Reuse timers</strong> for repeating events rather than creating new ones each time.</li></ul><h3>Example</h3><p>Here’s how you might use a Timer to respawn an enemy 3 seconds after it’s defeated.</p><p>In the code below, we <strong>create a timer</strong> as soon as our game starts. When it’s created, we also <strong>connect its timeout</strong> signal to our script (this can be done in the editor itself, instead of via code). When the enemy is defeated, say their health reaches below zero, we <strong>start the timer</strong>. The timer will run for however long we’ve set its wait_time, which is 3 seconds. When 3 seconds have passed, the <strong>timer will timeout</strong>, and the enemy will be respawned.</p><p>If the enemy dies again, the logic will start over again, unless we’ve set our timer to be one_shot enabled.</p><p><strong>GDScript:</strong></p><pre>extends Node2D<br><br># Create timer<br>func _ready():<br>    var respawn_timer = $RespawnTimer<br>    respawn_timer.wait_time = 3.0 <br>    respawn_timer.one_shot = false<br>    respawn_timer.timeout.connect(_on_respawn_timer_timeout)<br><br># Start timer<br>func on_enemy_defeated():<br>    print(&quot;Enemy defeated! Respawning in 3 seconds...&quot;)<br>    $RespawnTimer.start()<br><br># Stop timer<br>func _on_respawn_timer_timeout():<br>    print(&quot;Enemy has respawned!&quot;)</pre><p><strong>C#:</strong></p><pre>using Godot;<br><br>public partial class EnemySpawner : Node2D<br>{<br>    // Create timer<br>    private Timer RespawnTimer;<br>    public override void _Ready()<br>    {<br>        RespawnTimer = GetNode&lt;Timer&gt;(&quot;RespawnTimer&quot;);<br>        RespawnTimer.WaitTime = 3b.0f; <br>        RespawnTimer.OneShot = false;    <br>        RespawnTimer.Timeout += OnRespawnTimerTimeout;<br>    }<br>    // Start timer<br>    public void OnEnemyDefeated()<br>    {<br>        GD.Print(&quot;Enemy defeated! Respawning in 3 seconds...&quot;);<br>        RespawnTimer.Start();<br>    }<br>    // Stop timer<br>    private void OnRespawnTimerTimeout()<br>    {<br>        GD.Print(&quot;Enemy has respawned!&quot;);<br>    }<br>}</pre><h3>4. Physics Process vs. Process</h3><p>In Godot, both _process() and _physics_process() are special built-in functions that run every frame — but they serve different purposes.</p><ul><li>_process(delta) runs <em>every rendered frame</em> — perfect for animations, UI, and non-physics logic.</li><li>_physics_process(delta) runs <em>at a fixed time step</em> — perfect for movement, collisions, and anything that interacts with the physics engine.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uictZNVRAUl95lNzh_KmYg.png" /></figure><h3>Why It Matters</h3><p>Games depend on predictable, consistent updates.</p><p><strong>You’ll typically use:</strong></p><ul><li>_process() → for animations, timers, and UI transitions.</li><li>_physics_process() → for movement, gravity, and collisions.</li></ul><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre># Called every frame (variable time step)<br>func _process(delta):<br>    # do framerate related logic<br><br># Called at a fixed time step (default: 60 times per second)<br>func _physics_process(delta):<br>    # do physics related logic<br>    # usually ends in move_and_slide() or move_and_collide()</pre><p><strong>C#:</strong></p><pre>// Called every frame<br>public override void _Process(double delta)<br>{<br>   // do framerate related logic<br>}<br><br>// Called at a fixed physics tick rate<br>public override void _PhysicsProcess(double delta)<br>{<br>    // do physics related logic<br>    // usually ends in MoveAndSlide() or MoveAndCollide()<br>}</pre><h3>Best Practices</h3><ul><li><strong>Use</strong> _physics_process() for movement and collisions.</li><li><strong>Use</strong> _process() for visual effects and non-physics updates.</li><li><strong>Always multiply by</strong> delta to keep movement frame-rate independent.</li><li><strong>Don’t mix physics logic in</strong> _process() — it can cause jitter or inconsistencies.</li><li><strong>Pause logic carefully</strong> — timers and physics might still run if not managed properly.</li></ul><h3>Example</h3><p>Here’s a basic player script that plays a characters <strong>run animations continuously</strong> whilst the input keys are being held down in the process() function. It also then <strong>moves the character around</strong> whilst the input keys are being held down in the physics_process() function.</p><p><strong>GDScript:</strong></p><pre># This will play the run animations on inputs<br>func _process(delta):<br>    if Input.is_action_pressed(&quot;move_right&quot;) or Input.is_action_pressed(&quot;move_left&quot;):<br>        $AnimatedSprite2D.play(&quot;run&quot;)<br>    else:<br>        $AnimatedSprite2D.play(&quot;idle&quot;)<br><br># This will move the player on inputs.<br>func _physics_process(delta):<br>    var velocity = Vector2.ZERO<br>    if Input.is_action_pressed(&quot;move_right&quot;):<br>        velocity.x += 200<br>    elif Input.is_action_pressed(&quot;move_left&quot;):<br>        velocity.x -= 200<br>    velocity = move_and_slide(velocity)</pre><p><strong>C#:</strong></p><pre>using Godot;<br><br>public partial class Player : CharacterBody2D<br>{<br>    const float Speed = 200f;<br>    private AnimatedSprite2D sprite;<br>    public override void _Ready()<br>    {<br>        sprite = GetNode&lt;AnimatedSprite2D&gt;(&quot;AnimatedSprite2D&quot;);<br>    }<br><br>    // This will play the run animations on inputs<br>    public override void _Process(double delta)<br>    {<br>        if (Input.IsActionPressed(&quot;move_right&quot;) || Input.IsActionPressed(&quot;move_left&quot;))<br>            sprite.Play(&quot;run&quot;);<br>        else<br>            sprite.Play(&quot;idle&quot;);<br>    }<br><br>    //  This will move the player on inputs.<br>    public override void _PhysicsProcess(double delta)<br>    {<br>        Vector2 velocity = Vector2.Zero;<br>        if (Input.IsActionPressed(&quot;move_right&quot;))<br>            velocity.X += Speed;<br>        else if (Input.IsActionPressed(&quot;move_left&quot;))<br>            velocity.X -= Speed;<br>        Velocity = velocity;<br>        MoveAndSlide();<br>    }<br>}</pre><h3>5. Random Numbers</h3><p>Random numbers add variety and unpredictability to your game, thus ensuring no two playthroughs feel exactly the same. Godot’s random system lets you generate numbers, select random items from arrays, or even produce random directions for projectiles or enemies.</p><p>Randomness makes things feel dynamic, alive, and surprising.</p><h3>Why Random Numbers Are Needed</h3><p><strong>Games rely on randomness for endless replayability:</strong></p><ul><li>Loot drops or item rewards</li><li>Random enemy spawns</li><li>Critical hits or damage variation</li><li>Procedural generation (maps, terrain, puzzles)</li><li>Particle spread or visual effects</li></ul><p>Without randomness, your game would feel repetitive and predictable.</p><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre># Initialize random seed (usually in _ready)<br>randomize()<br><br># Generate a random float between 0 and 1<br>var r = randf()<br><br># Generate an integer between 0 and 10<br>var i = randi_range(0, 10)<br><br># Pick a random element from an array<br>var enemies = [&quot;Goblin&quot;, &quot;Orc&quot;, &quot;Troll&quot;]<br>var random_enemy = enemies.pick_random()<br><br># Random vector direction<br>var dir = Vector2(randf_range(-1, 1), randf_range(-1, 1)).normalized()</pre><p><strong>C#:</strong></p><pre>using Godot;<br>using System;<br><br>public partial class RandomExample : Node<br>{<br>    private Random rand = new Random();<br>    public override void _Ready()<br>    {<br>        // Random float between 0 and 1<br>        float r = (float)rand.NextDouble();<br><br>        // Random integer between 0 and 10<br>        int i = rand.Next(0, 11);<br><br>        // Random element from an array<br>        string[] enemies = { &quot;Goblin&quot;, &quot;Orc&quot;, &quot;Troll&quot; };<br>        string randomEnemy = enemies[rand.Next(enemies.Length)];<br><br>        // Random direction<br>        Vector2 dir = new Vector2(<br>            (float)rand.NextDouble() * 2 - 1,<br>            (float)rand.NextDouble() * 2 - 1<br>        ).Normalized();<br>        GD.Print($&quot;Enemy: {randomEnemy}, Direction: {dir}&quot;);<br>    }<br>}</pre><h3>Best Practices</h3><ul><li><strong>Use</strong> randomize() once at startup (in _ready()) to avoid repeating patterns.</li><li><strong>Choose the right random function</strong> — Godot offers several for different data types.</li><li><strong>Use seeded randomness</strong> for predictable results (great for replays or testing).</li><li><strong>Clamp or round values</strong> if you only need whole numbers or limits.</li><li><strong>Keep randomness balanced</strong> — too much can frustrate players.</li></ul><h3>Example</h3><p>Here’s how you might use randomness to spawn enemies at random positions on the map.</p><p><strong>GDScript:</strong></p><pre>extends Node2D<br><br>func _ready():<br>    randomize()<br>    for i in range(5):<br>        var enemy_scene = preload(&quot;res://Enemy.tscn&quot;).instantiate()<br>        enemy_scene.position = Vector2(<br>            randi_range(100, 800),<br>            randi_range(100, 400)<br>        )<br>        add_child(enemy_scene)</pre><p><strong>C#:</strong></p><pre>using Godot;<br>using System;<br><br>public partial class Spawner : Node2D<br>{<br>    private Random rand = new Random();<br>    public override void _Ready()<br>    {<br>        for (int i = 0; i &lt; 5; i++)<br>        {<br>            var enemyScene = GD.Load&lt;PackedScene&gt;(&quot;res://Enemy.tscn&quot;).Instantiate&lt;Node2D&gt;();<br>            enemyScene.Position = new Vector2(<br>                rand.Next(100, 801),<br>                rand.Next(100, 401)<br>            );<br>            AddChild(enemyScene);<br>        }<br>    }<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/926/1*D3FsrZSsw2qx0eWQVIO1ng.png" /></figure><h3>6. The Ready Function</h3><p>In Godot, the _ready() function is called <strong>once</strong> when a node enters the scene tree and is fully initialized. It’s the go-to place for setup logic such as loading resources, connecting signals, setting initial values, or caching references to other nodes.</p><h3>Why Its Needed</h3><p>When a node is created, not all of its children or dependencies exist yet. If you try to reference them too early, your code can break.</p><p>_ready() ensures the entire node hierarchy is loaded before running your setup code.</p><p><strong>You’ll typically use it to:</strong></p><ul><li>Get references to child nodes</li><li>Connect signals between nodes</li><li>Load textures, sounds, or scenes</li><li>Initialize variables or game states</li></ul><p>Without _ready(), you’d risk calling nodes or data that don’t exist yet.</p><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre># Called once when the node enters the scene tree<br>func _ready():<br>    # Set values to be initialized<br>    var sprite = $Sprite2D<br>    var player_health = 100</pre><p><strong>C#:</strong></p><pre>using Godot;<br><br>public partial class Player : Node2D<br>{<br>    public override void _Ready()<br>    {<br>        // Set values to be initialized<br>        Sprite2D sprite = GetNode&lt;Sprite2D&gt;(&quot;Sprite2D&quot;);<br>        int playerHealth = 100<br>    }<br>}</pre><h3>Best Practices</h3><ul><li><strong>Use</strong> _ready() for one-time setup only — not for logic that runs continuously.</li><li><strong>Keep it clean and short</strong> — just initialization, no heavy loops or updates.</li><li><strong>Access child nodes here safely</strong> — all children are guaranteed to exist.</li><li><strong>Connect signals or timers in</strong> _ready(), not in _process().</li><li><strong>Avoid creating dependencies between unrelated nodes</strong> — keep setups modular.</li></ul><h3>Example</h3><p>Here’s a simple player setup that uses _ready() to load assets, connect signals, and initialize variables before gameplay starts.</p><p><strong>GDScript:</strong></p><pre>extends CharacterBody2D<br><br>var health = 100<br><br>func _ready():<br>    $AnimatedSprite2D.play(&quot;idle&quot;)<br>    $HealthBar.max_value = health<br>    $Area2D.body_entered.connect(_on_body_entered)<br><br>func _on_body_entered(body):<br>    print(&quot;Collided with:&quot;, body.name)</pre><p><strong>C#:</strong></p><pre>using Godot;<br><br>public partial class Player : CharacterBody2D<br>{<br>    private int health = 100;<br>    private AnimatedSprite2D sprite;<br>    private ProgressBar healthBar;<br>    private Area2D hitArea;<br><br>    public override void _Ready()<br>    {<br>        sprite = GetNode&lt;AnimatedSprite2D&gt;(&quot;AnimatedSprite2D&quot;);<br>        sprite.Play(&quot;idle&quot;);<br>        healthBar.MaxValue = health;<br>        hitArea.BodyEntered += OnBodyEntered;<br>    }<br><br>    private void OnBodyEntered(Node body)<br>    {<br>        GD.Print($&quot;Collided with: {body.Name}&quot;);<br>    }<br>}</pre><h3>Part V: Scenes &amp; Nodes</h3><p>In Godot, everything is a node. From your player and camera to UI buttons, lights, and audio — every piece of your game inherits from the Node class.</p><p>Nodes are the building blocks of Godot. When you combine them, you create scenes, which are self-contained groups of nodes that can represent anything: a level, a character, or even a menu. Together, these form the Scene Tree, a hierarchy that defines how everything in your game is organized, updated, and connected.</p><p><strong>In this section, we’ll cover:</strong></p><ul><li><a href="https://docs.godotengine.org/en/stable/getting_started/step_by_step/nodes_and_scenes.html"><strong>Node Basics</strong></a> — understanding what nodes are and how they work</li><li><a href="https://docs.godotengine.org/en/stable/getting_started/step_by_step/nodes_and_scenes.html"><strong>Scenes</strong> </a>— reusable groups of nodes that form your game’s structure</li><li><a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#inheritance"><strong>Inheritance</strong> </a>— allows one scene or script to reuse another’s logic and structure.</li><li><a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_exports.html"><strong>Export variables</strong></a> — makes a variable visible and editable in the Inspector panel.</li><li><a href="https://docs.godotengine.org/en/stable/tutorials/scripting/groups.html"><strong>Groups </strong></a>— tagging nodes into similar categories</li></ul><h3>1. Accessing Nodes</h3><p>A Node is the most fundamental building block in Godot. Each node has a name, a type, and optional children. Nodes within a scene creates a tree-like structure that defines how your game world is built.</p><p><strong>Think of it like this:</strong></p><ul><li><em>Nodes</em> are the building blocks.</li><li><em>Scenes</em> are the containers which hold and organize nodes.</li></ul><p>For a more visual reference on all the nodes and their use-cases, refer to my <a href="https://app.gitbook.com/o/cPjnkDazMpEboptvVAgZ/s/2Aq0MNHhA8LaPrbZ2ewE/~/changes/130/free-resources/godot-book-of-nodes"><strong>Book of Nodes</strong></a><strong>.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*k1u8JhNCwhCOXNyM.png" /></figure><p>View the online version of the above depiction of scenes and nodes <a href="https://app.mural.co/t/godot5664/m/godot5664/1680196108691/c8c74015cd7e5090492ac132dd78a64656af7fcc?sender=u55125f0ffcb42f94c9c58346">here</a>.</p><h3>Why Nodes Are Needed</h3><p>Without nodes, there would be no game. They’re the foundation of everything you create in Godot.</p><p><strong>Nodes can represent almost anything:</strong></p><ul><li>A collider that detects hits</li><li>A sprite that displays a texture</li><li>A camera that follows the player</li><li>A light that illuminates the scene</li><li>A script that controls behavior</li></ul><p>Without nodes, every part of your game would have to be hard-coded, messy, unorganized, and nearly impossible to maintain.</p><h3>Syntax</h3><p>Nodes are usually added within the editor itself, but we can create and reference nodes directly within our code.</p><p><strong>GDScript:</strong></p><pre># Referencing an existing node and changing its property <br>var sprite = $Sprite2D<br>sprite.modulate = Color.RED<br><br># Getting a node by path<br>var camera = get_node(&quot;Camera2D&quot;)<br><br># Creating a new node<br>var new_label = Label.new()<br>new_label.text = &quot;Hello World&quot;<br>add_child(new_label)</pre><p><strong>C#:</strong></p><pre>// Referencing an existing node and changing its property <br>Sprite2D sprite = GetNode&lt;Sprite2D&gt;(&quot;Sprite2D&quot;);<br>sprite.Modulate = new Color(1, 0, 0);<br><br>// Getting a node by path<br>Camera2D camera = GetNode&lt;Camera2D&gt;(&quot;Camera2D&quot;);<br><br>// Creating a new node<br>Label newLabel = new Label();<br>newLabel.Text = &quot;Hello World&quot;;<br>AddChild(newLabel);</pre><h3>Best Practices</h3><ul><li><strong>Name nodes clearly</strong> (e.g., PlayerSprite, Hitbox, MainCamera).</li><li><strong>Use composition</strong> — build functionality by combining nodes, not writing giant scripts.</li><li><strong>Leverage node types</strong> — use the right node for the job (Area2D for detection, Sprite2D for visuals, etc.).</li><li><strong>Keep the tree organized</strong> — indentation and naming go a long way.</li><li><strong>Don’t overload one scene</strong> — break your game into multiple smaller, reusable ones.</li></ul><h3>Example</h3><p>Here’s how you can reference a node that already exists <strong>and</strong> create a new one dynamically in the same script.</p><p>In the code below, we assume that within the editor we’ve added a <strong>Sprite2D</strong> node called “Sprite2D”. We reference this node in our script, and then we change its texture (sprite). We also create a new <strong>Label</strong> node with the text “Player ready”, and position it to our screen. The add_child() method adds the newly created node to our scene via the script.</p><p><strong>GDScript:</strong></p><pre>extends Node2D<br><br>func _ready():<br>    # Reference an existing Sprite node<br>    var sprite = $Sprite2D<br>    sprite.texture = preload(&quot;res://sprites/player.png&quot;)<br><br>    # Create and add a new Label node<br>    var label = Label.new()<br>    label.text = &quot;Player ready!&quot;<br>    label.position = Vector2(10, 10)<br>    add_child(label)</pre><p><strong>C#:</strong></p><pre>using Godot;<br><br>public partial class Player : Node2D<br>{<br>    public override void _Ready()<br>    {<br>        // Reference an existing node<br>        Sprite2D sprite = GetNode&lt;Sprite2D&gt;(&quot;Sprite2D&quot;);<br>        sprite.Texture = (Texture2D)GD.Load(&quot;res://sprites/player.png&quot;);<br><br>        // Create and add a new node dynamically<br>        Label label = new Label();<br>        label.Text = &quot;Player ready!&quot;;<br>        label.Position = new Vector2(10, 10);<br>        AddChild(label);<br>    }<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/924/1*n5AiCbrDO3A0MZ18sCf_xg.png" /></figure><h3>2. Accessing Scenes</h3><p>A Scene in Godot is a collection of nodes. It can represent this such as a character, a level, a user interface, or even a single reusable object.</p><p>Every scene has a <strong>root node</strong>, and that root defines the scene’s type. For example, a CharacterBody2D might be the root for a player scene, or a Control node might be the root for a UI screen.</p><p>Think of scenes as blueprints: you build them once in the editor, then reuse, instance, or switch between them dynamically during gameplay.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*eMCKZh43LIvOsuPY.png" /></figure><p>View the online version of the above depiction of scenes and nodes <a href="https://app.mural.co/t/godot5664/m/godot5664/1680196108691/c8c74015cd7e5090492ac132dd78a64656af7fcc?sender=u55125f0ffcb42f94c9c58346">here</a>.</p><h3>Why Scenes Are Needed</h3><p><strong>Scenes make your game modular and reusable. They allow you to:</strong></p><ul><li>Build individual pieces of your game separately.</li><li>Reuse the same object (like enemies or items) multiple times.</li><li>Load and unload levels dynamically.</li><li>Transition between menus, gameplay, and cutscenes.</li></ul><p>Without scenes, your entire game would live in one massive, unmanageable hierarchy, which makes updates, debugging, and testing a nightmare.</p><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre># Load a scene from file<br>var enemy_scene = preload(&quot;res://scenes/Enemy.tscn&quot;)<br><br># Create an instance of that scene<br>var enemy_instance = enemy_scene.instantiate()<br><br># Add it to the current scene tree<br>add_child(enemy_instance)<br><br># Change to another scene file<br>get_tree().change_scene_to_file(&quot;res://scenes/Level2.tscn&quot;)<br><br># Reload the current scene<br>get_tree().reload_current_scene()<br><br># Get a node from another scene (if it&#39;s loaded)<br>var player = get_tree().get_root().get_node(&quot;World/Player&quot;)<br>player.health = 50</pre><p><strong>C#:</strong></p><pre>// Load a scene from file<br>PackedScene enemyScene = (PackedScene)GD.Load(&quot;res://scenes/Enemy.tscn&quot;);<br><br>// Create an instance of that scene<br>Node enemyInstance = enemyScene.Instantiate();<br><br>// Add it to the current scene tree<br>AddChild(enemyInstance);<br><br>// Change to another scene file<br>GetTree().ChangeSceneToFile(&quot;res://scenes/Level2.tscn&quot;);<br><br>// Reload the current scene<br>GetTree().ReloadCurrentScene();<br><br>// Get a node from another scene (if it&#39;s loaded)<br>Node player = GetTree().Root.GetNode(&quot;World/Player&quot;);<br>player.Set(&quot;health&quot;, 50);</pre><h3>Best Practices</h3><ul><li><strong>Keep scenes focused</strong> — each should represent one clear purpose (e.g., Player, MainMenu, Enemy, Level1).</li><li><strong>Use the root node type wisely</strong> — it defines what the scene <em>is</em> and how it behaves.</li><li><strong>Instance scenes instead of duplicating</strong> — this makes updates propagate automatically.</li><li><strong>Use</strong> get_tree().change_scene_to_file() for transitions, and preload() or load() for instancing.</li><li><strong>Keep references clean</strong> — don’t rely on global paths unless necessary.</li></ul><h3>Example</h3><p>The below code <strong>loads and spawns</strong> an enemy scene when the player presses a key, and <strong>transitions</strong> to a new scene when health reaches zero.</p><p><strong>GDScript:</strong></p><pre>extends Node2D<br><br># Load scene<br>var enemy_scene = preload(&quot;res://scenes/Enemy.tscn&quot;)<br>var health = 100<br><br>func _process(delta):<br>    # Spawn enemy when pressing &quot;E&quot;<br>    if Input.is_action_just_pressed(&quot;spawn_enemy&quot;):<br>        var enemy = enemy_scene.instantiate() # Spawn scene<br>        enemy.position = Vector2(400, 200)<br>        add_child(enemy)<br><br>    # Change to game over scene when health reaches zero<br>    if health &lt;= 0:<br>        get_tree().change_scene_to_file(&quot;res://scenes/GameOver.tscn&quot;) # Transition scene</pre><p><strong>C#:</strong></p><pre>using Godot;<br><br>public partial class Level : Node2D<br>{<br>    // Load scene<br>    private PackedScene enemyScene = (PackedScene)GD.Load(&quot;res://scenes/Enemy.tscn&quot;);<br>    private int health = 100;<br><br>    public override void _Process(double delta)<br>    {<br>        if (Input.IsActionJustPressed(&quot;spawn_enemy&quot;))<br>        {<br>            Node2D enemy = enemyScene.Instantiate&lt;Node2D&gt;(); // Spawn scene<br>            enemy.Position = new Vector2(400, 200);<br>            AddChild(enemy);<br>        }<br>        if (health &lt;= 0)<br>            GetTree().ChangeSceneToFile(&quot;res://scenes/GameOver.tscn&quot;); // Transition scene<br>    }<br>}</pre><h3>3. Inheritance</h3><p>Inheritance allows you to create new scenes or scripts that reuse and extend the functionality of existing ones. It’s one of the most powerful features of coding, as it lets you define a base behavior (like a generic “Enemy”) and then build specialized versions (like “Goblin” or “Orc”) without rewriting everything from scratch.</p><p><strong>There are two main types of inheritance in Godot:</strong></p><ol><li><strong>Script inheritance</strong> — extending another script’s code using the extends keyword.</li><li><strong>Scene inheritance</strong> — creating new scenes that build on existing ones in the editor.</li></ol><h3>Why Inheritance Is Needed</h3><p>Inheritance helps you write cleaner, reusable, and modular code. It keeps your project organized and avoids duplication when many objects share similar traits.</p><p><strong>For example:</strong></p><ul><li>A base Enemy scene might define health and damage logic for all enemies.</li><li>Derived scenes like Orc, Goblin, or Troll can inherit that and add their own unique animations or stats.</li><li>A CharacterBody2D script might define general movement for all characters in your game, and the Player script can extend it to handle input.</li></ul><p>Without inheritance, you’d have to duplicate large chunks of code or rebuild scenes from scratch every time.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rkbVsuifbLiSJU1Ra0JVcg.png" /></figure><h3>Best Practices</h3><ul><li><strong>Use inheritance for shared functionality</strong>, not for one-off behaviors.</li><li><strong>Keep base classes simple and focused</strong> — they should define core rules, not everything.</li><li><strong>Override functions responsibly</strong> — always call super() or ._process(delta) if you still want the parent’s logic to run.</li><li><strong>For scenes</strong>, use “Inherit Scene” in the editor instead of duplicating.</li><li><strong>Use composition</strong> (adding child nodes) alongside inheritance for flexibility.</li></ul><h3>Syntax</h3><h4>Script Inheritance</h4><p><strong>GDScript:</strong></p><pre># Base script: Base.gd<br># The base class defines the basic values of your enemies<br>class_name Base<br>extends Node<br><br>var health = 100<br><br>func take_damage(amount):<br>    health -= amount<br>    print(&quot;Enemy took&quot;, amount, &quot;damage. Health:&quot;, health)</pre><pre># Derived script: Derived.gd<br># The derived class gets everythign from the Base, such as health and take_damage() function<br>extends Base<br><br>func _ready():<br>    print(&quot;Goblin ready!&quot;)<br>    take_damage()</pre><p><strong>C#:</strong></p><pre>// Base script: Base.cs<br>// The base class defines the basic values of your enemies<br>using Godot;<br><br>public partial class Base : Node<br>{<br>    public int Health = 100;<br>    public virtual void TakeDamage(int amount)<br>    {<br>        Health -= amount;<br>        GD.Print($&quot;Enemy took {amount} damage. Health: {Health}&quot;);<br>    }<br>}</pre><pre>// Derived script: Derived.cs<br>// The derived class gets everythign from the Base, such as health and take_damage() function<br>using Godot;<br><br>public partial class Derived : Base<br>{<br>    public override void _Ready()<br>    {<br>        GD.Print(&quot;Goblin ready!&quot;);<br>        base.TakeDamage(amount);<br>    }<br>}</pre><h4>Scene Inheritance</h4><p>You can also inherit scenes directly in the editor:</p><ol><li>Right-click an existing scene (e.g., Enemy.tscn).</li><li>Choose <strong>“New Inherited Scene”</strong>.</li><li>Godot creates a new version with all the parent nodes — ready for customization.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/619/0*bTj7-ri4Yc1j6odD.png" /></figure><p><strong>You can then:</strong></p><ul><li>Add new child nodes (e.g., a weapon or animation).</li><li>Override properties (like textures, speeds, or collision shapes).</li><li>Change script references while keeping base functionality intact.</li></ul><h3>Example</h3><p>The code below creates a <strong>base</strong> <strong>Enemy class</strong>, which defines the <strong>shared behavior</strong> for all of our enemies. We then create <strong>two derived classes</strong> (Orc and Troll) which extends from it. These classes will get all the base functionality, such as health and take_damage(), but will still be able to get their own unique features.</p><p>This means our damage logic is the same for all enemies, but different enemies can have different animations, sound, and behavior.</p><p><strong>GDScript:</strong></p><pre># Base Enemy.gd<br>class_name Enemy<br>extends CharacterBody2D<br><br>var health = 100<br><br>func _ready():<br>    print(&quot;Enemy spawned with&quot;, health, &quot;HP&quot;)<br><br>func take_damage(amount):<br>    health -= amount<br>    print(&quot;Enemy took damage:&quot;, amount)</pre><pre># Orc.gd (inherits Enemy.gd)<br># Orc plays a dance anim on taking damage, whilst still losing health<br>extends Enemy<br><br>func _ready():<br>    print(&quot;Orc enters the battlefield!&quot;)<br><br>func take_damage(amount):<br>    print(&quot;Orc roars in anger!&quot;)<br>    $animated_sprite.play(&quot;dance_anim&quot;)</pre><pre># Troll.gd (inherits Enemy.gd)<br>extends &quot;res://scripts/Enemy.gd&quot;<br><br>func take_damage(amount):<br>    print(&quot;Troll regenerates some health!&quot;)<br>    health += 5</pre><h3>4. Export Variables</h3><p>Export variables let you expose script properties directly to the Godot Editor. They make your scripts customizable, flexible, and designer-friendly, allowing you to change values without modifying the code.</p><p>Think of them as editable parameters which appear in the Inspector just like any built-in node property.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/679/0*b8UJ8wbQhemiYumY.png" /></figure><h3>Why Export Variables Are Needed</h3><p>Export variables bridge the gap between code and design. Instead of opening scripts to adjust things like player speed or enemy health for testing, you can edit them visually in the editor.</p><p><strong>You’ll typically use them for:</strong></p><ul><li>Character attributes (health, speed, jump power)</li><li>Enemy AI settings (vision range, attack delay)</li><li>Visual tuning (colors, scale, offsets)</li><li>Audio or effect configuration</li><li>Linking external resources (textures, scenes, sounds)</li></ul><p>Without exports, tuning gameplay would mean constant code edits and reloads, which slows development dramatically.</p><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre># A variable visible and editable in the editor<br>@export var speed = 200<br>@export var player_name = &quot;Hero&quot;<br># Choose a texture in the editor<br>@export var icon: Texture2D</pre><p><strong>C#:</strong></p><pre>[Export]<br>public float Speed = 200f;<br>[Export]<br>public string PlayerName = &quot;Hero&quot;;<br>[Export]<br>public Texture2D Icon;</pre><h3>Best Practices</h3><ul><li><strong>Use clear names</strong> — make sure exports describe what they control.</li><li><strong>Set sensible defaults</strong> — keep values playable right away.</li><li><strong>Add type hints</strong> (int, float, Color, PackedScene) for editor validation.</li><li><strong>Use ranges and enums</strong> to restrict inputs and avoid mistakes.</li><li><strong>Keep export usage simple</strong> — avoid exporting everything just for convenience.</li></ul><h3>Example</h3><p>The below script exports variables which will be exposed to the Inspector panel for editing. This means whatever value we assign in the inspector panel will be the value assigned to that script — meaning it will overwrite hard-coded values in our script!</p><p><strong>GDScript:</strong></p><pre>extends CharacterBody2D<br><br>@export var speed: float = 200.0<br>@export var jump_force: float = 400.0<br>@export var sprite_texture: Texture2D</pre><p><strong>C#:</strong></p><pre>using Godot;<br><br>public partial class Player : CharacterBody2D<br>{<br>    [Export] public float Speed = 200f;<br>    [Export] public float JumpForce = 400f;<br>    [Export] public Texture2D SpriteTexture;<br>}</pre><h3>4. Groups</h3><p>Groups are a powerful way to organize and manage multiple nodes in Godot. They let you categorize nodes under a shared label (like “Enemies” or “Interactables”) so you can call functions, apply effects, or send signals to all of them at once.</p><p>Think of groups as tags for nodes. You can assign one or more groups to a node and then access them from anywhere in your game.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/685/0*NlFhgXmYJe1WQ1aN.png" /></figure><h3>Why Groups Are Needed</h3><p>Groups are essential when you have multiple instances of similar objects, like enemies, NPCs, or projectiles.</p><p><strong>You’ll typically use them to:</strong></p><ul><li>Apply damage or effects to every enemy.</li><li>Check interactions dynamically — for example, showing an “Interact” prompt when focusing on an interactable group item, or triggering dialog when focusing on an NPC.</li><li>Enable or disable all interactables at once (e.g., pausing player input or freezing AI).</li><li>Trigger multiple lights, doors, or sounds with a single command.</li><li>Broadcast global events, like GameOver, LevelCleared, or Pause</li></ul><p>Without groups, you’d need to manually loop through every node or keep separate arrays, which is messy, slow, and error-prone.</p><h3>Syntax</h3><p>You can assign nodes directly to Groups within the editor, but below is how you can add objects to groups using code.</p><p><strong>GDScript:</strong></p><pre># Add the node to a group<br>add_to_group(&quot;Enemies&quot;)<br><br># Check if the node belongs to a group<br>if is_in_group(&quot;Enemies&quot;):<br>    print(&quot;This node is an enemy!&quot;)<br><br># Remove the node from a group<br>remove_from_group(&quot;Enemies&quot;)</pre><p><strong>C#:</strong></p><pre>// Add the node to a group<br>AddToGroup(&quot;Enemies&quot;);<br><br>// Check if the node belongs to a group<br>if (IsInGroup(&quot;Enemies&quot;))<br>    GD.Print(&quot;This node is an enemy!&quot;);<br><br>// Remove the node from a group<br>RemoveFromGroup(&quot;Enemies&quot;);</pre><h3>Best Practices</h3><ul><li><strong>Use groups to organize related nodes</strong> (e.g., “Enemies”, “Collectibles”, “UI”).</li><li><strong>Add groups via code or the editor</strong> (under the “Node → Groups” tab).</li><li><strong>Avoid too many overlapping groups</strong> — keep naming meaningful.</li><li><strong>Use</strong> is_in_group() to check membership before applying logic.</li><li><strong>Leverage signals and</strong> call_group() for clean global communication.</li></ul><h3>Example</h3><p>Here’s how you can use groups to detect whether the player is focusing on an <strong>interactable object</strong> (like an item, door, or NPC). If the object is in the Interactable group, the player receives a message prompt.</p><p><strong>GDScript:</strong></p><pre># Player.gd<br>extends CharacterBody2D<br><br>func _process(delta):<br>    # Cast a ray forward to detect what the player is looking at<br>    var space_state = get_world_2d().direct_space_state<br>    var result = space_state.intersect_ray(global_position, global_position + Vector2(50, 0))<br>    <br>    # Get the collider of the object<br>    if result and result.has(&quot;collider&quot;):<br>        var target = result.collider<br><br>        # If its in interactable group, show prompt<br>        if target.is_in_group(&quot;Interactable&quot;):<br>            print(&quot;You&#39;re focusing on an interactable. Press [E] to pick up.&quot;)<br>        else:<br>            print(&quot;You&#39;re looking at:&quot;, target.name)</pre><p><strong>C#:</strong></p><pre>// Player.cs<br>using Godot;<br><br>public partial class Player : CharacterBody2D<br>{<br>    public override void _Process(double delta)<br>    {<br>        // Cast a ray forward to detect what the player is looking at<br>        var spaceState = GetWorld2D().DirectSpaceState;<br>        var result = spaceState.IntersectRay(GlobalPosition, GlobalPosition + new Vector2(50, 0));<br>    <br>        // Get the collider of the object<br>        if (result.Count &gt; 0 &amp;&amp; result.ContainsKey(&quot;collider&quot;))<br>        {<br>            var target = result[&quot;collider&quot;] as Node;<br><br>            // If its in interactable group, show prompt<br>            if (target.IsInGroup(&quot;Interactable&quot;))<br>                GD.Print(&quot;You&#39;re focusing on an interactable. Press [E] to pick up.&quot;);<br>            else<br>                GD.Print($&quot;You&#39;re looking at: {target.Name}&quot;);<br>        }<br>    }<br>}</pre><h3>Part VI: Bonus</h3><p>You’ve learned how to build the core gameplay systems of a Godot project. Now it’s time to focus on the <em>finishing touches</em> that make your project clean, stable, and scalable.</p><p>This section covers the tools and habits that keep your code readable, debuggable, and organized — especially as your project grows.</p><p><strong>We’ll go over:</strong></p><ul><li><a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_styleguide.html"><strong>Code Style Tips</strong></a> — write clear, consistent, and maintainable code.</li><li><a href="https://docs.godotengine.org/en/stable/tutorials/scripting/debug/overview_of_debugging_tools.html"><strong>Debugging</strong> </a>— identify and fix issues effectively.</li><li><a href="https://docs.godotengine.org/en/latest/tutorials/scripting/singletons_autoload.html"><strong>Autoloads (Singletons)</strong> </a>— manage global data and cross-scene communication.</li></ul><h3>1. Code Style Tips</h3><p>Clean code isn’t just about getting things to work, it’s about making your future self thank you later.<br>Readable scripts are easier to debug, share, and extend.</p><h3>Why It Matters</h3><p>As projects grow, consistency becomes critical. Having clear naming, structure, and commenting habits keeps your code easy to understand for everyone (including you, months later).</p><h3>Best Practices</h3><ul><li><strong>Use consistent naming:</strong></li><li>GDScript → snake_case for variables and functions.</li><li>C# → camelCase for variables and PascalCase for methods and classes.</li><li><strong>Comment with purpose:</strong> Explain <em>why</em>, not just <em>what</em>.</li><li><strong>Group related code:</strong> Keep setup, logic, and cleanup organized in sections OR code regions.</li><li><strong>Avoid magic numbers:</strong> Use constants or exported variables instead.</li><li><strong>Keep functions short:</strong> Each function should do one clear thing.</li><li><strong>Be descriptive:</strong> player_health is better than hp1.</li></ul><h3>Example (GDScript)</h3><pre># Bad<br>var s = 10<br>func x():<br>    s -= 1<br>    if s == 0: print(&quot;Dead&quot;)<br><br>func phello():<br>    print(&quot;Hello&quot;)</pre><pre># Good<br>#region [Player Health Management]<br>var player_health = 10<br><br>func take_damage(amount):<br>    player_health -= amount<br>    if player_health &lt;= 0:<br>        print(&quot;Player defeated&quot;)<br>        # Trigger game over sequence<br>#endregion<br><br>#region [Debugging]<br>func print_hello():<br>    print(&quot;Hello&quot;)<br>#endregion</pre><h3>2. Debugging</h3><p>Every developer runs into bugs, it’s just inevitable. The key is finding and understanding them quickly. Godot provides a range of built-in tools to help you diagnose problems effectively.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/938/1*zojFOa6DABh6-NB3N2uLBA.png" /></figure><h3>Best Practices</h3><ul><li><strong>Use</strong> print() or GD.Print() to log key values and track execution flow.</li><li><strong>Use</strong> assert(condition) to catch unexpected states early.</li><li><strong>Use the Debugger panel</strong> (bottom dock) to pause on errors and inspect variables.</li><li><strong>Leverage breakpoints</strong> to step through your code line by line.</li><li><strong>Enable “Visible Collision Shapes”</strong> in the Debug menu to check collisions.</li><li><strong>Don’t ignore warnings</strong> — they often point to subtle bugs before they crash your game.</li></ul><h3>Example</h3><p><strong>GDScript:</strong></p><pre>func _physics_process(delta):<br>    assert(player != null, &quot;Player reference missing!&quot;)<br>    if player.health &lt;= 0:<br>        print(&quot;Player is dead&quot;)</pre><p><strong>C#:</strong></p><pre>public override void _PhysicsProcess(double delta)<br>{<br>    GD.Assert(Player != null, &quot;Player reference missing!&quot;);<br>    if (Player.Health &lt;= 0)<br>        GD.Print(&quot;Player is dead&quot;);<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/924/1*7wIwq9T3U2400Ep62oj1DA.png" /></figure><h3>3. Using Autoloads (Singletons)</h3><p>Autoloads (also called Singletons) are special scripts that stay loaded across all scenes. They’re perfect for storing global data, such as player stats, game state, settings, or audio managers.</p><p>Once registered, they’re available from <strong>anywhere</strong> in your game using their name.</p><h3>Why Autoloads Are Needed</h3><p>Autoloads make it easy to share data between scenes without constantly passing references around.</p><p><strong>They’re great for things like:</strong></p><ul><li>Saving and loading game progress.</li><li>Keeping player stats between levels.</li><li>Handling global input or settings.</li><li>Managing background music, pause menus, or transitions.</li></ul><p><strong>They’re not great for:</strong></p><ul><li>Shared or per-instance values, like enemy health or temporary item states. If you store shared data such as enemy health in an autoload, then when one enemy dies, <em>all</em> others will die too.</li><li>Objects that exist multiple times in the world, such as bullets, items, or NPCs.</li><li>Autoloads are global, meaning every scene accesses the same data. Using them for per-object logic can cause synchronization issues or unexpected behavior between instances.</li></ul><h3>Setup</h3><ol><li>Create a new script, e.g. Global.gd.</li><li>Go to <strong>Project → Project Settings → Autoload</strong>.</li><li>Add your script and give it a name (e.g., Global).</li><li>Enable “Singleton.”</li></ol><p>Now you can access it anywhere using that name.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*BGAXMvJrDOnvO177.png" /></figure><h3>Syntax</h3><p><strong>GDScript:</strong></p><pre># GameManager.gd (autoload)<br>extends Node<br><br>var player_health = 100<br>var coins = 0<br><br>func add_coin():<br>    coins += 1</pre><pre># Access the autoload directly from any other script<br>GameManager.add_coin()<br>print(GameManager.coins)</pre><p><strong>C#:</strong></p><pre>// GameManager.cs (autoload)<br>using Godot;<br><br>public partial class GameManager : Node<br>{<br>    public int PlayerHealth = 100;<br>    public int Coins = 0;<br>    public void AddCoin() =&gt; Coins++;<br>}</pre><pre>// Access the autoload directly from any other script<br>GameManager game = (GameManager)GD.Load(&quot;res://GameManager.cs&quot;);<br>game.AddCoin();<br>GD.Print(game.Coins);</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/923/1*125zGyg1d4SXDUBoUozr7A.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=148ebdb60a3e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[I Made A Plugin To Update Godot From Within The Editor]]></title>
            <link>https://christinec-dev.medium.com/i-made-a-plugin-to-update-godot-from-within-the-editor-612662a35baa?source=rss-3d2c08006851------2</link>
            <guid isPermaLink="false">https://medium.com/p/612662a35baa</guid>
            <category><![CDATA[plugins]]></category>
            <category><![CDATA[tools]]></category>
            <category><![CDATA[godot]]></category>
            <dc:creator><![CDATA[Christine Coomans]]></dc:creator>
            <pubDate>Wed, 21 Aug 2024 14:31:12 GMT</pubDate>
            <atom:updated>2024-08-21T14:35:30.661Z</atom:updated>
            <content:encoded><![CDATA[<p>I understand the struggle of not having the Steam version of Godot that auto-updates. If you’re like me, you might have been manually updating Godot via the website. This involves downloading the new stable version, extracting it, and then opening it. This <em>tedious </em>process has caused me to stick with older versions of Godot for much longer than I’d like to admit. As a result, I’m always late to enjoy the latest updates and features — which in the fast-paced world of game dev, is stressful.</p><p>For example, I didn’t download Godot version 4.2 until <strong>three months </strong>after its release because I was too lazy to visit the website and update it. This isn’t the first time I’ve done this. Even now, I haven’t updated my engine to Godot 4.3, which was released a week ago.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/0*dA5M4ZD_09qqpr5Y.gif" /></figure><p>So, how can I fix this issue? Well, as a developer, I believe in working smarter — not harder. Instead of just manually updating by navigating to the website and clicking a button, I decided to invest hours of blood, sweat, and tears to create a plugin to automate this process. Easy, peasy.</p><p>And so my plugin was created. I’d like to share it with you, so you can update Godot within my editor and stay current with the latest version features. Okay, so let’s finally get rid of my ancient version of Godot and upgrade to Godot version 4.3.</p><p>Let’s start by opening up a blank project, and importing and activating my Plugin.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/218/1*m5az577kcPtujaxdKjgVXA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EWDQ9eGkjzEBa9VXnslXFw.png" /></figure><p>If activated and installed correctly, there should be a new workspace available at the top called “Godot Version Updater”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9z_f1CRqeon0usgi4nznXA.png" /></figure><p>Now, this workspace is made out of four parts:</p><ul><li>A <strong>directory </strong>selector (where you want to save your engine);</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*D4kyqgXzMH40ouaMWeTF-w.png" /></figure><ul><li>A <strong>console </strong>(to show you what’s happening);</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ksPbCC1k-R68ny9QhWa2Yg.png" /></figure><ul><li>A panel showing<strong> version information</strong> (current, latest, and an option to enable .NET support);</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Kdcb3J_UT0its_9jnhWmLQ.png" /></figure><ul><li>A <strong>button </strong>to install the latest update.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AqNGfEbjv_9azu2HKyhWNQ.png" /></figure><p>By default, the plugin will download the latest version to your /Downloads folder. If you want to change this directory, click on the “NEW DIRECTORY” button.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0gkzo_RaQKUV-aKBfSXQPA.png" /></figure><p>If you are using Godot and GDScript, leave the “.NET Support Enabled” checkbox unchecked. If you are using Godot and C#, check this button so that it can install the .NET binaries.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DW2fOhi0B9XoRnPUv7Y3cQ.png" /></figure><p>When you’re ready, click the download button. You will see the console update you on the progress it&#39;s made. It might hang a bit on the “Starting download…” output because this is where it is downloading the update — so just be patient.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*F8E145UGI3s-BmisH5eK-g.png" /></figure><p>Once it’s done, it will open the directory where it has installed the update and it will highlight the executable file.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9W3Wgkl-FNKZ7I6NpYAwfw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*I2oA7M9IqdjX1Ys1_IstVw.png" /></figure><p>If you are on Linux/MacOS/Android, you will still have to manually extract the folder and open the executable. If you are on Windows, the plugin will automatically open up your project in the latest version of Godot.</p><p>It might seem that your old version is crashing, but this is just because the project is open in two different versions. Just exit the old version, and it should be fine.</p><p>If everything works, it should open up the latest version, and the “Current Version” value should be the same as the “Latest Version” value!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UnH-C7xChkQeSkAj0g_5Xg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1015/1*q6-yTBZ9-eU8XVCj9cLlHA.png" /></figure><p>As you can see, my Godot has been updated from version 4.2.2 to version 4.3 without me having to manually do it on the website. Now whenever I need to update my project, I will just run my blank project with my plugin installed, and click the button. It will automatically fetch the latest version!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/0*UOTS2UfXAbNpIRhj.gif" /></figure><h3>Conclusion</h3><p>If you want to use my plugin to upgrade your Godot engine or just try it out, you can download it now:</p><ul><li><a href="https://github.com/christinec-dev/GodotVersionUpdater/tree/main/addons/godotversionupdater">GitHub</a></li><li>AssetLib (Awaiting Approval)</li></ul><p>If you find bugs in this plugin, please let me know and I will get to fixing the plugin!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=612662a35baa" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Going from Godot 3 to 4 (The Easy Way)]]></title>
            <link>https://christinec-dev.medium.com/going-from-godot-3-to-4-the-easy-way-485293aa35f7?source=rss-3d2c08006851------2</link>
            <guid isPermaLink="false">https://medium.com/p/485293aa35f7</guid>
            <category><![CDATA[godot]]></category>
            <category><![CDATA[godot-engine]]></category>
            <category><![CDATA[plugins]]></category>
            <category><![CDATA[tooling]]></category>
            <dc:creator><![CDATA[Christine Coomans]]></dc:creator>
            <pubDate>Fri, 09 Aug 2024 10:15:25 GMT</pubDate>
            <atom:updated>2024-08-12T09:36:32.548Z</atom:updated>
            <content:encoded><![CDATA[<p>When I started learning Godot, I started with Godot 3. When Godot 4 came out, a lot of things changed — especially GDScript. To this day, I still type deg2rad instead of deg_to_rad, and nothing annoys me more than getting that error &quot;Function &quot;deg2rad()&quot; not found in base self. Did you mean to use &quot;deg_to_rad()&quot;?&quot;</p><p>You’d think I know better by now, but it’s a habit I can’t break. That’s why I decided to play around with a plugin for Godot to help upgrade your GDScript code from Godot 3 to Godot 4. This plugin automates the process of updating deprecated methods, properties, and syntax, ensuring your projects are compatible with the latest version of Godot.</p><p>All you have to do is download the plugin, add the folder to your project (your project/addons/upgrader/…), activate it, and paste in the code that you want to convert. Easy enough, right? 😅</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mXPooJbM-trGbVaONnI76A.png" /></figure><p>Okay, you might not be convinced that this works, so let’s put it to the test by converting one of the very first 2D projects that I made in Godot 3 to Godot 4 — using only the plugin!</p><p>When I started learning, I came across this YouTube video that showed me how to make a simple farming system. It’s not the best looking and I never even completed the tutorial (wow, another unfinished project), but it made younger me feel like I had the power to make <strong>Stardew Valley 2.0</strong>. 😎</p><p>Let’s start by seeing what happens when I open my project that was made in Godot 3.2 the latest version of Godot.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vaEH8R-B9TMxccuwC9rGtA.png" /></figure><p>The project setup, the warnings, the blurry textures — it’s giving me the heebie-jeebies. My scripts aren’t organized, my nodes are all over the place- and don’t even get me started on my file management skills. All of these are the artifacts left by beginner me, and I am proud to be the archeologist of this expedition. But we’re not here to critique the project, we’re here to see if the plugin works.</p><p>Before we do that, I’m going to upgrade my textures and fix all of the yellow warnings (mostly StaticBody2D nodes requiring collision shapes).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PlbuoXK2IIRQsXmW0QrvSQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/889/1*DQHzgvXDT610B6H4Hso5KA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/943/1*7eC3fH4_RzzBUtpdzQfZgw.png" /></figure><p>Much better! Now let’s see what happens when I try to run my project.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*atlBqeYrkd79rnFKucPoCg.png" /></figure><p>Uh-oh! I get an error. My scenes can’t run because the scripts all contain outdated code. If only there was a quick fix for that…oh wait, the plugin!</p><p>Let’s download the plugin and enable it in the project.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*06QRkb7SxEYtLfPEIHV1Sw.png" /></figure><p>If enabled correctly, there should be a new dock on the left side of the editor. This dock has two parts: a code input and an output panel. We will paste our Godot 3 code in the input panel, and copy our Godot 4 code from the output panel.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/541/1*t3NfT7uTqo_O-hD27BH2Nw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/991/1*_NYXfbe_oGEY-xQfVCvxfA.png" /></figure><p>Let’s start with our first script, which is our Player script. Here is the code:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/702/1*k6_RdS5l_4xA8Wl33P2POQ.png" /></figure><p>As you can see, it uses KinematicBody3D and move_and_slide() with a parameter motion. In Godot 4, move_and_slide() does not call a parameter, and KinematicBody2D becomes CharacterBody2D. Let’s see if the plugin can detect these errors and fix it.</p><p>Paste in old code and press “Execute”:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/497/1*g5Ftuq-n83UHc14cvobU8Q.png" /></figure><p>New code should be upgraded automatically:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/498/1*rF1bBEz3wkV9serw236Uzw.png" /></figure><p>As you can see, it made the fixes automatically. Let’s paste these changes into our script.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/664/1*j1ij84jHtIfcvYoh5JHJag.png" /></figure><p>No more errors! Okay, let’s do another script. The next script is used to “plant” seeds on a plot of land. As you can see below, it uses BUTTON_LEFT when it should be MOUSE_BUTTON_LEFT.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/665/1*L8xl46UHUDTzgA-mLRawKQ.png" /></figure><p>Let’s run it in the upgrader:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*Uy3Ys7YAYJMB1pUjSUNIBw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/685/1*c5Voo0cR2nnEmTOTrz_1RQ.png" /></figure><p>You’ll see it made the fixes and no more errors. Let’s do some more. Below it uses rand_range, when in Godot 4 it would be randf_range (for floats) or randi_range (for ints). The plugin made the conversion.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/684/1*4Mo0hWiP_5qShWNbbuqU8g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/495/1*LEzx-mzyqSW-dh95y7h73g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/846/1*3GHsCA5DEYimNnmSrkX-hw.png" /></figure><p>Great! One last one. The code below uses the old method of reading JSON files. The converter will not fully be able to replace your code, as your layout might differ, but it will give you a guide on how to do it. All you have to do is remove your old code, uncomment the guide, and copy in your file path. It’s easy!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/901/1*npWVI_xDqhpYsQv48uK3CA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/448/1*o4AhrS8aB3d5RMcpMf5ksg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/903/1*gEr5H1XKYLofzz-HODYg3A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/840/1*WCEL_W46pD988-KfvX-AQA.png" /></figure><p>With the code fixes out of the way, let’s run the code to see if it works. As I said, I never finished the tutorial, so all I could do in this project was drag seeds over the land to plant them and then after a few seconds they started to grow. Don’t judge me before you look at all the unfinished projects you might have. 😅</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KcE3t2qsFTGHH-hoiLNmvQ.gif" /></figure><p>With my project working, I can now go back and add the finishing details, and Stardew Valley 2.0 will be live and ready to be published on Steam. Wish me luck! 😄</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ia9V-4QP8deVCZbetI_inA.png" /></figure><h3>Conclusion</h3><p>If you want to use my plugin to upgrade your code or try it out, you can download it now:</p><ul><li><a href="https://github.com/christinec-dev/GDScriptCodeUpgrader">GitHub</a></li><li><a href="https://godotengine.org/asset-library/asset/3217">AssetLib</a></li></ul><p>Please know that I might not have picked up all the changes, as there are a lot, so if you find ones that I haven’t added, you can let me know and I will implement those fixes into the plugin!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=485293aa35f7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Book of Nodes: 2D]]></title>
            <link>https://christinec-dev.medium.com/the-book-of-nodes-2d-16f13691ac5f?source=rss-3d2c08006851------2</link>
            <guid isPermaLink="false">https://medium.com/p/16f13691ac5f</guid>
            <category><![CDATA[node]]></category>
            <category><![CDATA[godot]]></category>
            <category><![CDATA[2d]]></category>
            <category><![CDATA[guid]]></category>
            <dc:creator><![CDATA[Christine Coomans]]></dc:creator>
            <pubDate>Wed, 07 Aug 2024 12:44:25 GMT</pubDate>
            <atom:updated>2024-08-25T12:49:02.634Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Usr2YFR46gWJWm44HzdgOg.png" /></figure><p>Below you can find a list of 2D nodes that can be used in Godot 4. This is part of my <a href="https://christinec-dev.medium.com/the-book-of-nodes-782ef4ba3c8a">Book of Nodes</a> series. If you want to see similar content on 3D or UI nodes, please refer to the parent page of this post for those links. 😊</p><p>Before we begin, if you need a base project to test these code snippets, feel free to download my FREE 2D and 3D templates <a href="https://christinecdevs.site/2d-3d-prototype-templates/">here</a>. I’ll be using these templates throughout this post.</p><p><em>*Please note that this list is not 100% complete yet, but I will be updating this list as time goes on.</em></p><ul><li><strong>AnimatedSprite2D</strong></li><li><strong>AnimationPlayer</strong></li><li><strong>AnimationTree</strong></li><li><strong>Area2D</strong></li><li><strong>AudioStreamPlayer2D</strong></li><li><strong>Camera2D</strong></li><li><strong>CharacterBody2D</strong></li><li><strong>CollisionShape2D</strong></li><li><strong>DirectionalLight2D</strong></li><li><strong>LightOccluder2D</strong></li><li><strong>MeshInstance2D</strong></li><li><strong>NavigationAgent2D, NavigationObstacle2D, NavigationRegion2D</strong></li><li><strong>Node2D</strong></li><li><strong>Path2D, PathFollow2D</strong></li><li><strong>PointLight2D</strong></li><li><strong>RayCast2D</strong></li><li><strong>RigidBody2D</strong></li><li><strong>Sprite2D</strong></li><li><strong>StaticBody2D</strong></li><li><strong>TileMap</strong></li><li><strong>TileMapLayer</strong></li><li><strong>Timer</strong></li></ul><h3>Node2D</h3><p>The Node2Dnode is the fundamental building block for all 2D scenes in Godot. It provides us with basic 2D spatial features like position, rotation, and scale. Almost all 2D nodes (like Sprite2D, Area2D, etc.) inherit from Node2D, which makes it the most essential node for any 2D game development.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MMWVOG8FurF4WXSbfbmB5w.png" /></figure><h4>Mechanic:</h4><p>Move a group of 2D nodes collectively.</p><h4>Implementation:</h4><ul><li>Add a Node2D to your scene to serve as a parent node. This could represent a game object like a vehicle or a character.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/419/1*4jxVEg5bM6mjFndNOmID0w.png" /></figure><ul><li>Add child nodes such as twoSprite2Dnodes. These children can represent visual components, collision areas, etc.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cpNoHXemEF4gEKilAYfA-g.png" /></figure><ul><li>In the Inspector, assign a texture (image file) of your choosing to the texture property of the Sprite2D node. This image will be what is displayed in the scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VlYcx0OJ5TUYhY-J5m7bVQ.png" /></figure><ul><li>Now if we manipulate the Node2D parent, it will affect all its children. For example, moving the Node2D will move all its children, whilst maintaining their relative positions and transformations.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/498/1*-MvjPol_BaGTiFks4UIjIg.gif" /></figure><ul><li>We can also do this via code — say if we press SPACE on our keyboard, the node moves -100 pixels on the x-axis.</li></ul><pre>### Main.gd<br><br>extends Node2D<br><br>@onready var node_2d = $Node2D<br><br>func _process(delta):<br> if Input.is_action_pressed(&quot;ui_select&quot;):<br>  node_2d.position.x -= 100 * delta</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/558/1*hz1CffZfezs8-3hOhrLHbQ.gif" /></figure><h3>Sprite2D</h3><p>The Sprite2Dnode in Godot is used to display 2D images in your scenes. Since it inherits from the Node2Dnode, it can handle various transformations like scaling, rotation, and translation. It’s one of the most commonly used nodes for representing characters, objects, and other visual elements in a 2D space.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LtAikMkrbcVGCHOY1ubTFA.png" /></figure><p>You can use either a singular image as the texture, or a tilesheet.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*IpsVa7TSEQJ1pO9T.png" /></figure><p>If you are using a Tilesheet(as I am in this example) as your texture, you will have to crop out your sprite using the HFramesand VFramesproperty in the Inspector Panel. Then, add a key at each frame. The Frame Coords x property determines which column you are trying to access, and the Frame Coords y property determines which row you are trying to access.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qUmT2fLdhQKJl_h8nX9GCA.png" /></figure><h4>Mechanic:</h4><p>Display and animate a character.</p><h4>Implementation:</h4><ul><li>Create a Sprite2D node in your scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/422/1*ImTdYG5Ujy6fW2XQlT5gtQ.png" /></figure><ul><li>In the Inspector, assign a texture (image file) to the texture property of the Sprite2D node. This image will be what is displayed in the scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VCZ9Y_NG9cOwSxMB9kI6XA.png" /></figure><ul><li>Adjust the position, scale, and rotation properties to position the sprite correctly within your game world.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IUMlUlQihh8D4otbcCYFTQ.png" /></figure><ul><li>If you want to animate the sprite, you can use an AnimationPlayer to animate properties like position, rotation_degrees, and scale. You can also swap out the texture of the Sprite2D if you have a sprite that has multiple animations, for example walking, idling, etc.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*krPxEe36jx3RSAYLQTkhAQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oiM2jo0S71Rf642S_Pth3g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Nfe0qWW7RwrLUBVkNJzkMw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/564/1*yDAIU03TcwCzpuVq2aBI4Q.gif" /></figure><ul><li>Add the code to play the animation on load.</li></ul><pre>### Main.gd<br><br>extends Node2D<br><br>@onready var animation_player = $AnimationPlayer<br><br>func _ready():<br> animation_player.play(&quot;move_character&quot;)</pre><ul><li>Run your project and your Sprite2D node should be in your scene. If you added an animation, it should play.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/502/1*_8j1VDyNvCTFBhdmKZgx1w.gif" /></figure><h3>AnimatedSprite2D</h3><p>The AnimatedSprite2D node utilizes a series of images (sprites) and displays them in a sequence to create an animation. Unlike a simple Sprite2D node that displays a single static image, AnimatedSprite2D can cycle through multiple frames to animate characters, objects, or effects within your 2D game. To create these frames, we can use either sprites or a spritesheet.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*IpsVa7TSEQJ1pO9T.png" /></figure><p>The AnimatedSprite2D node utilized the SpriteFrames Resource to create animations. This is a special resource in Godot that holds collections of images. Each collection can be configured as an animation by specifying the images (frames) that belong to it. You can create multiple animations within a single SpriteFrames resource, each with its own set of frames and playback properties like speed and loop settings.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*2unPwBL5wpitA2KG.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*2t3UNo7uKP4rl7X2.png" /></figure><h4>Mechanic:</h4><p>Create a character with walking animations.</p><h4>Implementation:</h4><ul><li>Create an AnimatedSprite2D node in your scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/420/0*WQkpkgNq-QMF4Zwx.png" /></figure><ul><li>Assign a SpriteFrames resource to the AnimatedSprite2D.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/464/0*iZtnN9Wzpz4kwCto.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/463/0*05xx1cS1pAQ9xqD5.png" /></figure><ul><li>Add a new animation by clicking on the page+ icon.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/258/0*rHjUExFn29eA6sJ0.png" /></figure><ul><li>Rename this animation by double-clicking on it.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1015/0*7jIxwXStyGf1irv7.png" /></figure><ul><li>Either drag in sprites into the frames box or click the spritesheet icon to add animations via an atlas.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1003/0*y_k53LDhpemmP4R5.png" /></figure><ul><li>Crop out the frames horizontally and vertically.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*mXKCCWfTPCVv17Ai.png" /></figure><ul><li>Select the frames you want. For instance, in my person atlas, I will choose frames 0–6 in row 5.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*7bZNW3kRxecM7Xn9.png" /></figure><ul><li>Then play the animation to see if you need to alter the FPS to make the character move faster/slower:</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1010/0*hNCT7XDMjFdGEs8H.gif" /></figure><ul><li>Repeat the process for all of your animations, for example walk_left, walk_up, walk_down, walk_up, idle_x, run_x, etc.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1020/0*Kit_I54AoAfcG3Q2.png" /></figure><ul><li>Play the animation in your code so that when your player moves during runtime the animation can play:</li></ul><pre>### Player.gd<br><br>extends CharacterBody2D<br><br># Scene-Tree Node references<br>@onready var animated_sprite = $AnimatedSprite2D<br><br># Variables<br>@export var speed = 100<br><br># Input for movement<br>func get_input():<br> var input_direction = Input.get_vector(&quot;ui_left&quot;, &quot;ui_right&quot;, &quot;ui_up&quot;, &quot;ui_down&quot;)<br> velocity = input_direction * speed<br><br># Movement &amp; Animation<br>func _physics_process(delta):<br> get_input()<br> move_and_slide()<br> update_animation()<br> <br># Animation<br>func update_animation():<br> if velocity == Vector2.ZERO:<br>  animated_sprite.play(&quot;idle&quot;)<br> else:<br>  if abs(velocity.x) &gt; abs(velocity.y):<br>   if velocity.x &gt; 0:<br>    animated_sprite.play(&quot;walk_right&quot;)<br>   else:<br>    animated_sprite.play(&quot;walk_left&quot;)<br>  else:<br>   if velocity.y &gt; 0:<br>    animated_sprite.play(&quot;walk_down&quot;)  <br>   else:<br>    animated_sprite.play(&quot;walk_up&quot;)</pre><ul><li>Run your project and move around:</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/398/1*sahPpQfYULa5NqtKVVj8CQ.gif" /></figure><h3>AnimationPlayer</h3><p>Unlike the AnimatedSprite2D which is specifically designed for sprite animations, the AnimationPlayer can animate virtually ANY node within a Godot scene. Instead of animating a simple sprite, you can animate the node’s properties — including but not limited to positions, rotations, scales, colors, and even variables.</p><p>The AnimationPlayer can hold a set of animations on a singular timeline, each containing keyframes that define the start and end points of any property that changes over time. You can create complex sequences and control animations in a non-linear fashion.</p><p>This node can be used to animate 2D, 3D, and even UI nodes!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bn2akzwuRF3evvn2bCCZgQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vD9XpLTs_iyaRZ52jPB0rA.gif" /></figure><h4>Mechanic:</h4><p>Animate a Sprite2D node of a potion that pulses in size to capture player&#39;s attention.</p><h4>Implementation:</h4><ul><li>Add a Sprite2D node and an AnimationPlayer node to your scene. The Sprite2D node is the node we want to animate, and the property we want to animate of this node is its scale.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/421/1*x2XP0MOh0vfn89ZaHAJNsA.png" /></figure><ul><li>Assign a sprite to the Sprite2D node. I’ll assign a potion.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/477/1*tO6GbWVmiOSRtC76-YBWZQ.png" /></figure><ul><li>Select the AnimationPlayer node.</li><li>In the animation panel, click “New Animation” and name it something descriptive like “pulse”.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/341/1*OjbyCQuO6-GGtv9SvwgXaQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/986/1*3JIgLsrhgB9wtOyrr-0fKg.png" /></figure><ul><li>Set the animation length to the duration you want for one pulse cycle (e.g., 1 second).</li><li>Enable the “Loop” option to make the animation repeat continuously.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1002/1*b6OuXK71qm2AfC9zFClWUw.png" /></figure><ul><li>Go to the beginning of the animation timeline (0 seconds).</li><li>Select the Sprite2D node, and in the Inspector, set the scale property to its initial value (e.g., Vector2(1, 1)).</li><li>Right-click the scale property in the Inspector and select &quot;Key&quot; to add a keyframe.</li><li>Move to the middle of the timeline (e.g., 0.5 seconds), change the scale to a larger value (e.g., Vector2(1.2, 1.2)), and add another keyframe.</li><li>At the end of the timeline (1 second), set the scale back to the initial value (Vector2(1, 1)) and add a final keyframe.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/473/1*AzPAk1QzC3FxeFZiN_jF6A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TFpV4IW0XrYAaejRXrQ2Iw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rI_09OsPWCsO_a7md0QeSQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cjTEmx_nUZ6uLG-DA8oX8w.png" /></figure><ul><li>You can control when the animation starts or stops via script, or let it run continuously since it’s set to loop.</li></ul><pre>### Main.gd<br><br>@onready var animation_player = $AnimationPlayer<br><br>func _ready():<br>    animation_player.play(&quot;pulse&quot;)</pre><ul><li>Start your project and observe the potion sprite pulsing in size.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/690/1*7fIETboY9qU7aeCj99UpaQ.gif" /></figure><h3>MeshInstance2D</h3><p>The MeshInstance2D node is used for displaying a Meshin a 2D space. In Godot, a mesh is used as a resource that can be applied to MeshInstance nodes to render geometry in a scene.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fjhrswDkoGOPGVG4Ylw5Qw.png" /></figure><p>It can be particularly useful for achieving effects or visual styles that are difficult with standard 2D sprites or animations, such as deformations or complex shading that reacts to lighting conditions.</p><h4>Mechanic:</h4><p>Dynamically deform a 2D mesh (Sprite2D conversion).</p><h4>Implementation:</h4><ul><li>Create a Sprite2D node in your scene. Assign it with a texture of your choice.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sa2_ZjYueaPRRS7PK_66oA.png" /></figure><ul><li>Use the editor’s conversion tool to convert it to MeshInstance2D node.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/646/1*cKvtPxKJ4F2HivsRKmpAwg.png" /></figure><ul><li>A dialog will appear, showing a preview of how the 2D mesh will be created. The yellow lines are the mesh polygons — and this will make up your 2D mesh shape. The default values are fine.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/904/1*bX3vm-DcYh7dAFacW6N0jA.png" /></figure><ul><li>The Sprite2Dnode should be converted into a MeshInstance2Dnode.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HoYxHyMzRMsyWRKnwkLIfw.png" /></figure><ul><li>Use scripts to deform the mesh dynamically.</li></ul><pre>### Main.gd<br><br>extends Node2D<br><br>@onready var sprite_2d = $Sprite2D<br><br>func _process(delta):<br>    var scale_factor = sin(Time.get_ticks_msec() / 1000.0) * 0.1 + 1.0<br>    sprite_2d.scale = Vector2(scale_factor, scale_factor)</pre><ul><li>Run the scene to observe the 2D mesh dynamically deform itself!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/546/1*0XHMDo3957FlPnilyQNtKw.gif" /></figure><h3>AnimationTree</h3><p>The AnimationTree node enhances the capabilities of the AnimationPlayer by providing advanced features for animations, such as blending, transitions, and states. This makes it extremely easy to make detailed character animations and interactive scene elements in 2D and 3D environments.</p><p>We usually use blending to create smooth transitions between animations, for example, smoothly transitioning between walking and running depending on the player’s speed.</p><p>We use state machines to switch our animations dynamically depending on the conditions, for example, switching between idle and attack animations if the player presses a key.</p><h4>Mechanic:</h4><p>Animate a 2D character with multiple actions (e.g., walking, idle).</p><h4>Implementation:</h4><ul><li>Add an AnimationPlayernode to your scene and create animations for it like &quot;walk_x”, &quot;idle&quot;. You’ll need to create these animations from a Sprite2Dnode, or else it won’t work.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*igN-0hrXGEXkYhF51Sbcfg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qDPPLHvyeXtmMgttFG0TIg.png" /></figure><ul><li>Now, add an AnimationTree node, linking it to the AnimationPlayer.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/421/1*Ipw2-cw07BU7Dbb8_u16zQ.png" /></figure><ul><li>Configure the tree_root as an AnimationNodeStateMachine for managing states.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/557/1*V8_slCifFsYtLJLDHLe7tg.png" /></figure><ul><li>Also assign the AnimationPlayeras the Anim Player property because this is where our AnimationTree will call the animations from, and the Advanced Expression as the root node because this is where our animation coding can be found (our script).</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/590/1*k5VAHSkZ5xpgAd68PCnFFg.png" /></figure><ul><li>If you open your AnimationTree, you will see if you right-click you can add animations, blend trees, and state machines. Add your ‘idle’ animation and a BlendSpace2Dso that we can play our walk animations depending on our player&#39;s Vector2() coordinates. Rename the BlendSpace2D to ‘walk’.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/952/1*QD10LziT4q0XjJFWv7nPzg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2CJZW0s9cAzghyK61WitfA.png" /></figure><ul><li>Add a transition between start -&gt; idle. The transition type should be immediatebecause we want the animation to play immediately.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/398/1*80PT-jFDwI0lkmFYvyCzzQ.png" /></figure><ul><li>Click the pencil icon to edit your walk BlendSpace2D. Then add a point with an animation at each coordinate. Up (0, -1). Down (0, 1). Left (-1, 0). Right (1, 0). Idle (0,0). Also change the blend mode to “Discrete”.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xTqzV_T75bHcPiNXnl4GCg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*w870DfaxsTat_Mv4yax25g.png" /></figure><ul><li>Add transitions between idle -&gt; walk and vice versa. The transition type for both should be syncbecause we want to blend the animation. Also set the mode to enabledbecause we will activate this animation via the code, and not automatically.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/505/1*86YunkK0y_YPbxUM5oOY2A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AD-S5R_wPsU03W-U7rhGSA.png" /></figure><ul><li>Now in our code, we can play our animations based on our input.</li></ul><pre>### Player.gd<br><br>extends CharacterBody2D<br><br># Scene-Tree Node references<br>@onready var animation_tree = $AnimationTree<br>@onready var animation_state = animation_tree.get(&quot;parameters/playback&quot;)<br><br># Variables<br>@export var speed = 100<br><br># Input for movement<br>func get_input():<br> var input_direction = Input.get_vector(&quot;ui_left&quot;, &quot;ui_right&quot;, &quot;ui_up&quot;, &quot;ui_down&quot;)<br> velocity = input_direction * speed<br><br># Movement &amp; Animation<br>func _physics_process(delta):<br> get_input()<br> move_and_slide()<br> update_animation()<br><br># Animation<br>func update_animation():<br> var blend_position = Vector2.ZERO<br> if velocity == Vector2.ZERO:<br>  animation_state.travel(&quot;idle&quot;)<br> else:<br>  blend_position = velocity.normalized()<br>  animation_state.travel(&quot;walk&quot;)<br> <br> animation_tree.set(&quot;parameters/walk/blend_position&quot;, blend_position)</pre><ul><li>Run the scene and control the character to observe the transitions and movement based on our state.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/756/1*1pP65F8ZEYzGfLp4SyMCvw.gif" /></figure><h3>CollisionShape2D</h3><p>The CollisionShape2D node allows you to specify the boundaries of an object for collision detection, which is essential for handling interactions between objects in your game.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*U_iJ3Nc8inSCD6v2XhHACg.png" /></figure><h4>Mechanic:</h4><p>Add a collision area to block the character from passing.</p><h4>Implementation:</h4><ul><li>Create a CollisionShape2D node as a child of a CharacterBody2D, RigidBody2D, orStaticBody2D. These nodes will block other collisions. To have a node pass through collisions, use an Area2D.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/385/1*vw2Al71Zzlsy6422ksIufg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/401/1*JNrbUmFsQB7vFEqASDDFfw.png" /></figure><ul><li>In the Inspector, assign a Shape2D resource to the shape property of the CollisionShape2D. The shape you choose will depend on the shape of your entity. For example, a player might have a capsule shape, a pickup a circle, an area a box.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cW1e66FnDXtnd6y6EzyxIA.png" /></figure><ul><li>Let’s enable debugging to see our collisions in action.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/419/1*NOSd3fnTzxguxb6hyRIROA.png" /></figure><ul><li>Run your scene to see how your player interacts with the collision shape. Since we used a StaticBody2D node, they should be blocked and unallowed to go through the collision.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/566/1*fCHd4fNg_r4dIt5mdSfeKg.gif" /></figure><h3>CharacterBody2D</h3><p>The CharacterBody2D node is a specialized class for physics bodies that are meant to be controlled or moved around by the user. Unlike other physics bodies such as the RigidBody2Dor StaticBody2Dnode, CharacterBody2Dis not affected by the engine’s physics properties like gravity or friction by default. Instead, you have to write code to control its behavior, giving you precise control over how it moves and reacts to collisions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*i60wGCTzpVD5YDitEOvdmg.png" /></figure><h4>Mechanic:</h4><p>Move a character with arrow keys, including handling gravity and jumping.</p><h4>Implementation:</h4><ul><li>Add a CharacterBody2D node to your scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/402/1*6RFwLWh2DIJesLAAgCRiDA.png" /></figure><ul><li>You’ll see it has a warning icon next to it. This is because it needs a collision shape to be able to interact with the world. Add a CollisionShape2D as a child of the CharacterBody2D and set its shape to match your character.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bqgcJ3qjr3zdkgIRfysXGw.png" /></figure><ul><li>Add a Sprite2D node to this scene so that we can see our character.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7gGgVMPYVgLvct4b6KNvwA.png" /></figure><ul><li>Attach a script to the CharacterBody2D to handle movement and jumping.</li></ul><pre>### Player.gd<br><br>extends CharacterBody2D<br><br># Variables<br>@export var speed = 200<br>@export var jump_force = -400<br>@export var gravity = 800<br><br># Input for movement<br>func get_input():<br> velocity.x = 0<br> if Input.is_action_pressed(&quot;ui_right&quot;):<br>  velocity.x += speed<br> if Input.is_action_pressed(&quot;ui_left&quot;):<br>  velocity.x -= speed<br> if Input.is_action_pressed(&quot;ui_down&quot;):<br>  velocity.y -= jump_force<br> if is_on_floor() and Input.is_action_just_pressed(&quot;ui_up&quot;):<br>  velocity.y = jump_force<br><br># Movement &amp; Gravity<br>func _physics_process(delta):<br> get_input()<br> velocity.y += gravity * delta<br> move_and_slide()</pre><ul><li>Run the scene and use the arrow keys to move the character and make it jump.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/370/1*FTlWou62Qhm1I1zjEeROzA.gif" /></figure><h3>StaticBody2D</h3><p>The StaticBody2D node is used to represent objects that do not move. This node is ideal for creating static elements in your game, such as walls, floors, and other immovable objects such as chests.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*i60wGCTzpVD5YDitEOvdmg.png" /></figure><h4>Mechanic:</h4><p>Create an obstacle.</p><h4>Implementation:</h4><ul><li>Create a StaticBody2D node in your scene. Add a CollisionShape2Das a child of the StaticBody and set its shape to match the obstacle.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ei2nqITVwaLWigbe4irdUQ.png" /></figure><ul><li>Give it a Sprite2D of your choice so that we can see the item.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3zRfbwB8ECfSy5qvwb1XRQ.png" /></figure><ul><li>Run your scene to see how your player interacts with the collision shape. They should be blocked and unallowed to go through the obstacle.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/424/1*1bg58y6A7EDXLh9wn0zScg.gif" /></figure><h3>RigidBody2D</h3><p>The RigidBody2D node is used for objects that are affected by the engine’s physics. These bodies can move, rotate, and respond to forces and collisions. They are ideal for creating dynamic objects that need realistic physics interactions, such as balls, bullets, moveable obstacles, etc.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*i60wGCTzpVD5YDitEOvdmg.png" /></figure><h4>Mechanic:</h4><p>Create a moveable obstacle.</p><h4>Implementation:</h4><ul><li>Create a RigidBody2D node in your scene. Add a CollisionShape2Das a child of the RigidBody and set its shape to match the obstacle.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4O1rQmPgeHKh7KInlDfgQA.png" /></figure><ul><li>Give it a Sprite2D of your choice so that we can see the item.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0lqBEXa_F2Uq6PPJLY3Jbw.png" /></figure><ul><li>Since we want to move this item when our player collides with it, we should disable its gravity so that it doesn’t get pulled downwards (unless you are making a 2D platformer).</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/555/1*DqNwMQC4oSQRhVxOrbYNFQ.png" /></figure><ul><li>Use GDScript to apply forces or impulses to the rigid body if the player pushes it.</li></ul><pre>### Player.gd<br><br>extends CharacterBody2D<br><br># Scene-Tree Node references<br>@onready var animation_tree = $AnimationTree<br>@onready var animation_state = animation_tree.get(&quot;parameters/playback&quot;)<br><br># Variables<br>@export var speed = 100<br>@export var push_force = 80.0<br><br># Input for movement<br>func get_input():<br> var input_direction = Input.get_vector(&quot;ui_left&quot;, &quot;ui_right&quot;, &quot;ui_up&quot;, &quot;ui_down&quot;)<br> velocity = input_direction * speed<br><br># Movement &amp; Animation<br>func _physics_process(delta):<br> get_input()<br> move_and_slide()<br> update_animation()<br> handle_collisions()<br><br># Animation<br>func update_animation():<br> var blend_position = Vector2.ZERO<br> if velocity == Vector2.ZERO:<br>  animation_state.travel(&quot;idle&quot;)<br> else:<br>  blend_position = velocity.normalized()<br>  animation_state.travel(&quot;walk&quot;)<br> <br> animation_tree.set(&quot;parameters/walk/blend_position&quot;, blend_position)<br><br># Handle Collisions<br>func handle_collisions():<br> for i in range(get_slide_collision_count()):<br>  var collision = get_slide_collision(i)<br>  if collision.get_collider() is RigidBody2D:<br>   var collider = collision.get_collider() as RigidBody2D<br>   var impulse = -collision.get_normal() * push_force<br>   collider.apply_central_impulse(impulse)</pre><ul><li>Run the scene and observe how the obstacle moves when the player pushes against it.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/586/1*MF-g1Oz5-Mr-a10A0T46Gw.gif" /></figure><h3>Area2D</h3><p>The Area2D node is used to detect when objects enter or exit a defined area. They do not represent physical bodies but are useful for triggering events such as cutscenes or map transitions, detecting overlaps, and creating zones for things such as enemy or loot spawning.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8LV6cwLaG3BA6bFWXZ11rg.png" /></figure><p>We can use the Area2D node’s on_body_entered() and on_body_exited() signals to determine whether or not a PhysicsBody has entered this zone.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mBQh-XhpM3ACQKsKQ1d1uw.png" /></figure><h4>Mechanic:</h4><p>Create a trigger zone that detects when the player enters a specific area.</p><h4>Implementation:</h4><ul><li>Create an Area2D node in your scene. You also need to add a CollisionShape2D as a child of the Area and set its shape to define the trigger zone. Adjust the collision shape&#39;s properties to fit the dimensions of your trigger zone.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/406/1*MElhKLCXRlw5KFruBVB1cQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*y1zyKyiGbNmcdjSIAUW63g.png" /></figure><ul><li>Attach the Area2D node’s on_body_entered() and on_body_exited() signals to your script.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1016/1*FR70wn_hjSsS9h_WMsSnfQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1014/1*bF11nyJqPXcOjCGp7LyNtw.png" /></figure><ul><li>Use GDScript to notify us when the Player enters or exits the area.</li></ul><pre>### Main.gd<br><br>extends Node2D<br><br>func _on_area_2d_body_entered(body):<br> if body.name == &quot;Player&quot;:<br>  print(&quot;The player has entered the area!&quot;)<br><br>func _on_area_2d_body_exited(body):<br> if body.name == &quot;Player&quot;:<br>  print(&quot;The player has exited the area!&quot;)</pre><ul><li>Enable debugging so we can see when our Player enters/exits our area.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/404/1*5tODHdR-eC7PlIYqPPQYTQ.png" /></figure><ul><li>Run the scene and observe how the area detects when the Player enters or exits the defined zone. Each time the player enters/exits the zone, the game should be notified.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/494/1*uGIGwCuxcaYO7Hy6xQknfA.gif" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/948/1*JlyriH1WKNJGJQEVFHGTog.png" /></figure><h3>RayCast2D</h3><p>The RayCast2D node is used to cast a ray in a 2D space to detect objects along its path. This is useful for various purposes such as line-of-sight checks, shooting and attacking mechanics, and collision detection.</p><p>It can collide with <strong>bodies </strong>such as StaticBody2D (useful for detecting loot and quest items), CharacterBody2D (useful for detecting interactions with enemies and NPCs), and RigidBody2D (useful for detecting interactions with moveable objects. It can also collide with <strong>areas</strong>, such as Area2D (useful for interactions with trigger zones).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NqUgtKtaqQAca45ezlkefw.png" /></figure><h4>Mechanic:</h4><p>Cast a ray from the player and detect what it hits.</p><h4>Implementation:</h4><ul><li>Add a RayCast2D node to the Player in your scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*U4lt9Bj4b_U9b7mRP9dQGQ.png" /></figure><ul><li>Set the target_position property to define the direction and length of the ray. I usually leave mine at its default values. You can also enable its collision with areas.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Rt5rhlU25YxvBX_9_uLTEQ.png" /></figure><ul><li>In the code, let’s update our raycast to always face the player’s last direction. We will also print the colliders it’s hitting.</li></ul><pre>### Player.gd<br><br>extends CharacterBody2D<br><br># Scene-Tree Node references<br>@onready var animation_tree = $AnimationTree<br>@onready var animation_state = animation_tree.get(&quot;parameters/playback&quot;)<br><br># Variables<br>@export var speed = 100<br>var direction = Vector2()<br>@onready var ray_cast_2d = $RayCast2D<br><br># Input for movement<br>func get_input():<br> var input_direction = Input.get_vector(&quot;ui_left&quot;, &quot;ui_right&quot;, &quot;ui_up&quot;, &quot;ui_down&quot;)<br> direction = input_direction<br> velocity = input_direction * speed<br><br># Raycast hit detection<br>func _process(delta):<br> if ray_cast_2d.is_colliding():<br>  var collider = ray_cast_2d.get_collider()<br>  print(&quot;Raycast hit: &quot;, collider.name)<br><br># Movement &amp; Animation<br>func _physics_process(delta):<br> get_input()<br> move_and_slide()<br> update_animation()<br> <br> # Update raycast to face player direction<br> if direction != Vector2.ZERO:<br>  ray_cast_2d.target_position = direction.normalized() * 50<br><br># Animation<br>func update_animation():<br> var blend_position = Vector2.ZERO<br> if velocity == Vector2.ZERO:<br>  animation_state.travel(&quot;idle&quot;)<br> else:<br>  blend_position = velocity.normalized()<br>  animation_state.travel(&quot;walk&quot;)<br> <br> animation_tree.set(&quot;parameters/walk/blend_position&quot;, blend_position)</pre><ul><li>Run your scene and interact with objects that have colliders. The raycast should detect the objects and notify the game.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/998/1*JWKbO_ejNMsrbTVFvAv0eQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/913/1*E3b-JUguP3IYLQAuA-uR5w.png" /></figure><h3>Camera2D</h3><p>The Camera2D node is used to control the view of a 2D scene. It allows the screen to follow the player or other objects. Only one Camera can be active per viewport, and it registers itself in the nearest Viewport node.</p><p>In 2D games, we usually attach the Camera2D node to either the Player or our World scene. Attach it to the Player if you want to follow the player around. Attach the Camera to your World (Main) scene if you want a birds-eye view of the environment. This usually requires a bit more configuration and coding, as you have to make the camera able to move, rotate, scroll, or zoom around the scene.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mrmlBStYpV3qnvCzvOmL1Q.png" /></figure><h4>Mechanic:</h4><p>Create a “God Mode” 2D camera that can move, zoom, and rotate based on user input.</p><h4>Implementation:</h4><ul><li>Add a Camera2D node to your Main (World) scene. Make sure this camera is enabled, and all other cameras are disabled.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/389/1*2RC7EwG8tY2XJ3afdcsnQg.png" /></figure><ul><li>Add the inputs to zoom and rotate your camera. The default up, down, left, and right inputs should be fine to move the camera.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*saGwiOeq9TKlltWzD-zDGA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xF7MnBzFGx3zB_RmgAqP5g.png" /></figure><ul><li>Use GDScript to handle the camera’s zoom, movement, and rotation. You can do this in a custom Camera.gd script (preferred), or directly in your root script.</li></ul><pre>### Main.gd<br><br>extends Node2D<br><br>@onready var camera_2d = $Camera2D<br>@export var zoom_speed = 0.5<br>@export var move_speed = 200<br>@export var rotate_speed = 0.5<br>@export var min_zoom = 0.5<br>@export var max_zoom = 2.0<br><br>func _process(delta):<br> handle_zoom(delta)<br> handle_movement(delta)<br> handle_rotation(delta)<br><br># Zooming<br>func handle_zoom(delta):<br> if Input.is_action_pressed(&quot;zoom_out&quot;):<br>  camera_2d.zoom -= Vector2(zoom_speed, zoom_speed) * delta<br> if Input.is_action_pressed(&quot;zoom_in&quot;):<br>  camera_2d.zoom += Vector2(zoom_speed, zoom_speed) * delta<br> camera_2d.zoom.x = clamp(camera_2d.zoom.x, min_zoom, max_zoom)<br> camera_2d.zoom.y = clamp(camera_2d.zoom.y, min_zoom, max_zoom)<br><br># Moving<br>func handle_movement(delta):<br> var direction = Vector2.ZERO<br> if Input.is_action_pressed(&quot;ui_right&quot;):<br>  direction.x += 1<br> if Input.is_action_pressed(&quot;ui_left&quot;):<br>  direction.x -= 1<br> if Input.is_action_pressed(&quot;ui_down&quot;):<br>  direction.y += 1<br> if Input.is_action_pressed(&quot;ui_up&quot;):<br>  direction.y -= 1<br> camera_2d.position += direction.normalized() * move_speed * delta<br><br># Rotating<br>func handle_rotation(delta):<br> if Input.is_action_pressed(&quot;rotate_left&quot;):<br>  camera_2d.rotation -= rotate_speed * delta<br> if Input.is_action_pressed(&quot;rotate_right&quot;):<br>  camera_2d.rotation += rotate_speed * delta</pre><ul><li>Run the scene and use the defined input actions to move, zoom, and rotate the camera.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/492/1*Mr5YZXt1MclwHGKNxzB02A.gif" /></figure><h3>DirectionalLight2D</h3><p>The DirectionalLight2D node is used to simulate sunlight or moonlight. It emits light in a specific direction, affecting all objects in the scene equally, regardless of their distance from the light source. This type of light is useful for outdoor scenes where you need consistent lighting across the entire scene.</p><p>This node’s two main properties are the energy and color properties. The energy property determines how bright/dim the light is, and the color is the shading of the light.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Sen7JrZNvyYuo_XeybakIQ.png" /></figure><p>By default, I prefer <strong>NOT </strong>to use this node without shaders because its features are a bit unfinished (and the shadows are not good). If you’d like an introductory tutorial on how to use this node with shaders ( by making a day and night cycle), please check out my <a href="https://github.com/christinec-dev/DayNightCycleGodot">YouTube video</a>.</p><h4>Mechanic:</h4><p>Illuminate a scene with sunlight.</p><h4>Implementation:</h4><ol><li>Create a DirectionalLight2D node in your scene.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4SwH5VjNWh_heRYLsZr_IA.png" /></figure><ul><li>As you can see, this is crazy bright. Let’s adjust our properties like energy andcolorto customize the light&#39;s appearance and behavior.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/599/1*ho7LjzOpK1wUkteb9SIQ_g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Zei6xPdVIC-qjwWvzgkFjQ.png" /></figure><ul><li>Now let’s do something fun. I recommend using shaders with this node, but just for testing sake, let’s have it randomize its color each second.</li><li>Add a Timer node to your scene. In the Inspector Panel, enable autostart, and connect its timeout signal to your script</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/401/1*SLoxAf2p9IS5QhSc_G2Zbg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/576/1*TeUoA8w2u5Avz4nCLpq8aQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sluJUdWxroNnyMMSMh3psw.png" /></figure><ul><li>In your code, let’s randomize our light’s color every time the timer times out (every second).</li></ul><pre>### Main.gd<br><br>extends Node2D<br><br>@onready var directional_light_2d = $DirectionalLight2D<br><br>func _on_timer_timeout():<br> directional_light_2d.color = Color(randf(), randf(), randf()</pre><ul><li>Run your scene and enjoy the overstimulation.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/744/1*ab-ghjK0vwX38r7NlGLz8g.gif" /></figure><h3>PointLight2D</h3><p>The PointLight2D node emits light in all directions from a single point. This type of light is useful for creating focused lighting effects, such as flashlights, lamps, or fires.</p><p>This node’s two main properties are the energy , color, texture scale, and textureproperties. The energy property determines how bright/dim the light is. The color is the shading of the light. The textureproperty allows us to give our light a shape. The texture scale property determines the size of our light.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Sen7JrZNvyYuo_XeybakIQ.png" /></figure><h4>Mechanic:</h4><p>Create a flickering torch.</p><h4>Implementation:</h4><ul><li>In your scene, add a PointLight2D node. We’ll need to give it a shape, so download <a href="https://opengameart.org/content/flashlight-pattern-incandescent">this</a> texture, and drag it into your texture property.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FiZc6ArlRaxmevW1z2yrlw.png" /></figure><ul><li>Play around with the energy, color, andtexture scalevalues.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rsjpxyDC3LqL13YNqGI3lg.png" /></figure><ul><li>Just for fun, let’s give it a flicker effect. We’ll do this via an AnimationPlayer node.</li><li>Create a new animation in the AnimationPlayer.</li><li>Add a track for the energy property of the PointLight2D.</li><li>Add keyframes to the energy track to simulate flickering.</li><li>Enable looping, and change the blend mode to discrete.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/411/1*NBaODpcdb9Oln4jng2L8IQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/898/1*wlllqgb0xLjPrkV-o2XJUw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PIaS1p0e2T3Ym77tcE6CRQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*U6IOwPK4JqZF7hA2QqstWQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tvbaz9YCIxm1hzVOj4kLkw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/914/1*nYwmUr6mKA6zRTrQfQg_fw.png" /></figure><ul><li>Then play this animation via the code when the game loads.</li></ul><pre>### Main.gd<br><br>extends Node2D<br><br>@onready var animation_player = $AnimationPlayer<br><br>func _ready():<br> animation_player.play(&quot;flicker&quot;)</pre><ul><li>Run the scene and observe the player’s color changes when they go into the flickering lights.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/636/1*rejV_mnlCSCW8fD0_lJgCQ.gif" /></figure><h3>LightOccluder2D</h3><p>The LightOccluder2D node is used to cast shadows from a light source that hits it. This light source can come from a DirectionalLight2D or PointLight2D. It requires an OccluderPolygon2D to define the shape of the occlusion.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Sen7JrZNvyYuo_XeybakIQ.png" /></figure><h4>Mechanic:</h4><p>Cast a shadow from our player.</p><h4>Implementation:</h4><ul><li>Create a LightOccluder2D node to the source you want to cast your shadows from.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/874/1*w0QcJvpXBv5CkI_23hQdHg.png" /></figure><ul><li>You’ll see it has an error. This is because we need to draw a OccluderPolygon2D shape to define the shape of our shadow. Give your LightOccluder2D a new OccluderPolygon2D resource, and draw the polygon around your player. Make sure that you <strong>complete </strong>your polygon by connecting all of your points.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/580/1*WxybCnJuoAAnK17QSnJV5A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wmKh-ER7cz1cKGbEvdG7uQ.png" /></figure><ul><li>Now in our Main scene, we will need a light source to cast this shadow shape. Let’s add a PointLight2D node and put it above our player. Make sure its shadows are enabled so that it can cast this shadow.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WSYXqhnI2JAUlLkNsUMpFw.png" /></figure><ul><li>Run your scene and watch the shadow move depending on where the light hits the occluder.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/442/1*DobKUc5hrIC5RRcr-0vpvA.gif" /></figure><h3>AudioStreamPlayer2D and AudioStreamPlayer</h3><p>These nodes are used to play audio in our games. We use the AudioStreamPlayer to play audio equally across our scene (such as background music or ambient sounds), and the AudioStreamPlayer2D to play audio positionally (such as from our players or NPCs).</p><h4>Mechanic:</h4><p>Play ambient music in the background, and sounds from the player when they move.</p><h4>Implementation:</h4><ul><li>Download your sound effects. You can find free ones on <a href="https://pixabay.com/sound-effects/search/game/">Pixabay</a>. Look for ones that work well in the background (they loop), and ones that are short effects, such as jumping sounds.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ow1d7YpvsSWPYqah-Stg5w.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iCzB9Df3lRp71foRVuc8Ww.png" /></figure><ul><li>Add an AudioStreamPlayer node to play background audio. Add an AudioStreamPlayer2D node to play positional audio.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/403/1*C1yHiRg_JkPvloXo-hRdZQ.png" /></figure><ul><li>You will need to reimport your audio that is supposed to loop. Double-click it, and enable looping.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/732/1*fznPUW2tCpy9aqxI9XeGsg.png" /></figure><ul><li>Set the stream property to the desired audio file.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/573/1*lF6ZSKfDoI6NezPU-FjnlQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/570/1*S1QD7YUU0Mqwj3raZNOvAQ.png" /></figure><ul><li>Adjust properties like volume_db, and pitch_scale if needed. We’ll enable autoplay on our AudioStreamPlayer node since that is our background music.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/575/1*yxwvs01ZT6APelQ1_7KyTg.png" /></figure><ul><li>We will play our sound effect audio (AudioStreamPlayer2D) when our player enters a certain area. To do this, add an Area2D node to your scene with a collision body, and attach its on_body_entered() signal to your script.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/853/1*9zGfWUIaRtWBy2aIdvZVJw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/993/1*g6ZgN_6D38JRvptEgJGR4A.png" /></figure><ul><li>Now play the audio when the player enters the area.</li></ul><pre>### Main.gd<br><br>extends Node2D<br><br>@onready var audio_stream_player_2d = $AudioStreamPlayer2D<br><br>func _on_area_2d_body_entered(body):<br> if body.name == &quot;Player&quot;:<br>  audio_stream_player_2d.play()</pre><ul><li>Run your scene. The background music should play, and the sound effect should play when your player enters the area.</li></ul><h3>NavigationAgent2D, NavigationObstacle2D, NavigationRegion2D</h3><p>The NavigationAgent, NavigationObstacle, and NavigationRegion nodes are used to manage navigation and pathfinding in both 2D and 3D environments. These nodes help create dynamic and realistic movement for characters and objects, allowing them to navigate around obstacles and follow paths.</p><ul><li>The NavigationAgent2Dnode is used to move characters along a path while avoiding obstacles.</li><li>The NavigationObstacle2Dnode is used to create obstacles that navigation agents will avoid.</li><li>The NavigationRegion2D node defines areas where navigation is allowed or restricted.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0iZy9Ub05IbpDC6JYms-nw.png" /></figure><p>These three nodes combined allow us to create a more immersive world through mechanics such as NPC and Enemy roaming, particle movements, and controlled entity spawning.</p><h4>Mechanic:</h4><p>Create an NPC that roams around a certain area on the map.</p><h4>Implementation:</h4><ul><li>In your Main scene, add a NavigationRegion2D to your scene to define the roaming area.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/390/1*yp7KpIBIsvP5MfvYJ-gB7A.png" /></figure><ul><li>Create a new NavigationPolygonresource for this node so that we can define our region. Draw the polygon to where you want your region to be.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/561/1*KMqWNcvHRzVp-SJYxaqrqw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/913/1*zlg2HjgBbu9z6pDfAo5DYg.png" /></figure><ul><li>Now all we need to do is select our NavigationRegion node and select <strong>“Bake Navigation”</strong>. You’ll see a blue-colored polygon get drawn over our floor, that is our navigation region!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sBmZK2iOiEcO16ZcBn82FQ.png" /></figure><ul><li>In a new scene, create your NPC using a CharacterBody2Dnode as the root node. Add the collisions and animations for this entity just as you did for your player.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1020/1*dJP2LVIYutrJ4bBiJXbSwA.png" /></figure><ul><li>To your NPC scene, add a NavigationAgent2D node. The NPC will be assigned to this agent so that they can roam in the region. Enable avoidance for this NPC so that they can avoid obstacles.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/391/1*MgjO19Lyqwe-VdASGmOCQg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/590/1*S3Kk9yRJyf_nmvl14tlHnA.png" /></figure><ul><li>Attach a script to your NPC. We will then need to connect our signals from our NavigationAgent2D node to <strong>1)</strong> compute the avoidance velocity of our NPC, and<strong> 2)</strong> redirect our NPC when that target is reached. For moving the NPC whilst avoiding obstacles, attach the velocity_computed signal to your script. For redirecting the NPC, attach the navigation_finished signal to your script.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1005/1*q_jhTHCoSCxSLQMQZhpcFw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1023/1*FvlT3Sf1LO0k7LeKlLz_Og.png" /></figure><ul><li>We also want our NPC to pause before redirecting. To do this, we will add a Timer node to our scene. Enable its one_shot property, and change its wait_time to however long you want the NPC to wait before roaming again.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/398/1*m_D-IJ2yg3846gPk761pCQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/602/1*RbWFyCQ6VXiN-PkCur2aOg.png" /></figure><ul><li>Also attach its timeout() signal to your script.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/992/1*Iw8JnzGohrg9KzVM6raHTg.png" /></figure><ul><li>Now add your roaming functionality.</li></ul><pre>### NPC.gd<br><br>extends CharacterBody2D<br><br>@onready var navigation_agent_2d = $NavigationAgent2D<br>@onready var navigation_region = $&quot;../NavigationRegion2D&quot;<br>@onready var animation_tree = $AnimationTree<br>@onready var animation_state = animation_tree.get(&quot;parameters/playback&quot;)<br>@onready var timer = $Timer<br><br># Variables<br>@export var movement_speed: float = 50.0<br>var roaming_area: Rect2 <br>var target_position: Vector2  <br><br>func _ready():<br> # Add a delay to ensure the navigation map is loaded<br> await get_tree().create_timer(1).timeout<br> set_roaming_area()<br> set_random_target()<br><br>func _physics_process(delta):<br> # Move NPC towards the target<br> var next_path_position = navigation_agent_2d.get_next_path_position()<br> var new_velocity = (next_path_position - global_position).normalized() * movement_speed<br> if navigation_agent_2d.avoidance_enabled:<br>  navigation_agent_2d.velocity = new_velocity<br> else: <br>  _on_navigation_agent_2d_velocity_computed(new_velocity)<br> <br> # Update the NPC&#39;s position<br> move_and_slide()<br><br> # Play walking animation<br> update_animation(velocity)<br><br><br>func set_roaming_area():<br> # Set the roaming area<br> var navigation_polygon = navigation_region.get_navigation_polygon()<br> if navigation_polygon.get_outline_count() &gt; 0:<br>  var outline = navigation_polygon.get_outline(0)<br>  # Calculate the bounding rect<br>  var min_x = INF<br>  var min_y = INF<br>  var max_x = -INF<br>  var max_y = -INF<br>  for point in outline:<br>   min_x = min(min_x, point.x)<br>   min_y = min(min_y, point.y)<br>   max_x = max(max_x, point.x)<br>   max_y = max(max_y, point.y)<br>  roaming_area = Rect2(min_x, min_y, max_x - min_x, max_y - min_y)<br> else:<br>  print(&quot;No outlines found in the navigation polygon.&quot;)<br><br><br>func set_random_target():<br> # Set next roaming position within the roaming area<br> target_position = Vector2(<br>  randf_range(roaming_area.position.x, roaming_area.position.x + roaming_area.size.x),<br>  randf_range(roaming_area.position.y, roaming_area.position.y + roaming_area.size.y)<br> )<br> navigation_agent_2d.set_target_position(target_position)<br><br>func update_animation(velocity: Vector2):<br> if velocity.length() == 0:<br>  animation_state.travel(&quot;idle&quot;)<br> else:<br>  animation_state.travel(&quot;walk&quot;)<br>  animation_tree.set(&quot;parameters/walk/blend_position&quot;, velocity.normalized())<br><br>func _on_navigation_agent_2d_velocity_computed(safe_velocity):<br> # Move NPC<br> velocity = safe_velocity<br><br>func _on_timer_timeout():<br> # Move NPC again<br> set_random_target()<br><br>func _on_navigation_agent_2d_navigation_finished():<br> # When path reached, redirect NPC<br> velocity = Vector2.ZERO<br> animation_state.travel(&quot;idle&quot;)<br> timer.start()</pre><ul><li>Instance your NPC in your Main scene. Move them into your region.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wTwiwb5RvGsLAXcSUSDWkQ.png" /></figure><ul><li>Optionally, add NavigationObstacle2D nodes to create obstacles. Add this node to a StaticBody2D with a collision shape.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kV3KZK_-NjZw5IR2l1swWQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*H_s0ZnKm32p39GsrbK_exQ.png" /></figure><ul><li>Run your scene and see your NPC randomly roam. They should avoid your obstacles.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/620/1*t6ouFhF1ssypSce7jgXBSw.gif" /></figure><h3>Path2D and PathFollow2D</h3><p>The Path2D and PathFollow2D nodes work together to create and follow paths in a 2D space. The Path2D node is used to define a path using a sequence of points. I like this more than using a NavMesh because it allows you to create and visualize a path in the Godot editor, instead of randomizing it. The PathFollow2D node is used to make an object follow a path defined by a Path2D node.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RCM31wIPKpff6QlV5OIs8g.png" /></figure><h4>Mechanic:</h4><p>Create an NPC that roams on a defined path on the map.</p><h4>Implementation:</h4><ul><li>Create a Path2D node in your scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/396/1*3k8EcosQwS9UT2s3pIzK0g.png" /></figure><ul><li>Add a PathFollow2D node as a child of the Path2D.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/406/1*ZuNkK5IjLu2Xl-7PjeDDcA.png" /></figure><ul><li>Ensure the rotatesvalue of this path is disabled since our NPC should only move diagonally.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/329/1*NwJhPcDQnp0iv5LLrmwM4Q.png" /></figure><ul><li>In a new scene, create your NPC using a CharacterBody2Dnode as the root node. Add the collisions and animations for this entity just as you did for your player.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1020/1*dJP2LVIYutrJ4bBiJXbSwA.png" /></figure><ul><li>Attach your NPC to your PathFollow2D node. This will tell the game that this is the object that should follow this Path.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/393/1*6C7odgtvDK_Zh7Ps0DRTpw.png" /></figure><ul><li>Now we can draw our path. In the Godot editor, select the Path2D node. Use the “Add Point” button in the toolbar to add points to draw the path shape that your NPC has to follow. Select the point to move it on your map.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/369/1*yWwcqCO3dyx9BNSD5t3S5A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_Crd79ge2nj3u-Tt4_aLAg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/327/1*Xs5TOY5e3ySBcbaqV-E9DQ.png" /></figure><ul><li>Add more points to complete your path.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*b9KuHvMSLXxqO2A8HMbyLA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/331/1*w6o6-Xg0t6Lsk2nTAZs53Q.png" /></figure><ul><li>With your path created, attach a script to your NPC. Then, add the logic for them to move along the path.</li></ul><pre>### NPC.gd<br><br>extends CharacterBody2D<br><br>@onready var animation_tree = $AnimationTree<br>@onready var animation_state = animation_tree.get(&quot;parameters/playback&quot;)<br>@onready var path_follow = get_parent()<br><br># Variables<br>@export var speed = 50.0<br>var current_offset = 0.0<br>var path_length = 0.0<br>var direction = 1<br><br>func _ready():<br> # Get the total length of the path<br> path_length = path_follow.get_parent().curve.get_baked_length()<br><br>func _physics_process(delta):<br> # Update the progress along the path<br> update_path_progress(delta)<br> update_animation(Vector2(direction, 0))<br> move_and_slide()<br><br>func update_path_progress(delta):<br> # Update the progress along the path<br> current_offset += speed * delta * direction<br>    # Reverse direction if the end or start of the path is reached<br> if current_offset &gt;= path_length or current_offset &lt;= 0:<br>  direction *= -1 <br> current_offset = clamp(current_offset, 0, path_length)<br> path_follow.progress = current_offset<br><br>func update_animation(velocity: Vector2):<br> if velocity.length() == 0:<br>  animation_state.travel(&quot;idle&quot;)<br> else:<br>  animation_state.travel(&quot;walk&quot;)<br>  animation_tree.set(&quot;parameters/walk/blend_position&quot;, velocity.normalized())</pre><ul><li>Run your scene and see your NPC roam. They should follow your path in a zig-zag (back-and-forth) motion.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/644/1*GZd6a25VmFGURSeRb2CBnw.gif" /></figure><h3>TileMap</h3><p>The<strong> </strong>TileMap node allows us to create 2D grid-based levels. With it, we can place our objects directly onto a grid to draw our world.</p><p>The TileMap is composed of cells. Each cell has the same dimensions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vI-UbEn07LnMtMO3_9s9Ug.png" /></figure><p>The TileMap uses a TileSet Resource that contains an array of 2D tiles that can be placed on cells on the grid. Each tile is comprised of a sprite, either from an atlas (tilesheet) or an individual image.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RZDgs9K4ooGu2d-8PwfszA.png" /></figure><p>We can also add these tiles on different layers, which are added on top of other objects on the grid. These tiles can also have their own collision, navigation, and physics properties.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*25CdEMaQg8wLqeR01a4fRg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NrGJU-TBqrmFShRY3axNng.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*npwhY-DebZ_VOCg2YcX3RQ.png" /></figure><p>In my 2D base template project, you will see that my entire (very basic) world was made using several layers and tilesheets on the TileMap node.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yVcX0aVHASQo_7lsh6z4pw.png" /></figure><h4>Mechanic:</h4><p>Create a tile-based map.</p><h4>Implementation:</h4><ul><li>Let’s start by adding a TileMap node to our scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/392/1*NOt02o0fZBCS8Xa1EpHBFA.png" /></figure><ul><li>In the Inspector Panel, give this node a new TileSet resource.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/338/1*YiH0bfKHgz9uGlEyfi4-VQ.png" /></figure><ul><li>You’ll see a panel open at the bottom. This is where we can add the sprites and tilesheets that we want to draw on our TileMap. To make it clear, we <strong>create </strong>tiles in the TileSet panel, and we <strong>draw </strong>these tiles onto our world in the TileMap panel.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zg7LZTCrvZ8GwaIoxwgU1g.png" /></figure><ul><li>Now, find a tilesheet or a sprite that you like, and drag it into this panel. Say yes to the prompt if you are using a tilesheet so that it can separate the tiles into grid cells.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*txQwQQp9as4KoWXEBQhXgw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wejCCAWmDElEZw4kv8lXXw.png" /></figure><ul><li>Here we can give our tileset a name and change its margins (if necessary, usually it’s not).</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3FXYbzH2l5JJbNFw1VWXsw.png" /></figure><ul><li>In the Select and Paint panels, we can draw properties such as navigation, collision, or other physics properties onto our tiles. We’ll get to that in a bit.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7yHnX-E9UqEa5fYVpUZXxA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eU5loYGqmG70Uh2ygWYGGw.png" /></figure><ul><li>Now, go to your TileMap panel. You will see that we can paint our tiles onto our screen. Using the tools, you can draw, erase, or select tiles that you have added.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/463/1*nNQnKZaqBYkon1K2TbD-hw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PwnoyJo8qFGj4ThFZKgiCg.png" /></figure><ul><li>You can also erase tiles by hovering over them and holding down your <strong>right </strong>mouse button.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/387/1*Q2R8AIVTXywfU0flrDHjrw.png" /></figure><ul><li>You can rotate tiles via the X and Z keys on your keyboard.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/328/1*7tBfXcy7Je14f0a5tKUtcQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/519/1*gNV-WTBhkvN-GdPTIcOCxw.png" /></figure><ul><li>You’ll see that we are drawing all the tiles on one layer. If you draw a tile over an existing one, it will replace that tile.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/332/1*N64Zwh1cmDHnEmOYoHN-cA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/207/1*pHC_0s_y2hKu-pcBFtrnEw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/189/1*34IiKhUeKs1q6Ou_sWfieg.png" /></figure><ul><li>We don’t want this, so let’s add more layers. In the Inspector Panel, underneath Layers, press the “Add Element” button to add more layers.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/657/1*Ze7e9cOm20smSm0ZIII-kw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/264/1*KBlLipl1qRH2zvYhX4mQMw.png" /></figure><ul><li>Now we can draw on our different layers.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/369/1*9sMAl2o6OWgBYhZD6fzwGg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/362/1*M6QNHS5GxNz-DU3qlOhbeA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/316/1*eqxPmmTTsXW69bFcvHtFPg.png" /></figure><ul><li>But what about collisions? For this, we need a Physics Layer. Click on the TileSet resource in your Inspector Panel, and navigate to Physics Layers.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/655/1*1WwwUSvc8Crjd5u5456BdA.png" /></figure><ul><li>Click “Add Element” to add a layer. You usually only need one layer for collisions.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/635/1*Wo2XNfmkoLGJUwuej184HQ.png" /></figure><ul><li>Now go back to your TileSet panel, and navigate to the Paint pane. Select the Physics property, and select the layer you just created.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*47It8H8gy6wAkbE2XL_DPA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/851/1*nSnSdwvX3zdpuyCTlEIJnA.png" /></figure><ul><li>Now we can draw collisions wherever we want the player and other entities to be blocked. We usually only do this on objects such as trees, buildings, or the outer boundaries of our map.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GBxB0oM36A-l9cufc4M0Jg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_dAl0IYNEv4WL3ZDJJ5IiQ.png" /></figure><ul><li>To delete collisions, just clear your polygon on the left and click on your existing cells.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6-oeK-dDB-_yMGWgMdxhcQ.png" /></figure><ul><li>You can add more tilesheets and draw collisions on there too.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*POs0zFva1B4Jx8b0No6sVg.png" /></figure><ul><li>The last thing I want to show you is autotiling. Drawing the ground tile for tile, with all of its edges is extremely tedious. We can make use of autotiling, also called TERRAINS, to speed up this process. This feature automatically selects and places the appropriate tile based on the surrounding tiles, ensuring that the tiles blend seamlessly together.</li><li>Click on the TileSet resource in your Inspector Panel, and navigate to Terrain Sets.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/654/1*3LjE3QszMoPcQRU_qwOV6Q.png" /></figure><ul><li>We will add an Element for each resource we want to “autotile”. So if you added a tilesheet for dirt, grass, water, and mud in your TileSet panel, you will create an element for each of those resources. Since we only have the “Dirt” tilesheet (we don’t want to autotile foliage or buildings), we will only add one element.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/646/1*P9KFl_rZJowPG_a536HGmw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/643/1*HujUpZe_4I9Jb3QkOs6QAA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*G0xOBa-nlL8nKKtFwQrAMg.png" /></figure><ul><li>If we had a “Grass” TileSet, we would add another element.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/643/1*wo5fqJwetweWZXuHAHAu9Q.png" /></figure><ul><li>Now go back to your TileSet panel, and navigate to the Paint pane. Select the Terrains property, and select the layer you just created.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*9r4aW8t1ea6EBm1Tid4sOQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/836/1*4Vk1_25Rtc_SLQsdFkIQVQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/499/1*VZPylSE-fjOYaPWfgt-luQ.png" /></figure><ul><li>Now we will draw in our bits. This is what the engine will use to check the tiles for the appropriate variant to use. This might be confusing to you, but just remember to select all the areas of your tilemap that ARE NOT on weird shapes or corners.</li><li>You will have to play around with the tiles to find the bits that give you the best results.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ExV4A0H_20FWYx9V6WZ_HQ.png" /></figure><ul><li>Navigate back to your TileMap node, and go to the terrains property. You’ll see that your autotiles have been created.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SarDY7OSGX4lb0UoO8xO4A.png" /></figure><ul><li>Select your terrain and draw it onto the screen.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/508/1*adjED_d-DWLLi1kaIK2cdg.gif" /></figure><ul><li>Go ahead and create your mini world. You can also select and drag commonly used tiles into the Patterns panel for easy access.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/882/1*Bj4eYLq5E0ROYE-RqfVaCQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/997/1*-grGISF9vmFW5R0fcufcTw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7bzBd-ifQFjCkm7HGPfoqQ.png" /></figure><ul><li>Run your scene and test your creation!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4GXoOFLBZaT0J6XjTBpSsg.png" /></figure><h3>TileMapLayer</h3><p>In Godot 4.3, the TileMap node has been replaced by TileMapLayer nodes. This node works exactly like the TileMap node, except each TileMapLayer node represents a single layer of tiles, making it easier to handle specific types of tiles separately (e.g., background, obstacles, interactive elements). This structure is meant to enhance our code clarity and allow for more granular control over the behaviors and properties of different tile layers.</p><p>Personally, I don’t like this way of TileMap creation because it reminds me too much of the way it was handled back in Godot 3. Now, instead of using one single TileMap node to create a map, we have to create many separate TileMapLayer nodes to achieve the same result. I guess I can’t complain because unless I make my own engine, this is the node that we will have to use going forward!</p><p>The way that we create collisions, autotiles, and tilesets is exactly the same as it was with the TileMap node — hence I left the details in for the TileMap node.</p><p>The TileMapLayer is composed of cells. Each cell has the same dimensions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vI-UbEn07LnMtMO3_9s9Ug.png" /></figure><p>The TileMapLayer uses a TileSet Resource that contains an array of 2D tiles that can be placed on cells on the grid. Each tile is comprised of a sprite, either from an atlas (tilesheet) or an individual image.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RZDgs9K4ooGu2d-8PwfszA.png" /></figure><p>To be able to create multiple layers of different tiles that can overlap with one another, we would have to create a TileMapLayer node for each layer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*25CdEMaQg8wLqeR01a4fRg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NrGJU-TBqrmFShRY3axNng.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*npwhY-DebZ_VOCg2YcX3RQ.png" /></figure><p>In my 2D base template project, you will see that my entire (very basic) world was made using several TileMapLayer nodes. I added all of my layers to a Node2D node for better organization.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hn72hZ6nXoRr7ZeC2S1WZQ.png" /></figure><h4>Mechanic:</h4><p>Spawn an item on a TileMapLayer.</p><h4>Implementation:</h4><ul><li>Let’s start by adding a TileMapLayernode to our scene. We will add this node to a Node2D node, renamed as “Map”.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/394/1*-PlE9b9-i0B3IpzefG6g3g.png" /></figure><ul><li>In the Inspector Panel, give this node a new TileSet resource.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/395/1*xGL47X_9G0t6rX0AWeUJaA.png" /></figure><ul><li>You’ll see a panel open at the bottom. This is where we can add the sprites and tilesheets that we want to draw on our TileMap. To make it clear, we <strong>create </strong>tiles in the TileSet panel, and we <strong>draw </strong>these tiles onto our world in the TileMap panel.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zg7LZTCrvZ8GwaIoxwgU1g.png" /></figure><ul><li>Now, find a tilesheet or a sprite that you like, and drag it into this panel. Say yes to the prompt if you are using a tilesheet so that it can separate the tiles into grid cells.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*txQwQQp9as4KoWXEBQhXgw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wejCCAWmDElEZw4kv8lXXw.png" /></figure><ul><li>Here we can give our tileset a name and change its margins (if necessary, usually it’s not).</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3FXYbzH2l5JJbNFw1VWXsw.png" /></figure><ul><li>Now when we select the TileMap panel, we can select tiles from this TileSet resource and draw it onto our screen.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zJ3Ph5bwgrZUtosijzNBjQ.png" /></figure><ul><li>Let’s draw a large piece of dirt on our screen.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*49KmjTPBRvAy5CCGqdzm5Q.png" /></figure><ul><li>Now, let’s add another TileMapLayer node to our scene. This is the layer we want our item to spawn on. Also give it a TileSet resource.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/414/1*GGb16QN_4DGIGTll9SHfCg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/389/1*LH-E83sgHrccdp8TJagOmg.png" /></figure><ul><li>Give it a tilesheet of your choice, and draw it on the screen.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*icUdvDLpVwOhyVmyANlS0w.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NTuaRAFZhpDneWBt6p3hNw.png" /></figure><ul><li>Now, in our code, let’s add the functionality to spawn an item on our second (grass) TileMapLayer nodes only. In our script, we use these TileMapLayer methods to manage where items can spawn in a game:</li><li>get_used_rect(): This is used to find out the area of the TileMapLayer where tiles have been placed so that you can limit where items might appear to just these areas.</li><li>map_to_local(): This converts the position from the tile map&#39;s grid (which might be in big numbers like tile coordinates) to the actual position in the game world (which is in pixels), so you know exactly where to put an item.</li><li>get_cell_source_id(): This checks if a specific tile exists at a given position in the TileMapLayer. In our script, it&#39;s used to determine if a position is valid for spawning an item based on whether there&#39;s a tile from the sand or grass layer at that position.</li></ul><pre>### Main.gd<br><br>extends Node2D<br><br># Node refs<br>var item_texture = load(&quot;res://Assets/Icons/icon1.png&quot;)<br>@onready var sand_layer = $Map/TileMapLayer<br>@onready var grass_layer = $Map/TileMapLayer2<br><br>var rng = RandomNumberGenerator.new()<br><br>func _ready():<br> # Spawn between 5 and 10 items<br> var spawn_item_amount = rng.randf_range(5, 10)<br> await spawn_item(spawn_item_amount)  <br><br># Valid item spawn location<br>func is_valid_spawn_location(layer, position):<br> var cell_coords = Vector2(position.x, position.y)<br> # Check if there&#39;s a tile on the sand layer<br> if sand_layer.get_cell_source_id(cell_coords) != -1:<br>  return false<br> # Check if there&#39;s a tile on the grass layer<br> if grass_layer.get_cell_source_id(cell_coords) != -1:<br>  return true<br> return false<br><br># Spawn item<br>func spawn_item(amount):<br> var spawned = 0<br> var attempts = 0<br> var max_attempts = 1000  # Arbitrary number, adjust as needed<br><br> while spawned &lt; amount and attempts &lt; max_attempts:<br>  attempts += 1<br>  var random_position = Vector2(randi() % grass_layer.get_used_rect().size.x, randi() % grass_layer.get_used_rect().size.y)<br>  var layer = randi() % 2  <br>  if is_valid_spawn_location(layer, random_position):<br>   var item_instance = Sprite2D.new()<br>   item_instance.texture = item_texture<br>   item_instance.position = grass_layer.map_to_local(random_position) + Vector2(16, 16) / 2<br>   add_child(item_instance)<br>   spawned += 1</pre><ul><li>Run your scene and test to see if the items spawn on the green (grass) tiles which is the TileMapLayer2 node.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mmBFr7Ef2aXa2MR00UQv-w.png" /></figure><h3>Timer</h3><p>The Timer node is used to create countdown timers that can trigger events after a specified period. The Timer node provides several properties to control its behavior, including wait_time, autostart, and one_shot.</p><ul><li><strong>wait_time</strong>: The duration in seconds that the timer will count down before emitting the timeout signal.</li><li><strong>autostart</strong>: If set to true, the timer will start automatically when the scene is loaded.</li><li><strong>one_shot</strong>: If set to true, the timer will stop after emitting the timeout signal once. If false, the timer will restart automatically after each timeout.</li></ul><p>It comes with a timeout signal, which is emitted when the timer reaches zero. This signal can be connected to a function to perform specific actions when the timer completes its countdown. The timeout signal is a crucial part of the Timer node&#39;s functionality, allowing you to trigger events at precise intervals.</p><h4>Mechanic:</h4><p>Spawn an enemy every 5 seconds.</p><h4>Implementation:</h4><ul><li>Add a Timer node to your scene. Set its wait_time to 5 seconds. Since we want the enemy to “spawn” as soon as the game loads, we should enable its autostart property.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/332/1*Sew9B01J9QxlwvT0gTo54A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/501/1*dRQl052lJst_pl0nbWwPxQ.png" /></figure><ul><li>Connect the timer node’s timeout signal to your script. This will execute our logic to spawn our enemy every 5 seconds.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/768/1*IIzpINBLUta2LuLxCaSSNQ.png" /></figure><ul><li>In your code, let’s “spawn” an enemy. Since we don’t have an actual enemy scene, we will just print the amount of enemies we have spawned.</li></ul><pre>### Main.gd<br><br>extends Node2D<br><br>@onready var timer = $Timer<br>var enemy_count = 0<br><br>func _ready():<br> if not timer.is_stopped():<br>  timer.start()<br><br>func _on_timer_timeout():<br> spawn_enemy()<br><br>func spawn_enemy():<br> enemy_count += 1<br> print(&quot;An enemy has spawned!&quot;)<br> print(&quot;Current enemy count: &quot;, enemy_count)</pre><ul><li>Run your game and your enemy should spawn each time the timer reaches 0!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*swh8PGWB5nOLpGF765Pd_w.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=16f13691ac5f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Book of Nodes: 3D]]></title>
            <link>https://christinec-dev.medium.com/the-book-of-nodes-3d-4482ce8a145c?source=rss-3d2c08006851------2</link>
            <guid isPermaLink="false">https://medium.com/p/4482ce8a145c</guid>
            <category><![CDATA[guide]]></category>
            <category><![CDATA[godot]]></category>
            <category><![CDATA[3d]]></category>
            <dc:creator><![CDATA[Christine Coomans]]></dc:creator>
            <pubDate>Wed, 07 Aug 2024 12:44:24 GMT</pubDate>
            <atom:updated>2024-08-20T13:42:57.722Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Usr2YFR46gWJWm44HzdgOg.png" /></figure><p>Below you can find a list of 3D nodes that can be used in Godot 4. This is part of my <a href="https://christinec-dev.medium.com/the-book-of-nodes-782ef4ba3c8a">Book of Nodes</a> series. If you want to see similar content on 2D or UI nodes, please refer to the parent page of this post for those links. 😊</p><p>Before we begin, if you need a base project to test these code snippets, feel free to download my FREE 2D and 3D templates <a href="https://christinecdevs.site/2d-3d-prototype-templates/">here</a>. I’ll be using these templates throughout this post.</p><p><em>*Please note that this list is not 100% complete yet, but I will be updating this list as time goes on.</em></p><ul><li><strong>AnimatedSprite3D</strong></li><li><strong>AnimationPlayer</strong></li><li><strong>AnimationTree</strong></li><li><strong>Area3D</strong></li><li><strong>AudioStreamPlayer3D</strong></li><li><strong>Skeleton3D, Bone3D, and BoneAttachment3D</strong></li><li><strong>Camera3D</strong></li><li><strong>CharacterBody3D</strong></li><li><strong>CollisionShape3D</strong></li><li><strong>DirectionalLight3D</strong></li><li><strong>GridMap</strong></li><li><strong>MeshInstance3D</strong></li><li><strong>NavigationAgent3D, NavigationObstacle3D, NavigationRegion3D</strong></li><li><strong>Node3D</strong></li><li><strong>OmniLight3D</strong></li><li><strong>Path3D, PathFollow3D</strong></li><li><strong>RayCast3D</strong></li><li><strong>RigidBody3D</strong></li><li><strong>Sprite3D</strong></li><li><strong>Spotlight3D</strong></li><li><strong>StaticBody3D</strong></li><li><strong>Timer</strong></li><li><strong>VehicleBody3D</strong></li><li><strong>WorldEnvironment</strong></li></ul><h3>Node3D</h3><p>The Node3Dnode is the fundamental building block for all 3D scenes in Godot. It is the base node for all 3D-related functionalities, providing 3D spatial features like position, rotation, and scale. Almost all 3D nodes (like CharacterBody3D, Area3D, etc.) inherit from Node3D, which makes it the core of any 3D scene structure in Godot.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*y6bTwrwmvUgkKXGk19PWeQ.png" /></figure><h4>Mechanic:</h4><p>Rotate a group of 3D nodes collectively.</p><h4>Implementation:</h4><ul><li>Add a Node3D to your scene to serve as a parent node. In an actual project, this could be used to represent a complex object like a spacecraft or a robot.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/356/1*xuQz3w-k3w9DmeaO9XIn3w.png" /></figure><ul><li>Add child nodes such as twoMeshInstance3Dnodes. These children can represent visual components, collision areas, etc.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/369/1*HNAe64mFCZRptRbhGXALcw.png" /></figure><ul><li>Give the MeshInstance3Dnodes a shape of your choosing. I’m going to choose a BoxMeshshape.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QTPF8kH6dyUzrsV5_XapXg.png" /></figure><ul><li>Now if we manipulate the Node3D parent, it will affect all its children. For example, rotating the Node3D will rotate all its children, whilst maintaining their relative positions and transformations.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/588/1*g2VshRO1sMhL6xy8yeWTZA.gif" /></figure><ul><li>We can also do this via code — say every second the game runs the node should rotate around the y-axis pivot point.</li></ul><pre>### Main.gd<br><br>extends Node3D<br><br>@onready var node_3d = $Node3D<br><br>func _process(delta):<br>    node_3d.rotate_y(1.0 * delta) </pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/624/1*WQrsbWY_O7ZcLihyvX3h0w.gif" /></figure><h3>MeshInstance3D</h3><p>The MeshInstance3D node in Godot is used to display 3D geometry. It takes a Meshresource and instances it in the current scene, which is essential for rendering 3D models.</p><p>In Godot, a mesh is used as a resource that can be applied to MeshInstance3D nodes to render 3D geometry in a scene. Meshes in Godot can be created directly in the engine or imported from external 3D modeling tools such as Blender. Godot supports several 3D model formats for importing meshes, including .fbx, .gltf and .glb (glTF), .obj, and others.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JIqwKztu1MI7C5Vmgfw_UA.png" /></figure><p>You can use this node to display characters, objects, or even simple shapes for prototyping, such as cylinders, planes, cubes, etc.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-r9RVsRag-vUiioIxlnqSw.png" /></figure><h4>Mechanic:</h4><p>Display a 3D model in a game scene.</p><h4><strong>Implementation:</strong></h4><ul><li>Create a MeshInstance3D node within your 3D scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/368/1*qVHwkZmtmMs8y5Xzis1BgQ.png" /></figure><ul><li>In the Inspector, assign a mesh resource to the mesh property of the MeshInstance3D.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/523/1*pLMhd943cAZW8nKcxJ-dPg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qbumV0l6o4BYo6mTsCpeMg.png" /></figure><ul><li>This is great for prototyping, but what if you created a Mesh in Blender and want to show that instead of a shape? Well, you can drag your imported objects directly into your scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/370/1*nRveqk8RUVBu9uOp2lsEYA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FPmzs2doNxpDpOVbzmq81Q.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1LdSoxV9PLArXwkVryoPZA.png" /></figure><ul><li>You’ll see that it is added to your scene underneath a Node3Dnode which has been imported as a scene. To access the MeshInstance3Dchild node from this parent, you’ll have to <strong>localize </strong>the scene. Do this by right-clicking on the node, and selecting “Make Local”.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/645/1*xEFy907EMWHlOuqasMsXwQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vUWSCjPo20eBdKltZY8eSA.png" /></figure><ul><li>You can now add collisions to this node, or even materials and shaders.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CTbyFjIU3xOd9K2ofjBupw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xzPIVhnoheSTWqWRVBQydA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1Dl9e9KVIT5TFj6sb-q5PQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rQSURQwcMcn2kO2ojXoOoA.png" /></figure><h3>Sprite3D</h3><p>The Sprite3Dnode in Godot is used to display 2D images in a 3D environment. This node can be set to always face the camera, which makes it useful for billboards, decals, NPC headlines, and other elements that need to be visible from all angles in a 3D space.</p><h4><strong>Mechanic:</strong></h4><p>Display a billboard that always faces the player.</p><h4><strong>Implementation:</strong></h4><ul><li>Create a Sprite3D node in your 3D scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FrC4dzgCZUjrxMCpBJ5Zcw.png" /></figure><ul><li>Assign a texture to the texture property of the Sprite3D node in the Inspector. This texture will appear as a flat image in the 3D space.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6F5emFEklP_Wjt_hSLercw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/986/1*EeY6vx7CAVIqbQ4A7rWLrg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oLGDMrkZxh1z5jTAA7MRGA.png" /></figure><ul><li>Currently the image is just flat. To change this, enable the billboard_mode property of the Sprite3D to ensure it always faces the camera, making it visible from any angle.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/511/1*h60op2TlgsuPdsNDoMZclQ.png" /></figure><ul><li>Run the scene and navigate around the 3D world to see the sprite always facing towards the camera, behaving like a billboard.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/596/1*9MsO690n_Vp7fKd-gHnxVg.gif" /></figure><h3>AnimatedSprite3D</h3><p>The AnimatedSprite3D node utilizes a series of images (sprites) and displays them in a sequence to create an animation in a 3D space. Unlike a simple Sprite3D node that displays a single static image in our world, AnimatedSprite3D can cycle through multiple frames to animate characters, objects, or effects within your 3D game. To create these frames, we can use either sprites or a spritesheet.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*IpsVa7TSEQJ1pO9T.png" /></figure><p>The AnimatedSprite3D node utilized the SpriteFrames Resource to create animations. This is a special resource in Godot that holds collections of images. Each collection can be configured as an animation by specifying the images (frames) that belong to it. You can create multiple animations within a single SpriteFrames resource, each with its own set of frames and playback properties like speed and loop settings.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cPJ28UYVB8fMfKCgUD2gCQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*h85pYiiw0immlBhZ2odWKA.png" /></figure><h4>Mechanic:</h4><p>Animate a 3D spinning coin.</p><h4>Implementation:</h4><ul><li>Download the <a href="https://www.vecteezy.com/png/11947487-set-of-rotating-golden-and-silver-coins-set-of-spinning-gold-silver-coins-in-many-views-rotate-in-different-angles-isolated-on-white-background-3d-rendering">spinning coin sheet</a>.</li><li>Create an AnimatedSprite3D node in your scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/359/1*3p-XY9B0y9qpR2jB7WzVSA.png" /></figure><ul><li>Assign a SpriteFrames resource to the AnimatedSprite3D.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/486/1*Eboip6FRUAq95o5DRQq4oQ.png" /></figure><ul><li>Add a new animation by clicking on the page+ icon.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/258/0*rHjUExFn29eA6sJ0.png" /></figure><ul><li>Rename this animation by double clicking on it.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/999/1*9qghTixfq4lq1O1tqpo_Rg.png" /></figure><ul><li>Either drag in sprites into the frames box, or click the spritesheet icon to add animations via our coin atlas.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1003/0*y_k53LDhpemmP4R5.png" /></figure><ul><li>Crop out the frames horizontally and vertically.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NXqXgNFwsmjJXZ9kZJWjcA.png" /></figure><ul><li>Select the frames you want. For instance, I will choose frames 0–4 in row 1.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2MUHc5ygj7Al2foVmQnpOQ.png" /></figure><ul><li>Scale the coin down and move it to where you want it.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lSkbSb_hAlhsBFguWPv1fQ.png" /></figure><ul><li>Then play the animation to see if you need to alter the FPS to make the coin rotate faster/slower.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/564/1*lwRQpQCOrGi5M-_tELa7pA.gif" /></figure><ul><li>Also enable billboard settings so that our image retains its 3D look and feel (instead of looking like a flat 2D coin).</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/510/1*DQJ-jyp-rR9vxmM8rIiBXg.png" /></figure><ul><li>Play the animation in your code so that when your player moves during runtime the animation can play:</li></ul><pre>### Main.gd<br><br>@onready var animated_sprite_3D = $AnimationSprite3D<br><br>func _ready():<br>    animated_sprite_3D.play(&quot;rotating_coin&quot;)</pre><ul><li>Start your project and observe the coin rotating around in your world.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/346/1*Aoh4dp3n6_uncE8q5bG9ZA.gif" /></figure><h3>AnimationPlayer</h3><p>Unlike the AnimatedSprite3D which is specifically designed for image animations, the AnimationPlayer can animate virtually ANY node within a Godot scene. Instead of animating a simple sprite, you can animate the node’s properties — including but not limited to positions, rotations, scales, colors, and even variables.</p><p>The AnimationPlayer can hold a set of animations on a singular timeline, each containing keyframes that define the start and end points of any property that changes over time. You can create complex sequences and control animations in a non-linear fashion.</p><p>This node can be used to animate 2D, 3D, and even UI nodes!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bn2akzwuRF3evvn2bCCZgQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vD9XpLTs_iyaRZ52jPB0rA.gif" /></figure><h4>Mechanic:</h4><p>Create a platform that moves up and down.</p><h4>Implementation:</h4><ul><li>Add a MeshInstance3Dnode and an AnimationPlayernode to your scene. The MeshInstance3Dnode is the node we want to animate, and the property we want to animate of this node is its position.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/361/1*Xete4O01viBPDCanXvxZoQ.png" /></figure><ul><li>Assign a shape to the MeshInstance3Dnode. I’ll assign a simple box shape.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YtWlOQVg3qc4YK4kN-E8ug.png" /></figure><ul><li>Change its scale to be more similar to a platform — Vector3(2, 0.2, 2).</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vorpyBIY1XbPrk84CSFKDQ.png" /></figure><ul><li>Select the AnimationPlayer node.</li><li>In the animation panel, click “New Animation” and name it something descriptive like “move_platform”.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/341/1*OjbyCQuO6-GGtv9SvwgXaQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/994/1*dzX-H7vtol3wO4KBGsqaEg.png" /></figure><ul><li>Set the animation length to the duration you want for one pulse cycle (e.g., 1 second).</li><li>Enable the “Loop” option to make the animation repeat continuously.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1002/1*b6OuXK71qm2AfC9zFClWUw.png" /></figure><ul><li>Go to the beginning of the animation timeline (0 seconds).</li><li>Select the MeshInstance3D node, and in the Inspector, set the position property to its initial value (e.g., Vector3(0, 0, 0)).</li><li>Right-click the position property in the Inspector and select &quot;Key&quot; to add a keyframe.</li><li>Move to the middle of the timeline (e.g., 0.5 seconds), change the position to a larger value (e.g., Vector3(0, 1, 0)), and add another keyframe.</li><li>At the end of the timeline (1 second), set the position back to the initial value (Vector3(0, 0, 0)) and add a final keyframe.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/519/1*9mFI6YEOkknyJw_ZscNl-Q.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kQfBWmqORAN29PNEUCFSRA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Xis4P4eWEadZuCDWDOxL9A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AMIoPGrUldSRPu8lEPUotA.png" /></figure><ul><li>You can control when the animation starts or stops via script, or let it run continuously since it’s set to loop.</li></ul><pre>### Main.gd<br><br>@onready var animation_player = $AnimationPlayer<br>func _ready():<br>    animation_player.play(&quot;move_platform&quot;)</pre><ul><li>Start your project and observe the platform move up and down.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/486/1*_0sDDSnZ8NLFjox7Je2RJQ.gif" /></figure><h3>AnimationTree</h3><p>The AnimationTree node enhances the capabilities of the AnimationPlayer by providing advanced features for animations, such as blending, transitions, and states. This makes it extremely easy to make detailed character animations and interactive scene elements in 2D and 3D environments.</p><p>We usually use blending to create smooth transitions between animations, for example, smoothly transitioning between walking and running depending on the player’s speed.</p><p>We use state machines to switch our animations dynamically depending on the conditions, for example, switching between idle and attack animations if the player presses a key.</p><h4>Mechanic:</h4><p>Animate a 3D character with multiple actions (e.g., running, jumping, idle).</p><h4>Implementation:</h4><ul><li>Add an AnimationPlayernode to your scene which has your 3D model, and create animations for it like &quot;run&quot;, &quot;attack&quot;, and &quot;idle&quot;.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BMCh0zDoFXsiImNKTfkYEg.png" /></figure><ul><li>You can also download animations from <a href="https://www.mixamo.com">Mixamo</a> if you don’t want to manually create these. If you want to download an animation from Mixamo, you will have to upload your 3D character (MAKE SURE IT’S THE SAME CHARACTER AS THE ONE IN YOUR GAME) to the dashboard, and then download the skin along with your desired animation.</li><li>Then you’ll have to import the character with the animation into your project, add it to your scene, localize the scene, extract the animation (by saving it as a .res), and then you’ll have to load it into your original AnimationPlayer.</li><li>Now, add an AnimationTree node.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/352/1*jAtzqC589FB5E4k3yyJdZA.png" /></figure><ul><li>Configure the tree_root as an AnimationRootNode for managing states.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/433/1*xfII8nyCbbW-stIwR1O8nw.png" /></figure><ul><li>Also assign the AnimationPlayeras the Anim Player property because this is where our AnimationTree will call the animations from, and the Advanced Expression as the root node because this is where our animation coding can be found (our script).</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/432/1*f2bwJhLPP7IeUzfQ3Jwdew.png" /></figure><ul><li>If you open your AnimationTree, you will see if you right-click you can add animations, blend trees, and state machines. Add a new animation for ‘idle’, ‘walk’, ‘run’, and ‘jump’.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rzzKPz1nEYTnCByWwprpZQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7Glli7kj8kbbNDbFWGlQmg.png" /></figure><ul><li>Add a transition between start -&gt; idle. The transition type should be immediatebecause we want the animation to play immediately.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/412/1*51amUda0gk3FaXVCC041hA.png" /></figure><ul><li>Add transitions between idle -&gt; walk and vice versa. The transition type for both should be syncbecause we want to blend the animation. Also set the mode to enabledbecause we will activate this animation via the code, and not automatically.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/496/1*JumBQJ4g5A2tIVG80UeVJQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ajI0-vv_Q5VBXmYkK27cyA.png" /></figure><ul><li>Select your transitions from walk &lt;-&gt; idle, and in the Inspector panel, change the x-fade time to a value such as 0.3. This is the blending time of the transitions, which smoothens out our transition from one animation to the next.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zrljVaYpGsx7u9s36pkMFg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/588/1*Yelr7EtfyG3Ij8uTL7f89g.gif" /></figure><ul><li>Do the exact same for the transitions between your walk &lt;-&gt; sprint.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZsKEjibVzJzuim_qgxDFpQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/560/1*RzlDcLB2nveNTmYF1YB6Cg.gif" /></figure><ul><li>Do the same for your jump animation, but it should transition back to all of your other animations (jump -&gt; idle, jump -&gt; walk, jump -&gt; sprint). The transition from your Jump to other animations should be of type At End, because we want to transition back to running, walking, or jumping when the player has finished jumping.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_HZSHOoJdpUHwXG9q_GOkw.png" /></figure><ul><li>Then, we only want to JUMP if our player is jumping by pressing the ui_jump key. For this we can make use if an expression. We can enter boolean values in the expressionpanel, which will then check our code for the conditions to play the animation based on this boolean. So, if in our code we say is_jumping = true when we press our jump button, this part of the AnimationTree will execute.</li><li>Add an expression is_jumping to all of the transitions that go towards your jump animation (idle -&gt; jump, walk -&gt; jump, sprint -&gt; jump).</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HT5hgBh_dbGPJJKciqDp-g.png" /></figure><ul><li>Now in our code, we can play our animations.</li></ul><pre>### Player.gd<br><br>extends CharacterBody3D<br><br># Vars<br>const run_speed = 3.0<br>const sprint_speed = 5.0<br>const jump_speed = 3.0<br>const gravity = 10<br><br>@onready var animation_tree = $AnimationTree<br>@onready var animation_state = animation_tree.get(&quot;parameters/playback&quot;)<br>@onready var camera = $ThirdPersonCamera/Camera<br><br>var is_jumping = false<br><br>func _ready():<br> Input.mouse_mode = Input.MOUSE_MODE_CAPTURED<br><br>func _process(delta):<br> handle_animation()<br><br>func _physics_process(delta):<br> handle_movement(delta)<br><br>func handle_animation():<br> var input_dir = Input.get_vector(&quot;ui_right&quot;, &quot;ui_left&quot;, &quot;ui_down&quot;, &quot;ui_up&quot;)<br> if is_jumping:<br>  if animation_state.get_current_node() != &quot;jump&quot;:<br>   animation_state.travel(&quot;jump&quot;)<br> elif input_dir.length() &gt; 0:<br>  if Input.is_action_pressed(&quot;ui_sprint&quot;):<br>   if animation_state.get_current_node() != &quot;sprint&quot;:<br>    animation_state.travel(&quot;sprint&quot;)<br>  else:<br>   if animation_state.get_current_node() != &quot;walk&quot;:<br>    animation_state.travel(&quot;walk&quot;)<br> else:<br>  if animation_state.get_current_node() != &quot;idle&quot;:<br>   animation_state.travel(&quot;idle&quot;)<br><br>func handle_movement(delta):<br> # Check if the player has landed<br> if is_on_floor() and velocity.y == 0:<br>  is_jumping = false<br> # Apply gravity<br> velocity.y += -gravity * delta<br><br> # Get input direction<br> var input_dir = Input.get_vector(&quot;ui_left&quot;, &quot;ui_right&quot;, &quot;ui_down&quot;, &quot;ui_up&quot;)<br> if input_dir.length() &gt; 0:<br>  # Calculate movement direction based on camera orientation<br>  var forward = -camera.global_transform.basis.z<br>  var right = camera.global_transform.basis.x<br>  forward.y = 0  <br>  right.y = 0   <br>  forward = forward.normalized()<br>  right = right.normalized()<br>  var movement_dir = (forward * input_dir.y + right * input_dir.x).normalized()<br><br>  # Rotate player to face movement direction<br>  var target_rotation = atan2(movement_dir.x, movement_dir.z)<br>  rotation.y = lerp_angle(rotation.y, target_rotation, 0.1)<br><br>  # Apply movement<br>  if Input.is_action_pressed(&quot;ui_sprint&quot;):<br>   velocity.x = movement_dir.x * sprint_speed<br>   velocity.z = movement_dir.z * sprint_speed<br>  else:<br>   velocity.x = movement_dir.x * run_speed<br>   velocity.z = movement_dir.z * run_speed<br> else:<br>  velocity.x = move_toward(velocity.x, 0, run_speed)<br>  velocity.z = move_toward(velocity.z, 0, run_speed)<br><br> # Handle jumping<br> if Input.is_action_just_pressed(&quot;ui_jump&quot;) and is_on_floor():<br>  velocity.y = jump_speed<br>  is_jumping = true<br><br> move_and_slide()</pre><ul><li>Run the scene and control the character to observe the transitions and movement based on our state.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/384/1*UVQ2U5vTar1d4d6NR9K-6Q.gif" /></figure><h3>CollisionShape3D</h3><p>The CollisionShape3D node allows you to specify the boundaries of an object for collision detection, which is essential for handling interactions between objects in your game.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*U_iJ3Nc8inSCD6v2XhHACg.png" /></figure><h4>Mechanic:</h4><p>Add a collision area to block the character from passing.</p><h4>Implementation:</h4><ul><li>Create a CollisionShape3D node as a child of a CharacterBody3D, RigidBody3D, orStaticBody3D. These nodes will block other collisions. To have a node pass through collisions, use an Area3D.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/354/1*QRzgFlERgzzNuBC-AaBspQ.png" /></figure><ul><li>In the Inspector, assign a Shape3D resource to the shape property of the CollisionShape3D. The shape you choose will depend on the shape of your entity. For example, a player might have a capsule shape, a pickup a sphere, an area a box.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ea4R3lvyWD1AGIHfMnLxgQ.png" /></figure><ul><li>Let’s enable debugging to see our collisions in action. You can also change the color of your debug lines to make it more visible.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/419/1*NOSd3fnTzxguxb6hyRIROA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/870/1*cOxA2niIiepRMMMycaV9MA.png" /></figure><ul><li>Run your scene to see how your player interacts with the collision shape. Since we used a StaticBody3D node, they should be blocked and unallowed to go through the collision.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/430/1*iemBd7zdmyhK9mUZmeQymg.gif" /></figure><h3>CharacterBody3D</h3><p>The CharacterBody3D node is a specialized class for physics bodies that are meant to be controlled or moved around by the user. Unlike other physics bodies such as the RigidBody3D or StaticBody3D node, CharacterBody3D is not affected by the engine’s physics properties like gravity or friction by default. Instead, you have to write code to control its behavior, giving you precise control over how it moves and reacts to collisions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*i60wGCTzpVD5YDitEOvdmg.png" /></figure><h4>Mechanic:</h4><p>Move a character with arrow keys, including handling gravity and jumping.</p><h4>Implementation:</h4><ul><li>Create a CharacterBody3D node in your scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/376/1*-4hbxbUKbCUjDbzfqluSbA.png" /></figure><ul><li>You’ll see it has a warning icon next to it. This is because it needs a collision shape to be able to interact with the world. Add a CollisionShape3D as a child of the CharacterBody3D and set its shape to match your character.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BYqu6oxrhOoqPZrxs0mIeg.png" /></figure><ul><li>Add a MeshInstance3D node to this scene so that we can see our character.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*64GjzYkilYz27m5V0eY0Eg.png" /></figure><ul><li>You’ll also need to attach a camera to your character so we can see them.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mrrlF1W020oydhPZwtvu8g.png" /></figure><ul><li>Attach a script to the CharacterBody3D to handle movement and jumping.</li></ul><pre>### Character.gd<br><br>extends CharacterBody3D<br><br># Variables<br>@export var speed = 5.0<br>@export var jump_force = 10.0<br>@export var gravity = 10.0<br><br># Input for movement<br>func get_input():<br> velocity.x = 0<br> velocity.z = 0<br> if Input.is_action_pressed(&quot;ui_up&quot;):<br>  velocity.z -= speed<br> if Input.is_action_pressed(&quot;ui_down&quot;):<br>  velocity.z += speed<br> if Input.is_action_pressed(&quot;ui_left&quot;):<br>  velocity.x -= speed<br> if Input.is_action_pressed(&quot;ui_right&quot;):<br>  velocity.x += speed<br> if is_on_floor() and Input.is_action_just_pressed(&quot;ui_accept&quot;):<br>  velocity.y = jump_force<br><br># Movement &amp; Gravity<br>func _physics_process(delta):<br> get_input()<br> velocity.y -= gravity * delta<br> move_and_slide()</pre><ul><li>Run the scene and use the arrow keys to move the character and make it jump.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/402/1*tzE_tkvlCA4xcAyrceSuig.gif" /></figure><h3>StaticBody3D</h3><p>The StaticBody3D node is used to represent objects that do not move. This node is ideal for creating static elements in your game, such as walls, floors, and other immovable objects such as chests.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*i60wGCTzpVD5YDitEOvdmg.png" /></figure><h4>Mechanic:</h4><p>Create an obstacle.</p><h4>Implementation:</h4><ul><li>Create a StaticBody3D node in your scene. Add a CollisionShape3Das a child of the StaticBody and set its shape to match the obstacle.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Dh5oa7jGmy8U7AaH9eK63g.png" /></figure><ul><li>Give it a MeshInstance3Dof your choice so that we can see the item.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UNehyPEx7RLAzKc0hKjxlA.png" /></figure><ul><li>Run your scene to see how your player interacts with the collision shape. They should be blocked and unallowed to go through the obstacle.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/430/1*WBrXmZHytHjpj9FUZZvM9g.gif" /></figure><h3>RigidBody3D</h3><p>The RigidBody3D node is used for objects that are affected by the engine’s physics. These bodies can move, rotate, and respond to forces and collisions. They are ideal for creating dynamic objects that need realistic physics interactions, such as balls, bullets, moveable obstacles, etc.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*i60wGCTzpVD5YDitEOvdmg.png" /></figure><h4>Mechanic:</h4><p>Create a moveable obstacle.</p><h4>Implementation:</h4><ul><li>Create a RigidBody3D node in your scene. Add a CollisionShape3Das a child of the RigidBody and set its shape to match the obstacle.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ARFSR41Y6vjabgV4Lgs66A.png" /></figure><ul><li>Give it a MeshInstance3Dof your choice so that we can see the item.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eqSwfBTNuuPnkgBn2F3f-A.png" /></figure><ul><li>Since we don’t want the object to fly into space when we collide with it, we will need to increase its mass value.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/431/1*nXNPYtYqp9OgqwNRMnVFdw.png" /></figure><ul><li>Use GDScript to apply forces or impulses to the rigid body if the player pushes it.</li></ul><pre>### Player.gd<br><br>extends CharacterBody3D<br><br># Vars<br>const run_speed = 3.0<br>const sprint_speed = 5.0<br>const jump_speed = 3.0<br>const gravity = 10<br><br>@export var push_force = 80.0<br><br>@onready var animation_tree = $AnimationTree<br>@onready var animation_state = animation_tree.get(&quot;parameters/playback&quot;)<br>@onready var camera = $ThirdPersonCamera/Camera<br><br>var is_jumping = false<br><br>func _ready():<br> Input.mouse_mode = Input.MOUSE_MODE_CAPTURED<br><br>func _process(delta):<br> handle_animation()<br><br>func _physics_process(delta):<br> handle_movement(delta)<br> handle_collisions()<br><br>func handle_animation():<br> var input_dir = Input.get_vector(&quot;ui_right&quot;, &quot;ui_left&quot;, &quot;ui_down&quot;, &quot;ui_up&quot;)<br> if is_jumping:<br>  if animation_state.get_current_node() != &quot;jump&quot;:<br>   animation_state.travel(&quot;jump&quot;)<br> elif input_dir.length() &gt; 0:<br>  if Input.is_action_pressed(&quot;ui_sprint&quot;):<br>   if animation_state.get_current_node() != &quot;sprint&quot;:<br>    animation_state.travel(&quot;sprint&quot;)<br>  else:<br>   if animation_state.get_current_node() != &quot;walk&quot;:<br>    animation_state.travel(&quot;walk&quot;)<br> else:<br>  if animation_state.get_current_node() != &quot;idle&quot;:<br>   animation_state.travel(&quot;idle&quot;)<br><br>func handle_movement(delta):<br> # Check if the player has landed<br> if is_on_floor() and velocity.y == 0:<br>  is_jumping = false<br> # Apply gravity<br> velocity.y += -gravity * delta<br><br> # Get input direction<br> var input_dir = Input.get_vector(&quot;ui_left&quot;, &quot;ui_right&quot;, &quot;ui_down&quot;, &quot;ui_up&quot;)<br> if input_dir.length() &gt; 0:<br>  # Calculate movement direction based on camera orientation<br>  var forward = -camera.global_transform.basis.z<br>  var right = camera.global_transform.basis.x<br>  forward.y = 0  <br>  right.y = 0   <br>  forward = forward.normalized()<br>  right = right.normalized()<br>  var movement_dir = (forward * input_dir.y + right * input_dir.x).normalized()<br><br>  # Rotate player to face movement direction<br>  var target_rotation = atan2(movement_dir.x, movement_dir.z)<br>  rotation.y = lerp_angle(rotation.y, target_rotation, 0.1)<br><br>  # Apply movement<br>  if Input.is_action_pressed(&quot;ui_sprint&quot;):<br>   velocity.x = movement_dir.x * sprint_speed<br>   velocity.z = movement_dir.z * sprint_speed<br>  else:<br>   velocity.x = movement_dir.x * run_speed<br>   velocity.z = movement_dir.z * run_speed<br> else:<br>  velocity.x = move_toward(velocity.x, 0, run_speed)<br>  velocity.z = move_toward(velocity.z, 0, run_speed)<br><br> # Handle jumping<br> if Input.is_action_just_pressed(&quot;ui_jump&quot;) and is_on_floor():<br>  velocity.y = jump_speed<br>  is_jumping = true<br><br> move_and_slide()<br> <br># Handle Collisions<br>func handle_collisions():<br> for i in range(get_slide_collision_count()):<br>  var collision = get_slide_collision(i)<br>  if collision.get_collider() is RigidBody3D:<br>   var collider = collision.get_collider() as RigidBody3D<br>   var impulse = -collision.get_normal() * push_force<br>   collider.apply_central_impulse(impulse)</pre><ul><li>Run the scene and observe how the obstacle moves when the player pushes against it.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/460/1*ruaX3s_gYS69PL4TqksGvQ.gif" /></figure><h3>VehicleBody3D</h3><p>TheVehicleBody3D node is used to simulate a 3D vehicle with realistic physics. It requires VehicleWheel3D nodes for each wheel to function correctly.</p><p>On our VehicleBody3D node, the most important properties are the engine_force, steer_angle, and brake_force properties. The engine_force allows our vehicle to move forward/backward. The steer_angle/steering properties allow our vehicle to move left/right. The brake_force property allows our car to stop.</p><h4>Mechanic:</h4><p>Create a controllable vehicle with wheels.</p><h4>Implementation:</h4><ul><li>For this part, you will need to download a car and import it into your project. If you’re using my base template, I recommend downloading the <a href="https://kenney.nl/assets/car-kit">Car Kit from Kenney</a> and importing it into your project.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/390/1*zQQRI2dcix6JbUdzdNK7nA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZbHNqylggFwhqDQKEuhtDg.png" /></figure><ul><li>Create a VehicleBody3D node in your scene. You will need to give it a CollisionShape3D, and a mesh (use the one you imported).</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4FnwpHVR2q1kNJ_wpj0L9A.png" /></figure><ul><li>Add VehicleWheel3D nodes as children of the VehicleBody3D node for each wheel. Name them FrontLWheel, BackLWheel, FrontRWheel, BackRWheel. Also move them into the positions of your wheels.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eCKaoQ1PRyjY2wuw4dM9NA.png" /></figure><ul><li>You’ll need to set your front wheels as the steering wheels, and your back wheels as your traction wheels.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/436/1*Osxpv4DXk4W8bh0po6mLqA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/453/1*1gYJnUU68gA8r2GvSOy5nA.png" /></figure><ul><li>Also attach a camera to your vehicle so you can follow it around.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FUMd5-vnZMKSPNfH6mlqeA.png" /></figure><ul><li>Attach a script to your vehicle to handle the vehicle’s movement and control. We’ll move our car with our arrow keys, and brake with our spacebar.</li></ul><pre># VehicleBody.gd<br><br>extends VehicleBody3D<br><br>@onready var front_l_wheel = $FrontLWheel<br>@onready var front_r_wheel = $FrontRWheel<br>@onready var back_r_wheel = $BackRWheel<br>@onready var back_l_wheel = $BackLWheel<br><br># Variables<br>@export var new_engine_force = 1000.0<br>@export var brake_force = 500.0<br>@export var steer_angle = 0.5<br><br>func _process(delta):<br> handle_input()<br><br>func handle_input():<br> var engine = 0.0<br> var brake = 0.0<br> var steer = 0.0<br><br> if Input.is_action_pressed(&quot;ui_up&quot;):<br>  engine = new_engine_force<br> if Input.is_action_pressed(&quot;ui_down&quot;):<br>  engine = -new_engine_force<br> if Input.is_action_pressed(&quot;ui_left&quot;):<br>  steer = steer_angle<br> if Input.is_action_pressed(&quot;ui_right&quot;):<br>  steer = -steer_angle<br> if Input.is_action_pressed(&quot;ui_accept&quot;):<br>  brake = brake_force<br><br> front_l_wheel.engine_force = engine<br> front_r_wheel.engine_force = engine<br> front_l_wheel.brake = brake<br> front_r_wheel.brake = brake<br> front_l_wheel.steering = steer<br> front_r_wheel.steering = steer</pre><ul><li>Run your scene and try to control your vehicle. It should move around your map when controlled!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/650/1*ahZBXzLYDisfAyUnNWVXxQ.gif" /></figure><h3>Area3D</h3><p>The Area3D node is used to detect when objects enter or exit a defined area. They do not represent physical bodies but are useful for triggering events such as cutscenes or map transitions, detecting overlaps, and creating zones for things such as enemy or loot spawning.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8LV6cwLaG3BA6bFWXZ11rg.png" /></figure><p>We can use the Area3D node’s on_body_entered() and on_body_exited() signals to determine whether or not a PhysicsBody has entered this zone.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mBQh-XhpM3ACQKsKQ1d1uw.png" /></figure><h4>Mechanic:</h4><p>Create a trigger zone that detects when the player enters a specific area.</p><h4>Implementation:</h4><ul><li>Create an Area3D node in your scene. You also need to add a CollisionShape3D as a child of the Area and set its shape to define the trigger zone. Adjust the collision shape&#39;s properties to fit the dimensions of your trigger zone.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/374/1*qItKECN0JVVdgb8HrXugvw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CWzOEVXxYUpIRyUYrVRwEA.png" /></figure><ul><li>Attach the Area3D node’s on_body_entered() and on_body_exited() signals to your script.</li><li>Use GDScript to notify us when the Player enters or exits the area.</li></ul><pre>### Main.gd<br><br>extends Node3D<br><br>func _on_area_3d_body_entered(body):<br> if body.name == &quot;Player&quot;:<br>  print(&quot;The player has entered the area!&quot;)<br><br>func _on_area_3d_body_exited(body):<br> if body.name == &quot;Player&quot;:<br>  print(&quot;The player has exited the area!&quot;)</pre><ul><li>Enable debugging so we can see when our Player enters/exits our area.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/404/1*5tODHdR-eC7PlIYqPPQYTQ.png" /></figure><ul><li>Run the scene and observe how the area detects when the Player enters or exits the defined zone. Each time the player enters/exits the zone, the game should be notified.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/604/1*NDaXvAcYvK5iSZeBELHfTg.gif" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*r6IRvMokkRHUVSMr1gSmbw.png" /></figure><h3>RayCast3D</h3><p>The RayCast3D node is used to cast a ray in a 3D space to detect objects along its path. This is useful for various purposes such as line-of-sight checks, shooting and attacking mechanics, and collision detection.</p><p>It can collide with bodies such as StaticBody3D (useful for detecting loot and quest items), CharacterBody3D (useful for detecting interactions with enemies and NPCs), and RigidBody3D (useful for detecting interactions with moveable objects. It can also collide with areas, such as Area3D (useful for interactions with trigger zones).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NqUgtKtaqQAca45ezlkefw.png" /></figure><h4>Mechanic:</h4><p>Cast a ray from the player and detect what it hits.</p><h4>Implementation:</h4><ul><li>Add a RayCast3D node to the Player in your scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/249/1*uxZNzTCA1GREQu0gRgs46A.png" /></figure><ul><li>Set the target_position property face the direction of your player (z: 1). You can also enable its collision with areas.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/444/1*Nw1PJz3scYymoT3o7G4QTw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FCMM6dS_aywIch58PNfDQw.png" /></figure><ul><li>Now we can print what we are colliding with in our code.</li></ul><pre>### Player.gd<br><br>extends CharacterBody3D<br><br># Vars<br>const run_speed = 3.0<br>const sprint_speed = 5.0<br>const jump_speed = 3.0<br>const gravity = 10<br><br>@onready var animation_tree = $AnimationTree<br>@onready var animation_state = animation_tree.get(&quot;parameters/playback&quot;)<br>@onready var camera = $ThirdPersonCamera/Camera<br>@onready var ray_cast_3d = $RayCast3D<br><br>var is_jumping = false<br><br>func _ready():<br> Input.mouse_mode = Input.MOUSE_MODE_CAPTURED<br><br>func _process(delta):<br> handle_animation()<br> <br>    if ray_cast_3d.is_colliding():<br>        var collider = ray_cast_3d.get_collider()<br>        print(&quot;Raycast hit: &quot;, collider.name)</pre><ul><li>Run your scene and interact with objects that have colliders. The raycast should detect the objects and notify the game.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ATbdQewjBZ1RiVK7tVd-Yw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ixMYQ9c73CsnJ9tJG7d0nQ.png" /></figure><h3>Camera3D</h3><p>The Camera3D node is used to control the view of a 3D scene. It allows the screen to follow the player or other objects. Only one Camera can be active per viewport, and it registers itself in the nearest Viewport node.</p><p>In<strong> third-person games</strong>, we usually attach the Camera3D node to a SpringArm3D node. The SpringArm node reacts to collisions in the environment. It ensures that the camera moves closer to the player when there are obstacles, preventing the camera from clipping through objects.</p><p>In <strong>first-person games</strong>, we usually attach the Camera3D directly to our Pivot — such as our Player head. This way we “see” from the player’s perspective.</p><p>In <strong>“God-Mode”</strong>, we attach the Camera to our World scene so that we can get a birds-eye view of the environment. This usually requires a bit more configuration and coding, as you have to make the camera able to move, rotate, and zoom.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*q-NnVoRSAujwqSkA8qxiSA.png" /></figure><h4>Mechanic:</h4><p>Create a “God Mode” 3D camera that can move, zoom, and rotate based on user input.</p><h4>Implementation:</h4><ul><li>Add a Camera3D node to your Main (World) scene. Make sure this camera is set to ‘current’, and all other cameras are disabled.</li><li>Also move the camera up on the y-axis (10), and rotate it slightly on the x-axis (-45) to point down at your world.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PZTyIX41RrsYxhbTdrsqUQ.png" /></figure><ul><li>Add the inputs to zoom, move, and rotate your camera.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KNsnPDRESTjzHjGNSEfg0g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZLjsA4EUjyXRT12kfv3MgA.png" /></figure><ul><li>Use GDScript to handle the camera’s zoom, movement, and rotation. You can do this in a custom Camera.gd script (preferred), or directly in your root script.</li></ul><pre>### Main.gd<br><br>extends Node3D<br><br>@onready var camera_3d = $Camera3D<br><br>@export var zoom_speed = 50.0<br>@export var move_speed = 5.0  # Adjusted to a more reasonable value<br>@export var rotate_speed = 0.5  # Adjusted to a more reasonable value<br>@export var min_zoom = 10.0<br>@export var max_zoom = 100.0<br><br>var target_fov = 70.0<br>var camera_drag = Vector2.ZERO<br>var camera_movement = Vector3.ZERO<br>var sensibility = 0.001<br><br>func _process(delta):<br> handle_zoom(delta)<br> handle_movement(delta)<br> handle_rotation(delta)<br><br>func handle_zoom(delta):<br> if Input.is_action_pressed(&quot;zoom_in&quot;):<br>  camera_3d.fov -= zoom_speed * delta<br> if Input.is_action_pressed(&quot;zoom_out&quot;):<br>  camera_3d.fov += zoom_speed * delta<br> camera_3d.fov = clamp(camera_3d.fov, min_zoom, max_zoom)<br><br>func handle_movement(delta):<br> if Input.is_action_just_pressed(&quot;camera_drag&quot;):<br>  camera_drag = get_viewport().get_mouse_position()<br> if Input.is_action_pressed(&quot;camera_drag&quot;):<br>  camera_movement.x += (get_viewport().get_mouse_position().x - camera_drag.x) * sensibility<br>  camera_movement.z += (get_viewport().get_mouse_position().y - camera_drag.y) * sensibility<br> camera_3d.position += camera_movement * move_speed * delta<br> camera_movement = lerp(camera_movement, Vector3.ZERO, 0.2)<br><br>func handle_rotation(delta):<br> if Input.is_action_pressed(&quot;rotate_left&quot;):<br>  camera_3d.rotate_y(rotate_speed * delta)<br> if Input.is_action_pressed(&quot;rotate_right&quot;):<br>  camera_3d.rotate_y(-rotate_speed * delta)</pre><ul><li>Run the scene and use the defined input actions to move, zoom, and rotate the camera.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/520/1*ohXYksHkIh-rUMQewp6pOg.gif" /></figure><h3>DirectionalLight3D</h3><p>The DirectionalLight3D node is used to simulate sunlight or moonlight in our 3D environment. It emits light in a specific direction, affecting all objects in the scene equally, regardless of their distance from the light source. This type of light is useful for outdoor scenes where you need consistent lighting across the entire scene.</p><p>This node’s two main properties are the energy and color properties. The energy property determines how bright/dim the light is, and the color is the shading of the light. You can also change the Mode of the shadows, which will change the way your shadows are rendered. By default, PSSM 2 is a good option to choose.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vikN4u-Biq7ucKvzIywHVQ.png" /></figure><p>In 3D environments, your light will also be influenced by your WorldEnvironment node, which contains the sky of your world.</p><h4>Mechanic:</h4><p>Illuminate a scene with sunlight.</p><h4>Implementation:</h4><ol><li>Create a DirectionalLight3D node in your scene.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/353/1*Snf8JESiWkvaXfFlNj7eaw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NU09iI1eCJiBVNluerw9Mg.png" /></figure><ul><li>By default, the light and energy looks pretty good. We can change these values if you want. For instance, we can make the energy brighter and the color darker to simulate night time.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qxjXwIbpF_Ae_QkShNwICQ.png" /></figure><ul><li>Now let’s do something fun. I recommend using shaders with this node, but just for testing sake, let’s have it randomize its color each second.</li><li>Add a Timer node to your scene. In the Inspector Panel, enable autostart, and connect its timeout signal to your script</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/380/1*vSao5ewcioim5pIhS9NB2w.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/576/1*TeUoA8w2u5Avz4nCLpq8aQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/897/1*-Pp33-7X6r0KNciNEZ8kYQ.png" /></figure><ul><li>In your code, let’s randomize our light’s color every time the timer times out (every second).</li></ul><pre>### Main.gd<br><br>extends Node3D<br><br>@onready var directional_light_3d = $World/DirectionalLight3D<br><br>func _on_timer_timeout():<br> directional_light_3d.color = Color(randf(), randf(), randf()</pre><ul><li>Run your scene and enjoy the overstimulation.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/632/1*mebhHIwL1dPbkuwlxVo-EQ.gif" /></figure><h3>SpotLight3D</h3><p>The SpotLight3D node emits light in a cone shape, illuminating objects within the cone’s range. This type of light is useful for creating focused lighting effects, such as flashlights or stage lights.</p><p>This node’s two main properties are the energy , color, specular, and range, and projectorproperties. The energy property determines how bright/dim the light is. The color is the shading of the light. The specular property affects how reflective surfaces appear under the spotlight. The range property defines the maximum distance the light can reach. The projector property allows us to give our light a texture, such as stained glass or a vignette effect.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vikN4u-Biq7ucKvzIywHVQ.png" /></figure><h4>Mechanic:</h4><p>Create a flashlight attached to your player.</p><h4>Implementation:</h4><ul><li>To your player, attach a Spotlight3D node. Move this node into one of their hands, and rotate it on the y-axis (180) so that it faces the direction that the player is facing.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cHORCNybRYJg5wrzYKvS5w.png" /></figure><ul><li>Play around with the energy, range, angle, color, and specular values.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/451/1*p9SwRzBdYcocoaa7ARHMrQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5Ffv7jS4YIxk9WW7D8xf3g.png" /></figure><ul><li>This light looks too bright. It needs to look more like a flashlight. Download <a href="https://opengameart.org/content/flashlight-pattern-incandescent">this</a> texture, and drag it into your projector property.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/414/1*k7atlqJXZCui-_F7YWtqhQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Tgno1hg1l9c8bZlaNnJkvg.png" /></figure><ul><li>Now, let’s move this flashlight when we move our camera.</li></ul><pre>### Player.gd<br><br>extends CharacterBody3D<br><br># Vars<br>const run_speed = 3.0<br>const sprint_speed = 5.0<br>const jump_speed = 3.0<br>const gravity = 10<br><br>@onready var animation_tree = $AnimationTree<br>@onready var animation_state = animation_tree.get(&quot;parameters/playback&quot;)<br>@onready var camera = $ThirdPersonCamera/Camera<br>@onready var flashlight = $SpotLight3D<br><br>var is_jumping = false<br><br>func _ready():<br> Input.mouse_mode = Input.MOUSE_MODE_CAPTURED<br><br>func _process(delta):<br> handle_animation()<br><br>func _physics_process(delta):<br> handle_movement(delta)<br><br>func handle_animation():<br> var input_dir = Input.get_vector(&quot;ui_right&quot;, &quot;ui_left&quot;, &quot;ui_down&quot;, &quot;ui_up&quot;)<br> if is_jumping:<br>  if animation_state.get_current_node() != &quot;jump&quot;:<br>   animation_state.travel(&quot;jump&quot;)<br> elif input_dir.length() &gt; 0:<br>  if Input.is_action_pressed(&quot;ui_sprint&quot;):<br>   if animation_state.get_current_node() != &quot;sprint&quot;:<br>    animation_state.travel(&quot;sprint&quot;)<br>  else:<br>   if animation_state.get_current_node() != &quot;walk&quot;:<br>    animation_state.travel(&quot;walk&quot;)<br> else:<br>  if animation_state.get_current_node() != &quot;idle&quot;:<br>   animation_state.travel(&quot;idle&quot;)<br><br>func handle_movement(delta):<br> # Check if the player has landed<br> if is_on_floor() and velocity.y == 0:<br>  is_jumping = false<br> # Apply gravity<br> velocity.y += -gravity * delta<br><br> # Get input direction<br> var input_dir = Input.get_vector(&quot;ui_left&quot;, &quot;ui_right&quot;, &quot;ui_down&quot;, &quot;ui_up&quot;)<br> if input_dir.length() &gt; 0:<br>  # Calculate movement direction based on camera orientation<br>  var forward = -camera.global_transform.basis.z<br>  var right = camera.global_transform.basis.x<br>  forward.y = 0  <br>  right.y = 0   <br>  forward = forward.normalized()<br>  right = right.normalized()<br>  var movement_dir = (forward * input_dir.y + right * input_dir.x).normalized()<br><br>  # Rotate player to face movement direction<br>  var target_rotation = atan2(movement_dir.x, movement_dir.z)<br>  rotation.y = lerp_angle(rotation.y, target_rotation, 0.1)<br><br>  # Apply movement<br>  if Input.is_action_pressed(&quot;ui_sprint&quot;):<br>   velocity.x = movement_dir.x * sprint_speed<br>   velocity.z = movement_dir.z * sprint_speed<br>  else:<br>   velocity.x = movement_dir.x * run_speed<br>   velocity.z = movement_dir.z * run_speed<br> else:<br>  velocity.x = move_toward(velocity.x, 0, run_speed)<br>  velocity.z = move_toward(velocity.z, 0, run_speed)<br><br> # Handle jumping<br> if Input.is_action_just_pressed(&quot;ui_jump&quot;) and is_on_floor():<br>  velocity.y = jump_speed<br>  is_jumping = true<br><br> move_and_slide()<br><br> # Rotate flashlight to match camera orientation<br> flashlight.global_rotation = camera.global_rotation</pre><ul><li>Run your scene and move your camera. Your flashlight should shine in that direction.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/528/1*octdVXI8aaLbvAEgxheIbQ.gif" /></figure><h3>OmniLight3D</h3><p>The OmniLight3D node emits light in all directions from a single point, similar to a light bulb. This type of light is useful for creating ambient lighting or point light sources.</p><p>This node’s two main properties are the energy , color, specular, and range, and projectorproperties. The energy property determines how bright/dim the light is. The color is the shading of the light. The specular property affects how reflective surfaces appear under the spotlight. The range property defines the maximum distance the light can reach.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vikN4u-Biq7ucKvzIywHVQ.png" /></figure><h4>Mechanic:</h4><p>Create a flickering torch.</p><h4>Implementation:</h4><ul><li>In your scene, add a OmniLight3D node. Move it to where you want your light to shine from.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jA0xOviX3C5nOu_-c_qPdg.png" /></figure><ul><li>Play around with the energy, range,color, and specular values.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uaGlXROJbETPztWLIclpAw.png" /></figure><ul><li>Just for fun, let’s give it a flicker effect. We’ll do this via an AnimationPlayer node.</li><li>Create a new animation in the AnimationPlayer.</li><li>Add a track for the energy property of the OmniLight3D.</li><li>Add keyframes to the energy track to simulate flickering.</li><li>Enable looping.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/357/1*E4bnU5Gj9qqMsfREKgrinQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Zn948Qe1mQAWCWQ1iWvleA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_TApuJaOSYE8NvyyDA0txw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*46DsB-vc0ogQP8yhgDEAdQ.png" /></figure><ul><li>Then play this animation via the code when the game loads.</li></ul><pre>### Main.gd<br><br>extends Node3D<br><br>@onready var animation_player = $AnimationPlayer<br><br>func _ready():<br> animation_player.play(&quot;flicker&quot;)</pre><ul><li>Run the scene and observe the player’s color changes when they go into the flickering lights.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/754/1*ggDu9XQlpdYk_zcRy-iepQ.gif" /></figure><h3>BoneAttachment3D</h3><p>When importing a 3D model with a skeleton from 3D software such as Blender, the Skeleton3Dnode should automatically be populated with bones if the model is correctly rigged, exported, and imported. Bone3Dare the parts of the body — such as the hands, arms, legs, etc.</p><p>The BoneAttachment3Dnode is used to attach nodes to specific bones in a Skeleton3D. This allows you to dynamically attach objects to bones, which will follow the bone’s transformations. For example, we can attach a flashlight to our player’s hand, or a bow on our player’s back.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*m_ZJa0e51qfzLZfAVnE_og.png" /></figure><p>If we look at the Player character in the base template, we can see that their Skeleton3D node from is already populated with bones. This came from the model (.glb) itself — we didn’t have to manually create this in Godot.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/449/1*m9fcMIayaX8sXbdz2jREiA.png" /></figure><h4>Mechanic:</h4><p>Attach a weapon to the right hand of our player.</p><h4>Implementation Steps</h4><ul><li>In your Player scene, add aBoneAttachment3D node as a child of the Skeleton3D node.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/407/1*VQps0EG54te6g8k676F8NQ.png" /></figure><ul><li>Set the bone_name property of theBoneAttachment3D to the name of the bone you want to attach to. In our case, it should be the arm-right.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/442/1*AEKBgwex_BdmkYljwWFj0g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/439/1*OFzteyHQFX8S9T823JUs-Q.png" /></figure><ul><li>Add the objects you want to attach as children of the BoneAttachment3D nodes. In our case, we want to add a weapon. Since we don’t have weapons in the base template, we will use the cane instead.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/413/1*5F9Pb40leECmNC1tku_hcQ.png" /></figure><ul><li>Move the cane into the players hand.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/915/1*9HZFNEIgb9bXUM0nd76IaA.png" /></figure><ul><li>In our script, let’s handle the attachment and detachment of this “weapon”. We also need to add an input to handle this action.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jwIz0b5T20z830IjxpRgXQ.png" /></figure><pre>### Player.gd<br><br>extends CharacterBody3D<br><br># Vars<br>const run_speed = 3.0<br>const sprint_speed = 5.0<br>const jump_speed = 3.0<br>const gravity = 10<br><br>@onready var animation_tree = $AnimationTree<br>@onready var animation_state = animation_tree.get(&quot;parameters/playback&quot;)<br>@onready var camera = $ThirdPersonCamera/Camera<br><br>@onready var bone_attachment_3d = $&quot;character-female-b2/character-female-b/Skeleton3D/BoneAttachment3D&quot;<br><br>## Older code<br><br>func _input(event):<br> if event.is_action_pressed(&quot;ui_equip&quot;):<br>  equip_object()<br><br>func equip_object():<br> bone_attachment_3d.visible = !bone_attachment_3d.visible</pre><ul><li>Run your scene. Your weapon should equip/unequip if you press TAB. When you move, the weapon should also move naturally with your bone.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/578/1*QZOPKFG810ajF-n_YUqZkQ.gif" /></figure><h3>AudioStreamPlayer3D and AudioStreamPlayer</h3><p>These nodes are used to play audio in our games. We use the AudioStreamPlayer to play audio equally across our scene (such as background music or ambient sounds), and the AudioStreamPlayer3D to play audio positionally (such as from our players or NPCs).</p><h4>Mechanic:</h4><p>Play ambient music in the background, and sounds from the player when they move.</p><h4>Implementation:</h4><ul><li>Download your sound effects. You can find free ones on <a href="https://pixabay.com/sound-effects/search/game/">Pixabay</a>. Look for ones that work well in the background (they loop), and ones that are short effects, such as jumping sounds.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ow1d7YpvsSWPYqah-Stg5w.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iCzB9Df3lRp71foRVuc8Ww.png" /></figure><ul><li>Add an AudioStreamPlayer node to play background audio. Add an AudioStreamPlayer3D node to play positional audio.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/411/1*NFqsWrJkq0jtlctOkdpzIw.png" /></figure><ul><li>You will need to reimport your audio that is supposed to loop. Double click it, and enable looping.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/732/1*fznPUW2tCpy9aqxI9XeGsg.png" /></figure><ul><li>Set the stream property to the desired audio file.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/573/1*lF6ZSKfDoI6NezPU-FjnlQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/562/1*H1Rs8wlKuub_9WnrMV3GrA.png" /></figure><ul><li>Adjust properties like volume_db, and pitch_scale if needed. We’ll enable autoplay on our AudioStreamPlayer node since that is our background music.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/575/1*yxwvs01ZT6APelQ1_7KyTg.png" /></figure><ul><li>We also want to enable the emission property on our AudiostreamPlayer3D so that the audio can sound more distanced.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/564/1*vypWmWaanRtCUTrfRXdVAQ.png" /></figure><ul><li>We will play our sound effect audio (AudioStreamPlayer3D) when our player enters a certain area. To do this, add an Area3D node to your scene with a collision body, and attach its on_body_entered() signal to your script.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-7PIdscnhjtVmN2WJK6Epw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/999/1*mQXuMFMlidt2tDPuHG4s3A.png" /></figure><ul><li>Now play the audio when the player enters the area.</li></ul><pre>### Main.gd<br><br>extends Node3D<br><br>@onready var audio_stream_player_3d = $AudioStreamPlayer3D<br><br>func _on_area_3d_body_entered(body):<br> if body.name == &quot;Player&quot;:<br>  audio_stream_player_3d.play()</pre><ul><li>Run your scene. The background music should play, and the sound effect should play when your player enters the area.</li></ul><h3>WorldEnvironment</h3><p>The WorldEnvironment node configures the default environment settings for a scene, including lighting, post-processing effects, and background settings. This node allows you to add a bunch of environmental effects, such as a skybox, fog, shadows, etc.</p><p>Everything you see in the screenshot below (the sky, light, fog) was done with the help of the WorldEnvironment node.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YeBGXP2rpQOfi4NZGCF6iQ.png" /></figure><p><strong>The most common properties you will change on this node will be:</strong></p><h4>Fog</h4><ul><li><strong>Purpose:</strong> Fog makes distant objects fade into a uniform color, simulating atmospheric effects.</li><li><strong>Key Properties:</strong></li><li>Light Color: The color of the fog.</li><li>Density: The ‘thickness’ of the fog until it is fully opaque.</li><li>Sky Affect: Adjusts the fog color based on the sun’s energy.</li></ul><h4>Volumetric Fog</h4><ul><li><strong>Purpose: </strong>Volumetric fog provides a more realistic fog effect by interacting with the lights in the scene.</li><li><strong>Key Properties:</strong></li><li>Density: The base exponential density of the volumetric fog.</li><li>Albedo: The color of the fog when interacting with lights.</li><li>Emission: The emitted light from the fog, useful for establishing ambient color.</li><li>Emission Energy: The brightness of the emitted light.</li><li>Anisotropy: The direction of scattered light through the fog.</li><li>Length: The distance over which the fog is computed.</li><li>Detail Spread: The distribution of detail in the fog.</li><li>Ambient Inject: Scales the strength of ambient light used in the fog.</li><li>Sky Affect: Controls how much the fog affects the background sky.</li></ul><p><strong>Glow</strong></p><ul><li><strong>Purpose: </strong>Glow adds bloom effects to bright areas, enhancing the visual appeal.</li><li><strong>Key Properties:</strong></li><li>Intensity: The strength of the glow effect.</li><li>Strength: The brightness for the glow effect.</li><li>Blend Mode: The blending mode for the glow effect.</li></ul><h4>Tonemapping</h4><ul><li><strong>Purpose:</strong> Tonemapping adjusts the color balance and contrast of the scene.</li><li><strong>Key Properties:</strong></li><li>Mode: The tone-mapping algorithm (e.g., Linear, Reinhard, Filmic).</li><li>Exposure: Adjusts the overall exposure of the scene.</li></ul><h4>Ambient Light</h4><ul><li><strong>Purpose: </strong>Ambient light provides a base level of illumination for the scene, ensuring that no part of the scene is completely dark.</li><li><strong>Key Properties:</strong></li><li>Color: The color of the ambient light.</li><li>Energy: The intensity of the ambient light.</li><li>Sky Contribution: The amount of light contributed by the sky.</li></ul><h4>Sky</h4><ul><li><strong>Purpose: </strong>The sky provides the background for the scene, which can be a solid color, a skybox, or a custom shader.</li><li><strong>Key Properties:</strong></li><li>Sky: The type of sky (e.g., PanoramaSky, ProceduralSky).</li><li>Custom FOV: The intensity of the sky’s contribution to the scene lighting.</li></ul><h4>Background</h4><ul><li><strong>Purpose: </strong>The background sets the visual backdrop for the scene, which can be a solid color, a sky, or a custom shader.</li><li><strong>Key Properties:</strong></li><li>Mode: The background mode (e.g., Clear Color, Sky, Custom Color).</li><li>Energy Multiplier: The intensity of the background’s contribution to the scene lighting.</li></ul><h4>Mechanic:</h4><p>Create a spooky atmosphere.</p><h4>Implementation:</h4><ul><li>Add a WorldEnvironment node to your scene. To this node, add an Environment resource and configure it.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/415/1*_e9RG2fh4sdU3r6A_yOO1g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/574/1*1hYVjXdhjSg85mAi9ptI0g.png" /></figure><ul><li>Set the background to be a color. Set the color to be black.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/533/1*r5liIVvZwv76KozP_h6XyQ.png" /></figure><ul><li>Change the ambient light color and energy.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/551/1*sZla7Kbv3o5McrDWmkv5Yw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/897/1*6K5n6GrJfRdwH4_i9u0p9g.png" /></figure><ul><li>Everything is too dark. Let’s enable our fog and change our properties.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/537/1*O6dfjIaTzYnM4gsFyn_mhw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/911/1*ktEycOOEzVtXoRB5IMPmFw.png" /></figure><ul><li>Let’s lighten the mood. Enable the glow property, and change its properties.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/524/1*QfFLHIPuhAAejerTOMLfnQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/893/1*83raXR6m_iMKax7E9Givow.png" /></figure><ul><li>Run your scene. Your game should look a little bit more spooky now!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*K31sCUvbzp6-oZSZPCQ8vg.png" /></figure><h3>NavigationAgent3D, NavigationObstacle3D, NavigationRegion3D</h3><p>The NavigationAgent, NavigationObstacle, and NavigationRegion nodes are used to manage navigation and pathfinding in both 2D and 3D environments. These nodes help create dynamic and realistic movement for characters and objects, allowing them to navigate around obstacles and follow paths.</p><ul><li>The NavigationAgent3Dnode is used to move characters along a path while avoiding obstacles.</li><li>The NavigationObstacle3D node is used to create obstacles that navigation agents will avoid.</li><li>The NavigationRegion3D node defines areas where navigation is allowed or restricted.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0iZy9Ub05IbpDC6JYms-nw.png" /></figure><p>These three nodes combined allow us to create a more immersive world through mechanics such as NPC and Enemy roaming, particle movements, and controlled entity spawning.</p><h4>Mechanic:</h4><p>Create an NPC that roams around a certain area on the map.</p><h4>Implementation:</h4><ul><li>In your Main scene, add a NavigationRegion3D to your scene to define the roaming area.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/406/1*o076ltZ665uWsIr4Q_VtDQ.png" /></figure><ul><li>Create a new NavigationMesh resource for this node so that we can define our region. Also move it to where you want your region to be.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/575/1*tLezqqJCU7PjaY7eJvUpcA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/422/1*mds9jjd0Vv8Iq541Qylf0g.png" /></figure><ul><li>Now to actually add our region, we’ll need to add our “floor” as a child of this NavigationRegion node. Since I’m not sure if you are using Terrains or GridMaps, we’ll use a MeshInstance3D for this.</li><li>Add a MeshInstance3D node as a child to this node. Change its size to something like 10 x 10 m.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Dr4q2M3FSewnnZByqXCGZA.png" /></figure><ul><li>Move your region so the floor is below the grass.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/427/1*tzc6ea3HXfbq0tEJWscVCg.png" /></figure><ul><li>Now all we need to do is select our NavigationRegion node and select <strong>“Bake Navigation”</strong>. You’ll see a blue-colored polygon get drawn over our floor, that is our navigation region!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1013/1*5wgxfKs6sY8U41t33RzSbA.png" /></figure><ul><li>In a new scene, create your NPC using a CharacterBody3Dnode as the root node. Add the collisions and animations for this entity just as you did for your player.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GcJMy-6MunMbE5xS_y8FlA.png" /></figure><ul><li>To your NPC scene, add a NavigationAgent3D node. The NPC will be assigned to this agent so that they can roam in the region. Enable avoidance for this NPC so that they can avoid obstacles.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/415/1*X4ZucJi_IehUZF3Tpd414A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/593/1*W9KnazMVlRUqCcpNhOghAg.png" /></figure><ul><li>Attach a script to your NPC. We will then need to connect our signals from our NavigationAgent3D node to <strong>1)</strong> compute the avoidance velocity of our NPC, and<strong> 2)</strong> redirect our NPC when that target is reached. For moving the NPC whilst avoiding obstacles, attach the velocity_computed signal to your script. For redirecting the NPC, attach the navigation_finished signal to your script.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/773/1*mn5ZgB0cHuS7ngOUNrRLXg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/868/1*4405zy_NQl9ExvNOL62rRw.png" /></figure><ul><li>We also want our NPC to pause before redirecting. To do this, we will add a Timer node to our scene. Enable its one_shot property, and change its wait_time to however long you want the NPC to wait before roaming again.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*19tBkMQz_Az55jOczidhmg.png" /></figure><ul><li>Also attach its timeout() signal to your script.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/689/1*YodNFScnE7kGEF8w0gwWjg.png" /></figure><ul><li>Now add your roaming functionality.</li></ul><pre>### NPC.gd<br><br>extends CharacterBody3D<br><br>@onready var navigation_agent_3d = $NavigationAgent3D<br>@onready var navigation_region = $&quot;../NavigationRegion3D/MeshInstance3D&quot;<br>@onready var animation_tree = $AnimationTree<br>@onready var animation_state = animation_tree.get(&quot;parameters/playback&quot;)<br>@onready var timer = $Timer<br><br># Variables<br>@export var movement_speed: float = 1.0<br>var roaming_area: AABB<br>var target_position: Vector3<br><br>func _ready():<br> # Add a delay to ensure the navigation map is loaded<br> await get_tree().create_timer(1).timeout<br> set_roaming_area()<br> set_random_target()<br><br>func _physics_process(delta):<br> # Move NPC towards the target<br> var next_path_position: Vector3 = navigation_agent_3d.get_next_path_position()<br> var new_velocity: Vector3 = global_position.direction_to(next_path_position) * movement_speed<br> if navigation_agent_3d.avoidance_enabled:<br>  navigation_agent_3d.velocity = new_velocity<br> else:<br>  _on_navigation_agent_3d_velocity_computed(new_velocity)<br> <br> # Rotate NPC to face the direction of movement<br> if velocity.length() &gt; 0:<br>  var target_rotation = atan2(velocity.x, velocity.z)<br>  rotation.y = lerp_angle(rotation.y, target_rotation, 0.1)<br> <br> # Play walking animation<br> if velocity != Vector3.ZERO:<br>  animation_state.travel(&quot;walk&quot;)<br> else:<br>  animation_state.travel(&quot;idle&quot;)<br> <br> move_and_slide()<br><br>func set_roaming_area():<br> # Set the roaming area based on the MeshInstance3D&#39;s bounding box<br> roaming_area = navigation_region.get_aabb()<br> print(&quot;Roaming area: &quot;, roaming_area)<br><br>func set_random_target():<br> # Set next roaming position<br> target_position = Vector3(<br>  randf_range(-roaming_area.position.x, roaming_area.position.x),<br>  randf_range(-roaming_area.position.y, roaming_area.position.y),<br>  randf_range(-roaming_area.position.z, roaming_area.position.z)<br> )<br> navigation_agent_3d.set_target_position(target_position)<br><br>func _on_navigation_agent_3d_velocity_computed(safe_velocity):<br> # Move NPC<br> velocity = safe_velocity<br><br>func _on_timer_timeout():<br> # Move NPC again<br> set_random_target()<br><br>func _on_navigation_agent_3d_navigation_finished():<br> # When path reached, redirect NPC<br> velocity = Vector3.ZERO<br> animation_state.travel(&quot;idle&quot;)<br> timer.start()</pre><ul><li>Instance your NPC in your Main scene. Move them into your region.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*wJ5FfKNxc0CawL3CpmoxAA.png" /></figure><ul><li>Optionally, add NavigationObstacle3D nodes to create obstacles. Add this node to a mesh with a collision shape.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/423/1*d2whKSflYy1IAxwKMQJrYA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*p7hCXUGAAW9Vz1MaBbAfrg.png" /></figure><ul><li>Run your scene and see your NPC randomly roam. They should avoid your obstacles.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/584/1*ACaqos7LQL0DwTR99dsPRA.gif" /></figure><h3>Path3D and PathFollow3D</h3><p>The Path3D and PathFollow3D nodes work together to create and follow paths in a 3D space. The Path3D node is used to define a path using a sequence of points. I like this more than using a NavMesh because it allows you to create and visualize a path in the Godot editor, instead of randomizing it. The PathFollow3D node is used to make an object follow a path defined by a Path3D node.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RCM31wIPKpff6QlV5OIs8g.png" /></figure><h4>Mechanic:</h4><p>Create an NPC that roams on a defined path on the map.</p><h4>Implementation:</h4><ul><li>Create a Path3D node in your scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/435/1*fuWjdCSIS7pb1pehmrYK4Q.png" /></figure><ul><li>Add a PathFollow3D node as a child of the Path3D.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/431/1*XNWvOcAIn5lrWLbFuQ0TFg.png" /></figure><ul><li>Enable its use_model_front property so that our NPC will always move whilst facing the front.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/477/1*wQRl-8I7Amkdv-YX_Pm_yQ.png" /></figure><ul><li>In a new scene, create your NPC using a CharacterBody3Dnode as the root node. Add the collisions and animations for this entity just as you did for your player.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GcJMy-6MunMbE5xS_y8FlA.png" /></figure><ul><li>Attach your NPC to your PathFollow3D node. This will tell the game that this is the object that should follow this Path.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/439/1*zqCJkKK-48qxMWilSVVn7w.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wt57KntwjYf4Ckzj4qEEuw.png" /></figure><ul><li>Now we can draw our path. In the Godot editor, select the Path3D node. Use the “Add Point” button in the toolbar to add points to draw the path shape that your NPC has to follow. Select the point to move it on your map.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/369/1*yWwcqCO3dyx9BNSD5t3S5A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YoNVcQdhM8b4YUQW3cUQvQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/467/1*MXSX4QLrQYPhCacgVgS6dA.png" /></figure><ul><li>Add more points to complete your path.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/891/1*8yDlGp-HKstaNV1bMadMTw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/450/1*9S8sowQHFYRnx0OtIE34oA.png" /></figure><ul><li>Make sure your path is on the ground, otherwise your NPC will fly!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/962/1*GmwGGYXRfq7WoKWrLg7nsA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/489/1*qT7AldbF2SWpubxevieWrQ.png" /></figure><ul><li>With your path created, attach a script to your NPC. Then, add the logic for them to move along the path.</li></ul><pre>### NPC.gd<br><br>extends CharacterBody3D<br><br>@onready var animation_tree = $AnimationTree<br>@onready var animation_state = animation_tree.get(&quot;parameters/playback&quot;)<br>@onready var path_follow = get_parent()<br><br># Vars<br>@export var movement_speed: float = 2.0<br>var current_offset: float = 0.0<br>var path_length: float = 0.0<br>var direction: int = 1<br>var previous_position: Vector3 = Vector3.ZERO<br><br>func _ready():<br> # Get the total length of the path<br> path_length = path_follow.get_parent().curve.get_baked_length()<br><br>func _physics_process(delta):<br> # Update the progress along the path<br> update_path_progress(delta)<br> <br> # Calculate the velocity based on the change in position<br> var current_position = path_follow.global_transform.origin<br> var velocity = (current_position - previous_position) / delta<br> previous_position = current_position<br> <br> # Update the animation based on the velocity<br> update_animation(velocity)<br> <br> # Update the NPC&#39;s position to follow the PathFollow3D node<br> global_transform.origin = current_position<br> <br> # Rotate NPC to face the direction of movement<br> if velocity.length() &gt; 0:<br>  var target_rotation = atan2(velocity.x, velocity.z)<br>  rotation.y = lerp_angle(rotation.y, target_rotation, 0.1)<br> <br> move_and_slide()<br><br>func update_path_progress(delta):<br> current_offset += movement_speed * delta * direction<br><br> # Reverse direction if the end or start of the path is reached<br> if current_offset &gt;= path_length or current_offset &lt;= 0:<br>  direction *= -1  # Reverse the direction<br> current_offset = clamp(current_offset, 0, path_length)<br> <br> # Update the progress of the PathFollow3D node<br> path_follow.progress = current_offset<br><br>func update_animation(velocity: Vector3):<br> if velocity.length() == 0:<br>  animation_state.travel(&quot;idle&quot;)<br> else:<br>  animation_state.travel(&quot;walk&quot;)<br>  animation_tree.set(&quot;parameters/walk/blend_position&quot;, velocity.normalized())</pre><ul><li>Run your scene and see your NPC roam. They should follow your path in a zig-zag (back-and-forth) motion.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/806/1*bHTEGg6rqeQHbFe5yJRXRA.gif" /></figure><h3>GridMap</h3><p>The GridMap node allows us to create 3D grid-based levels. With it, we can place our 3D models (tiles) on a grid interactively, similar to how TileMap works in 2D. If you have suitable models, you can use them to create, design, and manage large, repetitive environments like dungeons, cities, or landscapes.<br>The Gridmap is composed of cells. Each cell has the same dimensions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Z6n0kx9PW0v_eyh4axgWcQ.png" /></figure><p>These cells are formed using floors (horizontal grid), and planes (vertical grid). Each floor represents a different height level, whereas each plane represents a different depth level.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QYFktcix-LlKRGpeVgcxEw.png" /></figure><p>The GridMap uses a MeshLibrary Resource that contains an array of 3D tiles that can be placed on cells on the grid. Each tile is a mesh with materials, and it can also include optional collision and navigation shapes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UbT2QEAJJU8SvkZpLNaaLw.png" /></figure><p>In my 3D base template project, you will see that my entire world was made using several GridMap nodes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/423/1*zDFVq2zEWetnw2olyOEtfA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DSPR0LvIPAVYOviaxNYV8g.png" /></figure><p>All of these tiles that you see on the screen were originally .glb models.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/524/1*wof36gZzj02DJZSKeduNlA.png" /></figure><p>We add all of these models to a new scene, add whatever collisions we want, and then we convert this entire scene into MeshLibrary Resources so that they can be used by our GridMap as tiles.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Fd6tfW7_TnFXnR2-TR5lqA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/572/1*-8KJ9_R0k-ufNLSf__N71g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/521/1*2yBRLxo1VwzsWlZRpi7wpg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/829/1*4gJebNZa97Rf1bv5C2aunw.png" /></figure><h4>Mechanic:</h4><p>Create a grid-based map.</p><h4>Implementation:</h4><ul><li>To begin, we need a MeshLibrary, which is a collection of individual meshes that can be used in the GridMap.</li><li>Create a new scene with a Node3D node as the root node. Rename this node to “GridTiles”.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/557/1*T27UjntYx7NIwexfT_GCEQ.png" /></figure><ul><li>Import any .glb asset that you want to use, and then drag it into this new scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lxFgj8NHkqN4Cc4oxOW7KA.png" /></figure><ul><li>If you added meshes that need to block the player or the player should be able to walk on it (such as the ground), you’ll have to localize these scenes and add a collision shape to them.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/743/1*QqxRrM8fqlZF-XhqrKwyKQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/439/1*74z1Gz8dAlJfifFt6zuGvg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/554/1*teLbGGjb2dx_MRacvfJaSQ.png" /></figure><ul><li>After this is done, you can navigate to Scene &gt; Export As and export this scene as a MeshLibrary.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/561/1*sku_wZmsZu0GVMBgCmqFMA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5AAfwWbJLIjoZNqRfyx2jg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/520/1*n0CAZesw28ZBRrEOOiEU9A.png" /></figure><ul><li>Now we can add these to a GridMap. In your Main scene, add a new GridMap node.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/537/1*JPU3PiDKLyc6NqXIxL-pmw.png" /></figure><ul><li>Assign the library that you just created as its MeshLibrary resource. The tiles should now be available to be placed down.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sffRa8Qp1VGyKCb3SfmifA.png" /></figure><ul><li>If your meshes look odd, you can change the cell size to fit your mesh, as well as disable centering on a certain axis so it “snaps” better.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/707/1*nL-NYZXaBwiym3t-Bqr2yg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QcnUtVtDtpKEqvBvk3YPRw.png" /></figure><ul><li>You can alter between floors and planes by pressing the C, X, and Z keys on your keyboard.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tqQgzDfgnd8ZSL-p8rE5pQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0QT3E2Nz7vNFV4HPDyKqEQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uGIFj7q2E0QiKU08e0GL6g.png" /></figure><ul><li>You can delete tiles by hovering over them and holding down your <strong>right </strong>mouse button.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*G1QNKBjm9xqOct-b7f55HA.png" /></figure><ul><li>You can rotate meshes via the D and S keys on your keyboard.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/869/1*zCQlzTGLmtw7_hfOCOiHcQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/802/1*eTxVp_s5TIWSYvyakxH9hg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/852/1*f5osS9dqDpWdyudcgZxV_Q.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/855/1*8vm1NmpYIFhyj25QHCb1CQ.png" /></figure><ul><li>You will have to use multiple GridMaps with different settings to achieve a good result. Unless you’re using tiles that are all the same size, you won’t be able to do everything using only one GridMap.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/363/1*q5ZgCN6Rjutp-1b9jfTOHQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/495/1*Za4BHTKrllFwN1hm4jDKAw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/489/1*H0KPbOclrFkUBG-KkLAmeg.png" /></figure><ul><li>Go ahead and create your mini world.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6PtRUmu8R47aRupMMaa7xA.png" /></figure><ul><li>Run your scene and test your creation!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/822/1*hHn_LHziMo-NboUpEOw_zw.gif" /></figure><h3>Timer</h3><p>The Timer node is used to create countdown timers that can trigger events after a specified period. The Timer node provides several properties to control its behavior, including wait_time, autostart, and one_shot.</p><ul><li><strong>wait_time</strong>: The duration in seconds that the timer will count down before emitting the timeout signal.</li><li><strong>autostart</strong>: If set to true, the timer will start automatically when the scene is loaded.</li><li><strong>one_shot</strong>: If set to true, the timer will stop after emitting the timeout signal once. If false, the timer will restart automatically after each timeout.</li></ul><p>It comes with a timeout signal, which is emitted when the timer reaches zero. This signal can be connected to a function to perform specific actions when the timer completes its countdown. The timeout signal is a crucial part of the Timer node&#39;s functionality, allowing you to trigger events at precise intervals.</p><h4>Mechanic:</h4><p>Spawn an enemy every 5 seconds.</p><h4>Implementation:</h4><ul><li>Add a Timer node to your scene. Set its wait_time to 5 seconds. Since we want the enemy to “spawn” as soon as the game loads, we should enable its autostart property.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/351/1*siTDir0quHSj4WwJcxBquQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/501/1*dRQl052lJst_pl0nbWwPxQ.png" /></figure><ul><li>Connect the timer node’s timeout signal to your script. This will execute our logic to spawn our enemy every 5 seconds.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/938/1*gjy0H078FKlypI2EY3G1fA.png" /></figure><ul><li>In your code, let’s “spawn” an enemy. Since we don’t have an actual enemy scene, we will just print the amount of enemies we have spawned.</li></ul><pre>### Main.gd<br><br>extends Node3D<br><br>@onready var timer = $Timer<br>var enemy_count = 0<br><br>func _ready():<br> if not timer.is_stopped():<br>  timer.start()<br><br>func _on_timer_timeout():<br> spawn_enemy()<br><br>func spawn_enemy():<br> enemy_count += 1<br> print(&quot;An enemy has spawned!&quot;)<br> print(&quot;Current enemy count: &quot;, enemy_count) </pre><ul><li>Run your game and your enemy should spawn each time the timer reaches 0!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*swh8PGWB5nOLpGF765Pd_w.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4482ce8a145c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Book of Nodes: UI]]></title>
            <link>https://christinec-dev.medium.com/the-book-of-nodes-ui-b5ad5e0cad57?source=rss-3d2c08006851------2</link>
            <guid isPermaLink="false">https://medium.com/p/b5ad5e0cad57</guid>
            <category><![CDATA[3d]]></category>
            <category><![CDATA[godot]]></category>
            <category><![CDATA[node]]></category>
            <dc:creator><![CDATA[Christine Coomans]]></dc:creator>
            <pubDate>Wed, 07 Aug 2024 12:43:46 GMT</pubDate>
            <atom:updated>2024-08-20T14:21:30.890Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Usr2YFR46gWJWm44HzdgOg.png" /></figure><p>Below you can find a list of UI nodes that can be used in Godot 4. This is part of my <a href="https://christinec-dev.medium.com/the-book-of-nodes-782ef4ba3c8a">Book of Nodes</a> series. If you want to see similar content on 2D or 3D nodes, please refer to the parent page of this post for those links. 😊</p><p>Before we begin, if you need a base project to test these code snippets, feel free to download my FREE 2D and 3D templates <a href="https://christinecdevs.site/2d-3d-prototype-templates/">here</a>. I’ll be using these templates throughout this post.</p><p><em>*Please note that this list is NOT 100% complete yet, but I will be updating this list as time goes on.</em></p><ul><li><strong>Control</strong></li><li><strong>CanvasLayer</strong></li><li><strong>CanvasModulate</strong></li><li><strong>Container</strong></li><li><strong>HBoxContainer</strong></li><li><strong>VBoxContainer</strong></li><li><strong>GridContainer</strong></li><li><strong>ColorRect</strong></li><li><strong>Panel</strong></li><li><strong>Button</strong></li><li><strong>Label</strong></li><li><strong>RichTextLabel</strong></li><li><strong>ProgressBar</strong></li></ul><h3>Common Properties</h3><h4>Anchoring</h4><ul><li>Determines how a UI element is positioned and resized relative to its parent container.</li><li>anchor_left: Sets the left anchor point (0 to 1).</li><li>anchor_top: Sets the top anchor point (0 to 1).</li><li>anchor_right: Sets the right anchor point (0 to 1).</li><li>anchor_bottom: Sets the bottom anchor point (0 to 1).</li><li>full_rect: Sets the anchor point to all sides.</li></ul><h4>Margins</h4><ul><li>Defines the distance between the UI element and its parent container’s edges.</li><li>margin_left: Distance from the left edge.</li><li>margin_top: Distance from the top edge.</li><li>margin_right: Distance from the right edge.</li><li>margin_bottom: Distance from the bottom edge.</li></ul><h4>Font</h4><ul><li>Customizes the appearance of text within UI elements.</li><li>font: The font resource used for the text.</li><li>font_size: The size of the font.</li><li>font_color: The color of the font.</li></ul><h4>Color</h4><ul><li>Customize the color properties of UI elements.</li><li>color: The main color of the UI element.</li><li>font_color: The color of the text.</li><li>modulate: The alpha value applied to the UI element.</li></ul><h4>Transform</h4><ul><li>Controls the position, rotation, and scale of UI elements.</li><li>position: The position of the UI element.</li><li>rotation: The rotation of the UI element in degrees.</li><li>scale: The scale of the UI element.</li></ul><h4>Size Flags</h4><ul><li>Determines how a UI element resizes within its parent container.</li><li>size_flags_horizontal: Horizontal resizing behavior (e.g., fill, expand).</li><li>size_flags_vertical: Vertical resizing behavior (e.g., fill, expand).</li></ul><h4>Visibility</h4><ul><li>Controls the visibility of UI elements.</li><li>visible: Whether the UI element is visible.</li><li>self_modulate: The color modulation applied to the UI element, affecting its visibility.</li></ul><h4>Focus</h4><ul><li>Manages keyboard and controller focus for UI elements.</li><li>focus_mode: Determines if the UI element can receive focus (none, click, all).</li><li>focus_neighbour_*: Specifies neighboring UI elements for focus navigation (up, down, left, right).</li></ul><h4>Tooltip</h4><ul><li>Provides additional information when the user hovers over a UI element.</li><li>hint_tooltip: The text displayed as a tooltip.</li></ul><h4>Theme Overrides</h4><ul><li>Allows customization of the UI element’s appearance beyond the default theme.</li><li>theme: The theme resource applied to the UI element.</li><li>theme_type_variation: A variation of the theme type for more specific customization.</li></ul><h3>Control</h3><p>A Control node provides a bounding rectangle on the viewport that can be used for creating user interfaces in Godot, as they handle input events, focus, and other UI-specific functionalities.</p><h4>Mechanic:</h4><p>Drag and drop a sprite on the map.</p><h4>Implementation:</h4><ul><li>Add a Control node to your scene. This represents the item you will drag and drop.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/424/1*-W9TcfXvFappF48CJ5hrtA.png" /></figure><ul><li>We need to visualize it, so add a TextureRect node to it. This node allows us to add a sprite to the control so that we can see what we are moving. This works better than the Sprite2D/3D node because it can be anchored to our Control container.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/431/1*DcQaJdLeI5DgRPYEqKnezw.png" /></figure><ul><li>Add any texture (icon) to this TextureRect.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/258/1*9Yf-OAIzVFuKVZDJdZaW4Q.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/885/1*MTh5LlM7FfOIXoKkjm4h0g.png" /></figure><ul><li>Attach a script to your Control node. To be able to drag and drop this object, we will need to connect its gui_input() signal to our script.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/731/1*DFt9GkkM-lcZ3VEaHNuk2A.png" /></figure><ul><li>In your code, add the functionality to select the item using the LEFT mouse button. Use set_drag_preview to show a visual representation of the item being dragged.</li><li>Then, add the functionality to drop your item on the LEFT mouse button release.</li><li>And then also add the functionality to snap the item to your mouse cursor whilst dragging it around.</li></ul><pre>### Control.gd<br><br>extends Control<br><br>var drag_offset = Vector2.ZERO<br>var is_dragging = false<br><br>func _on_gui_input(event):<br> if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:<br>  # Select<br>  if event.pressed:<br>   is_dragging = true<br>   if get_viewport().gui_is_dragging():<br>    set_drag_preview(self)<br>  else:<br>   # Drop<br>   is_dragging = false<br> # Drag<br> elif event is InputEventMouseMotion and is_dragging:<br>  var global_pos = get_global_mouse_position()<br>  global_position = global_pos - size / 2</pre><ul><li>Test your logic by selecting the item and dragging it around.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*Y4BwglUnx2__NhytZGfadw.gif" /></figure><h3>CanvasLayer</h3><p>A CanvasLayer is used for independent rendering of objects within a 2D scene, often for UI elements or HUDs. It renders its child nodes independently of the main scene&#39;s camera, which means that they remain fixed on the screen.</p><h4>Example Use Cases:</h4><ul><li>HUD (Heads-Up Display).</li><li>Fixed-position UI elements.</li><li>Overlay menus.</li></ul><h4>Mechanic:</h4><p>To demonstrate the use of the layer property, we will create a simple scene with two CanvasLayer nodes. One will be used for a background layer, and the other for a UI layer. The background layer will be drawn behind the UI layer.</p><h4>Implementation:</h4><ul><li>Add two CanvasLayernodes to your scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/430/1*WRzVSlYWX2CsRCAhLhhH0Q.png" /></figure><ul><li>Add a Label node to the first CanvasLayer node. Add a ColorRect to the second CanvasLayer node.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/579/1*cknNQzw-53g1xLsFhK6kUw.png" /></figure><ul><li>Give the Label some text, and change the ColorRects color to a dark grey. Also, change its anchor-preset to be full_rect.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/261/1*pgUFk9uyDthruC0ijdiv9Q.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*huev4BE1d2yBmd6XZFjIng.png" /></figure><ul><li>You will see that our label isn’t showing, because our first CanvasLayer node has the same Layer value as the second CanvasLayer.</li><li>Change the first CanvasLayer node’s Layer value to be “2”.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/488/1*_Gv8RpBLcEbqO05YN0Sb8A.png" /></figure><ul><li>Your label should now be on top of the background!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/915/1*FyhjwJfy2yuiorpSbYR4qQ.png" /></figure><h3>Container</h3><p>A Container is used to organize UI elements, providing a base class for various container types. It inherits from Control and provides functionality for arranging child nodes. We usually don’t use this node a lot, as a normal Control node suffices.</p><p>You might, however, use a ScrollContainer node. A ScrollContainer is a type of container node in Godot that provides scrollbars to its child control when needed. It is useful for creating scrollable areas in your UI, such as lists, text areas, or any content that exceeds the visible area of the container.</p><h4>Example Use Cases:</h4><ul><li>Base class for custom container types.</li><li>Organizing complex UI layouts.</li><li>Creating custom layout behaviors.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*i8xOrJTgSqC5Tv5nZlFtdQ.png" /></figure><h3>HBoxContainer</h3><p>An HBoxContainer is used to arrange UI elements horizontally. It inherits from Container and arranges its children in a horizontal line.</p><h4>Example Use Cases:</h4><ul><li>Horizontal menus.</li><li>Toolbars.</li><li>Row-based layouts.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*unLYt5v9vrrpRJshd0gtRg.png" /></figure><h4>Mechanic:</h4><p>Arrange a list of items horizontally on the scene.</p><h4>Implementation:</h4><ul><li>Add a Control node to your scene. This will hold our HBoxContainer. Change its size to something like 600px by 600px to give it a bounding area.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/485/1*9_GTZRhAaixpqyVBMi0KKg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/981/1*iovlc4e2Ar-SSpf-5w1bXA.png" /></figure><ul><li>Add a HboxContainer node to this Control node. Also, make sure its anchor preset is full_rect so that it takes up the entire space provided by the Control node.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*12fRbGGuDxreX1vAhyTtwQ.png" /></figure><ul><li>Add three Label nodes to the HBoxContainer.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zMlA3skzLl2iVsd3zG3NkQ.png" /></figure><ul><li>You will see that the items stack next to each other, but they don’t take up the entire space. To fix this, we will need to set their container sizing properties to fill and expand.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/483/1*jBiLguWebBVlbh_9InGEBQ.png" /></figure><ul><li>Now your labels will be arranged horizontally across the entire 600px by 600px boundary box!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cBpjx-lLFMX04Ei-uQUSQg.png" /></figure><ul><li>To add some spacing between the items, select the HBoxContainer node, and change its separation constant to a value such as 10.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/475/1*05ckYghCuXvK-nskbJhVow.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gjtNmwbH719JwBem2KY16g.png" /></figure><h3>VBoxContainer</h3><p>A VBoxContainer is used to arrange UI elements vertically. It inherits from Container and arranges its children in a vertical line.</p><h4>Example Use Cases:</h4><ul><li>Vertical settings menus.</li><li>Stacked buttons.</li><li>Column-based layouts.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1U76e-QoJ5R2Fn9NC0jvRw.png" /></figure><h4>Mechanic:</h4><p>Arrange a list of items vertically on the scene.</p><h4>Implementation:</h4><ul><li>Add a Control node to your scene. This will hold our VBoxContainer. Change its size to something like 600px by 600px to give it a bounding area.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/485/1*9_GTZRhAaixpqyVBMi0KKg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/981/1*iovlc4e2Ar-SSpf-5w1bXA.png" /></figure><ul><li>Add a VboxContainer node to this Control node. Also, make sure its anchor preset is full_rect so that it takes up the entire space provided by the Control node.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0Vfgcb2mc0eie903bSu-1Q.png" /></figure><ul><li>Add three Label nodes to the VBoxContainer.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/797/1*1UoXqECugFL0jOpvMTFH9A.png" /></figure><ul><li>You will see that the items stack on top of each other, but they don’t take up the entire space. To fix this, we will need to set their container sizing properties to fill and expand.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/483/1*jBiLguWebBVlbh_9InGEBQ.png" /></figure><ul><li>Now your labels will be arranged horizontally across the entire 600px by 600px boundary box!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iU2gdjFZJby3Rg4f0NUWdQ.png" /></figure><ul><li>To add some spacing between the items, select the VBoxContainer node, and change its separation constant to a value such as 10.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/489/1*-AF4Nx4Sol68JVMSzA41Aw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Txp3K8kCyeD6sc9_VO07fA.png" /></figure><h3>GridContainer</h3><p>A GridContainer is used to arrange UI elements in a grid. It inherits from Container and arranges its children in a grid layout.</p><h4>Example Use Cases:</h4><ul><li>Keypads.</li><li>Control panels.</li><li>Inventory grids.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UqkZtg9cR-mRpLNUj693vA.png" /></figure><h4>Mechanic:</h4><p>Arrange a list of items in a grid on the scene.</p><h4>Implementation:</h4><ul><li>Add a Control node to your scene. This will hold our GridContainer. Change its size to something like 600px by 600px to give it a bounding area.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/485/1*9_GTZRhAaixpqyVBMi0KKg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/981/1*iovlc4e2Ar-SSpf-5w1bXA.png" /></figure><ul><li>Add a GridContainer node to this Control node. Also, make sure its anchor preset is full_rect so that it takes up the entire space provided by the Control node.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nnB2KzHbXSE6OTY4hFxsTg.png" /></figure><ul><li>Add fourLabel nodes to the GridContainer.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/707/1*va-zo1UIzxfICcDhtjcVvA.png" /></figure><ul><li>You will see that the items stack on top of each other, instead of in a grid. To fix this, we will need to up our columns property in our GridContainer node. Change this value to 2.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/485/1*4EXaXXV9e-0f0bJYxnlqbg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/803/1*CMyEI5tvSriAhQnDd8dIMA.png" /></figure><ul><li>Now they are arranged in the grid, but they don’t take up the entire space. To fix this, we will need to set their container sizing properties to fill and expand.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/481/1*5DX5gAEF4pWF6vF5911YmQ.png" /></figure><ul><li>Now your labels will be arranged in a grid format across the entire 600px by 600px boundary box!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JTq8Kz3Yqny2LsJl9P_SKA.png" /></figure><ul><li>To add some spacing between the items, select the GridContainer node, and change its separation constant to a value such as 10.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/490/1*T89_FF685-LLSqAiW5pTAg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GTPjkqWChI7spCIr6vrRyA.png" /></figure><h3>ColorRect</h3><p>A ColorRect is used to display a solid color rectangle, often for backgrounds or color overlays. It inherits from Control and provides a simple way to display a rectangle filled with a specified color. The color can be set and changed dynamically.</p><h4>Example Use Case:</h4><ul><li>Background overlays.</li><li>Progress bars.</li><li>Color indicators.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/840/1*YTW0QwCHCD6KENIQMUhwIg.png" /></figure><h4>Mechanic:</h4><p>Add a background color to your scene.</p><h4>Implementation:</h4><ul><li>Add a CanvasLayer node to your scene. This will ensure that our ColorRect is drawn on our UI, so it can be displayed in the background.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/424/1*fJeHt_uKvJVk8datXYU_sg.png" /></figure><ul><li>Add a ColorRectnode to your CanvasLayer node.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/417/1*1O087vkT5KufcpAWJ8ks8Q.png" /></figure><ul><li>Change its color, and change its anchor preset to be full_rect so that it takes up the entire screen.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*O298St8ufoKYQ0TBHvepWw.png" /></figure><ul><li>Change the CanvasLayer node’s layer property to something like -1 so that it is displayed behind our other nodes. We should now have a background color!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AoAkGy00oXUzsB5jdJm6_g.png" /></figure><h3>Panel</h3><ul><li>A Panel is used to create a panel for grouping UI elements, providing a background and border. It inherits from Control and provides a simple way to group and visually separate UI elements.</li></ul><h4>Example Use Case:</h4><ul><li>Settings panels.</li><li>Dialog boxes.</li><li>Grouping related UI elements.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/935/1*cdFKrERPEB7H8hIAOXJoFA.png" /></figure><h4>Mechanic:</h4><p>Add a background color to your scene.</p><h4>Implementation:</h4><ul><li>Add a CanvasLayer node to your scene. This will ensure that our Panel is drawn on our UI, so it can be displayed in the background.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/424/1*fJeHt_uKvJVk8datXYU_sg.png" /></figure><ul><li>Add a Panel node to your CanvasLayer node.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/425/1*gCLnp29zNBpk1RewwEjETA.png" /></figure><ul><li>To change its color, we’ll need to give it a new theme style of type StyleBoxFlat.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/497/1*ZJrpDAj5M1MalBy_lfyKzg.png" /></figure><ul><li>Now we can give it a new color, border, and even a shadow!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/492/1*7chiQmfaveChJoS3An9gxQ.png" /></figure><ul><li>Change its anchor preset to full_rect so that it takes up the entire screen.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SYXXe0nfpVWayag14GgRKw.png" /></figure><ul><li>Change the CanvasLayer node’s layer property to something like -1 so that it is displayed behind our other nodes. We should now have a background color!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tJQCn3QFSP55bl_JI6vR7A.png" /></figure><h3>CanvasModulate</h3><p>A CanvasModulate applies a color tint to all nodes on a canvas. It tints the canvas elements using its assigned color.</p><h4>Example Use Cases:</h4><ul><li>Night mode effect.</li><li>Global color adjustments.</li><li>Visual effects for different game states.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*3FyNxwWMqdHtO7Vn" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*7Egm282il7wWjlas" /></figure><h4>Mechanic:</h4><p>Add a day-and-night cycle to your game</p><h4>Implementation:</h4><ul><li>Add a CanvasModulatenode to your scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/429/1*oYy5sBhZnebf_aKGu_yc8A.png" /></figure><ul><li>If you change its color property, the entire scene&#39;s color should change.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BYUWnE3ew_7fq05y7QhJyg.png" /></figure><ul><li>We want to animate this property. So, add an AnimationPlayer node to your scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/419/1*5otaqrGaTdEEHdG4Dus3ow.png" /></figure><ul><li>Create a new animation called day_night_cycle. Set the length of this animation to be 24 seconds long. This will make our day 24 seconds long (for 24 hours).</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/980/1*UpfSE5-CFxcMyUkhFtIM0g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/980/1*_euiR-wDIQhiNhabZzFkTg.png" /></figure><ul><li>Also, enable looping.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/980/1*fByYZ3uU17ItcUSIfJp4iw.png" /></figure><ul><li>Now add a color keyframe for every 6 hours (or whatever preference you have). These colors should represent the colors from 0 AM to 24 PM.</li><li>On my timeline, my colors are:<br><strong>0:</strong> #0f0a49<br><strong>6:</strong> #7b5436<br><strong>12:</strong> #ffffff<br><strong>18: </strong>#5b6a99<br><strong>24: </strong>#0f0a49</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*p8S3D_9wwxiXBlKVITqr-w.png" /></figure><ul><li>Now when we run our game, we should play this animation.</li></ul><pre>### Main.gd<br><br>extends Node2D<br><br>@onready var animation_player = $AnimationPlayer<br><br>func _ready():<br> animation_player.play(&quot;day_night_cycle&quot;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*gebUaLjGpqh3qIFy1deCTQ.gif" /></figure><ul><li>If you want a better tutorial using shaders, check out the video on my <a href="https://github.com/christinec-dev/DayNightCycleGodot">YouTube channel</a>!</li></ul><h3>Button</h3><p>A Button is used to create a clickable button for user interactions. It inherits from BaseButton and provides functionality for detecting clicks and triggering actions.</p><p>If you want to use a button that has a texture (image) and more customizability, you should use a TextureButton node. The TextureButton node is a button that uses textures for its different states (normal, pressed, hover, etc.) instead of the default theme. This allows for more customized and visually appealing buttons. It inherits from BaseButton and provides properties to set textures for different states.</p><h4>Example Use Cases:</h4><ul><li>Interactive buttons.</li><li>Menu options.</li><li>Form submissions.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*arehKRAxyLNLhfRR.png" /></figure><h4>Mechanic:</h4><p>Add a clickable button to your scene.</p><h4>Implementation:</h4><ul><li>Add a CanvasLayer node to your scene. This will ensure that our Buttonis drawn on our UI, so it can be displayed at all times.</li><li>Add a Button node to your CanvasLayer node.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/931/1*tSx_LMaB5aXezm5K33X2lg.png" /></figure><ul><li>If you run your scene, your Button should be visible.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*s2rSvfSgEgrzYMVvYrBZfg.png" /></figure><ul><li>Now, attach its pressed() signal function to your script.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/874/1*BIhUmBphAPK4_7r_0hsulQ.png" /></figure><ul><li>If we press the button, we will change its text. We will keep track of the times we’ve pressed it and update it each time we press the button.</li></ul><pre>### Main.gd<br><br>extends Node2D<br><br>@onready var button = $CanvasLayer/Button<br><br>var button_pressed_count = 0<br><br>func _on_button_pressed():<br> button_pressed_count += 1<br> button.text = &quot;You&#39;ve pressed me: &quot; + str(button_pressed_count) + &quot; times!&quot;</pre><ul><li>Every time you press the button, the count should update!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/649/1*fZh-W7hhcmNQ5o1Y_j6yyA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/677/1*wQWoG1PNHxSM2QbTYpUQGQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/671/1*I8UbElRmgVaPdoTpsOXbUw.png" /></figure><h3>Label</h3><p>A Label is used to display text. It inherits from Control and provides functionality for displaying text.</p><h4>Example Use Cases:</h4><ul><li>Static text display.</li><li>Descriptive labels.</li><li>Titles and headings.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*En_IPRdJfycwj55w.png" /></figure><h4>Mechanic:</h4><p>Add a label that changes its text when you hover over it.</p><h4>Implementation:</h4><ul><li>Add a CanvasLayer node to your scene. This will ensure that our Label is drawn on our UI, so it can be displayed at all times.</li><li>Add a Labelnode to your CanvasLayer node.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KvLDNdTqmYEjheK3O1va3w.png" /></figure><ul><li>If you run your scene, your Label should be visible.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hDBP26J3WdiRHOdhF2PhwA.png" /></figure><ul><li>Now, let’s make it more visible. Change its font color to be red, and its size to something like 25px.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OkyPYwtQ-9pqCZA6tRl79w.png" /></figure><ul><li>Since we want it to detect mouse events, we need to change its Mouse Filtermode to Pass.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/411/1*5jJPEko-18ioWksrHjwyRw.png" /></figure><ul><li>Now, to detect that we are hovering over it, we need to attach its mouse_entered() and mouse_exited() signals to our script.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/879/1*k9d2c41tohL6tdW70TpKyA.png" /></figure><ul><li>Then, simply change the text when we enter and exit the label while hovering! If you run your game, the label should change depending on whether or not you are hovering over it.</li></ul><pre>### Main.gd<br><br>extends Node2D<br><br>@onready var label = $CanvasLayer/Label<br><br><br>func _on_label_mouse_entered():<br> label.text = &quot;You&#39;re hovering over a label&quot;<br><br><br>func _on_label_mouse_exited():<br> label.text = &quot;You&#39;re away from the label&quot;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kafn21XKTX_23bks9oWTiA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uyJ4hJgEK_q9EwDdgD43TQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2pmNQ1WPUtxS71Q-b1HuQg.png" /></figure><h3>RichTextLabel</h3><p>A RichTextLabel is used to display formatted text. It inherits from Control and provides functionality for displaying rich text with formatting, such as bold, italics, and images.</p><h4>Example Use Cases:</h4><ul><li>Formatted text display.</li><li>Dialogue boxes.</li><li>Instructional text.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ufXPEXpSU1N07oE9.png" /></figure><h4>Mechanic:</h4><p>Write words in a typewriter effect.</p><h4>Implementation:</h4><ul><li>Add a CanvasLayer node to your scene. This will ensure that our RichTextLabelis drawn on our UI, so it can be displayed at all times.</li><li>Add a RichTextLabel node to your CanvasLayer node.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/569/1*562GwtEqVa8vSqi4DKQ-rA.png" /></figure><ul><li>Change its size to something like 200px by 200px.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/420/1*jqw7gbuWfmtHDLiksueY7A.png" /></figure><ul><li>If you run your scene, your RichTextLabel should be visible.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*szA7hmOecj_peHIiQw2Swg.png" /></figure><ul><li>Now, let’s make it more visible. Change its font color to red, and its size to something like 25px.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rozEOh1IUg0o8ymor1Q8IQ.png" /></figure><ul><li>To create our typewriter animation, we will need to use an AnimationPlayer node. Add this node to your scene.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/570/1*PpWcE540uo5fHiM3S5ketw.png" /></figure><ul><li>Create a new animation called typewriter.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/914/1*fYrT9XDOIR0kPcvUosyeIA.png" /></figure><ul><li>To create a typewriter effect, we need to animate our RichTextLabel node’s visible ratio property, which defines how much of our text is visible.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tIs7T60q4Kx0JiIMHAgzog.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*o3sgYSKDbwxu4IvusATefQ.png" /></figure><ul><li>At the 0 keyframe mark, set your visible ratio to be equal to 0.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kb2gCmCbVusoyVi4njp98Q.png" /></figure><ul><li>At the 1 keyframe mark, set your visible ratio to be equal to 1. This means in 1 second, our text will go from no characters to full characters.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BEkEVebi418VbDBCTD7Lrw.png" /></figure><ul><li>Now if you play your animation, your text should play as if it&#39;s being typed out.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*B2j89MdBCuQcJN1FZx7FBQ.gif" /></figure><h3>ProgressBar</h3><p>ProgressBar is a UI element that visually represents a value within a range. It inherits from the Range class, which provides properties like min_value, max_value, and value to control the progress.</p><h4>Example Use Cases:</h4><ul><li>Loading screens.</li><li>Health bars.</li><li>Progress indicators.</li></ul><h4>Mechanic:</h4><p>Create a health bar that increases every 1 second, and when we press SPACE, it decreases in value.</p><h4>Implementation:</h4><ul><li>Add a CanvasLayer node to your scene. This will ensure that our ProgressBar node is drawn on our UI, so it can be displayed at all times.</li><li>Add a ProgressBarnode to your CanvasLayer node.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/325/1*cwlTYJob7zexNHYDiFIBSw.png" /></figure><ul><li>Change its size to something like 200px by 27px, and enable its Rounded value so it only shows whole numbers.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*L0eipvmZ0tulvGXYQ6A72Q.png" /></figure><ul><li>To be able to increase our ProgressBar, we should add a Timer node. Enable this timer node’s autostart property, and connect its timeout signal to your script.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/330/1*AFBtBBvJPKpBFvT4Er79TQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/479/1*v9haZ7FZuVHVbv6zTGxB4A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/946/1*DD-_BX_PEWzedwipYHNbng.png" /></figure><ul><li>Whenever it times out (every 1 second its active), we will increase our “health” value.</li></ul><pre>### Main.gd<br><br>extends Node2D<br><br>@onready var progress_bar = $CanvasLayer/ProgressBar<br><br>func _on_timer_timeout():<br> progress_bar.value += 1</pre><ul><li>Whenever we press our SPACE bar, we should decrease our health value.</li></ul><pre>### Main.gd<br><br>extends Node2D<br><br>@onready var progress_bar = $CanvasLayer/ProgressBar<br><br>func _on_timer_timeout():<br> progress_bar.value += 1<br><br>func _input(event):<br> if event.is_action_pressed(&quot;ui_accept&quot;):<br>  progress_bar.value -= 5</pre><ul><li>Now if we run our scene, our health should increase every second, but decrease when we press our button.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*rr3wN4aaGVxU4e0FWGiXKw.gif" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b5ad5e0cad57" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Book of Nodes]]></title>
            <link>https://christinec-dev.medium.com/the-book-of-nodes-782ef4ba3c8a?source=rss-3d2c08006851------2</link>
            <guid isPermaLink="false">https://medium.com/p/782ef4ba3c8a</guid>
            <category><![CDATA[node]]></category>
            <category><![CDATA[godot]]></category>
            <dc:creator><![CDATA[Christine Coomans]]></dc:creator>
            <pubDate>Wed, 07 Aug 2024 12:43:23 GMT</pubDate>
            <atom:updated>2025-10-19T18:14:01.763Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Usr2YFR46gWJWm44HzdgOg.png" /></figure><p>If you’ve ever delved into the Godot Documentation, you likely noticed two things: first, there is an abundance of nodes (many of which you might never use), and second, some of these nodes can be quite confusing.</p><p>As a beginner Godot developer, understanding the purpose of each node can be confusing. Even as a somewhat seasoned Godot creator, I am still learning about new nodes and methods of doing things to this day. For example, when I first started learning Godot, I used a ColorRect node for all my UI backgrounds. However, this meant that if I wanted a background with a border, I had to use multiple ColorRects to achieve this.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*xrBSbF0FO2F0ruZkGaM7IQ.png" /><figcaption>Figure 1: Using ColorRects to create a box with a border and a shadow.</figcaption></figure><p>Later on, I discovered the Panel node, which offers a lot more customization, built-in borders, and even rounded corners! Although I still use ColorRects, I now know that there are better nodes (like the Panel node) that I can use to achieve the same result.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*WV17Q0b2fwkLp2n-4LyvxQ.png" /><figcaption>Figure 2: Using a Panel to create a box with a border, curved corners, and a shadow.</figcaption></figure><p>That’s why I wanted to create an encyclopedia of sorts, focusing on the most essential nodes to know as a beginner.</p><p>Now, to complete this encyclopedia, I’ve decided to organize the content into three distinct <strong>categories</strong>: 2D, 3D, and UI. Within each category, you’ll find a list of <strong>nodes</strong> relevant to that section. For each node, I will cover the <strong>mechanics</strong> that can be implemented using this node, along with basic examples of how this mechanic can be <strong>implemented</strong> into a project. This ensures that you not only learn about each node’s functionality but also how they can be practically applied during your game development process.</p><p>Before we begin, if you need a base project to test these code snippets, feel free to download my FREE 2D and 3D templates <a href="https://christinecdevs.site/2d-3d-prototype-templates/">here</a>. I’ll be using these templates throughout this post.</p><p><em>*Please note that this list is not 100% complete yet, but I will be updating this list as time goes on.</em></p><h3>Categories:</h3><ul><li><a href="https://christinec-dev.medium.com/the-book-of-nodes-2d-16f13691ac5f">2D Nodes</a></li><li><a href="https://christinec-dev.medium.com/the-book-of-nodes-3d-4482ce8a145c">3D Nodes</a></li><li><a href="https://christinec-dev.medium.com/the-book-of-nodes-ui-b5ad5e0cad57">UI Nodes</a></li></ul><h3>UPDATE:</h3><p>Get your copy of My Book of Nodes for Godot 4 in an offline PDF format (and Word for GIF previews) now!</p><p>In the offline version, all the categories (2D, 3D, and UI) are combined in a single document. No more waiting for my websites and blogs to load — take the content with you and access it instantly, wherever you are.</p><p>Check it out, it’s free. 😊</p><p><a href="https://ko-fi.com/s/e9f19e30ff">https://ko-fi.com/s/e9f19e30ff</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=782ef4ba3c8a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Godot 2D & 3D Prototype Templates]]></title>
            <link>https://christinec-dev.medium.com/godot-2d-3d-prototype-templates-73898a183790?source=rss-3d2c08006851------2</link>
            <guid isPermaLink="false">https://medium.com/p/73898a183790</guid>
            <category><![CDATA[3d]]></category>
            <category><![CDATA[prototyping]]></category>
            <category><![CDATA[godot]]></category>
            <category><![CDATA[2d]]></category>
            <category><![CDATA[template]]></category>
            <dc:creator><![CDATA[Christine Coomans]]></dc:creator>
            <pubDate>Mon, 05 Aug 2024 12:21:23 GMT</pubDate>
            <atom:updated>2024-08-05T12:21:23.440Z</atom:updated>
            <content:encoded><![CDATA[<p>I have created templates for my Godot 2D and 3D projects on YouTube and KoFi. These templates are designed for prototyping and developing systems in Godot. They make it easier for us because we no longer have to build everything from scratch! 😊</p><p>The 2D template contains a basic top-down character and a map, and the entire project template was created by me with the help of the following assets:</p><ul><li><a href="https://seliel-the-shaper.itch.io/character-base">https://seliel-the-shaper.itch.io/character-base</a></li><li><a href="https://quintino-pixels.itch.io/assorted-icons">https://quintino-pixels.itch.io/assorted-icons</a></li><li><a href="https://cupnooble.itch.io/sprout-lands-asset-pack">https://cupnooble.itch.io/sprout-lands-asset-pack</a></li></ul><p>The 3D template contains a basic third-person character and a map, and the entire project was created by me with the help of the following assets:</p><ul><li><a href="https://godotshaders.com/shader/stylized-sky/ ">https://godotshaders.com/shader/stylized-sky/</a></li><li><a href="https://kenney.nl/assets/category:3D?sort=update">https://kenney.nl/assets/category:3D</a></li><li><a href="https://godotengine.org/asset-library/asset/1815 ">https://godotengine.org/asset-library/asset/1815</a></li></ul><h4><strong>3D Preview:</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*b31hiO4ynbDLRrXWEFF4aQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Co1QtOHK_J70GDMo.png" /></figure><h4><strong>2D Preview:</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*LPvNjGKMwZANon7i.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*aHYKNddyKH-XBO5P.png" /></figure><p>You can download these projects for free on my KoFi page. They are not available on GitHub because the file size was too large to upload. You don’t need to pay to use them!</p><p>➡️<a href="https://ko-fi.com/s/ab188420b7">Download Templates</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=73898a183790" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Try Hack Me: Friday Overtime Complete Write-up ]]></title>
            <link>https://christinec-dev.medium.com/try-hack-me-friday-overtime-complete-write-up-be998d855d31?source=rss-3d2c08006851------2</link>
            <guid isPermaLink="false">https://medium.com/p/be998d855d31</guid>
            <category><![CDATA[virustotal]]></category>
            <category><![CDATA[tryhackme]]></category>
            <category><![CDATA[tutorial]]></category>
            <category><![CDATA[cyberchef]]></category>
            <category><![CDATA[writeup]]></category>
            <dc:creator><![CDATA[Christine Coomans]]></dc:creator>
            <pubDate>Fri, 26 Jul 2024 11:15:34 GMT</pubDate>
            <atom:updated>2024-07-26T11:15:34.831Z</atom:updated>
            <content:encoded><![CDATA[<p>In my most recent side-quest in life, I decided to dive back into the world of <a href="https://www.kaspersky.co.za/blog/mission-hacking-grandma/10373/"><em>hAcK1_nG</em></a>. The last time I was dabbling in the art of Cybersecurity, I focused more on red-teaming, but this time my interest is peaked by the life of a blue-teamer.</p><p>That’s right, I’m joining The Defenders (trademark coming soon). But before I do that, let’s have a look at completing the <a href="https://tryhackme.com/r/room/fridayovertime"><em>Friday Overtime</em></a> room.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/0*IPsF9wDChC53j_VR.gif" /></figure><p>So in this scenario, we are the only remaining CTI Analyst working overtime at PandaProbe Intelligence. We need to investigate potential file attachment breaches that are suspected to contain malware samples.</p><p>Let’s start by running our machine. This might take a few minutes, so wait until your machine IP is populated.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/645/1*iBOCqMwUjPM_4dFP_L1yYw.png" /></figure><p>After your machine is up and running, let’s start answering our questions.</p><h4>1. Who shared the malware samples?</h4><p>On the machine, open up Chromium and navigate to your given IP Address. At the top of the CentOS screen is an option to “Log in — DocIntel”. Click on it and log in with the given credentials.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*r52rD9wEalxfRe14Vhd5Yw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8HNmZ1LhlBAolJ2fW_5n0w.png" /></figure><p>From there on you will be greeted by your email, which contains the information on the breach as well as the document containing the files, and the password to the document.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LORkvVqv61OhDn2rEUDzyw.png" /></figure><p>If you scroll down, you will see the name of the person who shared the malware sample files.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8HNmZ1LhlBAolJ2fW_5n0w.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sBlXcZyaSTzXyphgXsPHQw.png" /></figure><h4>2. What is the SHA1 hash of the file “pRsm.dll” inside samples.zip?</h4><p>To find this out, we will need to first download the actual attachment and extract the files from it. Click on the attatchment on the right to download the file called “sample.zip”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LORkvVqv61OhDn2rEUDzyw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*K6i9jAhtUCOr5f_tagC63Q.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SwYAVNEHedTJpOLx03FwAA.png" /></figure><p>Now, you can either extract this zipped folder in the Command Line, or just by right-clicking it and extracting it manually. Remember, the password to this file is <em>Panda321!, </em>so enter that when prompted.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*f9qBVBLQYKafrusEBkn9ow.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qTO_FPkKKgkCSEoS8G0jGQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*A31hLgEa-aej7G5add8W2A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yYZP9BlV96Nedb0FwzOp6A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xyc19NFkRZPCKrMZW9aTgg.png" /></figure><p>Let’s open up our Terminal so that we can read the SHA1 hash of the file “pRsm.dll”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BK-QB7TRwSufRx1LmBHfHg.png" /></figure><p>To read the hash of our file, we will make use of <a href="https://man7.org/linux/man-pages/man1/sha1sum.1.html">sha1sum</a>. SHA1sum is a utility that computes and verifies SHA-1 cryptographic hash values. The primary reason you can use SHA1sum to read the hash of our.dll file — or any file, for that matter — is due to its ability to process binary data and generate a fixed-size hash value, which is a representation of the file’s content.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*J23aCYiaTDwL_0QUAnUu2g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fWJGXNt5HA28vEYich6xAg.png" /></figure><h4>3. Which malware framework utilizes these DLLs as add-on modules?</h4><p>For this question, I turned to good old Google to see if I could find any information on “pRsm.dll” and the framework used.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Rlv5LrA08FrhjDQlhMoInw.png" /></figure><p>I saw it flagged in <a href="https://www.welivesecurity.com/2023/04/26/evasive-panda-apt-group-malware-updates-popular-chinese-software/">this</a> article, so I clicked on it and searched for “framework”. Without too much effort, you can find the answer in the second paragraph of the article.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Kdu-2fuqjwq_vZ_eL0DI6Q.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DHsIt5WK86EdF2Mw8XEkrA.png" /></figure><h4>4. Which MITRE ATT&amp;CK Technique is linked to using pRsm.dll in this malware framework?</h4><p>For this, we need to search “pRsm.dll” and look out for a Technique ID (TXXXX), since we see that the answer required has 5 characters.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BGXn6q9LkIhNR6aENSejAw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0UMaek7AJJpejcHDvRwgbQ.png" /></figure><h4>5. What is the CyberChef defanged URL of the malicious download location first seen on 2020–11–02?</h4><p>Now we need to find a URL connected to 2020–11–02, and <a href="https://medium.com/@hammazahmed40/understanding-defanging-of-ip-addresses-cf3efc190403">defang</a> it on <a href="https://gchq.github.io/CyberChef/">CyberChef</a>. Defanging URLs and IPs is a critical practice for safely sharing potentially harmful links, and CyberChef simplifies this process, especially when handling large volumes of data. This practice is essential when sharing resources that might contain harmful data.</p><p>Let’s head back to our article, and search for our date “2020–11–02”. We can see that it lands on a result that has a URL attached to it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wm2yudCbBzm_Kk4mU0bZog.png" /></figure><p>Copy and paste this URL into the CyberChef input, and add the recipe “Defang URL”. The output will give you your answer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-A8vmBZWgL1ior23ACSM_w.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LQplWXzV8fXVgxRpFk8q-A.png" /></figure><h4>6. What is the CyberChef defanged IP address of the C&amp;C server first detected on 2020–09–14 using these modules?</h4><p>For this, we also need to search our date on the article we sourced previously. The IP should correlate to the date first seen on “2020–09–14”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mW3JcULjvJVTjtgr5pklDQ.png" /></figure><p>Let’s copy this IP into the CyberChef input (remove the brackets because it should be pure data). Change the recipe to “Defang IP”. The result should be your answer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9nhXJAlRBqtgbe-_xiZwXQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*r47TokFaJCRW8mE0BgQzxg.png" /></figure><h4>7. What is the SHA1 hash of the spyagent family spyware hosted on the same IP targeting Android devices on November 16, 2022?</h4><p>Unfortunately, we cannot find the SHA1 hash of the malware on the article that we’ve been using. But, luckily for us, we can make use of a service that analyzes files and URLs for viruses, worms, trojans, and other kinds of malicious content detected by antivirus engines and website scanners.</p><p>Can you think of a service that can do all of the above? Well, we can use <a href="http://hat analyzes files and URLs for viruses, worms, trojans, and other kinds of malicious content detected by antivirus engines and website scanners. It is particularly useful in the cybersecurity field for researching the characteristics of malware and the infrastructure used by cyber threats.">VirusTotal</a>. VirusTotal will allow us to search for the malware connected to the IP address, and analyze all of its characteristics. So let’s open up the site and search for any data connected to our “fanged” IP address.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5Upg9-LErBoSiugFBSNRBQ.png" /></figure><p>Click on the “Relations” panel to see where this IP has been flagged. This panel helps us explore the relationships between various indicators of compromise (IoCs) such as files, URLs, domains, and IP addresses.</p><p>You will see that an IoC from this IP was picked up on Android on the date of 2022–11–16. This is the name of our spyagent family spyware.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9BYK0u-MzJV51vvW-TJUJg.png" /></figure><p>Now all we need is the hash value of this spyagent. Copy the name and search it. If we click on the “Details” of our result, we will see that there is a SHA1 hash value, which is what we need for our final flag!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LCmYzjR42uHJ2KrvuN5RHQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*l11auO8CB6ghLtL3e0ohJw.png" /></figure><h4>Conclusion</h4><p>Congratulations on completing the room. I hope you found the writeup valuable, and that you could follow along with ease.</p><p>Now, onwards to the next room! 😊</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=be998d855d31" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android: How to Install ADB, APK’s, and JADX-GUI on Parrot OS]]></title>
            <link>https://christinec-dev.medium.com/android-how-to-install-adb-apks-and-jadx-gui-on-parrot-os-76a96abe9336?source=rss-3d2c08006851------2</link>
            <guid isPermaLink="false">https://medium.com/p/76a96abe9336</guid>
            <category><![CDATA[apk]]></category>
            <category><![CDATA[parrot]]></category>
            <category><![CDATA[adb]]></category>
            <category><![CDATA[jadx]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[Christine Coomans]]></dc:creator>
            <pubDate>Wed, 24 Jul 2024 20:15:50 GMT</pubDate>
            <atom:updated>2024-07-24T20:15:50.804Z</atom:updated>
            <content:encoded><![CDATA[<p>Recently I have dived into the journey of learning how to hack, or more precisely pen-testing. Since I focused previously on Android Pentesting when I explained how to install Genymotion and VirtualBox on Parrot OS, I thought, why don’t I write another tutorial on how to install <a href="https://developer.android.com/studio/command-line/adb">ADB</a> and <a href="https://github.com/skylot/jadx">JADX-GUI</a> on Parrot OS as it is related and quite easy to do. This tutorial assumes you have Android Studio and Parrot OS installed. A good Android Studio installation tutorial can be found <a href="https://tutorialforlinux.com/2021/04/05/step-by-step-android-studio-parrot-linux-installation/">here</a>.</p><p>In case you wonder, the version I use of Parrot is the <a href="https://www.parrotsec.org/download/">Security Edition</a>, which comes with most tools installed.🤠</p><p>First, let’s go over ADB and JADX-GUI. Android Debug Bridge (ADB) is a command-line tool that lets us communicate with a device, but more importantly for this tutorial, install APKs onto our emulated device. JADX-GUI is at its core a code decompiler, so we can take our APK that we downloaded and pop it into the decompiler and voila, we can see all the source code that it contains!</p><h3>Installing ADB &amp; APK’s</h3><p>Once you have started up your Parrot OS system, open up a terminal using CTRL + ALT + T and run the following command: <strong>sudo apt-get update</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/775/0*KCPbudtqiQ5binZ0.png" /></figure><p>Then we can install adb with the following command <strong>sudo apt-get install android-tools-adb</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/775/0*Cibsli7MlGTuzyZC.png" /></figure><p>Once it has been installed, we can check if it “worked” by opening Genymotion and running our installed device. Check my <a href="https://dev.to/christinecdev/how-to-install-genymotion-virtualbox-on-parrot-os-287p">previous tutorial</a> on how to do this. Once your emulated device is up and running, we can go back to our terminal and type in <strong>adb devices</strong>. We can see that we get a list of devices that are attached, one of which is our emulated device.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/757/0*MhX7qdmoz5xt821V.png" /></figure><p>From there on, we need to install an APK to install onto our device. An APK put simply, is a packaged app that we can download and install for testing purposes. Twelve-year-old me would be so shocked to know that that is how my friend copied their GTA San Andreas game onto my phone!😶</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/0*rJzkjkwdHBqf-W6c.gif" /></figure><p>The APK that we will download in this tutorial is the Diva APK. You can download the APK <a href="https://www.payatu.com/wp-content/uploads/2016/01/diva-beta.tar.gz">here</a>. Once you have downloaded it, head over to your Downloads folder and extract it by right-clicking and saying extract here.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/458/0*u7OyGnYy7iI2dixB.png" /></figure><p>Finally, we can download our apk. Head back into your terminal, making sure you are in the directory of your APK and that you have extracted the DIVA file. Type in the following command: <strong>adb install diva-beta.apk</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/630/0*LX2ORNkzZ82ZEZ08.png" /></figure><p>Good job! When we head back to our emulated device we can see that our app is now installed onto our device.😄</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*XNCdNXSA6sffUbyz.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*4TOyFErrYSURjbbo.png" /></figure><h3>Installing JADX-GUI</h3><p>Now that we have our ADB and APK installed, we can now use this APK in JADX-GUI to decompile the code. ADB allowed us to access the app. JADX-GUI will allow us to access the app’s code.</p><p><strong>To install JADX-GUI, we can simply do the following in the terminal:</strong></p><ol><li>sudo apt-get install jadx</li><li>git clone <a href="https://github.com/skylot/jadx.git">https://github.com/skylot/jadx.git</a></li><li>cd jadx</li><li>./gradlew dist</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/749/0*zh8m-juhVbjxfVRB.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/807/0*4fJSGdyd7lnIEY8B.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/807/0*780mqhVeyLiBo1iC.png" /></figure><p>Then, we can go over to where we downloaded it, go into <strong>jadx &gt; build &gt; jadx &gt; bin</strong>, and double click on <strong>JADX-GUI</strong>. Select the option to run it in the terminal.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/0*fNuhSCU24uxzsgyf.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*cr_uVT0SuaAmPATE.png" /></figure><p>If that does not work for you, you can <strong>cd </strong>into the <strong>jadx </strong>directory, and run the<strong> jadx-gui </strong>command directly.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*A4f9emnPcDjq0eU5.png" /></figure><p>It works! Now we can select our Diva APK that we extracted and pop it into the window that just opened. We can see that we can now access most of the source code of the app.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/620/0*Nulh1KPAdfglo5Hq.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Kea4648Ujwllt243.png" /></figure><h3>BONUS: JADX-GUI Bash Script</h3><p>To have to go to the directory where JADX-GUI is every time we want to open/use it is tedious, and time-wasting. Thus we can create a quick bash script to save on our desktop to quickly run it when needed. In Pluma, type in the following:</p><pre>#!/bin/bash<br><br>cd /home/yourname/Downloads/jadx/build/jadx/bin &amp;&amp; ./jadx-gui</pre><p>Or if you had to follow the second execution step (running <strong>jadx-gui</strong> directly), you will need to type the following:</p><pre>#!/bin/bash<br><br>cd /home/yourname/Downloads/jadx &amp;&amp; jadx-gui</pre><p>Save this file as whatever, like <strong>run_jdx_gui</strong>. Save it in the directory of your desktop (or wherever you save your bash scripts), and then you can open up a new terminal and cd into the directory where run_jdx_gui is stored.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/146/0*Oo26SsQn0EGUZlR3.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/745/0*6Am-PiTlC8xZsCMq.png" /></figure><p>Run the following command: <strong>chmod +x run_jdx_gui</strong>. Now when you click on the script it (or run it in the terminal via <strong>./run_jdx-gui</strong>) will open up the terminal and run the app!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/516/0*3a7yf8SOdXPtBIK2.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*WG42w6--NADS7Eak.png" /></figure><p>That’s it for now, I hope this made sense. Let me know if you need help. See ya next time!</p><p>(You can see this post on my <a href="https://github.com/christinec-dev/ADB_JDX_Parrot_Install/blob/main/README.md">GitHub</a> and pull it for future use❤️)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=76a96abe9336" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>