Skip to content

Commit 4baba4f

Browse files
committed
New blog post about godot types
1 parent 7bdab85 commit 4baba4f

File tree

5 files changed

+99
-0
lines changed

5 files changed

+99
-0
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
title: "Godot Types: The Good, The Bad, The Ugly"
3+
tags: ['godot', 'gdscript', 'coding']
4+
date: "2025-10-23T12:00:00+00:00"
5+
---
6+
I have seen a lot of confusion about how types work in [Godot Engine](https://godotengine.org). Specifically, the difference between [RefCounted](https://docs.godotengine.org/en/stable/classes/class_refcounted.html), [Object](https://docs.godotengine.org/en/stable/classes/class_object.html#class-object) and built-in types like [String](https://docs.godotengine.org/en/stable/classes/class_string.html) or [Color](https://docs.godotengine.org/en/stable/classes/class_color.html). At the end of this post, you will understand the exact differences between them.
7+
8+
![clint-eastwood](/images/clint-eastwood.webp)
9+
10+
# The Good: RefCounted and Built-in Types
11+
12+
Many types in Godot such as [AStar3D](https://docs.godotengine.org/en/stable/classes/class_astar3d.html#class-astar3d) are of type [RefCounted](https://docs.godotengine.org/en/stable/classes/class_refcounted.html#class-refcounted). Godot will keep track of how many references you have for a given instance. If the reference count becomes 0, the instance will be freed:
13+
```gd
14+
extends Node
15+
16+
func fun_with_refs() -> void:
17+
var my_ref = AStar3D.new() # creates a refcount
18+
19+
func _ready() -> void:
20+
fun_with_refs()
21+
# the AStar3D instance will be gone! Nothing is referencing it!
22+
```
23+
This can be extremely handy: you don't actually have to worry about freeing objects or any sort of memory management. Godot will take care of it automatically. Prefer `RefCounted` if you don't want to worry about freeing instances and you have mostly short-lived objects anyways.
24+
25+
For in-built types like `Color` or `String`, Godot utilizes **value semantics**, this means that when you assign a color or string, it will create always an independent copy.
26+
```gd
27+
var a = Vector2(5, 10)
28+
var b = a
29+
b.x = 99
30+
31+
print(a) # (5, 10)
32+
print(b) # (99, 10)
33+
```
34+
35+
# The Bad: Object
36+
37+
Well... not really bad. Just bad for beginners. `Object` type can bite you if you don't actually understand how it works! Godot will **not** free objects for you. When you are not careful, it can lead to a [memory leak](https://en.wikipedia.org/wiki/Memory_leak):
38+
39+
```gd
40+
extends Node
41+
42+
func memory_leak() -> void:
43+
var node = Node.new()
44+
45+
func _ready() -> void:
46+
memory_leak()
47+
# oops, memory for the node instance is still reserved!
48+
```
49+
In order to free `Object` you have to call `.free()` on it. You are in luck, though: most `Object` instances that Godot creates (like nodes) it will free for you! So while technically, `Object` requires manual memory management, Godot will do a lot for you. However, when rolling your own `Object` types you need to be careful!
50+
51+
# The Ugly: Invisible Side-Effects
52+
53+
I have been recently contributing again to the [FMOD GDExtension](https://github.com/utopia-rise/fmod-gdextension) by [utopia-rise](https://github.com/utopia-rise) because I was trying to investigate an issue in my game where no sound was playing. I won't go into too much technical detail here, but [FMOD](https://www.fmod.com/) basically is an audo engine that I am using for [my game](https://bitbrain.itch.io/cave). FMOD has the concept of "audio banks" that you gotta load at runtime, and those banks contain the audio to play. The code usually looks like this:
54+
```gd
55+
# init.gd autoload script
56+
extends Node
57+
58+
func _init() -> void:
59+
FmodServer.load_bank("res://fmod/main.bank", FmodServer.FMOD_STUDIO_LOAD_BANK_NORMAL)
60+
```
61+
This used to work fine but at some point, it stopped working. After a long time of debugging, it turned out that the signature of `load_bank` had been changed. `FmodBank` no longer was of type `Object` but of type `RefCounted`!
62+
63+
![worried-kermit](/images/worried-kermit.webp)
64+
65+
If you have paid attention before, you should already know what the problem is: the GDExtension correctly loads the `main.bank` file into memory but we are actually not referencing it! Godot will then go ahead and free the instance again (because it is a `RefCounted` and its reference count reaches `0`). So the correct fix is this:
66+
67+
```gd
68+
# init.gd autoload script
69+
extends Node
70+
71+
var banks = []
72+
73+
func _init() -> void:
74+
banks.append(FmodServer.load_bank("res://fmod/main.bank", FmodServer.FMOD_STUDIO_LOAD_BANK_NORMAL))
75+
```
76+
since `banks` itself is a reference that will stay around as long the `init.gd` autoload is around (for the entire duration of the game's runtime). We are saved!
77+
78+
# Confusion about valid instances
79+
80+
Before we finish, I wanted to say a few more words about [is_instance_valid](https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html#class-globalscope-method-is-instance-valid). Code like this may seem confusing at first:
81+
```gd
82+
var my_color = Color(255, 255, 255)
83+
print(is_instance_valid(my_color)) # returns false
84+
```
85+
but this is intended behaviour: `Color` is an in-built type and won't have an **instance id**. `is_instance_valid` only operates on **instance ids** so Godot cannot know if it is valid or not -> it will always be `false`.
86+
87+
I hope this was somewhat useful. If you have follow up questions, you can always reach me on 🐘[Mastodon](https://mastodon.gamedev.place/@bitbraindev) or over at ⛅[BlueSky](https://bsky.app/profile/bitbra.in).

node_modules/.package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

static/images/clint-eastwood.webp

39.6 KB
Loading

static/images/worried-kermit.webp

25.7 KB
Loading

0 commit comments

Comments
 (0)