Loading...
Loading...
Expert patterns for CharacterBody2D including platformer movement (coyote time, jump buffering, variable jump height), top-down movement (8-way, tank controls), collision handling, one-way platforms, and state machines. Use for player characters, NPCs, or enemies. Trigger keywords: CharacterBody2D, move_and_slide, is_on_floor, coyote_time, jump_buffer, velocity, get_slide_collision, one_way_platforms, state_machine.
npx skill4agent add thedivergentai/gd-agentic-skills godot-characterbody-2dmove_and_slide()move_and_slide()is_on_floor()velocity.x = direction * SPEEDmove_toward(velocity.x, 0, FRICTION * delta)velocity.yvelocity.y += gravity * deltavelocity.y = gravityMANDATORY: Read the appropriate script before implementing the corresponding pattern.
Do First: Read expert_physics_2d.gd for platformer foundation before adding dash/wall-jump.
extends CharacterBody2D
const SPEED := 300.0
const JUMP_VELOCITY := -400.0
# Get the gravity from the project settings
var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity")
func _physics_process(delta: float) -> void:
# Apply gravity
if not is_on_floor():
velocity.y += gravity * delta
# Handle jump
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Get input direction
var direction := Input.get_axis("move_left", "move_right")
# Apply movement
if direction:
velocity.x = direction * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
move_and_slide()extends CharacterBody2D
const SPEED := 300.0
const JUMP_VELOCITY := -400.0
const ACCELERATION := 1500.0
const FRICTION := 1200.0
const AIR_RESISTANCE := 200.0
# Coyote time: grace period after leaving platform
const COYOTE_TIME := 0.1
var coyote_timer := 0.0
# Jump buffering: remember jump input slightly before landing
const JUMP_BUFFER_TIME := 0.1
var jump_buffer_timer := 0.0
var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity")
func _physics_process(delta: float) -> void:
# Gravity
if not is_on_floor():
velocity.y += gravity * delta
coyote_timer -= delta
else:
coyote_timer = COYOTE_TIME
# Jump buffering
if Input.is_action_just_pressed("jump"):
jump_buffer_timer = JUMP_BUFFER_TIME
else:
jump_buffer_timer -= delta
# Jump (with coyote time and buffer)
if jump_buffer_timer > 0 and coyote_timer > 0:
velocity.y = JUMP_VELOCITY
jump_buffer_timer = 0
coyote_timer = 0
# Variable jump height
if Input.is_action_just_released("jump") and velocity.y < 0:
velocity.y *= 0.5
# Movement with acceleration/friction
var direction := Input.get_axis("move_left", "move_right")
if direction:
velocity.x = move_toward(velocity.x, direction * SPEED, ACCELERATION * delta)
else:
var friction_value := FRICTION if is_on_floor() else AIR_RESISTANCE
velocity.x = move_toward(velocity.x, 0, friction_value * delta)
move_and_slide()extends CharacterBody2D
const SPEED := 200.0
const ACCELERATION := 1500.0
const FRICTION := 1000.0
func _physics_process(delta: float) -> void:
# Get input direction (normalized for diagonal movement)
var input_vector := Input.get_vector(
"move_left", "move_right",
"move_up", "move_down"
)
if input_vector != Vector2.ZERO:
# Accelerate toward target velocity
velocity = velocity.move_toward(
input_vector * SPEED,
ACCELERATION * delta
)
else:
# Apply friction
velocity = velocity.move_toward(
Vector2.ZERO,
FRICTION * delta
)
move_and_slide()extends CharacterBody2D
const SPEED := 200.0
const ROTATION_SPEED := 3.0
func _physics_process(delta: float) -> void:
# Rotation
var rotate_direction := Input.get_axis("rotate_left", "rotate_right")
rotation += rotate_direction * ROTATION_SPEED * delta
# Forward/backward movement
var move_direction := Input.get_axis("move_backward", "move_forward")
velocity = transform.x * move_direction * SPEED
move_and_slide()func _physics_process(delta: float) -> void:
move_and_slide()
if is_on_floor():
print("Standing on ground")
if is_on_wall():
print("Touching wall")
if is_on_ceiling():
print("Hitting ceiling")func _physics_process(delta: float) -> void:
move_and_slide()
# Process each collision
for i in get_slide_collision_count():
var collision := get_slide_collision(i)
print("Collided with: ", collision.get_collider().name)
print("Collision normal: ", collision.get_normal())
# Example: bounce off walls
if collision.get_collider().is_in_group("bouncy"):
velocity = velocity.bounce(collision.get_normal())extends CharacterBody2D
func _physics_process(delta: float) -> void:
# Allow falling through platforms by pressing down
if Input.is_action_pressed("move_down") and is_on_floor():
position.y += 1 # Move slightly down to pass through
velocity.y += gravity * delta
move_and_slide()extends CharacterBody2D
enum State { IDLE, RUNNING, JUMPING, FALLING, DASHING }
var current_state := State.IDLE
var dash_velocity := Vector2.ZERO
const DASH_SPEED := 600.0
const DASH_DURATION := 0.2
var dash_timer := 0.0
func _physics_process(delta: float) -> void:
match current_state:
State.IDLE:
_state_idle(delta)
State.RUNNING:
_state_running(delta)
State.JUMPING:
_state_jumping(delta)
State.FALLING:
_state_falling(delta)
State.DASHING:
_state_dashing(delta)
func _state_idle(delta: float) -> void:
velocity.x = move_toward(velocity.x, 0, FRICTION * delta)
if Input.is_action_pressed("move_left") or Input.is_action_pressed("move_right"):
current_state = State.RUNNING
elif Input.is_action_just_pressed("jump"):
current_state = State.JUMPING
move_and_slide()
func _state_dashing(delta: float) -> void:
dash_timer -= delta
velocity = dash_velocity
if dash_timer <= 0:
current_state = State.IDLE
move_and_slide()# ✅ Good - easy to tweak
const SPEED := 300.0
const JUMP_VELOCITY := -400.0
# ❌ Bad - magic numbers
velocity.x = 300
velocity.y = -400@export@export var speed: float = 300.0
@export var jump_velocity: float = -400.0
@export_range(0, 2000) var acceleration: float = 1500.0func _physics_process(delta: float) -> void:
_handle_movement(delta)
_handle_animation()
move_and_slide()
func _handle_movement(delta: float) -> void:
# Movement logic only
pass
func _handle_animation() -> void:
# Animation state changes only
if velocity.x > 0:
$AnimatedSprite2D.flip_h = false
elif velocity.x < 0:
$AnimatedSprite2D.flip_h = truefunc _ready() -> void:
# Set floor parameters
floor_max_angle = deg_to_rad(45) # Max slope angle
floor_snap_length = 8.0 # Distance to snap to floor
motion_mode = MOTION_MODE_GROUNDED # Vs MOTION_MODE_FLOATING# Solution: Increase friction
const FRICTION := 1200.0# Solution: Enable platform snap
func _physics_process(delta: float) -> void:
move_and_slide()
# Snap to platform velocity
if is_on_floor():
var floor_velocity := get_platform_velocity()
velocity += floor_velocity# Solution: Track if jump was used
var can_jump := true
func _physics_process(delta: float) -> void:
if is_on_floor():
can_jump = true
if Input.is_action_just_pressed("jump") and can_jump:
velocity.y = JUMP_VELOCITY
can_jump = false