【Godot】Understanding Input Detection Methods: just_pressed, pressed, and released

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

Learn the differences between Godot Engine's input methods is_action_just_pressed, is_action_pressed, and is_action_just_released. Includes practical code examples for jumping, movement, charge attacks, and variable-height jumps.

Overview

Creating a game that responds precisely to player input requires a solid understanding of input "states." Is the key being held down continuously, or was it just pressed this instant? Mastering this distinction dramatically improves character control feel.

"Press the button to jump"—this seemingly simple operation can severely damage your game's feel (game feel) if implemented incorrectly. For example, holding the jump button might cause the character to bounce infinitely, or pressing a movement key briefly might make the character slide endlessly. Many of these issues stem from not properly distinguishing between input "states."

This article explains the differences between Godot's main input detection methods: is_action_just_pressed, is_action_pressed, and is_action_just_released.

The Three Main Input Methods

Godot's input handling primarily uses the Input singleton (a globally accessible object). Let's examine the three most commonly used polling methods. These methods are used inside functions called every frame, like _process and _physics_process, to check the current input state.

The argument is a string specifying the action name defined in Project > Project Settings > Input Map (e.g., "jump", "move_right").

MethodDetection TimingReturnsPrimary Use
is_action_pressed()While the action is held downtrueContinuous movement, rapid fire, fast UI scrolling
is_action_just_pressed()Only the first frame when the action is pressedtrueJump, confirm, open menu, single shot
is_action_just_released()Only the first frame when the action is releasedtrueCharge attack release, jump height adjustment, drag & drop end

Practical Code Examples

Now that you understand the theory, let's look at concrete code. We'll use a 2D platformer character as an example to demonstrate how to use each method.

1. Basic Movement and Jump (pressed & just_pressed)

This is the most fundamental combination. Use continuous input for left/right movement and instantaneous input for jumping.

extends CharacterBody2D

const SPEED = 300.0
const JUMP_VELOCITY = -400.0

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

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

    # Jump: execute once at the "instant" button is pressed
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = JUMP_VELOCITY

    # Horizontal movement: apply force "while" being pressed
    var direction = Input.get_axis("move_left", "move_right")
    if direction:
        velocity.x = direction * SPEED
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)

    move_and_slide()

Input.get_axis() internally performs the same check as is_action_pressed(), returning a value from -1 to 1 while keys are held. This enables smooth movement.

2. Charge Attack (pressed & released)

A charge attack that builds power while held and releases when the button is let go can be implemented by combining pressed and released.

extends Sprite2D

var is_charging = false
var charge_time = 0.0
const MAX_CHARGE_TIME = 2.0

func _process(delta):
    # Charging: accumulate time while held
    if Input.is_action_pressed("charge_attack"):
        is_charging = true
        charge_time += delta
        # Change appearance based on charge level (e.g., change color)
        modulate = Color.WHITE.lerp(Color.RED, charge_time / MAX_CHARGE_TIME)
        print("Charging... %.2fs" % charge_time)

    # Attack release: execute at the instant button is released
    if Input.is_action_just_released("charge_attack"):
        if is_charging:
            var power = clamp(charge_time, 0.5, MAX_CHARGE_TIME)
            print("Attack released with power: %.2f!" % power)
            # Call bullet firing logic here
            # fire_bullet(power)

            # Reset
            is_charging = false
            charge_time = 0.0
            modulate = Color.WHITE

3. Variable-Height Jump (just_pressed & released)

A "variable jump" where holding the button longer produces a higher jump is a technique used in many platformers like Mario. This greatly expands the range of player control.

# (Add to CharacterBody2D code)

const SHORT_JUMP_MULTIPLIER = 0.5

func _physics_process(delta):
    # ... (gravity and movement code remains the same)

    # Start jump
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = JUMP_VELOCITY

    # When jump button is released, cut the ascent
    if Input.is_action_just_released("jump") and velocity.y < 0:
        velocity.y *= SHORT_JUMP_MULTIPLIER

    # ... (move_and_slide())

This code applies a brake to the upward velocity when the jump button is released while the character is still ascending (velocity.y < 0). This achieves "short press = low jump, long press = high jump."


Common Mistakes and Best Practices

Input handling has several error-prone areas. Use the following table as a reference for writing more robust code that behaves as intended.

CategoryCommon MistakeBest Practice
Jump HandlingUsing is_action_pressed(). Holding the button causes unintended repeated jumps.Use is_action_just_pressed(). Execute jump logic only once at the moment of pressing.
UI OperationsUsing is_action_pressed() for confirm buttons. The action fires every frame, causing menus to skip rapidly.Use is_action_just_pressed(). UI operations should generally be treated as one-shot actions.
Processing LoopProcessing physics-related input in _process(). Physics behavior may become unstable due to frame rate fluctuations.Process physics-related input (movement, jumping, etc.) in _physics_process() to ensure stable behavior.
Action DefinitionsHardcoding key codes in scripts (Input.is_key_pressed(KEY_SPACE)). Key remapping becomes tedious.Define actions in InputMap and reference them by name like is_action_pressed("jump"). This makes key configuration much easier.

Performance and Alternatives: Polling vs Event-Driven

The is_action_* methods introduced so far use "polling"—actively checking every frame "is there any input?"

Godot also offers an "event-driven" approach as another major input handling method. This involves overriding virtual functions like _input() or _unhandled_input() to receive notifications from Godot when input occurs.

# Event-driven approach example
func _unhandled_input(event: InputEvent):
    if event.is_action_pressed("jump"):
        # An event fires when the key is pressed, calling this function
        # Note: With key repeat enabled, this may be called repeatedly during long presses
        print("Jump action event!")

    if event.is_action_released("ui_cancel"):
        get_tree().quit()

Which should you use?

  • Polling (is_action_*):

    • Pros: Check input state at any desired timing within _process or _physics_process. Very intuitive when the "held state" matters, like character movement.
    • Cons: Check processing runs every frame. However, on modern PCs, this rarely becomes a bottleneck.
  • Event-Driven (_input, _unhandled_input):

    • Pros: Code executes only when input occurs, making it efficient. Ideal for one-shot event processing like getting mouse click positions, UI operations, or displaying pause menus.
    • Cons: Managing "being held" states requires maintaining flag variables yourself, which can make code slightly more complex.

An effective approach is to use polling for real-time character control and event-driven handling for UI operations and one-shot actions.

Summary

Input detection is a crucial element that defines your game's feel. By correctly utilizing the three methods Godot provides, you can achieve more intuitive and responsive controls.

  • Movement and other continuous actions: is_action_pressed()
  • Jump, confirm, and other one-shot actions: is_action_just_pressed()
  • Charge release and other special actions: is_action_just_released()

Master these fundamentals and apply them to your game!