Table of contents
Are you eager to get started making your own games?
Game development has never been more popular before – with sales stretching into the billions, and thousands of indie developers sharpening their skills and bringing their creations to life. Even if you’re a beginner who has never coded before, with numerous engines available, just about anybody can create and program their dream game project.
In this comprehensive tutorial, we’re going to be learning how to create your first game using Godot, a free, open-source game engine that allows you to create 2D and 3D games. Due to its open-source nature, which means users can add and remove things from the engine at their leisure, it has quickly been gaining vast popularity. With a vibrant community ready to assist, it is a perfect choice for creating your first game.
After installing Godot and learning the basics of the editor, we’re going to be creating a 2D platformer game, so strap yourself in and get ready to start developing games!
For this project, we’re going to be needing a few assets for the player, tiles, coins, enemy, etc. You can create your own or use the ones featured in this tutorial. The assets can be downloaded here.
FREE COURSES FINAL DAYS: Unlock coding courses in Unity, Unreal, Python, Godot and more.
FINAL DAYS: Unlock coding courses in Unity, Unreal, Python, Godot and more.
To download and install Godot, let’s go over to https://gamedevacademy.org/what-is-godot/. Here, you can view Godot’s features, community pages and more. We want to click on the Download button.
Select your platform, then download the 64 or 32 bit standard version (depending on your operating system).
This will download a .ZIP file. Inside of that is an application which you can extract to anywhere on your computer, and that’s it – Godot is installed.
Creating a New Project
Open up the Godot application we download to see the Project Manager. Here, we can create projects, view others and download templates.
Click on the New Project button to create a new project.
This will open up a new window.
- Enter in a name for your project
- Click the Create Folder button to create a new folder for the project in the Documents folder
- Click the Create & Edit button to launch the engine and begin creating the game
Exploring the Editor
When the editor pops up it may look pretty daunting with all the buttons and options, but let’s break it down.
Godot has 4 main panels which we’ll be using to create our game and each serves a specific purpose.
- This here is the Scene panel. It will display the node layout and hierarchy of the scene we’re currently in. We’ll go over what a scene and a node are very shortly.
- The FileSystem panel shows us all of the assets and files we have. Sprites, models, scripts, scenes, folders, audio, etc.
- This is where we can see and create our game. Moving things around, selecting, scripting, etc. Above this panel are four buttons and they toggle what the panel becomes. We can switch between 2D and 3D modes, the script editor and external asset library.
- This is the Inspector and this shows us the details of a node when we select one. The position, rotation and any other attributes which we can modify.
How Godot Works
Above we were talking about scenes and nodes. What are they? Well, a game in Godot is made up of a hierarchy of nodes. A node can be anything: a player, camera, 3D model, light, UI, etc. Nodes make up all of the entities in your game and also have the ability to be a child of another node.
Here’s an example of a player in a game. First, we have the KinematicBody node which can control movement and some physics interactions. Then, as a child of this, we have a collider and sprite node. When the kinematic body node moves, rotates, etc, the others will follow. Even if we delete the parent node, the children will follow.
Games in Godot are made up of many parent-children nodes to create the various different elements and systems of a game.
Since a game is made up of nodes, it will eventually get to a point where there are hundreds or even thousands of them in the scene tree panel. This will make it hard to find certain ones and overall make working on the game confusing. To solve this issue, we can divide up our nodes into scenes.
Scenes are self-contained node packages which we can then drop into other scenes as nodes. Let’s take our player example and turn that node hierarchy into a scene. It is a saved file in the FileSystem which we can then drag into another scene.
A benefit of dividing your game up into nodes is also the fact that we can remove the need to repeat common node structures. Instead of having 100 tile nodes which all have a sprite, collider, etc all in the same scene, we can just create one of those as a scene and drag in multiple instances.
We’ll be exploring nodes and scenes throughout this project.
Creating our First Scene
Alright, let’s get started on our 2D platformer project. First, we’ll need to create a main scene which will be the basis for our game, containing other scenes such as the player, tiles, coins, and enemies.
In the scene panel, click on the 2D Scene button to create a new 2D scene.
With our new node, let’s double click it and rename it to MainScene.
It’s also good to save your scenes when you create them. Save with either Ctrl + S or Scene > Save Scene. Hit enter and you should see it down in the file system.
Creating the Player
Alright, we got our main scene. Next, let’s go and create our player scene which will hold all the nodes we need and the corresponding script.
To create a new scene, we can go Scene > New Scene. In the scene panel, select Custom Node. This will open a window and we want to create the KinematicBody2D node.
Rename the node to Player, then save it.
With our platformer game, we’ll be needing a few sprites and other assets. Included with this tutorial, is a .ZIP file containing all the assets we’ll need. Download that and drag the three folders into our Godot project folder.
Back in Godot, let’s right-click on the Player node and select Add Child Node. We want to add in a CollisionShape2D node as a child. Then in the file system, find the Player_Idle image and drag that into the scene window to add that as a child node. Rename that node to Sprite.
We want our sprite to be centered, so select it and over in the Inspector:
- Open the Transform drop-down
- Set the Position to 0, 0
You might see that the collision node has an error symbol next to it. This means we need to give it a shape. Select the collision node and in the inspector set the Shape to Capsule. To make it visible, re-order the node hierarchy by dragging the collider node underneath the sprite node.
To edit the collider properties, select the shape in the inspector and it will open up more options.
- Set the Radius to 27
- Set the Height to 12
- Set the Position to -0.5, 14.7
Scripting the Player
Now that we have the node structure setup, we can begin to script our player. This will involve the movement, jumping and collecting coins.
To create a script, select the parent Player node and in the Inspector, create a new script. This will open up another window, just hit enter.
What you’ll see then is that the main window will switch from 2D to Script mode.
Godot uses its own scripting language called GDScript. This is similar in syntax to Python. We won’t be going over every aspect of the language or the basic concepts of programming – so a basic understanding is required going forward.
Right now in the script we have two things.
Extends is similar to using or import. We’re extending from the kinematic body 2D object we’re attached to, so we’ll have direct access to those attributes.
func _ready (): pass
This is a function that is basically a block of re-usable code we can call. The _ready function is built into Godot and gets called once when the node is initialized.
The pass is simply a filler line to define the function. If it’s empty then an error will occur. The pass does nothing.
We’re going to start by adding in some variables.
# stats var score : int = 0 # physics var speed : int = 200 var jumpForce : int = 600 var gravity : int = 800 var vel : Vector2 = Vector2() var grounded : bool = false
A Vector2 defines an x and y value which can be used for position, scale, rotation, velocity. We’re going to be using it to store our player’s current velocity.
We need one more variable. This is a reference to the sprite component which is a child node. onready means that we’re going to find the Sprite node when the node is initialized.
# components onready var sprite = $Sprite
Now that we have our variables defined, let’s start to get our player moving. To do this, we first want to define some key inputs. Open the Project Settings window (Project > Project Settings) and in there, navigate to the Input Map tab.
In the Action field, enter in a name for the input and click add.
We want to create 3 new actions.
Next to each action is a ‘+’ icon. Click that, then select Key. This will allow us to enter in a key for that action.
Enter in a new key for each of the three actions.
Once that’s done, we can close the window and continue scripting.
We’re now going to create a function which is built into Godot. The _physics_process function gets called 60 times a second and is what we use for physics calculations. We can remove the pass once we start filling the function in.
The delta parameter is the time between each frame. We can multiply our movement by this in order to move based on pixels per second, rather than pixels per frame.
func _physics_process (delta): pass
First, we want to reset the horizontal velocity. Then check for the left and right movement key inputs. These will change the horizontal velocity.
# reset horizontal velocity vel.x = 0 # movement inputs if Input.is_action_pressed("move_left"): vel.x -= speed if Input.is_action_pressed("move_right"): vel.x += speed
Next, we’ll move the player using the move_and_slide function which will move along a certain velocity, detecting colliders and other things. The second parameter is the ground normal (which way is the ground pointing?).
# applying the velocity vel = move_and_slide(vel, Vector2.UP)
After this, we can apply gravity and check for if we’re pressing the jump button and on the floor. If so, jump.
# gravity vel.y += gravity * delta # jump input if Input.is_action_pressed("jump") and is_on_floor(): vel.y -= jumpForce
Finally, we can flip the sprite depending on which way we’re moving.
# sprite direction if vel.x < 0: sprite.flip_h = true elif vel.x > 0: sprite.flip_h = false
We’re done scripting for now, so let’s switch back to 2D mode and go to the MainScene. Here, we want to drag in the Player.tscn scene from the file system.
What we can do now is test the game out. In the top right corner of the screen, click the Play button.
A window will pop up saying that there’s no main scene selected. Click the Select button and select the MainScene.tscn scene as the default one.
The game should then open up but we’ll quickly fall off-screen due to gravity. What we need to do is create a tile scene which we can then duplicate in the main scene.
Creating a Tile
Let’s now create a new scene (Scene > New Scene), select Custom Node and search for the StaticBody2D node. This is a physics node which is static so it doesn’t move or do anything but detect collisions.
- Rename the node to Tile
- Attach a new CollisionShape2D child node
- Set the Shape to Rectangle
- Set the Extents to 32, 32
- Drag in the Tile image from the file system to create a new sprite node
- Rename it to Tile
- Set the Position to 0, 0
- Save the scene as Tile.tscn
Back in the MainScene, we can drag the tile scene in. Then duplicate it with Ctrl + D and move it around.
To make it easier to position the tiles, we can turn on snapping. At the top left of the scene panel, click on the snap button to toggle it. Then click on the three dots to bring up the snap settings. Make sure only Use Pixel Snap is selected. Now when we move a tile around, it will snap based on the pixels.
Now we can press play and test it out on our new level.
Creating an Enemy
Create a new scene with a root node of Area2D. As a child attach a collision shape 2D node in the shape of a circle and drag the Enemy sprite in too (make sure to center it). Then save the scene as Enemy.tscn.
Select the Area2D root node and create a new script called Enemy.
First, we want to create the variables.
export var speed : int = 100 export var moveDist : int = 100 onready var startX : float = position.x onready var targetX : float = position.x + moveDist
export means that this variable is exposed in the inspector, allowing us to change the property on the instance of the scene.
Then in the _physics_process function, we’re going to be moving between the startX and targetX positions. The move_to function is our own custom function we’ll make after.
func _physics_process (delta): # move to the "targetX" position position.x = move_to(position.x, targetX, speed * delta) # if we're at our target, move in the other direction if position.x == targetX: if targetX == startX: targetX = position.x + moveDist else: targetX = startX
The move_to function translates a given value to a target based on a given step.
# moves "current" towards "to" in an increment of "step" func move_to (current, to, step): var new = current # are we moving positive? if new < to: new += step if new > to: new = to # are we moving negative? else: new -= step if new < to: new = to return new
Back in the MainScene, drag the Enemy.tscn into the scene. Figure out the distance between the enemy and where you want to move to and set that in the Move Dist property.
We can now press play and see the enemy in action. You may notice that it’s quite slow, so let’s set the Speed to 500.
Colliding With the Enemy
Right now, the player can walk through the enemy with no issue. What we want, is for the game to reload if we hit the enemy. To do this, we need to work with signals. These are events that can be called when a certain thing happens to a node.
In the Enemy scene, select the root Area2D node and in the Inspector, switch to the Node tab. Here, we can see all the signals that the node can emit. We want to double click on the body_entered signal. Press enter and it should create a new function in the Enemy script.
In this function, we’re going to check if the body we entered was called Player. If so, call the die function on that node.
# called when we collide with a physics body func _on_Enemy_body_entered (body): if body.name == "Player": body.die()
Let’s now go to the Player script and create the die function.
# called when we hit an enemy func die (): get_tree().reload_current_scene()
Now when the player hits the enemy, the scene will reload.
We want the player to achieve a certain goal, so let’s add in some coins. Before we create the scene, let’s go and set up the scripting side of things over in the Player script.
The collect_coin function gets called when we run into a coin and increments our score by the given parameter value.
# called when we run into a coin func collect_coin (value): score += value
Next, create a new scene and make the root node an Area2D. Give it a collision shape node as a child with the shape being a circle. Then drag in the Coin sprite and center it.
On the Area2D root node, create a new script called Coin. This is just going to detect when a player has entered the collider, then call the collect_coin function.
We’ll start with our only variable which is just the value of the coin.
export var value = 1
The _process function is built into Godot and gets called every frame. We’re going to use it in order to rotate the coin over time.
func _process (delta): # rotate 90 degrees a second rotation_degrees += 90 * delta
Next, we need to select the Area2D node, in the Inspector go to the Node tab and double-click on the body_entered signal to attach it to the script.
Back in the script, we can fill in the function to check if the entering body is the player. If so call the collect_coin function and destroy the coin.
# called when something collides with us func _on_Coin_body_entered (body): # was it the player? if body.name == "Player": body.collect_coin(value) queue_free()
In the main scene, we can now drag in the coins to populate the level.
Right now our camera is static, so let’s implement the ability for the camera to move horizontally.
The camera is a new node we’ll create as right now, the game is being rendered just based on the position of the nodes and pixels, not the position of the camera. So in the MainScene, right click on the parent node and create a new child node of type Camera2D.
- Enable Current so that the camera will be active
- Move the camera to cover the level
Now we can create a new script on the camera node called CameraController.
First, we want to create a variable to store a reference to the player in order to track them. get_node will search for a node based on the given path.
onready var player = get_node("/root/MainScene/Player")
Then in the _process function (called every frame), we can set our X position to be the same as the player’s.
# tracks the player along the X axis func _process (delta): position.x = player.position.x
Now when we press play, you’ll see that the camera follows along the player’s X position. Except that it does act a bit weird. Select the camera and in the Inspector, set the Drag Margins to 0, 0, 0, 0. This means that there’s no margin for us to move around freely, the camera will always be tracking our X position.
Press play and you should see that it’s all working fine now.
Now that we have the game pretty much done, all we need to do is add in some sort of user interface to show us our current score.
To begin, create a new scene with the root node being User Interface (control).
- Rename the node to UI
- Save the scene
You’ll see that there is a colored rectangle. This is the bounds of the control node which is how we build our UI elements.
This rect right now is covering the whole screen. Let’s add in two new things.
- A coin icon
- A text box to show our current score
Let’s start with the icon. Create a new child node of type TextureRect. This is a control node where we can show an image. In our file system, find the coin image and drag that into the Texture field in the inspector.
Also set the Position to 20, 20.
Then for the text, we want to create a new child node of type Label. This is a control node where we can display text.
- Rename it to ScoreText
- Set the Position to 90, 20
- Set the Size to 100, 64
Right now we can’t see our text, and that’s because we need a font. There is a default font but it’s quite limiting so we’ll setup our own.
In the file system, open the Font folder, right click on the font file we have and select New Resource... Find the DynamicFont resource and create that.
Select our new resource and in the inspector, drag the .ttf font file into the Font Data property to assign it.
Under the Settings tab, set the Size to 40.
Select the ScoreText and under the Custom Fonts tab, drag in our new dynamic font resource.
We can now type in a placeholder number up in the Text property. To make the text centered vertically, set the Valign property to Center.
Alright, we’ve got our UI setup. If we go to the MainScene we can drag the UI scene into the node list and it should appear.
If you press play though, you’ll see that it doesn’t follow the camera. To fix this, we need to make this UI scene a child of a new node called CanvasLayer. This node defines what gets rendered onto the screen as UI.
Scripting the UI
All that’s left to do is connect the UI to a script. In the UI scene, create a new script called UI and attach it to the parent node.
First, we need to create a variable which will get the score text node upon starting the game.
onready var scoreText = get_node("ScoreText")
Then in our only function, we’ll set the score text to whatever the parameter sends over.
func set_score_text (score): scoreText.text = str(score)
Over in the Player script, let’s create a variable to reference the UI node.
onready var ui = get_node("/root/MainScene/CanvasLayer/UI")
Then down in the collect_coin function, let’s add a line of code to set the UI text.
If we press play now the score text will update when we collect a coin. Let’s also set it up so that the text is set initially when the game begins. In the UI script…
func _ready (): scoreText.text = "0"
Now the text should be set to 0 when we start the game.
And that’s it! If you hit Play on your Godot project now, everything should work. Congratulations on completing your first game!
We now have a functional 2D platformer with a playable character, coins, UI, and a dangerous enemy to obstruct our path. While you can certainly improve upon this framework to add even more challenges, you’ve no doubt seen how the foundations of the framework taught here will provide a useful starting point as you increase your game development skills further. The sky is truly the limit, and you can expand as needed to create new game projects with these fundamentals.
Either way, you’ve taken the first crucial step and created your first game! We wish you luck in the future, and we can’t wait to see the sorts of games you create!