How to comfortably store nested Dictionaries in a Resource?

Yes and No. Resource inherits from RefCounted, which inherits from Object. So both Resource and RefCounted are Objects.

They are also lowercase objects because an object is any specific created version of a class at runtime.

How you view them is irrelevant in a discussion with others. The way I would phrase it is a Resource is not a Node. Then everything you were saying would make sense and there’d be no semantic argument.

This opinion is understandable, but based on the thinking that one needs to optimize their code to that degree. Node objects are incredibly lightweight. When people come here with optimization problems, the answer is never that they have too many Node objects. The answer is instead they have too many objects on the screen at the same time, or are processing too many objects offscreen. And by too many, I mean 10s of thousands.

It’s actually experienced programmers that more often need to be encouraged to use nodes. The reason being that they tend to overcomplicate their code because they future-proof. They are used to optimizing, and they think about it while architecting.

What they don’t understand is that GDScript, unlike C# for Godot, Unity, and C++ for Unreal, is optimized for running games. You do not need to do nearly as much optimizing because the GDScript interpreter handles that under the hood better than any developer can - if you use Godot the way it is intended.

You see the same thing in someone using C++ who doesn’t understand 32-bit and 64-bit architecture. They’ll declare short ints everywhere when they don’t need 32 bits in an int to optimize. And this was a good thing in the 80s. But with the advent of 32-bit and 64-bit processors, it actually slows down processing. Because modern processors handle 32-bit and 64-bit integers faster than they do smaller ones. However, if you’ve been around long enough (like me), it’s an optimization habit that is better unlearnt.


A Story About Singletons

A perfect example of this over-optimization by experienced programmers is the singleton. As developers coming into Godot we (and I mean to say I did this and have seen many others do this on here) start by creating a singleton the way we know how. We create an Object because even a RefCounted is wasteful with a singleton pattern. We declare every variable and function as static. Then we give it a class name, and we can call it from anywhere. MyClass.my_function()

Then, we want to add a signal to it. This requires some hoops, where you have to declare a private non-static signal and call it when the static public signal is called.

This leads to us learning that an Autoload doesn’t have this signal problem. So we take our my_class.gd file, remove the class name, and make it an Autoload called MyClass. It’s weird, but now we can use signals.

Then we get used to @export variables, which are really useful in the editor. We want to tweak something for testing, or more likely - we want to make something configurable so we can use it in multiple projects. Problem is, we need a Scene for that, and for that, we need a Node object.

At this point we learn that we can create a Scene with just one Node in it and attach our script to it, extending Node instead of Object. Now we can use signals and @export variables.

That whole journey took me, personally, a few months. I relied on my knowledge of programming and architecture and refused to use Autoloads for a long time, because at some point I’d read another experienced programmer talk about how they didn’t like Autoloads. And they made a lot of sense coming from where I was coming from, but I learned they didn’t understand GDScript and the Godot Engine.

For people who don’t come with a development background, they ask how to do something that would allow multiple unrelated objects see data, and we give them a link and tell them “Make an Autoload.” They say, “ok”, do it and move on. They don’t go through the days, weeks, or months-long process of unlearning singletons and learning Autoloads.

Autoload is the Godot implementation of the singleton pattern that handles a bunch of things for us - including making everything static.

Using Nodes for State Machines

I see new people to Godot tell people all the time that they don’t need to use Nodes for state machines. And that’s true. You can use an Enum and a single script. You can use Object, RefCounted or Resource objects instead. And it feels more optimized.

It’s not, really. The amount of additional memory and processing power needed for a Node-based state machine is negligible - even if they’re all over your game. (I use them for my main loop, every enemy, player, NPC, and various other things.)

But there are also a number of benefits that people who again, have experience in other languages, do not see.

  • When I open up a scene, I immediately know if it uses a StateMachine, and what States it has. It saves me a lot of time digging through code.
  • It keeps my code atomic, because each Node is logically separated out into its own file.
  • It allows me to see at a glance if I changed the default code for the StateMachine or a State based on the color or the attached script.
  • I can add a StateMachine, and its States like any other Node, which is faster than copy-pasting code. It literally takes me seconds in a new project to create a StateMachine and default States for characters. Then I can start working on new custom States.
  • I can add additional Node objects that only the state cares about to the State. For example an AudioStreamPlayer for a PlayerJumpState, or an Area2D/3D for an EnemyShootState to detect the player. As a character object gets more complex, this organization helps a lot.

Composition and Inheritance

I agree it makes sense to use both. I disagree that you should try to use composition over inheritance. They both have their places.

A favorite example is having a Health component, which is really applying an ECS pattern to an OOP project. It works, and seems neat, but for many games, it just creates loosely coupled code with no real benefit. Still, most simple examples of using composition in Godot use the idea of a Health component node. While I think it’s overkill, I describe how to do it and a lot of other thoughts on how to use composition in Godot in this post: Am I doing Components/composition right? - #3 by dragonforge-dev