A little monster.
A lot to learn.

Build its scene. Make it move. Give it something to collect.
Learn Godot one working idea at a time, then take it into your own game.

Our green monster is ready to build
0 of 3 activities complete · Completion is saved in this browser.

A player is a few nodes working together.

Build a scene tree and assign properties in a small editor model.

Activity 1 of 3
Your task

Give the monster an image and a body that can collide. Select a node to edit its properties, then test the scene.

Scene

Add a child to Player
2D preview
+

Build your player, then test the scene.

Inspector Player

CharacterBody2D provides movement and collision support for a player.

Move the parent. Its image and collision shape move together.

An image makes it visible. A shape gives the body its collision boundary.

Simplified browser model with gravity and a floor supplied for the test. In Godot, a CharacterBody2D needs a movement script; it does not fall automatically.

Rebuild this in Godot

  1. Create a CharacterBody2D named Player. Add Sprite2D and CollisionShape2D children.
  2. Assign your image to Sprite2D’s Texture. Give CollisionShape2D a RectangleShape2D and size it around the image.
  3. Attach a movement script to Player. Add a StaticBody2D floor with its own CollisionShape2D.
  4. Save Player.tscn. Continue to the movement activity to learn what its script does.
Open the editor guide

Take this into your own game

Read the full explanation. Each tutorial links back here when you want to try it again.

Fix the code. See it move.

Edit speed and direction, then measure what the player actually does.

Activity 2 of 3
Your task

Move 300 pixels in one second, in either direction. This script always moves right. Test it, then fix the two editable lines.

player.gd Two editable lines

extends CharacterBody2D
func _physics_process(delta):
    var direction = Input.get_axis(
        "move_left", "move_right")
    move_and_slide()

These fields edit one line each. The preview supports the expressions in the hint below.

One second of movement0 px
−300+3000 Green monster moving along a measured track

Try Test left first. Does the monster go where you expected?

○ Left: −300 px○ Right: +300 px

Need a hint?

Use var speed = 300. Left input is −1 and right input is +1. Multiply direction by speed: velocity.x = direction * speed.

Also try velocity.x = direction * speed * delta. It moves only 5 pixels in this one-second test at 60 physics ticks per second: move_and_slide() already handles the timestep.

JavaScript simulation of these expressions, not a GDScript runtime. In Godot, attach this horizontal movement script to a CharacterBody2D and create move_left / move_right actions in Project Settings → Input Map.

Take this into your own game

Read the full explanation. Each tutorial links back here when you want to try it again.

A tiny coding experiment

Change a value.
Feel the difference.

A variable is a value with a name. Here, speed controls how far the player moves each second. Change it, then collect the three coins.

player.gd GDScript
extends CharacterBody2D
@export var speed: float = 200.0

func _physics_process(_delta):
    var direction = Input.get_axis(
        "move_left", "move_right")
    velocity.x = direction * speed
    move_and_slide()
200 px/s

Browser simulation of movement. Run the full player script in Godot for gravity and jumping; create the named actions in Input Map first.

Use a whole number from 100 to 400.

A sunny pixel-art garden with a grassy path
AD Move Space Jump
0 of 3 coins collected. Press Play a little to start.
Check your understanding

At 200 px/s, how far does the player move in half a second?

One coin. A signal. A changing score.

Connect the score handler and follow the code when a body enters the coin.

Activity 3 of 3
Your task

Make the coin increase the score for the player and ignore a crate. Test the missing connection, then wire it up and try both visitors.

Score: 0Green monster player◆▦
    ○ Crate ignored○ Player scored

    coin.gd Attach to Area2D

    extends Area2D
    signal collected(value: int)
    @export var value: int = 10
    
    func _ready():
        body_entered.connect(_on_body_entered)
    
    func _on_body_entered(body):
        if not body.is_in_group("player"):
            return
        collected.emit(value)
        queue_free()

    level.gd Attach to the level root

    extends Node2D
    var score: int = 0
    
    func _ready():
        pass # No listener connected yet
    
    func _on_collected(amount: int):
        score += amount
        $Score.text = str(score)

    Browser simulation of signal flow. The coin emits a value; the connected level script updates the Score label. A signal without a listener cannot update the score.

    Set up this scene in Godot

    Create a Node2D level root with Coin (Area2D), Score (Label), and Player (CharacterBody2D) children. Coin needs Sprite2D and CollisionShape2D children with their resources assigned; Player needs a collision shape too.

    Add Player to the player group. Enable Coin’s Monitoring and make its collision mask include Player’s collision layer. Attach the two scripts to Coin and the level root. The example handles a single entering body; larger games may also need a guard against repeated collection in the same frame.

    Read the signals tutorial

    Explore complete player and coin scripts, line by line

    extends CharacterBody2D

    Player movement

    Twenty lines that move a character, jump, and collide. This is the first script most people write in Godot and it touches every idea that matters.

    player.gd
    extends CharacterBody2D @export var speed := 300.0@export var jump_force := -400.0 var gravity: float = ProjectSettings.get_setting("physics/2d/default_gravity") func _physics_process(delta: float) -> void:    # Apply gravity    velocity.y += gravity * delta     # Jump when on floor    if Input.is_action_just_pressed("jump") and is_on_floor():        velocity.y = jump_force     # Horizontal movement    var direction := Input.get_axis("move_left", "move_right")    velocity.x = direction * speed     move_and_slide()
    1. There is no component to add. This file is attached to a CharacterBody2D and becomes its behaviour, so velocity, is_on_floor() and move_and_slide() are simply available.

      Unity: a MonoBehaviour plus GetComponent<CharacterController>() collapsed into one thing.

    2. @export shows the value in the Inspector so a designer tunes it without opening the script. := infers the type, so speed is a float from now on and will refuse a string later.

      Unity: [SerializeField]. Unreal: UPROPERTY(EditAnywhere).

    3. Gravity comes from Project Settings so every body in the game agrees on it. This is a plain var, not exported: it is internal state, not something to tune per instance.

      Unity: Physics2D.gravity.

    4. _physics_process runs at a fixed rate, 60 ticks per second by default and configurable in Project Settings. Update character velocity and call move_and_slide here so movement follows the physics step.

      Unity: FixedUpdate. Unreal: the physics tick.

    5. velocity is a built-in property of the body. Each tick you add gravity scaled by delta, the time since the last tick, so the fall feels the same at any frame rate.

    6. is_action_just_pressed is true for exactly one frame, which is what a jump wants. is_on_floor() is only accurate after the previous move_and_slide() call, which is why the order in this function matters.

      Unity: Input.GetButtonDown plus a grounded check.

    7. get_axis subtracts the strength of the first action from the second, giving a value from -1 to 1. Keyboard input usually gives -1, 0 or 1; an analog stick can give values in between. Named actions let you remap controls without changing this line.

      Unity: Input.GetAxis("Horizontal"). Unreal: an Enhanced Input axis value.

    8. move_and_slide() reads velocity, moves the body, resolves collisions, slides along walls and floors, and updates is_on_floor(). Nothing moves without this call.

      Unity: CharacterController.Move with the slope handling built in.

    extends Area2D

    Coin pickup

    A coin that notices the player, announces it was collected, and removes itself. It never touches the score; it only tells whoever is listening.

    coin.gd
    extends Area2D signal collected(value: int) @export var value: int = 10 func _ready() -> void:    body_entered.connect(_on_body_entered) func _on_body_entered(body: Node2D) -> void:    if not body.is_in_group("player"):        return    collected.emit(value)    set_deferred("monitoring", false)    queue_free()
    1. Area2D notices bodies entering its shape without stopping them. It needs a CollisionShape2D child in the scene, or body_entered never fires.

      Unity: a Collider2D with isTrigger on. Unreal: a TriggerBox.

    2. A signal is a named event the node can emit. The coin declares that it can be collected and what it will pass along. It does not know or care who is listening.

      Unity: a C# event or UnityEvent. Unreal: an event dispatcher.

    3. Because value is exported, every coin placed in a level can carry its own number in the Inspector while sharing this one script.

    4. _ready runs once when the node enters the tree. body_entered is a signal Area2D already has; connect it to the method that should run. The same connection can also be made in the editor's Node dock.

      Unity: OnTriggerEnter2D, but chosen explicitly rather than by method name.

    5. The handler receives the body that entered. Groups are Godot's tags: a node can be in many, and is_in_group is the standard way a pickup checks for the player.

      Unity: CompareTag("Player").

    6. emit sends the value to connected listeners. A level script can update the score and an audio script can play a chime. Without a connection, neither happens: the coin only announces the event.

    7. Physics state must not change in the middle of a physics callback, so monitoring is switched off with set_deferred. queue_free removes the node at the end of the frame, not instantly.

      Unity: Destroy(gameObject), which is also deferred.

    Take this into your own game

    Read the full explanation. Each tutorial links back here when you want to try it again.

    Ready to try 3D?

    Take the body, visual, and collision-shape idea into a real Godot project. Build a playable room, then use it to explore lighting and rendering.

    More lessons & earlier saved progress

    Short reference lessons for individual Godot concepts. Your earlier completions are still saved.

    Browse GDScript recipes · Coming from Unity or Unreal?

    Before you start

    Do I need programming experience to learn Godot?
    No. Start by assembling a player, then edit two movement lines and connect a coin signal. Each activity includes a hint or a setup guide, plus a tutorial for the full explanation.
    Do these activities run Godot in my browser?
    These are focused JavaScript simulations of Godot concepts. They show the result of supported changes, not arbitrary GDScript execution. Each activity explains how to recreate the example in Godot.
    What happened to my earlier lessons?
    All earlier lesson URLs and saved completions remain available under More lessons below. Workshop activities have their own progress, earned by testing a working result.