#Struggling to create a good parabolic jump arc.

1 messages · Page 1 of 1 (latest)

severe sonnet
#

Context

For the last few months Ive been working on a 3D platformer. While I wanted this project to be an exercise in level design I have been struggling to progress as I am currently bashing my head against making a simple jump for my character.

The intent

I want it so when the player presses the jump action their character jumps in the air. This jump should be a quick rise followed by a light controllable decent following a smooth parabolic arc.

The Problem

Right now the character isnt following that parabola. It less feels like their jumping into the air and more like their teleporting, as shown in the above GIF. I tried an approach where by the amount of jump force that is applied to the player decreases every physics tick, but this hasn't worked in the slightest and its making me frustrated. The character is falling as intended, but the rising motion isn't what Im looking for. What am I doing wrong? Do I need to tween the motion to make it smoother? Should I use an animation player? What do I do?

Supporting Resources

Source code: https://pastebin.com/uKRZy9z8
OS: Windows 10
Godot Version: 4.4.1

cursive sand
#

I ended up getting too into this. gdwheeze

There were a number of problems.
The main one was trying to deal with floor and air all in one go, so I separated those.

# tested with these vars
# max_speed = 15
# acceleration = 30
# deceleration = 60
# acceleration_air = 30

# gravity = 4
# jump_force = 20

func _physics_process(delta: float) -> void:
    var move_input_vector : Vector2 = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
    var move_direction := Vector3 (move_input_vector.x, 0.0, move_input_vector.y)

    # get_gravity() will return the actual gravity from environment
    # (Vector3.DOWN * 9.8m/s^2) Base everything else on this unit.
    velocity += self.get_gravity() * gravity * delta # use gravity var as a scalar; keep it < 8.0

    if is_on_floor():
        _physics_floor(move_direction, delta)
    else:
        _physics_air(move_direction, delta)
    
    velocity = velocity.limit_length(terminal_velocity)
    
    move_and_slide()


func _physics_floor(move_direction: Vector3, delta: float) -> void:
    if move_direction != Vector3.ZERO:
        var v := velocity + move_direction * acceleration * delta
        velocity = v.limit_length(max_speed)
    else:
        velocity = velocity.move_toward(Vector3.ZERO, deceleration * delta)

    if Input.is_action_just_pressed("jump"):
        velocity += up_direction * jump_force # impulse, no delta here


func _physics_air(move_direction: Vector3, delta: float) -> void:
    if move_direction != Vector3.ZERO:
        # separate horizontal motion
        var v := velocity.slide(up_direction) + move_direction * acceleration_air * delta
        
        # separate vertical motion, add horizontal
        velocity = velocity.project(up_direction) + v.limit_length(max_speed)
cursive sand
#

and to slow descent further, in the air function something like

if Input.is_action_pressed(&"jump") and velocity.y < 0.0:
  velocity -= velocity * parachute * delta

With the parachute var being between 0.0(freefall) and 1.0(levitating),

uneven fog
bold kraken
#

Go use a state machine!

#

I suspect a lot of your problems are arising from the fact that you're coupling states that are totally different. State machines, aside from eliminating bugs that result from mixing state that shouldn't be mixed, are also mainly an organizational structure for you as the programmer to reason about your state logic and behavior.