1
\$\begingroup\$

First of all I want to mention that English is NOT my mother tongue, I am using GODOT to make my game and that I am only 16, so I don't have any prior programming experience, although, I understand what certain things do.

I am working on my first video game (a 2D Platformer about a mole in a deserted world), and I want to implement a "Varying Jump Height Mechanic" for my character. I try to make the controls feel like a hybrid between the games Super Mario Bros 1, Super Castlevania IV, Super Meat-Boy and Mega Man 7.

For now, I managed to give the player the desired air control, however, without having the possibility to control the height of the jump, the movement will feel quite sloppy.
One more thing before I give you the script, all the sprites are placeholders for now, if that has to do with anything of course

SCRIPT:

extends KinematicBody2D

const SPEED = 125
const GRAVITY = 18
const JUMP_POWER = -325
const FLOOR = Vector2(0, -1)

const FIREBALL = preload("res://Fireball.tscn")

var velocity = Vector2()

var on_ground = false

var is_dead = false

func physicsprocess(delta):

if is_dead == false:


     if Input. is_action_pressed("ui_right"):
         velocity.x = SPEED
         $AnimatedSprite.play("run")
         $AnimatedSprite.flip_h = true
         if sign($Position2D.position.x) == -1:
             $Position2D.position.x *= -1

     elif Input. is_action_pressed("ui_left"):
         velocity.x = -SPEED
         $AnimatedSprite.play("run") 
         $AnimatedSprite.flip_h = false
         if sign($Position2D.position.x) == 1:
              $Position2D.position.x *= -1

     else:
         velocity.x = 0
         if on_ground == true:
             $AnimatedSprite.play("idle")

     if Input. is_action_pressed("ui_up"):
         if on_ground == true:
             velocity.y = JUMP_POWER
             on_ground = false 




     if Input. is_action_just_pressed("ui_focus_next"):
         var fireball = FIREBALL.instance()
         if sign($Position2D.position.x) == 1:
             fireball.set_fireball_direction(1)
         else:
             fireball.set_fireball_direction(-1)


         get_parent().add_child(fireball)
         fireball.position = $Position2D.global_position

     velocity.y += GRAVITY

     if is_on_floor():
         on_ground = true 
     else:
         on_ground = false
         if velocity.y < 0:
             $AnimatedSprite.play("jump")
         else:
             $AnimatedSprite.play("fall")

     velocity = move_and_slide(velocity, FLOOR)

     if get_slide_count() > 0:
         for i in range(get_slide_count()):
             if "Enemy" in get_slide_collision(i).collider.name:
                 dead()


     if Input. is_action_just_pressed("ui_cancel"):
         get_tree().change_scene("titlescreen.tscn")
func dead():
is_dead = true
velocity = Vector2(0, 0)
$AnimatedSprite.play("dead")
$CollisionShape2D.disabled = true
$Timer.start()

func onTimertimeout():
gettree().change_scene("titlescreen.tscn")

...............................................................

I hope that I can find someone that is kind, and patient enough to help me with this issue, I would do it myself, but like I said I am 16 so I do not have any experience when it comes to programming. :)

\$\endgroup\$
1
  • \$\begingroup\$ so, I haven't played those games, are you trying to make it so you can hold the jump button to jump longer? \$\endgroup\$ Commented Nov 30, 2020 at 20:18

2 Answers 2

1
\$\begingroup\$

I had been doing forms of programming for a decade by the time I was 16, so you may want to not use that as an excuse in the future :)

Generally, the best way to do this is to store a flag when you first initiate a jump by pressing the button. This flag should get unset once your vertical velocity passes through zero again (so, when you fall) OR when you fail to pass the modified ground check.

What modified ground check? Glad you asked. When the jump flag is set, we actually want to look a little further below the character than normal to see if we are still very close above ground. This will require some tuning, but generally we like this to be the space of 3 to 5 input frames.

While this jump flag is set, we keep reading input from the jump button. If it stays pressed, we keep adding vertical velocity at a rate you find suitable.

What does this end up looking like? You press the button, and velocity keeps getting added as long as A) You keep the button pressed AND B) you keep close to the ground AND C) You are actually moving up.

As you are looking for responsive, smooth controls, it may also be worth looking into 'coyote time' which implements much the same idea as I described above to make tight jumps feel more fair.

\$\endgroup\$
4
  • \$\begingroup\$ Hi, can you please explain what a flag is and what does "store a flag" means? I understand what I have to do to make it work, but I am unfamiliar with this term :)) \$\endgroup\$ Commented Nov 29, 2020 at 13:01
  • \$\begingroup\$ @Help_da_Noob A boolean value. You store by setting to true, unset by resetting to false. Like the flag on a traditional mailbox, it is simply signalling a state. \$\endgroup\$ Commented Nov 29, 2020 at 18:34
  • \$\begingroup\$ you started programming when you were 6? \$\endgroup\$ Commented Nov 30, 2020 at 20:17
  • \$\begingroup\$ @Millard Technically learned the basics of GW-BASIC in my mum's lap when I was 4, but I only count when I started learning autonomously. \$\endgroup\$ Commented Dec 1, 2020 at 7:31
1
\$\begingroup\$

One thing that can help enormously is to start by defining your jump in terms of height instead of "power": (Credit to this tutorial.)

extends CharacterBody2D

@export(float) var gravity = 981.0  # positive so going down
## Maximum many pixels high we'll jump.
@export(float) var jump_height = 128.0


func _process(dt):
    if is_on_floor() and Input.is_action_just_pressed("jump"):
        velocity.y = -sqrt(2 * gravity * jump_height)

    move_and_slide()

    velocity.y += gravity * dt

Even better, if your game uses tiles you could define jump_tile_height and calculate jump_height = (jump_tile_height + 0.1) * TILE_SIZE. (I add 0.1 to to ensure you get over that last tile.)

On to variable height. I'd suggest two parts:

  1. reduce velocity on the frame jump button is released
  2. increase gravity while jump button is released

I've cobbled together this rough example, but haven't run it. Checkout demo projects like movement2 for a functional example that also covers many more fiddly game feel details.

@export var jump_cut : float = 0.4
@export var gravity : float = 3840.0
@export var max_speed_for_gravity : float = 1020.0
@export var max_speed_for_gravity_in_jump : float = 1000.0

var is_jumping := false

func _process(dt):
    if is_on_floor() and Input.is_action_just_pressed("jump"):
        velocity.y = -sqrt(2 * gravity * jump_height)
        # Jumps must start on the ground, so track whether we're in a jump.
        is_jumping = true

    elif Input.is_action_just_released("jump") and velocity.y < 0:
        # Still soaring, but released jump? Immediately cut velocity.
        velocity.y -= (jump_cut * velocity.y)

    if not Input.is_action_pressed("jump"):
        is_jumping = false

    move_and_slide()


    var applied_gravity = 0.0

    # Normal gravity limit
    if velocity.y <= max_speed_for_gravity:
        applied_gravity = gravity * dt

    # Limit gravity when moving upwards while jumping to achieve lower gravity -- two stage jumps.
    if (is_jumping and velocity.y < 0) and velocity.y > max_speed_for_gravity_in_jump:
        applied_gravity = 0.0

    velocity.y += applied_gravity

If you want to know more of the math behind a jump, you could watch Math for Game Programmers: Building a Better Jump.

\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.