2D Platformer
This is a tutorial for a basic 2D platformer in Godot. We will be starting with a new project.
Please download the asset pack linked here (it is free so you don’t need to pay).
Making the Player
-
Click “Other Node” and add a CharacterBody2D. Rename this to Player.
-
In the Inspector, click on “Collision” in the CollisionObject2D section and change the layer to only have 2 selected
-
Give your CharacterBody2D 2 child nodes, an AnimatedSprite2D and a CollisionShape2D.
-
In the project menu at the top left, open the project settings, go to the rendering heading in the general tab, click on texture, and change the default texture filter to Nearest.
-
Click on AnimatedSprite2D and click on “Animation” in the inspector.
-
Click on Sprite Frames and New SpriteFrames.
-
Click on the SpriteFrames you just made. This should open a SpriteFrames dock at the bottom of the screen. Here we are going to add the sprite animations for our character.
-
Click on the new animation button at the top left of the new dock and create 4 new animations so you have 5 total. These will be the Idle, Run, Roll, Hit, and Death animations.
Animating The Sprite
-
Starting with Idle, click the grid icon to the right of the play/pause buttons. Navigate to the knight.png image in the sprites folder of the asset pack and open it.
-
On the right, you will need to change both Vertical and Horizontal to 8, as the images are in an 8x8 grid.
-
Select the first 4 squares as these are our Idle animation frames. Add these frames and you can now watch your knight idle if you click the play button.
-
Make sure to have this animation auto play by clicking the icon to the right of the trash can above the “Filter Animations” bar.
-
Now repeat these steps (not including the auto play) for each of the animations.
-
Click on CollisionShape2D in the Node Tree and make the Shape in the Inspector a New CapsuleShape2D. Modify it so that it roughly covers the knight.
You can save this scene as we are done with the knight for right now.
Creating a Level
-
Create a new scene by clicking the + button in the scene tabs. Add a 2D Scene and rename it Level 1.
-
Click and drag your Player scene from the FileSystem into the Level 1 Scene Node Tree as a child of Level 1.
-
Add a Camera2D node as a child of the Player and set the Zoom for it in the inspector to 4 for both x and y. If you now run the game, you should see your knight floating in a gray void.
-
Add a TileMapLayer node as a child of Level 1. Rename this to Foreground and create a new Tile Set using the Inspector. This should open 2 new menus at the bottom of the screen, a TileSet menu and a TileMap menu.
-
Inside the TileSet menu, click the + button and select “Atlas”.
-
Navigate to the world_tileset.png image in the sprites folder and load that. You will be asked if Godot can automatically create tiles, select yes.
-
Click the “TileSet” button in the inspector. Go to the “Physics Layers” drop down and click the “Add Element” button.
-
Move back to the TileSet menu. Click the Paint button and then click the drop down that appears and select “Physics Layer 0”.
-
Click on some of the square platforms and they should be given a blue collision box. It will look highlighted on the Base Tiles sections.
-
Now open the TileMap menu at the bottom of the screen and click on a tile to enable drawing in the editor. You can click to place individual tiles, or click and drag to paint them following the cursor. Play around with the tile paint tools to create a fun level to play. Some cool things to check out are the Rect tool and the Place Random Tile options.
-
Add a new TileMapLayer as a child of Level 1 and place it above Foreground in the Scene Tree.
-
Repeat the steps used for the Foreground (ignoring the physics steps) to create your Background.
Adding Movement
Now we are going to add gravity and movement to the player.
-
Go back to your Player scene and attach a script by right clicking the player node and clicking “Attach Script”. Click “Create” as the defaults are what we want. Now if you run your game, you should be able to run around.
-
You may notice that your player doesn’t flip when going to the left, nor does the animation change from idle to run when you’re moving. To solve this, add the below code
above
move_and_slide()
as well as adding@onready var sprite_node = $AnimatedSprite2D
belowextends CharacterBody2D
as shown below. Note that the animation names are case sensitive, so if your player disappears that may be why.
Adding Killzones
Next let’s make a killzone so that when you fall off the map or hit an enemy, you’ll restart the level.
-
Create a new scene with an Area2D node and rename it Killzone. Go to Collision in the Inspector and change the mask to only have 2 selected.
-
Add a Timer node as a child of Killzone and change the wait time in the Inspector to 0 (this will auto change to 0.001).
-
Attach a script to Killzone. Default options are ok.
-
Click on Timer and in the node menu on the right connect the “timeout” signal to the Killzone script. Now click on Killzone and in the node menu, connect the “body entered” signal to the Killzone script. Replace the all of the code in the script with this:
-
After saving, you can now drag this scene into your Level 1 scene.
-
Give the Killzone a CollisionShape2D as a child node and make the shape a New WorldBoundaryShape2D. Drag this below your platforms so the player can fall into it.
Making Enemies
-
Create a new scene with a CharacterBody2D and rename this to Slime.
-
Give it 3 child nodes: an AnimatedSprite2D, a CollisionShape2D, and a Killzone. The Killzone will also need a CollisionShape2D as a child.
-
To add the sprite, simply repeat the steps we used for the player, but this time picking a different sprite (e.g. slime_green.png).
-
For the Killzone, add a New RectangleShape2D to the CollisionShape2D and make it roughly cover the slime. Make the other CollisionShape2D the same as the Killzone’s one.
-
We need to give it some way to recognise walls and turn around. Add a RayCast2D as a child of Slime and move it to the centre of the sprite and rotate the arrow so it points forward and stays close to the edge of the sprite.
-
Duplicate this node and flip the arrow the other way. At this point I’d suggest renaming them RayCastLeft and RayCastRight (or something similar) so it’s not confusing which is which when we put them in code.
-
Now attach a script to Slime and change all the code to this:
Now you can add your Slime to your level and if you run into it, you will reset the level.
Adding Items
Now let’s add a coin pickup.
-
Create a new scene with an Area2D node and rename it Coin.
-
Change the collision mask to only have 2 selected.
-
Give this node a AnimatedSprite2D and a CollisionShape2D as children. Add the coin sprite and animation to the node as before.
-
Give the CollisionShape2D a New CircleShape2D and have it cover the coin.
-
Attach a script to the Area2D node and connect the “body entered” signal to it. Inside the
_on_body_entered
function make it print something so we can test that it’s working. If you put the coin in your level, it should print out your message when you run over it. To make the coin disappear when you run over it, addqueue_free()
after the print.
Connecting Levels
Now let’s create a trigger to load the next level.
-
In Level 1 add a new Area2D node and give it a CollisionShape2D as a child.
-
On the Area2D node, change the collision mask to only have 2 selected.
-
Give the CollisionShape2D a shape and place it where you’d like the end of the level to be.
-
Attach a script to the Level 1 node, then click on the new Area2D node you just made and connect the “body entered” signal to Level 1. Change the code to this:
-
Create a new Scene with a Node2D called Game and attach a script to it. Change the code to below and make sure the preload filepath goes to your Level 1 scene.
-
Go into Project -> Project Settings -> Application -> Run and change the main scene to your new Game scene. If you now run the project, you should be met with a gray void upon reaching the end of the level.
-
You can make another scene called Level 2 and once you’ve done that, change the code in the Game script to the below:
You will also need to change the code in the
_on_timer_timeout()
function in the Killzone’s script toget_tree().get_root().get_node("Game").reload_current_level()
keeping in mind that “Game” is the name of the node in your game scene and is case sensitive. Now when you reach the end of Level 1, it will load Level 2 and you can play it.
Next Steps
Congratulations, you now have a working 2D platformer. Here is a list of possible extensions you might want to add to your game:
- Investigate adding moving platforms using AnimationPlayers
- Killing enemies if you jump on them or some form of combat
- On screen health or life system
- On screen timer or score
- Game over screen