Code your first complete 2D game with godot

By: Nathan Lovato - April 13, 2021

In this lesson, you will get to create a complete 2D game from scratch and step-by-step.

This video is based on an open-source game by KidsCanCode.

We go over a lot of code, so I don’t have the time to explain how every single node works or other features it offers; it would make the video extremely long and tedious.

Here, you will be learning by example.

The goal is for you to create a complete game to see the entire process in one hour.

You will learn to:

  1. Create a complete game from start to finish.
  2. Code a player with simple animations.
  3. Use code to spawn enemies around the screen randomly.
  4. Design and code interface to start and replay the game.
  5. Count and display the player’s score.
  6. Code a game over condition and restart the game.

You can download the starter project here: Dodge the Creeps starter assets

Below, I give you a few more insights regarding the yield keyword we use in the tutorial. You can read on after watching the video.

The yield keyword and coroutines

When you use yield in a function, it causes it to stop execution. The function then waits for a signal and resumes at a later time.

Here’s how it works under the hood.

When you call a function, the GDScript compiler creates an object in memory to represent its current state.

This object keeps track of:

  • The current line of code being executed. In case there is an error, Godot can tell you on which line it happened.
  • Any variable (and value) local to the function. These variables are created with the function and deleted once the call ends.

The object representing the function gets freed from memory as soon as the function returns.

When you use yield, Godot keeps the function’s state in memory for longer.

These functions that you pause and resume later are called coroutines.

We call them that way because they allow functions to delegate work to one another and wait for the result.

You have to be careful with these functions' asynchronous nature. If they don’t get the signal back, they never complete and keep piling up in your memory.

You should always test that they are working as expected early, as we did in the course when writing the start game sequence.

In any case, coroutines are a really powerful feature that helps you keep code grouped in one place.

That’s it for your first 2D game. In the next lesson, you’ll get to create a complete 3D game, from scratch.

Made by

Nathan Lovato

GDQuest founder. Courteous designer with a taste for Free Software. I promote sharing and collaboration.

6 comments

Alejandro De Oliveira

Hi! How are you?

First of all, thank you very very much for all the tutorials, I appreciate very much your work.

Saying that, I would like to ask your help, because I have submitted my email for the email daily course, but every time I confirm my email nothing happens, I’ve tried like 4 times and yet nothing happens, maybe it’s because I’m in Venezuela and there may be restrictions for this zone but I tried using a VPN as well and nothing happens, I haven’t received the daily email course and I would really like to learn with it.

Having said that, I would like to thank you again for all the high quality material you have made, and I hope you can help me with my issue, thanks in advance.

Alejandro.

Reply to Alejandro De Oliveira

Hi Alejandro,

That could be your email provider that treated the confirmation email as spam. Did you check your spam folder to see if it was there?

If you can contact me on Discord or via email and send me the email address you used to subscribe, I can look at what happened.

You can email me at nathan [at] gdquest dot com.

Reply to GDQuest

Hi Nathan, my player’s animations won’t play. Here’s my code and scene trees (so far):

Player Code:

extends Area2D

export var speed = 400.0 var screen_size = Vector2.ZERO

func _ready(): screen_size = get_viewport_rect().size

func _process(delta): var direction = Vector2.ZERO if Input.is_action_pressed(“move_right”): direction.x += 1 if Input.is_action_pressed(“move_left”): direction.x -= 1

if Input.is_action_pressed("move_down"):
	direction.y += 1
if Input.is_action_pressed("move_up"):
	direction.y -= 1

if direction.length() > 1:
	direction = direction.normalized()
	$AnimatedSprite.play()
else:
	$AnimatedSprite.stop()

position += direction * speed * delta
position.x = clamp(position.x, 0, screen_size.x)
position.y = clamp(position.y, 0, screen_size.y)

if direction.x != 0:
	$AnimatedSprite.animation = "right"
	$AnimatedSprite.flip_h = direction.x <  0
	$AnimatedSprite.flip_v = false
elif direction.y != 0:
	$AnimatedSprite.animation = "up"
	$AnimatedSprite.flip_v = direction
Reply to Dominic

Hi Nathan, my player’s animations won’t play. Here’s my code and scene trees (so far):

Player Code:

extends Area2D

export var speed = 400.0 var screen_size = Vector2.ZERO

func _ready(): screen_size = get_viewport_rect().size

func _process(delta): var direction = Vector2.ZERO if Input.is_action_pressed(“move_right”): direction.x += 1 if Input.is_action_pressed(“move_left”): direction.x -= 1

if Input.is_action_pressed(“move_down”): direction.y += 1 if Input.is_action_pressed(“move_up”): direction.y -= 1

if direction.length() > 1: direction = direction.normalized() $AnimatedSprite.play() else: $AnimatedSprite.stop()

position += direction * speed * delta position.x = clamp(position.x, 0, screen_size.x) position.y = clamp(position.y, 0, screen_size.y)

if direction.x != 0: $AnimatedSprite.animation = “right” $AnimatedSprite.flip_h = direction.x < 0 $AnimatedSprite.flip_v = false elif direction.y != 0: $AnimatedSprite.animation = “up” $AnimatedSprite.flip_v = direction

Reply to Dominic

Enter your email to get notified when someone replies to your comment.
We encrypt your addres with a strong 256-bit AES encryption.
We'll only use your address for notifications. You can unsubscribe anytime.