Godot game engine introduction for beginners
Sandro MaglioneGet in touch with me
16 July 2023•
7 min read
For a long time I have been using Unity as a game engine to implement most of my games. I considered Unity as the standard, with more tutorials, features, community. Apparently an obvious choice (especially years ago, ~2014).
Nonetheless, Unity has always been frustrating to use. The software is slow and heavy (6Gb), I find the tutorials and documentation not user-friendly, and it has been always a struggle to implement complex behaviors in my games.
Lately this frustration convinced my to give a try to Godot, and I am glad that I did!
In this post I share my experience implementing some core functionalities which are needed for most 2D games using Godot:
- Objects interactions
This experience convinced my to switch to Godot 👇
This is not a Godot step by step tutorial, but more of a general overview of how to get started with the game engine.
I left links to every node and class so you can read more from the documentation about each topic covered in the post.
Scene contains a tree of nodes.
Each node encodes some functionality:
Nodes can be then nested into each other to implement more complex behaviors. Furthermore, a node itself can contain other
Scene as children.
This makes each
Node composable, which allows to build complex behaviors and interactions.
Animations (2D) are as easy as it gets to setup and use.
I used the
AnimatedSprite2D node, which allows to select a sequence of frames (images) to build an animation.
The process is simple:
- Insert an
- Add a
- Cut and select the frames for each animation (idle, walking, running) and give each animation a unique name identifier
- From the player script set the animation and call the
func _ready(): $AnimatedSprite2D.animation = "idle_down" $AnimatedSprite2D.play()
Now it is only a matter of updating the current animation each time the player state changes:
func _physics_process(delta): var animation_state = $AnimatedSprite2D.animation var input_direction = Input.get_vector("move_left", "move_right", "move_up", "move_down") velocity = input_direction * speed if velocity.length() == 0: if animation_state.begins_with("walk_"): if animation_state == "walk_up": animation_state = "idle_up" elif animation_state == "walk_x": animation_state = "idle_x" else: animation_state = "idle_down" elif velocity.length() > 0: if velocity.x != 0: animation_state = "walk_x" if velocity.x < 0: $AnimatedSprite2D.flip_h = true else: $AnimatedSprite2D.flip_h = false elif velocity.y > 0: animation_state = "walk_down" elif velocity.y < 0: animation_state = "walk_up" move_and_slide() $AnimatedSprite2D.animation = animation_state $AnimatedSprite2D.play()
Implementing a top-down movement system is also a breeze.
I created my character using the
CharacterBody2D provides a
move_and_slide() method that allows to move the node in any direction and speed specified by the node's own
From the settings (
Project > Project Settings... > Input Map) I defined the input buttons for each of the 4 movement directions (left, right, top, down).
In the player script I then update the
velocity based on the clicked buttons, and then call
func _physics_process(delta): var input_direction = Input.get_vector("move_left", "move_right", "move_up", "move_down") velocity = input_direction * speed move_and_slide()
These 3 lines of code are enough to implement a complete 2d movement system.
CollisionShape2D allows to define visually from the editor the shape of the collision area (using a
I then added a similar setup for all the nodes that are expected to collide with the player:
- I added a Physics Layer to the
TileSet, which allows to add a collider shape to a tile before painting a
- I added a
CollisionShape2Dtogether with a
RigidBody2Dto NPCs (Non Playable Characters)
There is more.
Every week I build a new open source project, with a new language or library, and teach you how I did it, what I learned, and how you can do the same. Join me and other 400+ readers.
After adding collision shapes and rigidbody it becomes possible to check for collisions between nodes.
I used a
RayCast2D node to define a line in front of the player for checking collisions.
From the player script I have full control over the properties of
RayCast2D. I added some code to change the direction of the raycast based on the direction in which the player is facing:
func _physics_process(delta): # Get current raycast direction var raycast_direction = $RayCast2D.target_position # Set new value for target_position $RayCast2D.target_position = raycast_direction
This allows to then detect collisions with objects in the scene from the player perspective.
For NPCs instead, I attached an
Area2D node to each of them, which I use to check if the player is nearby (using signals, ready below 👇).
Signals are the real game-changer when using Godot.
Signals allow to listen for any event from any script (Observer pattern)
Instead of hard-coding connection between nodes and manually define events and interactions, you can use Signals to send and react to any event between any node.
In my example I defined a custom signal inside the NPC script (by adding a
signal at the top of the script):
emit this signal every time the NPC detects the player nearby (by listening to an
Area2D signal) and the user presses the "interaction" button:
func _process(delta): if is_player_inside_area && Input.is_action_pressed("interact"): interacted.emit(speech_dialogue) func _on_area_2d_body_entered(body): is_player_inside_area = true
I also passed a custom value
text to the signal, which represent the sentence that the NPC is going to say to the player.
This signal triggers a speech bubble and starts the interaction between the player and the NPC:
func _on_npc_interacted(text): set_text(text)
I am mostly interested in 2d games. For a 2d games, having a fully-featured tilemap editor is critical. Godot provides all that you need using the
TileMap as root node of a new
TileMap contains a
TileSet, which represents the actual tiles that we are then going to "paint" inside the
Godot allows you to:
- Create a
TileSetfrom a tile sheet image, by splitting the image based on the resolution of each tile (I used a 16x16 tile sheet in my project)
- Define layers and ordering for each tile in the
- Add collision, physics, navigation (path finding), and more to the
- Visually print your level into the
TileMapusing the tiles inside
The editor is feature complete while also being easy to navigate. I definitely found every feature that I needed for my game example by just using
This is all that you need to start working on your game.
You can now add and control any animation to any node using
AnimatedSprite2D allows to control the duration of each single frame and preview the final result from the inspector.
You can use Signals to define and implement any interaction between any node (picking up objects, breaking a wall, attacking an enemy).
All of these blocks can be then composed together using
Scene to create each level for your game.
After this initial experience I am going to switch to Godot for my next games:
- More lightweight (200Mb instead of 6Gb of Unity)
- Integrated IDE
- Documentation easier to navigate
- Open source
- Better developer experience
These are all important points for me when working with any technology. So, welcome to Godot, more content coming soon 🚀
You can subscribe to my newsletter here below for more updates and tips 👇