//rp.wtf

Organizing files in Godot

Thu Oct 03 2024

When you first start a project, file organization isn't really an issue. With a handful of files, you don't even need subfolders where to put them. Then the project grows, and it gets increasingly difficult to find your way around it.

It's easier if you have a system. Then you can find things where you expect them to be. And in this article, I want to share my system with you.

Structuring by type

I'll start with an example. A Godot project usually consists of files of multiple types, like scripts, sprites, and scenes, so one obvious way is to group them by their type:

.
├── scenes/
│   ├── player.tscn
│   └── enemy.tscn
├── scripts/
│   ├── player.gd
│   └── enemy.gd
└── sprites/
    ├── player.png
    └── enemy.png

This does the job just fine, and I have seen this way of organizing project files many times, but, personally, I don't like it. This simple example makes it easy to reason about what the role of each file is, but as soon as you start adding files with generic names like head.png, it starts to become hard to understand where things belong. Of course, you can be strict about your naming conventions and, for example, systematically prefix sprite names to further describe what they belong to, e.g., zombie_head.png. Also, if you decide that the game no longer needs zombies, you need to go and hunt for all the related files in the separate scripts, sprites and scenes subfolders.

Structuring by entity

My original background is web development. In modern web development projects, files related to a particular entity (the web people call them "components") are grouped together. So, after restructuring our sample files using this entity-based structuring paradigm, I'd end up with a structure like this:

.
├── enemy/
│   ├── enemy.gd
│   ├── enemy.png
│   └── enemy.tscn
└── player/
    ├── player.gd
    ├── player.png
    └── player.tscn

You can disagree, but I think that this is an improvement. Everything that belongs to the player entity is stored in the player subfolder. Everything that belongs to the enemy is in the enemy subfolder. If we decide that our game won't be violent, we can just remove the enemy entity and all its related assets by deleting the enemy subfolder from the project.
If we want to add this enemy as part of an expansion to our game or a DLC, it's easier to split it from the main game's content when exporting and then supply it as a separate "pck" download.

Of course, it's perfectly fine to categorize assets in their subfolders, which you can very well split by type:

.
└── player/
    ├── player.gd
    ├── player.tscn
    ├── sounds/
    │   ├── attack.wav
    │   └── footstep.wav
    └── sprites/
        ├── body.png
        ├── head.png
        └── legs.png

Also, you absolutely don't have to restrict yourself to the depth of a single folder, but create a hierarchy of folders representing sub-entities of your entities in more complex cases:

.
└── player/
    ├── player.gd
    ├── player.tscn
    ├── armor/
    │   ├── armor_list.res
    │   ├── sprites/
    │   │   ├── tier1.png
    │   │   ├── tier2.png
    │   │   └── tier3.png
    │   └── shaders/
    │       ├── magic.gdshader
    │       └── frost.gdshader
    └── weapons/
        ├── weapon_list.res
        ├── hammer/
        │   ├── hammer.gd
        │   └── hammer.png
        └── sword/
            ├── sword.gd
            └── sword.png

The "shared" folder

This is a good point to ask—what if I want to reuse stuff like weapons or shaders? This is where I use the shared folder. Now, the shared folder is a bit of a Wild West. Most of the things are organized by type; for example, little utility scripts meant for reuse have nowhere else to go other than a lone scripts folder, but there might also be reusable entities like UI components. That might result in a structure like this:

.
├── player/
│   └── ...
├── enemy/
│   └── ...
└── shared/
    ├── ambiences/
    │   ├── forest.ogg
    │   └── cave.ogg
    ├── fonts/
    │   ├── heading.ttf
    │   └── body.ttf
    ├── scripts/
    │   ├── utils.gd
    │   └── state_machine.gd
    ├── sounds/
    │   ├── footstep.wav
    │   └── burning.wav
    ├── themes/
    │   └── default.theme
    ├── ui/
    │   ├── button/
    │   │   ├── button.gd
    │   │   ├── button.tscn
    │   │   ├── click.wav
    │   │   └── select.wav
    │   ├── panel/
    │   │   ├── panel.stylebox
    │   │   └── panel_alt.stylebox
    │   └── popup/
    │       ├── popup.gd
    │       └── popup.tscn
    └── world/
        └── door/
            ├── door.gd
            ├── door.tscn
            └── door.png

There are no hard rules regarding the shared folder, as I mentioned before, just that all things that should be reused go in there. Of course there's nuance. For instance, you could argue that the ui folder doesn't really need to be in the shared folder, but you can build many things just by composing panels and buttons for a HUD, and I'd put the HUD in a hud folder at the project root. I did mention before that I don't restrict myself to the depth of one subfolder, but also I try to take care not to create a deep hierarchy of subfolders, which is hard to navigate. Then again, if you notice that you have been creating folders with equal postfixes, like pause_menu, main_menu and so on, it's probably a good idea to create a menus folder, drop the _menu postfix, and put those folders in there.

Another rule I have is that once a resource or scene needs to be reused and is therefore "promoted" to the shared folder, it doesn't get moved out of it, unless I am certain that not more than one resource or scene depends on it, and then I can move the shared resource alongside that resource.

A more complete example

I've shared two of the basic building blocks in the structure of my Godot projects, and now I want to show a more complete example. We'll take an RPG game as our use case:

.
├── export_templates
├── src/
│   ├── acts/
│   │   ├── 01_moor/
│   │   │   ├── enemies/
│   │   │   │   ├── demon/
│   │   │   │   │   ├── demon.gd
│   │   │   │   │   ├── demon.png
│   │   │   │   │   ├── demon.tscn
│   │   │   │   │   ├── demon_attack.wav
│   │   │   │   │   └── ...
│   │   │   │   ├── shaman/
│   │   │   │   │   └── ...
│   │   │   │   └── zombie/
│   │   │   │       └── ...
│   │   │   ├── npc/
│   │   │   │   ├── blacksmith/
│   │   │   │   │   ├── blacksmith.gd
│   │   │   │   │   └── blacksmith.tscn
│   │   │   │   └── ...
│   │   │   ├── moor.gd
│   │   │   ├── moor.tscn
│   │   │   └── quests.res
│   │   ├── 02_desert/
│   │   │   └── ...
│   │   ├── 03_jungle/
│   │   │   └── ...
│   │   └── 04_heat/
│   │       └── ...
│   ├── addons/
│   │   ├── dialogic
│   │   ├── gdUnit4
│   │   └── ...
│   ├── autoload/
│   │   ├── storage_manager.gd
│   │   ├── quest_manager.gd
│   │   └── ...
│   ├── characters/
│   │   ├── warrior/
│   │   │   └── ...
│   │   ├── holy_warrior/
│   │   │   └── ...
│   │   ├── wizardress
│   │   └── ...
│   ├── script_templates/
│   │   └── node2d/
│   │       └── npc.gd
│   ├── shared/
│   │   ├── items/
│   │   │   └── ...
│   │   ├── weapons/
│   │   │   └── ...
│   │   └── ...
│   ├── ui/
│   │   └── ...
│   └── project.godot
├── storefront_assets
├── scripts
├── .gitignore
├── README.md
└── ...other non-godot related files

I might have gotten carried away a little here (this is still a very incomplete list), so let's stop and break this stuff down, starting with the top-most folder.

The src folder contains the actual Godot project itself. It's a very common practice not to keep the project sources in the topmost folder. That allows the rest of the folder to contain files that aren't directly related to the Godot project. Even though Godot won't import unsupported files, I think it's just nicer that the stuff we don't need for the game directly is outside of the game folder. For example:

The .gitignore is there so that no unnecessary junk pollutes my git repository. I keep every single experiment and prototype in git. You never know when you might need some code you wrote on a productive Sunday afternoon months ago. In case you were wondering, the .gitignore for Godot 4 is very simple:

.godot/
*.translation

Now let's delve into the src folder, the Godot project. Overall, you can see the entity-based folder structure I outlined earlier. The same applies for the given shared folder. Since all classes in Diablo 2 this hypothetical RPG game share weapons and items (we'll conveniently ignore class-specific stuff), they are good candidates to go in the shared folder.

I went with the acts folder as a more complete example of the hierarchy I usually have in mind when I create a folder structure. The acts themselves are prefixed with a number so future me or a teammate knows the order they are played in, and this order will also be reflected when these folders are sorted by name ascendingly (which is the default in the FileSystem tab of Godot's editor). The act itself contains folders for the enemies and NPCs the player encounters, as well as a script and a scene file for the respective act. Maybe the act won't even require a script file because it will be assembled from various reusable components located in the shared folder. An obvious omission for brevity's sake are the various assets each act requires.

Continuing, we have the addons folder, which, as you probably already know, is the home of 3rd party plugins for the Godot engine. Both "dialogic" and "gdUnit4" are addons I like using a lot.

Next up, we have the autoloads folder. These are all folders that are added as Autoloads in my "Project Settings". So, all kinds of manager singletons, global constants, maybe a signal bus, that kind of stuff.

The characters folder has all the playable classes a player can pick. As they have little in common between themselves, they exist outside of the shared folder. This folder could also be stored inside a player folder, because there's definitely more that belongs to the "player" entity than just character classes, but as long as I can, I try to keep a flatter folder structure.

The script_templates folder is where you can store templates for scripts. These appear as a selection in the "Attach Node Script" dialog. This allows you to easily create similar scripts for, let's say, NPC behavior without having to setup the boilerplate every time. You can read more about them in Godot's documentation.

I did outline the shared folder earlier, so we got that out of the way.

And last, but not least, we have the ui folder. It would basically be a hierarchy of scenes for conversation dialogs, the inventory, and other screens, composed out of UI components found in a hypothetical shared/ui folder. In a scenario where you'd have class-specific UIs, like in Starcraft, where each race has its own HUD style, I'd put that in the characters folder I mentioned earlier and would reserve the top-level ui folder for more generic things like a main menu.

Conclusion

If you've reached this point in the article, thank you for reading! I've been wanting to write this for a while now. I think it's a fun exercise to try and formalize something as trivial as putting files into folders into some form of theory.

I want to give a shoutout to @pyxus. We talked about project structuring a while ago someplace on Discord, and they showed me a Notion document they were keeping that had a documented style guide for a Godot project structure. That, essentially, planted the seed for this article. I also got the opportunity to think about this stuff before starting to work on The Rise of the Golden Idol and that game's folder structure also loosely adheres to the principles outlined here.

I hope this will give you some ideas on how you can improve the way you structure your Godot projects. If you want to chat about files, let me know on X or mastodon!

UPDATE: After I posted the article, @njamster, pointed out to me that his approach is kinda similar to mine. I'd say it's even better, considering it's so simple to explain that it fits in a mastodon post!