JVDesignStudio · Godot 4 Tutorial

Build
Space Invaders
From Scratch

A coding adventure for kids aged 8 and up. 11 chapters. Real GDScript. A complete, playable game by the end — and you write every line yourself.

Godot 4 GDScript 👾 11 Chapters 🆓 Free Ages 8+
EngineGodot 4
LanguageGDScript
Chapters11
Aliens55
Ages8+
PriceFree

A Complete Space Invaders Clone

By the end of Chapter 10 you'll have a fully playable game. Every feature, written by you.

👾
55 Alien Formation
A grid of aliens that march left and right, drop down, and speed up as you shoot them.
🔫
Player Spaceship
A ship you control with the keyboard move left, right, and fire bullets at the aliens.
🛡️
Four Shields
Destructible barriers you can hide behind both your bullets and alien bullets chip them away.
💥
Explosions
Animated pixel-art explosions when aliens and the player ship get hit.
❤️
Lives & Score
Three lives, a live score counter, and a hi-score that persists across waves.
🌊
Endless Waves
Each wave resets the aliens faster the game gets harder until you run out of lives.

How to Use This Guide

Do one chapter at a time. Test after each one. Don't skip ahead. This is how real programmers work.

🟢 DO THIS
Numbered Steps
Follow these in order. Always complete every step before moving on don't skip.
✅ Checkpoint
Check Your Work
At the end of each chapter, verify these things in Godot before moving on.
💡 Tip
Helpful Hints
Shortcuts, tricks, and things that make the next step easier to understand.
⚠️ Watch Out
Common Mistakes
Things that trip people up. Read these carefully they'll save you time.
📖 Info
Concept Explainers
Plain-English explanations of a tricky idea before you need to use it.
Rule #1
Type the Code Yourself
Don't copy-paste. Your fingers learn coding like they learn piano by doing it.

11 Chapters

Start at Chapter 1 and work through in order. Each chapter adds one piece to the game.

Chapter 1 · Setup
Setting Up Godot
Godot is a free program that turns your code into a real game. It handles the window, graphics, sounds all the boring stuff. You focus on the fun part: telling the game what to DO.
  • Go to godotengine.org → Download Godot 4 for your computer → Open it
  • In the Project Manager, click New Project → name it "Space Invaders" → Click Create & Edit
  • Click Project → Project Settings → Display → Window → Set width to 480, height to 600, Stretch Mode to canvas_items
Godot is open · Project "Space Invaders" exists · Window is 480×600
Godot 4Project Setup480×600
Chapter 2 · Game State
The Game State Script

Before building anything visible, create a script that remembers the score, lives, and wave number. This is called GameState.

A normal script only works while its scene is open. An Autoload script runs all the time — forever. Perfect for score and lives, because they need to survive scene changes.
# GameState.gd — type this exactly
extends Node

# var creates a variable — a box that holds a number
var score: int = 0
var lives: int = 3
var wave:  int = 1
var hi_score: int = 0
  • FileSystem panel → right-click res://New Script → name it GameState.gd → type the code above
  • Press Ctrl+S to save
  • Project → Project Settings → Autoload tab → add GameState.gd → click Add
GameState.gd saved · Appears in Autoload list · No red error lines
Chapter 3 · Player Ship
The Player Ship

The player ship moves left/right and fires bullets. It's built from three nodes stacked together.

Three nodes needed: Area2D (the container — detects when things touch it), Sprite2D (shows the ship picture), CollisionShape2D (invisible hit shape).
  • Scene → New Scene → add Area2D root → rename it "Player"
  • Add Sprite2D child → add CollisionShape2D child → set shape to RectangleShape2D
  • Save as Player.tscn → attach a new script Player.gd
# Player.gd
extends Area2D
const SPEED = 180.0
const SCREEN_W = 480.0
var can_shoot = true

func _process(delta):
    var dir = int(Input.is_action_pressed("ui_right")) - int(Input.is_action_pressed("ui_left"))
    position.x = clamp(position.x + dir * SPEED * delta, 20, SCREEN_W - 20)
    if Input.is_action_just_pressed("ui_accept") and can_shoot:
        shoot()
Player.tscn and Player.gd saved · No red error lines
Chapter 4 · Bullet
The Bullet Scene

Both the player and the aliens fire bullets. Build one bullet scene and reuse it for both — just change the direction.

# Bullet.gd
extends Area2D
# -1.0 = flies UP (player)  +1.0 = flies DOWN (enemy)
@export var direction: float = -1.0
@export var speed: float = 400.0
@export var is_enemy: bool = false

func _process(delta):
    position.y += speed * direction * delta
    # Delete bullet when it leaves the screen
    if position.y < -20 or position.y > 650:
        queue_free()
queue_free() tells Godot to delete this node safely at the end of the current frame — not immediately while the game is still using it.
Bullet.tscn and Bullet.gd saved · No errors
Chapter 5 · One Alien
The Alien Scene

Build a single alien scene. Later, the AlienGrid script will create 55 copies of it automatically.

Use AnimatedSprite2D (not Sprite2D) — this one plays frame animations like a cartoon. You need 3 animations: 'a' (squid), 'b' (crab), 'c' (ufo). Each has 2 frames for walking.
# Alien.gd
extends Area2D
signal alien_killed(points, pos)
# 0=squid(10pts)  1=crab(20pts)  2=ufo-row(30pts)
@export var alien_type: int = 0
@export var points: int = 10

func _ready():
    add_to_group("aliens")
    match alien_type:
        0: $AnimatedSprite2D.play("a")
        1: $AnimatedSprite2D.play("b")
        2: $AnimatedSprite2D.play("c")

func die():
    alien_killed.emit(points, global_position)
    queue_free()
Alien.tscn and Alien.gd saved · No errors · Images set up in Chapter 8
Chapter 6 · Alien Grid
The Alien Grid

The most complex script in the game. Take your time — read each section before typing it. AlienGrid does three jobs:

  • Fills the screen with 55 aliens (11 columns × 5 rows)
  • Marches them sideways, drops them down when they hit an edge
  • Picks a random alien at the bottom of each column to shoot at the player
# AlienGrid.gd — variables section
extends Node2D
signal all_dead
signal reached_bottom
signal enemy_bullet_fired(pos)

const COLS = 11
const ROWS = 5
const SPACING_X = 36
const SPACING_Y = 32
const ROW_POINTS = [30, 20, 20, 10, 10]
var _dir: float = 1.0
var _step_interval: float = 0.8
const AlienScene = preload("res://Alien.tscn")
This script has 5 parts. Type them in order — each part uses variables or functions from the part above it. Check for errors after each part.
preload() loads a scene when the game starts. Then AlienScene.instantiate() creates a new copy. Like a cookie cutter — load it once, stamp out as many as you need.
Chapter 7 · Barriers
Barriers & Explosions

Four destructible shields the player can hide behind. Both player and alien bullets chip chunks out of them.

  • Create a new scene → StaticBody2D root → rename "Barrier"
  • Add a Sprite2D child (your shield image) and a CollisionShape2D
  • Attach Barrier.gd — the script tracks health and destroys the node when it hits zero
  • Save as Barrier.tscn

Also create an Explosion scene — an AnimatedSprite2D that plays a short animation then deletes itself with queue_free().

# Barrier.gd — simplified
extends StaticBody2D
var health = 4

func take_hit():
    health -= 1
    # Update sprite to show damage
    $Sprite2D.frame = 4 - health
    if health <= 0:
        queue_free()
Barrier.tscn and Explosion.tscn saved · No errors
Chapter 8 · Sprites
Drawing the Sprites

Every game needs pictures. This chapter covers drawing your pixel art sprites and wiring them into the scenes.

Use any pixel art tool: Aseprite (paid, excellent), Piskel (free, browser-based), or LibreSprite (free desktop). Export each sprite as PNG with a transparent background.

Sprites you need to draw:

  • Player ship — facing up, roughly 32×24px
  • 3 alien types × 2 walk frames each — squid, crab, ufo-row
  • Explosion — 3–4 frames of pixel burst
  • Barrier — 5 damage states (full → destroyed)
  • Player bullet — thin vertical sprite
  • Enemy bullet — 2 frames for zigzag animation
Keep a consistent colour palette — 4 to 6 colours maximum. Classic Space Invaders uses white aliens on a black background. Simple is fine for a first game.
All PNG sprites saved into res://assets/ · Assigned to correct scenes
Chapter 9 · Main Scene
The Main Scene

The Main scene brings everything together — it creates the player, alien grid, and barriers, and connects all the signals.

  • Scene → New Scene → Node2D root → rename "Main"
  • Add AlienGrid as a child node (attach AlienGrid.gd to it)
  • Add Player (instance Player.tscn) → position at bottom centre
  • Add 4 Barrier instances → spread evenly across the screen
  • Add a CanvasLayer → add Labels for score, hi-score, and lives HUD
# Main.gd — signal connections in _ready()
func _ready():
    $AlienGrid.all_dead.connect(_on_wave_clear)
    $AlienGrid.reached_bottom.connect(_on_game_over)
    $AlienGrid.enemy_bullet_fired.connect(_on_enemy_shoot)
    $Player.player_shot.connect(_on_player_shoot)
Signals are Godot's way of sending a message from one node to another without them needing to know about each other. When an alien dies, it emits a signal. Main listens and updates the score.
Main.tscn saved · All nodes added · No errors
Chapter 10 · Press Play!
Press Play!

This is the moment you've been working toward. Set Main.tscn as the main scene and press F5.

  • Project → Project Settings → Application → Run → set Main Scene to Main.tscn
  • Press F5 to run the game
  • Move with arrow keys, shoot with Space
If something doesn't work — read the red error message in the Output panel. It tells you exactly which file and line has a problem. That's how you fix it.

Common fixes:

  • Aliens not moving → Check AlienGrid.gd is attached to the AlienGrid node
  • Player invisible → Sprite2D has no texture assigned
  • Can't shoot → Check Player.gd signal is connected in Main._ready()
  • Score not updating → GameState Autoload not registered in Project Settings
Game launches · Aliens march · Player moves and shoots · Score updates · Game Over shows
★ Chapter 11 · Make It Yours
Make It Even Better

Your game works. Now make it yours. Here are challenges to push further.

  • Add a UFO that flies across the top row occasionally for bonus points
  • Add sound effects — laser shoot, alien death, player death, wave clear
  • Make aliens speed up faster as their numbers drop — tweak _step_interval
  • Add a High Score that saves to disk using FileAccess
  • Draw custom alien sprites to replace the placeholders
  • Add a main menu scene with a Play button and instructions
  • Export to HTML5 and upload to itch.io — share your game!
You built a complete game from scratch using real GDScript. That makes it REAL programming. 🎉

Key Patterns from the Guide

Real code from the actual game. Each pattern appears in the guide with a full explanation before you type it.

AlienGrid.gd Marching Logic
func _march():
    if _drop_next:
        _drop_next = false
        _dir = -_dir
        position.y += 12
        if position.y > 420:
            reached_bottom.emit()
        return
    position.x += _dir * 12
AlienGrid.gd Speed Up on Kill
func _on_alien_killed(pts, pos):
    _alive_count -= 1
    # Speed up as fewer aliens remain
    _step_interval = max(
        0.08,
        0.8 * (_alive_count / float(COLS * ROWS))
    )
    if _alive_count <= 0:
        all_dead.emit()
AlienGrid.gd Random Shooting
func _random_shoot():
    # Find bottom alien in each column
    var shooters = []
    for c in range(COLS):
        for r in range(ROWS-1, -1, -1):
            if is_instance_valid(aliens[r][c]):
                shooters.append(aliens[r][c])
                break
Bullet.gd Enemy Zigzag
var _anim_t: float = 0.0

func _process(delta):
    position.y += speed * direction * delta
    # Enemy bullets zigzag between 2 frames
    if is_enemy:
        _anim_t += delta * 8
        $Sprite2D.frame = int(_anim_t) % 2

Keep Building