//rp.wtf

Godot project configuration with GDScript

Sun Jun 16 2024

If you are like me, you create new Godot projects regularly, whether it's to prototype a new idea, try something quick, participate in a game jam, or just start a new game project.

There are probably certain settings you change every time for each project. The bulk of my personal changes in a new project happen in the "GDScript" section of the "Debug" category, where you can set up how the editor validates GDScript, so I don't create silly, easily avoidable bugs. That's a lot of time spent in configuration; I'd rather spend it building cool stuff.

The GDScript validation configuration screen in Godot 4

Doing this stops being fun after the second time.

To avoid having to configure this every time, one way is to set up a template—a folder with a barebones Godot project—which you copy into another folder, rename some things, and you are good to go.

That's fine, but I'd like more flexibility. For example, the template's project.godot file will have a hardcoded Godot version that you will have to change, and that's just one potential incompatibility. A good friend of mine proposed a better solution using a feature of Godot that I was absolutely not aware of (partially because I use an external editor for editing scripts): the possibility to simply execute a script from the editor itself.

The Godot documentation, being the fantastic collection of text that it is, even mentions this feature, mostly as a way to do batch operations, which would be tedious to do by hand. And I'd say that first-time project configuration is the perfect fit for this. So, let's set that up!

How?

Create a new Godot project the usual way, go to Project Settings, and make all the changes you'd like the first-time configuration script to apply. In my case, I'll set some GDScript validation settings in the Debug section, such as errors on untyped variable declarations.

Now create a new script and give it a name like setup.gd. Replace the contents with the following:

@tool
extends EditorScript

static var settings: Array[Array] = [] # we will fill this out later

func _run() -> void:
    for pair in settings:
        @warning_ignore("unsafe_call_argument") # trust me bro
        ProjectSettings.set_setting(pair[0], pair[1])
    ProjectSettings.save()
    print("setup.gd ran!")

To summarize, we are creating a script that extends EditorScript and overriding it's virtual _run method, which gets executed when a script is run from an editor. We have defined an empty array of arrays called settings. The _run method loops over this array using the first element of the value as a key and the second element as a value in the call to set_setting(). After that is done, we save the changes to project.godot and print a message. We're also silencing a potential warning because Godot doesn't know that we're passing a string into set_setting().

We now have the framework for our setup script, and we just need to add the data. Since our project.godot has only overridden values now, we can yank those and, with some minor changes, add them to the settings array. So, open project.godot in any text editor so you can copy the values you set earlier. If you want to use Godot for opening it, you have to change "All Recognized" to "All Files" in the "Open" dialog; otherwise, the file will be invisible.

Now we have to convert the values in project settings to property path and value pairs and put them in the settings array. The ConfigFile documentation has a pretty good description about the formatting of values in Godot's "project.settings", but, in short, the text between the square brackets is a section name, and that's followed by lines of a property path, an equals sign, and a value.

For set_setting(), the full property path must also include the section name, so if we take this theoretical value from project.settings:

[foo]

bar/baz/some_setting=2

As a call to set_setting() it would look like this:

# See that the section name "foo" is included as a part of the full path.
ProjectSettings.set_setting("foo/bar/baz/some_setting", 2)

The logic in our _run() function in the loop assumes that the settings array contains arrays where the first element is a property path and the second element is a value, which then gets passed to set_settings(). Therefore, we can convert overrides in project.godot which look like this:

[debug]

gdscript/warnings/untyped_declaration=2
gdscript/warnings/unsafe_property_access=1
gdscript/warnings/unsafe_method_access=1
gdscript/warnings/unsafe_cast=1
gdscript/warnings/unsafe_call_argument=1

and add them to the settings array like this:

static var settings: Array[Array] = [
    ["debug/gdscript/warnings/untyped_declaration", 2],
    ["debug/gdscript/warnings/unsafe_property_access", 1],
    ["debug/gdscript/warnings/unsafe_method_access", 1],
    ["debug/gdscript/warnings/unsafe_cast", 1],
    ["debug/gdscript/warnings/unsafe_call_argument", 1],
]

This is a good opportunity to practice some find-and-replace-fu. Now the full script looks something like this:

@tool
extends EditorScript

static var settings: Array[Array] = [
    ["debug/gdscript/warnings/untyped_declaration", 2],
    ["debug/gdscript/warnings/unsafe_property_access", 1],
    ["debug/gdscript/warnings/unsafe_method_access", 1],
    ["debug/gdscript/warnings/unsafe_cast", 1],
    ["debug/gdscript/warnings/unsafe_call_argument", 1],
]

func _run() -> void:
	for pair in settings:
		@warning_ignore("unsafe_call_argument") # trust me bro
		ProjectSettings.set_setting(pair[0], pair[1])
	ProjectSettings.save()
	print("setup.gd ran!")

And that's all you need to do. Now you can copy your setup.gd file to a new or existing project and run it with File → Run. If you see the final print statement and no errors were output, everything should be properly set. After running it, I recommend reloading the project, for good measure, so all the new settings take effect.

The File context menu showing a highlighted Run menu item

You can also conveniently use a keyboard shortcut, Ctrl+Shift+X, by default.

Conclusion

You can absolutely go wild with this approach - you can extend it to set any setting, create new input actions (that's what my friend uses it for), modularize it to create a flexible template for any type of project you create, etc. I recommend storing this in a separate repository so you always have a place to grab the most up-to-date version from.

As a final note, a quick self-plug: we just released the demo for The Rise of the Golden Idol, which you should absolutely play, even if the project was configured the old-fashioned way, without any fancy scripts.

If you have any feedback or questions, ping me on X or mastodon!