In VSC let’s make a new folder in our root directory called Scripts, and then make a new file inside the Sripts folder called main.gd. Once this is made we will go into the Godot Engine and drag and drop the newly made file into the main scenes script found on the right side

Godot Main Scene Inspector

Extends keyword

To know what to extend, match your script to the type of node it’s attached to or the functionality you need. In the case of the main.tscn because we made a Node2D to start, we will extend Node2D.

  1. For basic functionality like general logic or managing other nodes, extend Node or Node2D
  2. For UI elements, extend from the UI category, Button, Label, Control.
  3. For physics and movement, extend KinematicBody2D, RigidBody2D, or Area2D depending on whether you’re controlling, simulating, or detecting.
  4. For visuals, extend nodes like Sprite2D for images or AnimationPlayer for animations.

We can think of extending like choosing a base template.

_Init() or _ready()

Before getting into the code, we need a place to start. Every Godot game, begins with core functions that serve as entry points to the games main loop. Godot script runs within the engine’s scene tree, and the primary functions that every game starts with are:

  • _init() (Constructor) – This function is called immediately when an instance of a script is created before it becomes part of the scene tree. This is useful for setting default values or initializing variables that don’t depend on the node being loaded into the scene.
extends Node

var health: int
var name: String

# Constructor
func _init():
    health = 100
    name = "Player"
    print("Character created with", health, "HP and name:", name)
  • _ready() – This function runs once when the node enters the scene tree. It’s typically where you initialize references, set up signals, or prepare anything that requires access to the game world. If a script is attached to a node, _ready() ensures that all child nodes are available before execution.
extends Node

var label: Label

# Runs when the node is added to the scene tree
func _ready():
    label = $Label  # Finds a child node called "Label"
    label.text = "Game Started!"
    print("Label text set:", label.text)

In this example, _ready() is used to find a child node and modify it. This would fail in _init() because child nodes aren’t available until the script enters the scene tree.

Hello World

To create “Hello World” in Godot, we’ll use the _init() function since it runs immediately when the script instance is created:

extends Node2D

func _init():
    print("Hello, World!")

When you run the scene, “Hello, World!” will appear in the output console.

Understand the diference

To understand the difference between _init() and _ready(), let’s look at this example

extends Node2D

func _init():
    print("I always come first")

func _ready():
    print("I get processed next")

When you run this script, the console will output:

I always come first

I get processed next

Remember, here, _init() runs first because it’s called when the script instance is created. _ready() follows after the node enters the scene tree, making it useful for setup that depends on other nodes or the scene being fully initialized.

Variables and Basic Data Types

Now that we’ve covered _init() and _ready(), let’s introduce variables. A variable stores information that we may want to access later. In most languages, variables can store different data types like integers, floats, strings, booleans, arrays, objects and many more.

Declaring a Variable

In Godot, we use var to create a variable. use a comma or plus sign to append the variable to your string. Here’s an example:

extends Node2D

var player_name = "Royal Chicken"
var player_level = 20

func _init():
	print("Player name (lv): ", player_name," (",player_level,")")
	print("Player name (lv): " + player_name," (",player_level,")")

When we run this script, the console will output:

Player name (lv): Royal Chicken (20)

Player name (lv): Royal Chicken (20)

Changing Variables

Variables can be updated anytime. Let’s modify our level inside _ready():

extends Node2D

var player_name = "Royal Chicken"
var player_level = 20

func _init():
	print("Player name (lv): ", player_name," (",player_level,")")

func _ready():
	player_level += 1
	print("Congratulations, ", player_name, " has leveled up to ", player_level, "!")

This will print:

Player name (lv): Royal Chicken (20)

Congratulations, Royal Chicken has leveled up to 21!

Since _ready() runs after _init(), we can see how values change during execution.

Node Properties

Every node has properties that define how it looks and behaves. Since we’re working in VSC (Visual Studio Code), we can’t see these properties in the Godot Inspector, so we’ll use get_property_list() to list all editable properties in a node.

extends Node2D

func _init():
    print(get_property_list())

Running this will print a large list of properties and their details in a JSON-like format. While useful, this output can be overwhelming, so let’s break it down.

What Does get_property_list() Do?

get_property_list() returns a list (also called an array) where each item is a dictionary . A dictionary is a type of data structure that stores key-value pairs —meaning each property has a name and some associated information.

Here’s what one of these dictionaries looks like:

`{ "name": "position", "type": 5 } `

This means:

  • "name" → Stores the property name , like "position".
  • "type" → Stores the data type of the property, represented as a number .

Since get_property_list() returns many of these dictionaries (one for each property of the node), we need a loop to go through them one at a time.

Making It Readable

Before we clean up the data from get_property_list(), we need to learn about loops.

Right now, get_property_list() gives us a huge list of properties, and if we just print it out directly, it’s overwhelming. Instead of looking at all that raw data, we can loop through itand extract only the useful parts.

What is a For Loop?

A for loop repeats a block of code for each item in a list. This is useful when we have multiple things to process, like our list of node properties .

The general structure of a for loop in Godot looks like this:

for variable in list:
    # Code to run for each item in the list
  • variable → This stores the current item from the list as the loop runs.
  • list → This is the collection of items that we’re looping through.
  • The loop will repeat once for each item in the list.

How the Loop Woks in GDScript

When we call get_property_list(), it returns a long list of dictionaries, each containing details about a property in the node. Instead of trying to read through all that data at once, we use a for loop to go through each property one at a time.

In this script, we start by storing the full list in a variable called properties. Then, the for loop picks one dictionary at a time from the list and assigns it to prop. Since each prop is a dictionary, we can access its "name" key to get the property’s name and "type" key to see what kind of data it holds. The print() statement then formats this into a readable output, making it much easier to see the available properties and their types.

extends Node2D

func _ready():
    var properties = get_property_list()  # Get a list of all properties in this node
  
    for prop in properties:  # Loop through each property
        print(prop.name, " - Type: ", prop.type)  # Print the name and type

This method allows us to efficiently process and display only the useful information from get_property_list() instead of dealing with an overwhelming block of raw data.

Understanding Data Types

The type number corresponds to specific data types in Godot. Here’s the full list of all 20 data types :

  • 0 → Nil (Represents no value)
  • 1 → bool (true/false)
  • 2 → int (whole numbers)
  • 3 → float (decimal numbers)
  • 4 → String (text)
  • 5 → Vector2 (X, Y positions for 2D nodes)
  • 6 → Vector2i (X, Y integer positions for 2D nodes)
  • 7 → Rect2 (A 2D rectangle with position and size)
  • 8 → Rect2i (A 2D rectangle with integer position and size)
  • 9 → Vector3 (X, Y, Z positions for 3D nodes)
  • 10 → Vector3i (X, Y, Z integer positions for 3D nodes)
  • 11 → Transform2D (A transformation matrix for 2D nodes)
  • 12 → Vector4 (A four-component vector)
  • 13 → Vector4i (A four-component integer vector)
  • 14 → Plane (A mathematical plane in 3D space)
  • 15 → Quat (Quaternion for 3D rotation)
  • 16 → AABB (3D bounding box)
  • 17 → Basis (3D transformation matrix)
  • 18 → Transform3D (Full 3D transformation data)
  • 19 → Projection (4×4 matrix used for 3D projections)
  • 20 → Color (RGBA values for colors)
  • 21 → StringName (Optimized internal string representation)
  • 22 → NodePath (A reference to another node in the scene)
  • 23 → RID (Resource ID, used for low-level access)
  • 24 → Object (A general object reference)
  • 25 → Callable (A function reference)
  • 26 → Signal (A signal used for event handling)
  • 27 → Dictionary (Key-value pairs, similar to JSON objects)
  • 28 → Array (A list of values)
  • 29 → PackedByteArray (Optimized array for storing bytes)
  • 30 → PackedInt32Array (Optimized array for storing 32-bit integers)
  • 31 → PackedInt64Array (Optimized array for storing 64-bit integers)
  • 32 → PackedFloat32Array (Optimized array for storing 32-bit floats)
  • 33 → PackedFloat64Array (Optimized array for storing 64-bit floats)
  • 34 → PackedStringArray (Optimized array for storing strings)
  • 35 → PackedVector2Array (Optimized array for storing Vector2 elements)
  • 36 → PackedVector3Array (Optimized array for storing Vector3 elements)
  • 37 → PackedColorArray (Optimized array for storing Color elements)

Conditionals for Cleaning Up Property Types

Now that we understand loops and how to extract property names and types from get_property_list(), we can take it a step further by using conditionals to replace the type number with a more human-readable name.

Step 1: Understand the Problem

Right now, when we print properties, we see something like this:

position - Type: 5

rotation - Type: 3

visible - Type: 1

modulate - Type: 20

The type numbers don’t mean much unless you memorize them. Instead, we want it to look like this:

position - Type: Vector2

rotation - Type: float

visible - Type: bool

modulate - Type: Color

To achieve this, we need to convert these numbers into their actual data type names using conditionals.

Step 2: Introducing Conditionals

A conditional statement lets us check if a value matches a certain condition and then execute code based on that.

A basic example:

var type_number = 1

if type_number == 0:
    print("Nil")
elif type_number == 1:
    print("Boolean")
else:
    print("Unknown type")

If type_number is 1, this will print "Boolean". This is the same logic we’ll use to clean up prop.type.

Step 3: Applying Conditionals to Our Loop

Now we modify our script to replace type numbers with readable text .

extends Node2D

func _ready():
    var properties = get_property_list()

    for prop in properties:
        var type_name = ""  # Placeholder for the readable type
      
        if prop.type == 0:
            type_name = "Nil"
        elif prop.type == 1:
            type_name = "bool"
        elif prop.type == 2:
            type_name = "int"
        elif prop.type == 3:
            type_name = "float"
        elif prop.type == 4:
            type_name = "String"
        elif prop.type == 5:
            type_name = "Vector2"
        elif prop.type == 6:
            type_name = "Rect2"
        elif prop.type == 7:
            type_name = "Vector3"
        elif prop.type == 8:
            type_name = "Transform2D"
        elif prop.type == 10:
            type_name = "Quat"
        elif prop.type == 11:
            type_name = "AABB"
        elif prop.type == 12:
            type_name = "Basis"
        elif prop.type == 13:
            type_name = "Transform"
        elif prop.type == 14:
            type_name = "Color"
        elif prop.type == 18:
            type_name = "Dictionary"
        elif prop.type == 19:
            type_name = "Array"
        else:
            type_name = "Unknown"  # Default if type isn't in our list

        print(prop.name, " - Type: ", type_name)

Now, instead of seeing numbers, we get a readable output.

position - Type: 5

rotation - Type: 3

visible - Type: 1

modulate - Type: 20

Step 4: Making It More Efficient

While this works, it’s a bit long and repetitive. A better way is to use a dictionary or match statement to make the code cleaner.

extends Node2D

func _ready():
	var properties = get_property_list()

	var type_lookup = {
		0: "Nil", 1: "bool", 2: "int", 3: "float", 4: "String", 5: "Vector2", 6: "Vector2i",
		7: "Rect2", 8: "Rect2i", 9: "Vector3", 10: "Vector3i", 11: "Transform2D", 12: "Vector4",
		13: "Vector4i", 14: "Plane", 15: "Quat", 16: "AABB", 17: "Basis", 18: "Transform3D",
		19: "Projection", 20: "Color", 21: "StringName", 22: "NodePath", 23: "RID", 24: "Object",
		25: "Callable", 26: "Signal", 27: "Dictionary", 28: "Array", 29: "PackedByteArray",
		30: "PackedInt32Array", 31: "PackedInt64Array", 32: "PackedFloat32Array", 33: "PackedFloat64Array",
		34: "PackedStringArray", 35: "PackedVector2Array", 36: "PackedVector3Array", 37: "PackedColorArray"
	}


	for prop in properties:
		var type_name = type_lookup.get(prop.type, "Unknown")
		print(prop.name, " - Type: ", type_name)

This does the exact same thing , but cleaner and easier to update .

Node - Type: Nil

_import_path - Type: Unknown

name - Type: Unknown

unique_name_in_owner - Type: bool

Grouping Data Types for Better Readability

Now that we have a full list of data types, let’s organize them into groups so that when we print out properties, they aren’t all mixed together.

Why Group Properties?

Now that we’ve learned how to retrieve and print node properties, we’re taking it a step further by grouping similar properties together. This will make our output more structured and readable rather than having everything mixed together.

Right now, when we print properties, they appear in a random order, making it difficult to spot patterns. Instead of this:

position - Type: Vector2

visible - Type: bool

rotation - Type: float

modulate - Type: Color

children - Type: Array

We want organized groups like this:

Organized CLI Groups

By grouping properties by type , we make it much easier to understand what each property does.

Step 1: Understanding Lists in GDScript

A list (also called an array ) stores multiple values. We use them to group properties of the same type.

Example:

var numbers = []  # Empty list for number-based properties
var colors = []   # Empty list for color-based properties

numbers.append("visible - Type: bool")  # Adding an entry to the list
colors.append("modulate - Type: Color")  # Adding a color-related entry

Now, numbers and colors contain:

[“visible - Type: bool”]

[“modulate - Type: Color”]

Step 2: Using Conditionals to Classify Properties

To decide which list a property should go into, we use conditionals (if-elif statements).

Example:

if prop.type == 1:
    numbers.append(prop.name + " - Type: bool")  # Adds to the numbers list
elif prop.type == 20:
    colors.append(prop.name + " - Type: Color")  # Adds to the colors list

Step 3: Full Script for Grouping Properties

Here’s our fully structured script that retrieves properties, groups them , and prints them separately .

extends Node2D

func _ready():
	var properties = get_property_list()

	var numbers = []
	var strings = []
	var bools = []
	var vectors = []
	var colors = []
	var objects = []
	var arrays = []
	var nil = []

	var type_lookup = {
		0: "Nil", 1: "bool", 2: "int", 3: "float", 4: "String", 5: "Vector2", 6: "Vector2i",
		7: "Rect2", 8: "Rect2i", 9: "Vector3", 10: "Vector3i", 11: "Transform2D", 12: "Vector4",
		13: "Vector4i", 14: "Plane", 15: "Quat", 16: "AABB", 17: "Basis", 18: "Transform3D",
		19: "Projection", 20: "Color", 21: "StringName", 22: "NodePath", 23: "RID", 24: "Object",
		25: "Callable", 26: "Signal", 27: "Dictionary", 28: "Array", 29: "PackedByteArray",
		30: "PackedInt32Array", 31: "PackedInt64Array", 32: "PackedFloat32Array", 33: "PackedFloat64Array",
		34: "PackedStringArray", 35: "PackedVector2Array", 36: "PackedVector3Array", 37: "PackedColorArray"
	}


	for prop in properties:
		var type_name = type_lookup.get(prop.type, "Unknown")

		if prop.type in [1]:
			bools.append(prop.name + " - Type: " + type_name)
		elif prop.type in [2, 3]:  # bool, int, float
			numbers.append(prop.name + " - Type: " + type_name)
		elif prop.type in [4, 21]:
			strings.append(prop.name + " - Type: " + type_name)
		elif prop.type in [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]:  # Vector types & transforms
			vectors.append(prop.name + " - Type: " + type_name)
		elif prop.type in [20]:  # Colors
			colors.append(prop.name + " - Type: " + type_name)
		elif prop.type in [22, 23, 24, 25, 26]:  # Node and object references
			objects.append(prop.name + " - Type: " + type_name)
		elif prop.type in [27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37]:  # Arrays and dictionaries
			arrays.append(prop.name + " - Type: " + type_name)
		else:
			nil.append(prop.name + " - Type: " + type_name)


	print("Numbers:")
	for entry in numbers:
		print(entry)

	print("\nStrings:")
	for entry in strings:
		print(entry)

	print("\nBools:")
	for entry in bools:
		print(entry)

	print("\nVectors:")
	for entry in vectors:
		print(entry)

	print("\nColors")
	for entry in colors:
		print(entry)

	print("\nObjects:")
	for entry in objects:
		print(entry)

	print("\nArrays:")
	for entry in arrays:
		print(entry)

	print("\nNil")
	for entry in nil:
		print(entry)

This will produce:

Formatted Output

Introducing Functions for Modularity and Object-Oriented Design

Now that we have successfully organized node properties into groups , it’s time to refactor our code using functions to improve modularity and reusability. Right now, all our logic sits inside _ready(), which means:

  1. The code cannot be reused elsewhere – We can only check properties for the current node (self).
  2. If we need to check another object’s properties , we would have to duplicate the entire script .
  3. The script assumes every object is a Node – This will cause errors when passing other object types like Color or Vector2.

To fix these issues , we will:

  • Create a function that categorizes properties.
  • Pass objects into this function so we can check any class, not just Node2D.
  • Prevent errors by handling different object types correctly.

What is a Function?

A function is a reusable block of code that performs a specific task. Instead of copy-pasting code , we define a function once and call it whenever we need it .

Example:

func say_hello():
    print("Hello, world!")

say_hello()  # Prints: Hello, world!

Step 1: Creating a Function to Categorize Properties

Instead of hardcoding everything inside _ready(), we move the logic into a function:

extends Node2D

func _ready():
    get_obj_properties(self)  # Call function for the current node

func get_obj_properties(obj):
	var properties = obj.get_property_list()

	var numbers = []
	var strings = []
	var bools = []
	var vectors = []
	var colors = []
	var objects = []
	var arrays = []
	var nil = []

	var type_lookup = {
		0: "Nil", 1: "bool", 2: "int", 3: "float", 4: "String", 5: "Vector2", 6: "Vector2i",
		7: "Rect2", 8: "Rect2i", 9: "Vector3", 10: "Vector3i", 11: "Transform2D", 12: "Vector4",
		13: "Vector4i", 14: "Plane", 15: "Quat", 16: "AABB", 17: "Basis", 18: "Transform3D",
		19: "Projection", 20: "Color", 21: "StringName", 22: "NodePath", 23: "RID", 24: "Object",
		25: "Callable", 26: "Signal", 27: "Dictionary", 28: "Array", 29: "PackedByteArray",
		30: "PackedInt32Array", 31: "PackedInt64Array", 32: "PackedFloat32Array", 33: "PackedFloat64Array",
		34: "PackedStringArray", 35: "PackedVector2Array", 36: "PackedVector3Array", 37: "PackedColorArray"
	}


	for prop in properties:
		var type_name = type_lookup.get(prop.type, "Unknown")

		if prop.type in [1]:
			bools.append(prop.name + " - Type: " + type_name)
		elif prop.type in [2, 3]:  # bool, int, float
			numbers.append(prop.name + " - Type: " + type_name)
		elif prop.type in [4, 21]:
			strings.append(prop.name + " - Type: " + type_name)
		elif prop.type in [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]:  # Vector types & transforms
			vectors.append(prop.name + " - Type: " + type_name)
		elif prop.type in [20]:  # Colors
			colors.append(prop.name + " - Type: " + type_name)
		elif prop.type in [22, 23, 24, 25, 26]:  # Node and object references
			objects.append(prop.name + " - Type: " + type_name)
		elif prop.type in [27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37]:  # Arrays and dictionaries
			arrays.append(prop.name + " - Type: " + type_name)
		else:
			nil.append(prop.name + " - Type: " + type_name)


	print("Numbers:")
	for entry in numbers:
		print(entry)

	print("\nStrings:")
	for entry in strings:
		print(entry)

	print("\nBools:")
	for entry in bools:
		print(entry)

	print("\nVectors:")
	for entry in vectors:
		print(entry)

	print("\nColors")
	for entry in colors:
		print(entry)

	print("\nObjects:")
	for entry in objects:
		print(entry)

	print("\nArrays:")
	for entry in arrays:
		print(entry)

	print("\nNil")
	for entry in nil:
		print(entry)

Now, if we have another object , we can call:

var my_sprite = Sprite2D.new()
categorize_properties(my_sprite)  # Works on Sprite2D too!

This removes duplicate code and makes the function work on any node.

Step 2: Handling Non-Node Objects

The Problem: What Happens If We Pass a Non-Node?

So far, our function assumes every object has get_property_list() , which only exists in Nodes .

Let’s try calling:

categorize_properties(Color(1, 0, 0, 1))
categorize_properties(Vector2(100, 200))
categorize_properties(5)

These will break , because Color, Vector2, and int do not have get_property_list() .

Common Errors You’ll Encounter

If we try:

func _ready():
    categorize_properties(Color(1, 0, 0, 1))  # A red color

We get an error like:

Invalid call. Nonexistent function ‘get_property_list’ in base ‘Color’.

Why?

  • Color objects do not have properties like Nodes.
  • Only Nodes have get_property_list().

Step 3: Checking the Object Type Before Using It

To prevent errors, we use type checking in Godot.

1. Using typeof()

typeof(obj) checks basic types like int, float, String, Vector2, Color, etc.

Example:

print(typeof(5))  # Prints: 2 (int)
print(typeof(Color()))  # Prints: 20 (Color)

2. Using is to Check for Node-Based Objects

To check if something is a Node, we use keywork is to check if an object is a certain type:

var my_node = Node2D.new()
print(my_node is Node)  # Prints: true
print(my_node is Color)  # Prints: false

We can use is to help prevent us from calling get_property_list() on non-Node objects by wrapping the statements in an if obj is obj check. Updated function below

func get_obj_properties(obj):
	if obj is Node:
		var properties = obj.get_property_list()

		var numbers = []
		var strings = []
		var bools = []
		var vectors = []
		var colors = []
		var objects = []
		var arrays = []
		var nil = []

		var type_lookup = {
			0: "Nil", 1: "bool", 2: "int", 3: "float", 4: "String", 5: "Vector2", 6: "Vector2i",
			7: "Rect2", 8: "Rect2i", 9: "Vector3", 10: "Vector3i", 11: "Transform2D", 12: "Vector4",
			13: "Vector4i", 14: "Plane", 15: "Quat", 16: "AABB", 17: "Basis", 18: "Transform3D",
			19: "Projection", 20: "Color", 21: "StringName", 22: "NodePath", 23: "RID", 24: "Object",
			25: "Callable", 26: "Signal", 27: "Dictionary", 28: "Array", 29: "PackedByteArray",
			30: "PackedInt32Array", 31: "PackedInt64Array", 32: "PackedFloat32Array", 33: "PackedFloat64Array",
			34: "PackedStringArray", 35: "PackedVector2Array", 36: "PackedVector3Array", 37: "PackedColorArray"
		}


		for prop in properties:
			var type_name = type_lookup.get(prop.type, "Unknown")

			if prop.type in [1]:
				bools.append(prop.name + " - Type: " + type_name)
			elif prop.type in [2, 3]:  # bool, int, float
				numbers.append(prop.name + " - Type: " + type_name)
			elif prop.type in [4, 21]:
				strings.append(prop.name + " - Type: " + type_name)
			elif prop.type in [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]:  # Vector types & transforms
				vectors.append(prop.name + " - Type: " + type_name)
			elif prop.type in [20]:  # Colors
				colors.append(prop.name + " - Type: " + type_name)
			elif prop.type in [22, 23, 24, 25, 26]:  # Node and object references
				objects.append(prop.name + " - Type: " + type_name)
			elif prop.type in [27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37]:  # Arrays and dictionaries
				arrays.append(prop.name + " - Type: " + type_name)
			else:
				nil.append(prop.name + " - Type: " + type_name)


		print("Numbers:")
		for entry in numbers:
			print(entry)

		print("\nStrings:")
		for entry in strings:
			print(entry)

		print("\nBools:")
		for entry in bools:
			print(entry)

		print("\nVectors:")
		for entry in vectors:
			print(entry)

		print("\nColors")
		for entry in colors:
			print(entry)

		print("\nObjects:")
		for entry in objects:
			print(entry)

		print("\nArrays:")
		for entry in arrays:
			print(entry)

		print("\nNil")
		for entry in nil:
			print(entry)

	else:
		print("'/obj'/ passed was not a Node") 

3. Using sort to alphabetically sort the lists

sort() is a built in function that can be used with all lists by calling the list.sort()

...
			else:
				nil.append(prop.name + " - Type: " + type_name)

		numbers.sort()
		strings.sort()
		bools.sort()
		vectors.sort()
		colors.sort()
		objects.sort()
		arrays.sort()
		nil.sort()

		print("Numbers:")
		for entry in numbers:
			print(entry)
...

Step 4: Refactoring to Return Data Instead of Printing

Now that we’ve organized properties into groups and sorted them alphabetically, the next step is to return the data instead of printing it directly.

Why We Need to Refactor

Right now, our function immediately prints the results, making it inflexible .

Current Behavior (Direct Printing)
print("Numbers:")
for entry in numbers:
    print(entry)

The function forces output into the console, meaning we cannot reuse the data elsewhere.

Updated Behavior (Returning Data)

Instead of printing:

return sorted_obj_prop_dict

Now, we can store the results in a variable and use them later:

var data = get_obj_properties(self)
print(data["numbers"])  # Now we can choose when to print

Step 1: Updating the Function to Return Data

Here’s the updated code that returns a dictionary instead of printing:

extends Node2D

var type_lookup: Dictionary = {
			0: "Nil", 1: "bool", 2: "int", 3: "float", 4: "String", 5: "Vector2", 6: "Vector2i",
			7: "Rect2", 8: "Rect2i", 9: "Vector3", 10: "Vector3i", 11: "Transform2D", 12: "Vector4",
			13: "Vector4i", 14: "Plane", 15: "Quat", 16: "AABB", 17: "Basis", 18: "Transform3D",
			19: "Projection", 20: "Color", 21: "StringName", 22: "NodePath", 23: "RID", 24: "Object",
			25: "Callable", 26: "Signal", 27: "Dictionary", 28: "Array", 29: "PackedByteArray",
			30: "PackedInt32Array", 31: "PackedInt64Array", 32: "PackedFloat32Array", 33: "PackedFloat64Array",
			34: "PackedStringArray", 35: "PackedVector2Array", 36: "PackedVector3Array", 37: "PackedColorArray"
		}

func _ready():
	for x in get_obj_properties(self)["numbers"]:
		print(x)



func get_obj_properties(obj):
	if obj is Node:
		var properties = obj.get_property_list()

		var numbers = []
		var strings = []
		var bools = []
		var vectors = []
		var colors = []
		var objects = []
		var arrays = []
		var nil = []


		for prop in properties:
			var type_name = type_lookup.get(prop.type, "Unknown")

			if prop.type in [1]:
				bools.append(prop.name + " - Type: " + type_name)
			elif prop.type in [2, 3]:  # bool, int, float
				numbers.append(prop.name + " - Type: " + type_name)
			elif prop.type in [4, 21]:
				strings.append(prop.name + " - Type: " + type_name)
			elif prop.type in [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]:  # Vector types & transforms
				vectors.append(prop.name + " - Type: " + type_name)
			elif prop.type in [20]:  # Colors
				colors.append(prop.name + " - Type: " + type_name)
			elif prop.type in [22, 23, 24, 25, 26]:  # Node and object references
				objects.append(prop.name + " - Type: " + type_name)
			elif prop.type in [27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37]:  # Arrays and dictionaries
				arrays.append(prop.name + " - Type: " + type_name)
			else:
				nil.append(prop.name + " - Type: " + type_name)

		numbers.sort()
		strings.sort()
		bools.sort()
		vectors.sort()
		colors.sort()
		objects.sort()
		arrays.sort()
		nil.sort()

		var sorted_obj_prop_dict = {"numbers": numbers, "strings": strings, "bools": bools,
		"vectors": vectors, "colors": colors, "objects": objects, "arrays": arrays, "nil": nil }

		return sorted_obj_prop_dict

	else:
		print("'/obj'/ passed was not a Node") 

Why This Approach is Better

  1. Prevents Unnecessary Console Spam
    • We only print when necessary , instead of forcing console output from the function.
  2. Improves Reusability
    • Now, get_obj_properties() can be used anywhere without needing console output.
    • We can store results for later use, send them to a UI , or log them to a file .
  3. Better Error Handling
    • The function returns a proper error message instead of crashing when an invalid object is passed.