Skip to content
Development
Skill

/player-controller

Use when implementing player movement — CharacterBody2D/3D patterns, input handling, physics, common movement recipes

From plugin
godot-prompter
54157 skills9 agents1 hook
Install
$ npx -y skills add jame581/GodotPrompter --skill player-controller --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/player-controller

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when implementing player movement — CharacterBody2D/3D patterns, input handling, physics, common movement recipes

SKILL.md

player-controller.SKILL.md
name: player-controller
description: Use when implementing player movement — CharacterBody2D/3D patterns, input handling, physics, common movement recipes

Player Controllers in Godot 4.3+

All examples target Godot 4.3+ with no deprecated APIs. GDScript is shown first, then C#.

> **Related skills:** **physics-system** for RigidBody, Area, raycasting, and collision shapes, **2d-essentials** for TileMaps, parallax, and 2D lighting, **3d-essentials** for CharacterBody3D and 3D movement setup, **state-machine** for movement state management, **camera-system** for camera follow and shake, **component-system** for hitbox/hurtbox integration, **animation-system** for animation driven by movement state, **input-handling** for InputMap actions and controller support, **ai-navigation** for enemy movement and pathfinding.

---

1. Core Concepts

CharacterBody vs RigidBody

| Body Type | Use For | Physics Control | Notes | |-------------------|--------------------------------|-----------------|----------------------------------------------------------| | `CharacterBody2D/3D` | Player, enemies, NPCs | Manual (full) | You control velocity; `move_and_slide()` handles collisions | | `RigidBody2D/3D` | Projectiles, props, debris | Engine-driven | Physics engine applies forces; harder to control precisely | | `RigidBody2D/3D` | Projectiles with bouncing | Engine-driven | Set `linear_velocity` once; let physics resolve bounces | | `CharacterBody2D` | Platformers, top-down, FPS | Manual (full) | Reliable and predictable; best for responsive game feel |

**Rule of thumb:** Use `CharacterBody` when you need tight, responsive control. Use `RigidBody` when you want realistic physics simulation.

The Movement Loop

Every physics frame follows this order:

1. Read input          → get axis/action values
2. Apply forces        → gravity, friction, acceleration
3. Modify velocity     → move_toward, lerp, clamp
4. move_and_slide()    → engine resolves collisions, updates position
5. Post-movement state → check is_on_floor(), is_on_wall(), landing events

Always put this loop in `_physics_process(delta)`, never `_process(delta)`.

---

2. 2D Top-Down Controller

GDScript

extends CharacterBody2D

@export var speed: float = 200.0
@export var acceleration: float = 1500.0
@export var friction: float = 1200.0

func _physics_process(delta: float) -> void:
    # 1. Read input (normalized 4-directional vector)
    var input_dir: Vector2 = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")

    # 2 & 3. Apply acceleration or friction to velocity
    if input_dir != Vector2.ZERO:
        velocity = velocity.move_toward(input_dir * speed, acceleration * delta)
    else:
        velocity = velocity.move_toward(Vector2.ZERO, friction * delta)

    # 4. Move and resolve collisions
    move_and_slide()

C#

using Godot;

public partial class TopDownPlayer : CharacterBody2D
{
    [Export] public float Speed { get; set; } = 200.0f;
    [Export] public float Acceleration { get; set; } = 1500.0f;
    [Export] public float Friction { get; set; } = 1200.0f;

    public override void _PhysicsProcess(double delta)
    {
        // 1. Read input (normalized 4-directional vector)
        Vector2 inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down");

        // 2 & 3. Apply acceleration or friction
        if (inputDir != Vector2.Zero)
            Velocity = Velocity.MoveToward(inputDir * Speed, Acceleration * (float)delta);
        else
            Velocity = Velocity.MoveToward(Vector2.Zero, Friction * (float)delta);

        // 4. Move and resolve collisions
        MoveAndSlide();
    }
}

---

3. 2D Platformer Controller

GDScript

extends CharacterBody2D

@export var speed: float = 200.0
@export var jump_velocity: float = -400.0
@export var acceleration: float = 1200.0
@export var deceleration: float = 900.0

# Coyote time and jump buffer
@export var coyote_time: float = 0.12
@export var jump_buffer_time: float = 0.12

var _gravity: float = ProjectSettings.get_setting("physics/2d/default_gravity")
var _coyote_timer: float = 0.0
var _jump_buffer_timer: float = 0.0
var _was_on_floor: bool = false

func _physics_process(delta: float) -> void:
    # Coyote time: allow jump briefly after walking off a ledge
    if is_on_floor():
        _coyote_timer = coyote_time
        _was_on_floor = true
    else:
        _coyote_timer -= delta

    # Jump buffer: register jump input before landing
    if Input.is_action_just_pressed("ui_accept"):
        _jump_buffer_timer = jump_buffer_time
    else:
        _jump_buffer_timer -= delta

    # Apply gravity when airborne
    if not is_on_floor():
        velocity.y += _gravity * delta

    # Jump: consume coyote time and buffer together
    var can_jump: bool = _coyote_timer > 0.0
    if _jump_buffer_timer > 0.0 and can_jump:
        velocity.y = jump_velocity
        _coyote_timer = 0.0
        _jump_buffer_timer = 0.0

    # Variable jump height: cut velocity when button released early
    if Input.is_action_just_released("ui_accept") and velocity.y < 0.0:
        velocity.y *= 0.5

    # Horizontal movement with deceleration
    var input_x: float = Input.get_axis("ui_left", "ui_right")
    if input_x != 0.0:
        velocity.x = move_toward(velocity.x, input_x * speed, acceleration * delta)
    else:
        velocity.x = move_toward(velocity.x, 0.0, deceleration * delta)

    move_and_slide()

C#

using Godot;

public partial class PlatformerPlayer : CharacterBody2D
{
    [Export] public float Speed { get; set; } = 200.0f;
    [Export] public float JumpVelocity { get; set; } = -400.0f;
    [Export] public float Acceleration { get; set; } = 1200.0f;
    [Export] public float Deceleration { get; set; } = 900.0f;
    [Export] public float CoyoteTime { get; s
Read more
Ships withgodot-prompter

Agentic skills framework for Godot 4.x game development. Gives AI coding agents domain-specific expertise for GDScript and C# projects.

Get the whole plugin