Skip to content

Coding tips and best practices

Paul Joannon edited this page May 4, 2022 · 6 revisions

Here are some basic advices and tips about using Godot, coding in general and specifically in this project.

External resources

There is a lot of good content out there to learn how Godot works and how to work with the engine, including:

Repository file structure

Please try to keep the repository organized. It is far from perfect right now, but we are slowly working our way through cleaning it. In the meantime, it'd be nice if new content was organized from the get go.

The basic rules are:

  • Do not flood the root of the repository. You should not need to place anything there.
  • Assets go in their respective folders (audio/music/ for music files, graphics/sprites/ for sprites, etc.).
    • Feel free to further organize inside said folders.
  • Files that logically belong together should be grouped together.

For reference, here is the kind of structure we are ultimately shooting for.

.
├── audio
│   ├── music
│   └── sfx
├── fonts
├── graphics
│   ├── sprites
│   ├── textures
│   └── tiles
├── localization
├── scenes
│   ├── DDR
│   ├── platformer
│   │   ├── FollowCamera.gd
│   │   ├── FollowCamera.tscn
│   │   ├── Gravity.gd
│   │   ├── Gravity.tres
│   │   ├── enemies
│   │   │   ├── Bub.gd
│   │   │   └── Bub.tscn
│   │   └── player
│   │       ├── CoinCollector.gd
│   │       ├── Inventory.gd
│   │       ├── Inventory.tres
│   │       ├── Player.gd
│   │       └── Player.tscn
│   ├── sokoban
│   ├── title
│   └── ui
│       ├── CoinCount.gd
│       ├── PauseMenu.tscn
│       └── themes
│           └── default.tres
├── scripts
│   └── autoload
│       └── EventBus.gd
└── shaders

Use decent naming

Whether it is nodes in your scenes or variables in your code, try and name things decently.

For example, avoid naming your variable data or object or single letter words¹. Prefer descriptive names conveying information about that variable's content and purpose.

On the same level, avoid creating scenes that contain a bunch of similar nodes with incremental names (e.g. a UI scene containing RichTextLabel, RichTextLabel2, RichTextLabel3 and so on). Again, try to convey information about the nodes through their names.

¹ Obviously, this doesn't apply for commonly accepted things such as i for iterators or x, y, z for coordinate systems, etc.

Do not reference upward

It is generally not considered a good practice to reference nodes upward in the scene tree. The usual moto is call down, signal up. Meaning that it is fine for a node to have direct access onto its children (and further down) but should rely on looser means of communication with its parent (and further up). Solutions for this include signals, or even groups.

For further reading, I recommend this article by KidsCanCode.

Self-contained components

We recommend writing your code (and scenes) in the most self-contained and atomic way possible. It will end up being easier to maintain, and less prone to issues during merge (e.g. difficult and numerous merge conflicts).

For example, do not hack a fireball launching logic directly in the main player script. It can be implemented it in a dedicated node that’ll get added to the player scene.

# Shooter.gd

var flower_power: bool = false

func _ready() -> void:
    EventBus.connect("fire_flower_collected", self, "_on_flower_collected")

func _input(event: InputEvent) -> void:
    if flower_power && event.is_action_pressed("shoot"):
        shoot()

func shoot() -> void:
    pass # The actual shooting code

func _on_flower_collected() -> void:
    flower_power = true

Here, we don’t even need anything from the main player script. So there’s really no reason for our logic to be implemented over there.

Remember that when you are working within a PackedScene, you hold a reference of the root node of that scene through the owner property. We could for instance want to call a visual feedback on the player whenever we shoot a fireball.

# Shooter.gd

onready var player: Player = owner

func shoot() -> void:
    # The actual shooting code
    player.bounce()

Collision layers and masks

When working with objects that need to collide with each other (physics bodies or areas), it is important to actually configure their collision layers and masks.

If you haven't already, we invite you to read the official documentation on the matter. But long story short, we usually don't want everything to report collisions with everything else. That's exactly what layers and masks are for. One represents what your object is, the other what it should be colliding with. Take a minute to think about it and configure your object accordingly (the right answer won't be 1 and 1).

We are currently trying to move everything away from layer 1, so we can easily check for misconfiguration. Please try to ease that process by not using this layer any more.