About 'Jumping'!

gdt_s03_jumping

#1

In this video (objectives)…

1 Instance a scene 2 Apply gravity to the player 3 Allow the player to jump

After watching (learning outcomes)…

Learn how to instance a scene, change the main scene of the game, make the player fall and let the player jump!

(Unique Video Reference: 5_HD_GDT)

We would love to know…

  • What you found good about this lecture?
  • What we could do better?

Remember that you can reply to this topic, or create a new topic. The easiest way to create a new topic is to follow the link in Resources. That way the topic will…

  • Be in the correct forum (for the course).
  • Be in the right sub-forum (for the section)
  • Have the correct lecture tag.

Enjoy your stay in our thriving community!


#2

I just spent good amount of time figuring out why when standing still on the ground is_on_floor() constantly keeps switching:

func _physics_process(delta):
	move_and_slide(motion, UP)
	
	if is_on_floor():
		motion.y = 0
		print ("On the ground")
	else:
		motion.y += GRAVITY * delta
		print ("Jumping")

Resulted to:
image

This looks to related to Godot bug https://github.com/godotengine/godot/issues/16250, and can be fixed (by removing line containing motion.y = 0) as follows:

--- jmp.gd	2018-07-08 09:53:10.936708716 +0200
+++ jmp_patch.gd	2018-07-08 09:53:21.664538915 +0200
@@ -2,7 +2,6 @@ func _physics_process(delta):
 	move_and_slide(motion, UP)
 	
 	if is_on_floor():
-		motion.y = 0
 		print ("On the ground")
 	else:
 		motion.y += GRAVITY * delta

or maybe:

--- jmp.gd	2018-07-08 09:57:10.936708716 +0200
+++ jmp_patch.gd	2018-07-08 09:57:21.664538915 +0200
@@ -2,7 +2,6 @@ func _physics_process(delta):
 	move_and_slide(motion, UP)
 	
 	if is_on_floor():
-		motion.y = 0
+		motion.y = 150
 		print ("On the ground")
 	else:
 		motion.y += GRAVITY * delta


#3

I’ve noticed problems like that before and had no idea why they were happening.


#4

Being new kid on the street is just hard :-). I was not entirely sure if this is a bug or not until I’ve found that Github Issue … Hope this post will save some time to other new kid ;-).


#5

I’m glad I wasn’t the only one to have this issue. I thought I had made some kind of mistake with the collision shape, but it looks like it is a bug.


#6

How odd. I know that move_and_slide() can be a little buggy from time to time. A couple of things to check.

(1) Did you scale the collision shape or use the extant? If you scaled it, it’ll behave oddly.
(2) Try reducing the Safe Margin in the Player’s inspector to 0.01 or less and seeing if that works.

safe_margin


#7

I did try to reduce Safe Margin, and the behaviour was even worse for some reason. Adding the motion.y = 150 it’s a hack, but it’s the only thing that seems to work to me, for now.


#8

Well, I apologize in advance for my poor English.

I don’t shure, but it seems I understand what is the problem. When the player on a floor you change motion.y to 0. So now if the player moves, it moves along the floor, just on its level but don’t touch it, because motion.y = 0. So at the next _physics_process player think it doesn’t on the floor and so add gravity and now it moves a bit lower, touch the floor and again knows it is on the floor. Maybe when player on a floor you should not do

motion.y = 0

but

motion.y = GRAVITY * delta

so it always touchs the floor when on it but does not speed up. And it seems to work good.


#9

EDIT: The downside of my suggested method:
You never have motion.y = 0 although the player is not moving. This could be a problem in the script of the AnimationPlayer node if you rely on that for animation. In our case it works just fine because we only check motion.y < 0:

@Andrey_Zhulanov hit the nail on the head!

@Yann_Burrett I would suggest to use this code for the fall method:

func fall(delta):
	if is_on_floor():
		motion.y = 0 # to prevent the player from accelerating while on the ground
	motion.y += GRAVITY * delta # always accelerate so we always get a fresh collision for is_on_floor()

# old version:
func fall():
	if is_on_floor():
		motion.y = 0
	else:
		motion.y += GRAVITY * delta

Why?

  • is_on_floor() should reliably return true, whether the player stays on the ground or just fell down on it
  • no “silent acceleration” while moving on the ground as demonstrated by Yann in the lecture starting around 7:55 min

A little background info on is_on_floor:

is_on_floor() just needs a fresh collision, otherwise it will not be set to true.

This is a shortened snippet from the c++ source of KinematicBody2D as of today on Github:

Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) {

	Vector2 floor_motion = floor_velocity;
	if (on_floor && on_floor_body.is_valid()) {
		//this approach makes sure there is less delay between the actual body velocity and the one we saved
		Physics2DDirectBodyState *bs = Physics2DServer::get_singleton()->body_get_direct_state(on_floor_body);
		if (bs) {
			floor_motion = bs->get_linear_velocity();
		}
	}

	Vector2 motion = (floor_motion + p_linear_velocity) * get_physics_process_delta_time();
	Vector2 lv = p_linear_velocity;

	on_floor = false;
	on_floor_body = RID();
	on_ceiling = false;
	on_wall = false;
	colliders.clear();
	floor_velocity = Vector2();

	while (p_max_slides) {

		Collision collision;
		bool found_collision = false;

		for (int i = 0; i < 2; i++) {
			bool collided;
			if (i == 0) { //collide
				collided = move_and_collide(motion, p_infinite_inertia, collision);
				if (!collided) {
					motion = Vector2(); //clear because no collision happened and motion completed
				}
			} else { //separate raycasts (if any)
				collided = separate_raycast_shapes(p_infinite_inertia, collision);
				if (collided) {
					collision.remainder = motion; //keep
					collision.travel = Vector2();
				}
			}

			if (collided) {
				found_collision = true;
			}

			if (collided) {

				motion = collision.remainder;

				if (p_floor_direction == Vector2()) {
					//all is a wall
					on_wall = true;
				} else {
					if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //floor

						on_floor = true;

is_on_floor() is just a Getter for the on_floor variable.

btw, I love open source software and the fact, that I can just check the code and see for myself :heart_eyes:


#10

The jump method would not just jump with the inclusion of “is_on_floor()”, no matter if I decreased the margin or never set y to 0…

After some tries, this is what I ended up with, retrieving the “is_on_floor()” only once every frame and then using it in the subroutines:

func _physics_process(delta):
    var on_floor = is_on_floor()
    fall(delta, on_floor)
	run()
	jump(on_floor)
	move_and_slide(motion, UP)
func fall(delta, on_floor):
	if on_floor:`Preformatted text`
		motion.y = GRAVITY * delta
	else:
		motion.y += GRAVITY * delta
func jump(on_floor):
	if on_floor and Input.is_action_pressed("ui_up"):
		motion.y = JUMP_SPEED

#11

I’ve always been under the impression that you set your velocity to the (left over) velocity that is returned from the move_and_slide call. This seems to eliminate the need for any odd manipulation of the motion.y and makes the is_on_floor() check much more reliable. I’m no expert on the inner workings of KinematicBody, but I believe this method is also required to get slope feature of the node working as well.

func _physics_process(delta):
	_get_input()
	_fall(delta)
	_run(delta)
	_jump(delta)
	_set_animation()
	vel = move_and_slide(vel, UP)

func _fall(delta):
    vel.y += GRAVITY	

func _jump(delta):
    if is_on_floor() and jump_input:
        vel.y = -jump_speed

#12

Thanks so much! Your suggestion really helped me out with fixing the problem of my bunny sticking to the floor every third jump or so.