Jumping twice with one jump input

Godot Version

4.1

Question

Hi, so I’m quite new to Godot I came from unity a bit ago and I’m trying to make a 2D character controller in c# but when i jump the character jumps twice no matter what the key for jump is

here is the code any help would be greatly appreciated, thanks in advance

using Godot;
using System;
using System.ComponentModel;

public partial class Player : CharacterBody2D
{
    [ExportCategory("Attributes")]
    [Export] private float movementSpeed = 150f;
    [Export] private float acceleration = 7f;
    [Export] private float deceleration = 10f;
    [Export] private float jumpForce = -300f;

    [ExportCategory("Gravity")]
	[Export] private float gravity = 2000f;
    [Export] private float terminalVelocity = 600f;

    [ExportCategory("Inputs")]
    [Export] private bool useRawInput = true;
    private Vector2 input;
    [Export] private StringName up = new StringName("Up"), left = new StringName("Left"), right = new StringName("Right"), down = new StringName("Down");
    [Export] private Key jumpKey = Key.Space;
    [Export] private bool didJump = false;
    
    //velocity
    private Vector2 velocity;
    private float targetVelocity;
    private float previousVelocityY, newVelocityY;

    private Sprite2D sprite;
    private AnimationPlayer anim;

    // Called when the node enters the scene tree for the first time.
    public override void _Ready()
	{
        sprite = GetNode<Sprite2D>("Sprite2D");
        anim = GetNode<AnimationPlayer>("AnimationPlayer");
        sprite.TopLevel = true;
    }

    // Called every frame. 'delta' is the elapsed time since the previous frame.
    public override void _Process(double delta)
    {
        GatherInput();
        FlipSprite();
        sprite.GlobalPosition = sprite.GlobalPosition.Lerp(GlobalPosition + Vector2.Up * 8 , (float)Engine.GetPhysicsInterpolationFraction());
    }

    public override void _PhysicsProcess(double delta)
	{
        MoveAndSlide();
        CalculateVelocity((float)delta);

	}

    #region Input
    //Gathers input from the player
    private void GatherInput()
    {
        input = Input.GetVector(left, right, up, down);
        if (useRawInput) input = input.GetRaw();
        if (Input.IsKeyPressed(keycode:jumpKey))
        {
            didJump = true;
        }
    }

    #endregion

    #region Gravity
    //Apply gravity to the charater body velocity
    public void ApplyGravity(ref Vector2 velocity, float delta)
	{
        if (IsOnFloor()) return;
        previousVelocityY = velocity.Y;
        newVelocityY = velocity.Y + gravity * delta;
        newVelocityY = Mathf.Clamp(newVelocityY, -Mathf.Inf, terminalVelocity);
        velocity.Y = (previousVelocityY + newVelocityY) * 0.5f;
    }
    #endregion

    #region Velocity
    //calcualte the bi-directional velocity 
    private void CalculateVelocity(float delta)
    {
        velocity = Velocity;
        CalculateVelocityY(ref velocity, delta);
        CalculateVelocityX(ref velocity);
        Velocity = velocity;
    }

    //calcualte the Y velocity 
    private void CalculateVelocityY(ref Vector2 vel, float delta)
    {
        HandleJump(ref vel);
        ApplyGravity(ref vel, delta);
    }

    //calcualte the X velocity 
    private void CalculateVelocityX(ref Vector2 vel)
    {
        targetVelocity = input.X * movementSpeed;
        vel.X = Mathf.MoveToward(vel.X, targetVelocity, Mathf.Sign(input.X) == Mathf.Sign(velocity.X)? acceleration: deceleration);
    }

    #endregion

    #region Jump
    private void HandleJump(ref Vector2 vel)
    {
        if(!IsOnFloor() || !didJump) 
            return;
        didJump = false;
        vel.Y = jumpForce;
        GD.Print("Jump");
    }
    #endregion

    #region Animation
    
    //makes charater face the way of it's movement 
    private void FlipSprite()
    {
        sprite.FlipH = input.X switch { <0 => true, >0 => false, _ => sprite.FlipH};
    }

    private void AnimationStates()
    {
        //checks if charather is idle
        if(Velocity.X == 0 && Velocity.Y == 0)
        {
            anim.Play("Idle");
        }
    }
    #endregion
}

Not sure if this is it but…
This is equivalent of Unity’s Input.GetKey.
Which just tells if the key is currently pressed or not.
Meaning, if you keep holding the jump key the player will immediately jump again when it hits the floor.

To get Input.GetKeyDown you need to use Input.IsKeyJustPressed (or something similar, its really hard to find documentation about godot c#)

The gdscript version is is_action_just_pressed( Input — Godot Engine (stable) documentation in English)

You can also get you inputs inside the _input/_unhandled_input functions. Inside there you can get more info about keys with event.just_pressed.

Example:

public override void _UnhandledInput(InputEvent @event)
{
    if (@event is InputEventKey eventKey)
        if (eventKey.Pressed && eventKey.Keycode == Key.Escape)
            GetTree().Quit();
}

This don’t seem to help and I cant find the other method you were talking about

Reading rapidly through the code, it basically goes:

  • Every frame:
    • If a specific key is down, you set _didJump to true
  • Every physics frame:
    • If the body is on the floor, and _didJump is true:
      • Run the jump logic
      • Force _didJump back to false

Notice how, when the key is pressed and the body is not on the floor, _didJump is set to true, and stays that way until after the jump logic runs a second time. Unless you release the key frame perfectly, it’s going to jump twice in a row. The same thing is going to happen if you press the key again while the body is in the air, and so on.

As an additional thing, even if not directly related to the issue at hand, you should note that _Process and _PhysicsProcess might not always happen at the same rate (see _process vs. _physics_process vs. *_input).

So what do i do to fix it since ive tried moving caculateVelocity to prossess and moving gather inputs to physics prossess sepratly so they go into the same one, any thing im doing wrong?

The c# equivalent would just be:

var jump = Input.IsActionJustPressed(“jump”)

However this assumes InputMap actions are being used, which it doesn’t appear to be. It would probably make things a lot easier (and likely solve this problem) to use the built in Input system than trying to do it this way in code unless it’s really necessary.
MoveAndSlide() should probably be the last thing in the physics process to avoid any physics weirdness creeping in.
If you have a look at the bottom of Godot Physics Introduction it shows an example of using inputs that could be helpful.

1 Like

if (Input.IsKeyPressed(keycode:jumpKey) && IsOnFloor())

This would then only set didJump again if the character is not jumping. I agree with octopuddle, the InputMap is preferred, but would not solve your problem.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.