【Godot】Understanding move_and_slide vs move_toward: Implementing Knockback

Created: 2025-06-20Last updated: 2025-12-16

Learn the differences between move_and_slide used with CharacterBody and the versatile move_toward function in Godot Engine. Includes practical code examples for knockback, enemy tracking, and UI animation.

Overview

When trying to move characters in Godot, you'll encounter two similarly named functions: move_and_slide() and move_toward(). Both are for moving objects, but their underlying mechanisms and optimal use cases are completely different.

Understanding the differences between these two is crucial for implementing smooth physics-based movement and game logic that behaves exactly as intended (like knockback).

Godot Knockback Processing

move_and_slide(): Citizen of the Physics World

move_and_slide() is a sophisticated movement method exclusive to nodes controlled by the physics engine, like CharacterBody2D and CharacterBody3D. Simply calling it makes Godot's physics engine perform complex calculations behind the scenes, achieving very natural movement.

Key Features:

  • Physics-Based Collision Handling: Works closely with Godot's physics engine, automatically detecting and responding to collisions with other physics bodies and tilemaps. Prevents objects from overlapping.
  • Smooth Movement on Walls and Slopes: When hitting a wall, the object doesn't stop but "slides" along it. This dramatically improves controls, especially in narrow passages. Combined with the up_direction property, it enables smooth movement up and down slopes.
  • Moving Platform Support: move_and_slide() automatically detects moving platforms and adjusts velocity so characters move along with the platform while standing on it.
  • Integration with velocity Property: This function takes the node's velocity property as input and executes movement. Importantly, it returns the velocity after it's been modified by collisions.

Basic Usage:

The standard practice is to use move_and_slide() inside _physics_process(delta), which is called every physics frame.

extends CharacterBody2D

const SPEED = 300.0
const JUMP_VELOCITY = -400.0

# Get gravity from project settings (default: 980)
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")

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

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

    # Get left/right input direction
    var direction = Input.get_axis("ui_left", "ui_right")
    velocity.x = direction * SPEED

    # Let physics engine handle movement and collision
    move_and_slide()

move_toward(): Simple Mathematical Interpolation

move_toward() is a general-purpose mathematical function built into Godot. It has nothing to do with the physics engine—it linearly changes a value (number, vector, etc.) toward a target by a specified amount.

Key Features:

  • Completely Ignores Physics: This function doesn't detect collisions. It only performs value calculations. If you directly modify an object's position with this function, it may pass through walls.
  • Constant Rate of Change: Regardless of current position or distance to target, it always changes the value by the specified constant step (the delta argument). This makes constant-speed movement or changes easy to achieve.
  • High Versatility: Usable with various data types beyond Vector2 and Vector3 positions—float (HP, experience), Color (fade in/out), Transform2D, and more.

Primary Uses:

  1. Moving Non-Physics Objects: UI elements, background objects, cameras—objects that don't need collision.
  2. Value Smoothing: Smoothing out rapidly changing values. For example, slightly delaying camera following for a smoother look.
  3. State Change Effects: HP bars decreasing smoothly, character colors gradually changing.
  4. Physics Calculation Assistance: When directly manipulating physics properties like velocity, used to smoothly change values toward a target (e.g., Vector2.ZERO). Recovery from knockback is a classic example.
# Example 1: Move UI element to target position
func _process(delta):
    var target_position = get_viewport_rect().size / 2
    # Move toward center at 200 pixels per second
    position = position.move_toward(target_position, 200 * delta)

# Example 2: Enemy character follows player
func _physics_process(delta):
    # player_node is reference to player
    if player_node:
        var direction = global_position.direction_to(player_node.global_position)
        velocity = direction * ENEMY_SPEED
        move_and_slide()

Practical Example: Knockback with State Management

The best example of move_and_slide and move_toward working together is "knockback." Here, we incorporate simple state machine concepts for a more robust implementation.

extends CharacterBody2D

const SPEED = 300.0
const JUMP_VELOCITY = -400.0
const KNOCKBACK_FRICTION = 500.0

var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")

enum State { MOVE, KNOCKBACK }
var current_state = State.MOVE

var knockback_vector = Vector2.ZERO

func _physics_process(delta):
    match current_state:
        State.MOVE:
            move_state(delta)
        State.KNOCKBACK:
            knockback_state(delta)

# Normal movement state
func move_state(delta):
    if not is_on_floor():
        velocity.y += gravity * delta

    if Input.is_action_just_pressed("ui_accept") and is_on_floor():
        velocity.y = JUMP_VELOCITY

    var direction = Input.get_axis("ui_left", "ui_right")
    velocity.x = direction * SPEED

    move_and_slide()

# Knockback state
func knockback_state(delta):
    # Overwrite velocity with knockback vector
    velocity = knockback_vector
    move_and_slide() # Let physics engine handle collision

    # Use move_toward to gradually reduce knockback force to 0 (deceleration)
    knockback_vector = knockback_vector.move_toward(Vector2.ZERO, KNOCKBACK_FRICTION * delta)

    # Return to normal state when knockback force is nearly 0
    if knockback_vector.is_equal_approx(Vector2.ZERO):
        current_state = State.MOVE

# Call this function from external sources (e.g., enemy attack hitbox area)
func apply_knockback(direction: Vector2, power: float):
    current_state = State.KNOCKBACK
    knockback_vector = direction.normalized() * power

In this implementation, current_state clearly separates character behavior. In State.MOVE, player input is accepted; in State.KNOCKBACK, input is ignored and movement is forced. The key point is using move_toward within knockback_state to smoothly decay knockback_vector. This achieves natural recovery from knockback.


Common Mistakes and Best Practices

Here's a summary of common pitfalls when using these functions and best practices to avoid them.

FunctionCommon MistakeBest Practice
move_and_slide()Calling it inside _process (not synchronized with physics)Always call it inside _physics_process. Physics calculations run at a fixed frame rate, so synchronization is essential.
Trying to change position directly instead of setting velocityWith CharacterBody, the principle is to always control movement through the velocity property, not direct position manipulation.
move_toward()Using it directly for CharacterBody movement, causing wall pass-throughDon't use for objects that need physical collision. Use as assistance for changing velocity values, or limit to non-physics objects like UI.
Forgetting to multiply by delta (frame-rate dependent movement)Multiply the change amount by delta like move_toward(target, speed * delta) to maintain constant speed regardless of frame rate.
Not understanding the difference from lerpmove_toward is constant speed; lerp decreases change amount with distance (easing). Use move_toward for hard stops, lerp for smooth stops.

Performance and Alternatives

  • Cost of move_and_slide(): This function performs multiple raycasts and collision checks internally, making it not a lightweight operation. Placing many CharacterBody nodes in a scene can impact performance. Optimization like disabling _physics_process itself (set_physics_process(false)) for distant enemies that don't need to move is effective.

  • move_toward() vs Tween: For UI animation and simple object movement, the Tween node is also a very powerful option. While move_toward controls linear movement in code, Tween allows setting diverse easing asynchronously from the inspector. Use Tween for complex effects and sequences, move_toward for simple interpolation and physics property control.

Summary

move_and_slide() and move_toward() are completely different tools, each with its own philosophy.

  • move_and_slide(): A high-level movement method for CharacterBody that follows physics world laws, considering collision and gravity.
  • move_toward(): A mathematical interpolation tool that ignores physics and moves values toward targets. An unsung hero that handles everything from velocity control to UI animation.

By deeply understanding these two characteristics and combining them appropriately—like in knockback processing—your game characters will move significantly more lifelike and exactly as intended.