Genre: Platformer
Expert blueprint for platformers emphasizing movement feel, level design, and player satisfaction.
NEVER Do
- •NEVER skip coyote time — Without 6-frame grace period after leaving ledge, jumps feel unresponsive. Players blame themselves.
- •NEVER ignore jump buffering — Pressing jump 6 frames before landing should queue jump. Missing this makes controls feel sluggish.
- •NEVER use fixed jump height — Variable jump (hold longer = jump higher) gives players agency. Tap for short hop, hold for full jump.
- •NEVER forget camera smoothing — Instant camera snapping causes motion sickness. Use position_smoothing or lerp for smooth follow.
- •NEVER skip squash/stretch on landing — Landing without visual impact feels weightless. Add 0.1s squash on land for juice.
Available Scripts
MANDATORY: Read the appropriate script before implementing the corresponding pattern.
advanced_platformer_controller.gd
Complete platformer with coyote time, jump buffer, apex float, and variable gravity. Move_toward-based friction for polished game feel.
Core Loop
Jump → Navigate Obstacles → Reach Goal → Next Level
Skill Chain
godot-project-foundations, godot-characterbody-2d, godot-input-handling, animation, sound-manager, tilemap-setup, camera-2d
Movement Feel ("Game Feel")
The most critical aspect of platformers. Players should feel precise, responsive, and in control.
Input Responsiveness
# Instant direction changes - no acceleration on ground
func _physics_process(delta: float) -> void:
var input_dir := Input.get_axis("move_left", "move_right")
# Ground movement: instant response
if is_on_floor():
velocity.x = input_dir * MOVE_SPEED
else:
# Air movement: slightly reduced control
velocity.x = move_toward(velocity.x, input_dir * MOVE_SPEED, AIR_ACCEL * delta)
Coyote Time (Grace Period)
Allow jumping briefly after leaving a platform:
var coyote_timer: float = 0.0
const COYOTE_TIME := 0.1 # 100ms grace period
func _physics_process(delta: float) -> void:
if is_on_floor():
coyote_timer = COYOTE_TIME
else:
coyote_timer = max(0, coyote_timer - delta)
# Can jump if on floor OR within coyote time
if Input.is_action_just_pressed("jump") and coyote_timer > 0:
velocity.y = JUMP_VELOCITY
coyote_timer = 0
Jump Buffering
Register jumps pressed slightly before landing:
var jump_buffer: float = 0.0
const JUMP_BUFFER_TIME := 0.15
func _physics_process(delta: float) -> void:
if Input.is_action_just_pressed("jump"):
jump_buffer = JUMP_BUFFER_TIME
else:
jump_buffer = max(0, jump_buffer - delta)
if is_on_floor() and jump_buffer > 0:
velocity.y = JUMP_VELOCITY
jump_buffer = 0
Variable Jump Height
const JUMP_VELOCITY := -400.0
const JUMP_RELEASE_MULTIPLIER := 0.5
func _physics_process(delta: float) -> void:
# Cut jump short when button released
if Input.is_action_just_released("jump") and velocity.y < 0:
velocity.y *= JUMP_RELEASE_MULTIPLIER
Gravity Tuning
const GRAVITY := 980.0
const FALL_GRAVITY_MULTIPLIER := 1.5 # Faster falls feel better
const MAX_FALL_SPEED := 600.0
func apply_gravity(delta: float) -> void:
var grav := GRAVITY
if velocity.y > 0: # Falling
grav *= FALL_GRAVITY_MULTIPLIER
velocity.y = min(velocity.y + grav * delta, MAX_FALL_SPEED)
Level Design Principles
The "Teaching Trilogy"
- •Introduction: Safe environment to learn mechanic
- •Challenge: Apply mechanic with moderate risk
- •Twist: Combine with other mechanics or time pressure
Visual Language
- •Safe platforms: Distinct color/texture
- •Hazards: Red/orange tints, spikes, glow effects
- •Collectibles: Bright, animated, particle effects
- •Secrets: Subtle environmental hints
Flow and Pacing
Easy → Easy → Medium → CHECKPOINT → Medium → Hard → CHECKPOINT → Boss
Camera Design
# Look-ahead camera for platformers
extends Camera2D
@export var look_ahead_distance := 100.0
@export var look_ahead_speed := 3.0
var target_offset := Vector2.ZERO
func _process(delta: float) -> void:
var player_velocity: Vector2 = target.velocity
var desired_offset := player_velocity.normalized() * look_ahead_distance
target_offset = target_offset.lerp(desired_offset, look_ahead_speed * delta)
offset = target_offset
Platformer Sub-Genres
Precision Platformers (Celeste, Super Meat Boy)
- •Instant respawn on death
- •Very tight controls (no acceleration)
- •Checkpoints every few seconds of gameplay
- •Death is learning, not punishment
Collectathon (Mario 64, Banjo-Kazooie)
- •Large hub worlds with objectives
- •Multiple abilities unlocked over time
- •Backtracking encouraged
- •Stars/collectibles as progression gates
Puzzle Platformers (Limbo, Inside)
- •Slow, deliberate pacing
- •Environmental puzzles
- •Physics-based mechanics
- •Atmospheric storytelling
Metroidvania (Hollow Knight)
- •See
godot-genre-metroidvaniaskill - •Ability-gated exploration
- •Interconnected world map
Common Pitfalls
| Pitfall | Solution |
|---|---|
| Floaty jumps | Increase gravity, especially on descent |
| Imprecise landings | Add coyote time and visual landing feedback |
| Unfair deaths | Ensure hazards are clearly visible before encountered |
| Blind jumps | Camera look-ahead or zoom out during falls |
| Boring mid-game | Introduce new mechanics every 2-3 levels |
Polish Checklist
- • Dust godot-particles on land/run
- • Screen shake on heavy landings
- • Squash/stretch animations
- • Sound effects for every action (jump, land, wall-slide)
- • Death and respawn animations
- • Checkpoint visual/audio feedback
- • Accessible difficulty options (assist mode)
Godot-Specific Tips
- •CharacterBody2D vs RigidBody2D: Always use
CharacterBody2Dfor platformer characters - precise control is essential - •Physics tick rate: Consider 120Hz physics for smoother movement
- •One-way platforms: Use
set_collision_mask_value()or dedicated collision layers - •Wall detection: Use
is_on_wall()andget_wall_normal()for wall jumps
Example Games for Reference
- •Celeste - Perfect game feel, assist mode accessibility
- •Hollow Knight - Combat + platforming integration
- •Super Mario Bros. Wonder - Visual polish and surprises
- •Shovel Knight - Retro mechanics with modern feel
Reference
- •Master Skill: godot-master