Protect your games from bugs with these GDScript features!

Have you ever written a function and thought "Hm, if this gets called in the wrong circumstance things might go wrong. Oh well, I'll just remember to use it right!"

Be careful! If you code with this mindset, you are setting yourself up for many messy debugging sessions in the future. As your codebase grows larger, you will not remember the specifics of code you wrote weeks or months ago. This is true for both teams and solo developers alike.

So protect yourself from your own foolishness by using doc comments and assertions.

Documentation comments

You know how you can hover over built-in Godot classes and functions to get a neat, verbal description of them? Well, you can make your own classes, variables, and functions do the same! Just use a double hashtag (##) to make a documentation comment.

Example:

var default_health = 100  ## The starting health of the player character

Or:

## The starting health of the player character
var default_health = 100

This comment will now show up whenever I hover over the default_health variable anywhere in my code. Documentation comments also have a lot of features that let you style and format the text that appears. Read more (Godot docs). (Also works in VSCode with the Godot Tools extension!)

Besides letting you make neat documentation, don't underestimate the power of actually trying to describe your own code to yourself in words! It's often what makes me notice flaws in my code.

Assertions

What if you want to prevent a function from even being used wrong in the first place? For this, use assertions!

assert (condition, message)

An assertion takes a condition, and if it's false, it will stop the game and show an error in Godot (at the bottom, where all the other errors and warnings appear). Next to the condition, you can also add an error message.

If the assertion's condition is true, the program will instead just continue to the next line as if nothing happened.

Edit: Should mention that assertions are automatically stripped from release builds. They are only for debugging.

An example from my own code I was working on today:

## Spawns the provided [Creature] in the level. The [Creature] MUST have its "race" property set.
func add_creature (new_creature: Creature) -> void:
  assert (new_creature.race != null, "Tried to add a creature with a null race to the level")

  level_creatures.append (new_creature)
  add_child (new_creature)

If the creature hasn't been given a race, new_creature.race != null will equal false and the game will stop, showing the written error message in Godot.

If it was possible to add a creature without a race to my level, it would cause some of my later functions to break down the line, and it wouldn't be clear why.
This assertion can save me a bunch of pain when debugging since it will show just what went wrong the moment it happens, not later when the cause is unclear. Future me won't even be able to use the function wrong.

Bonus mentions

  • Static typing - this is a no-brainer. Explicitly defining types takes very little effort and makes your code at least 10000% more protected against bugs. Godot docs.
  • OS.alert() - If you want to shove an important error in your face without stopping the whole game, this will create a popup window with the provided message.
  • print("sdfodsk") - Self-explanatory.