-
Notifications
You must be signed in to change notification settings - Fork 184
Coding tips and best practices
Here are some basic advices and tips about using Godot, coding in general and specifically in this project.
There is a lot of good content out there to learn how Godot works and how to work with the engine, including:
- The official Godot documentation
- KidsCanCode's Godot recipes
- GDQuest's Godot tutorials
- HeartBeast's YouTube channel
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
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.
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.
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()
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.