Back to "Introduction to Godot 4: From Zero to Platformer" Course

Part 4: Player Movement & Gamefeel in Godot 4

Valentino Phiri
Instructor Valentino Phiri
Published
Duration 4 min read
Series Status in progress

In Part 3, we learned the core ideas of GDScript. Now we use them for something viscerally satisfying: making a character move.

Advertisement Space

Reserved for AdSense Revenue

And not just move. We want movement that feels good. Snappy, responsive, and professional. There’s real craft in a good player controller, so let’s build one properly.

Choosing Your Physics Body

Godot gives you three main options for 2D physics, and picking the wrong one causes endless pain:

NodeBest For
StaticBody2DFloors, walls, and things that never move.
RigidBody2DObjects controlled by physics (crates, balls).
CharacterBody2DPlayer characters. You control the logic; Godot handles the collisions.

We’ll be using CharacterBody2D. It lets us tell Godot exactly how fast the player should move, while the engine handles the “hard math” of stopping at walls.

Setting Up the Player Scene

Always give your player their own scene. It makes your project modular and clean.

  1. Create a New Scene.
  2. Choose CharacterBody2D as the root and name it Player.
  3. Add a CollisionShape2D child. Set its shape to a CapsuleShape2D.
  4. Add a Sprite2D child and drag icon.svg into the Texture slot.

Pro-Tip: Using a Capsule instead of a Square for your collision helps the player “slide” off corners smoothly instead of getting pixel-stuck on the edge of a platform.

Writing the Movement Script

Attach a new script to your Player node. Here is our “Ice Skating” movement logic:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
extends CharacterBody2D

const SPEED = 300.0

func _physics_process(delta):
    var direction = Input.get_axis("ui_left", "ui_right")

    if direction != 0:
        velocity.x = direction * SPEED
    else:
        # This creates a tiny "slide" instead of a robotic stop
        velocity.x = move_toward(velocity.x, 0, SPEED)

    move_and_slide()

Why _physics_process?

Unlike _process, which runs as fast as your computer can handle, _physics_process runs at a rock-steady 60 times per second. This ensures your physics behave exactly the same way on a high-end gaming PC and a budget laptop.

Adding Gravity and Jumping

In Godot 2D, there is one rule you must never forget: Y increases downward.

Warning: To move UP, you must use a negative Y value. This trips up everyone! Jumping is a negative velocity; falling is a positive one.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
extends CharacterBody2D

const SPEED = 300.0
const JUMP_VELOCITY = -600.0

# Get the gravity from your Project Settings
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")

func _physics_process(delta):
    # 1. Apply Gravity
    if not is_on_floor():
        velocity.y += gravity * delta

    # 2. Handle Jump
    if Input.is_action_just_pressed("ui_accept") and is_on_floor():
        velocity.y = JUMP_VELOCITY

    # 3. Movement Logic
    var direction = Input.get_axis("ui_left", "ui_right")
    if direction != 0:
        velocity.x = direction * SPEED
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)

    move_and_slide()

The Pro Secret: Coyote Time

Have you ever played a platformer where you jumped just as you walked off a ledge, but the game didn’t register it? It feels terrible.

Professional games use Coyote Time (named after the cartoon). We give the player a tiny “grace window” (usually 0.1 seconds) where they can still jump even if they are technically in mid-air.

Adding Coyote Time to your script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
var coyote_timer = 0.0
const COYOTE_TIME = 0.1

func _physics_process(delta):
    if is_on_floor():
        coyote_timer = COYOTE_TIME
    else:
        coyote_timer -= delta

    # Now, we check the timer instead of is_on_floor()
    if Input.is_action_just_pressed("ui_accept") and coyote_timer > 0:
        velocity.y = JUMP_VELOCITY
        coyote_timer = 0 # Use it up!

Key Takeaways

  • CharacterBody2D is the king of player controllers.
  • Y-Axis is flipped: Up is Negative, Down is Positive.
  • move_and_slide() handles all the collision math for you.
  • Coyote Time is a tiny detail that makes your game feel 10x more professional.

In Part 5: Physics & Collisions, we’ll learn how to actually interact with the world—detecting enemies, picking up coins, and hitting switches!

Advertisement Space

Reserved for AdSense Revenue

Share this article:
Stay in the loop

Join the Veigatec Dev Loop

Get the latest Godot, Flutter, and Web Development engineering insights delivered straight to your inbox. No fluff, just code.

We respect your privacy. Unsubscribe at any time.

Sponsored

Special Developer Offer: 20% off at TechGadgets

Upgrade your workspace with premium hardware and software tools today.