How do I properly add a bounce function?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By asgerregsa

Hi!

I have a player, a KinematicBody2D, that moves around using the arrow keys using move_and_slide(). The “slide” part of move and slide is important to me, as I have 45° slopes in this platformer game, on which the player can roll up and down. An integral part of the game is the ability to use WASD to bounce. For example, if you’re falling down and holding S as you hit the ground, you will bounce back up with 75% of the velocity you hit the ground with. If you hit one of said 45° slopes, you will bounce to the left or right accordingly, as well as bouncing upwards.

This works for the most part, but it is sometimes buggy. Sometimes it results in unexpected behaviour such as the player coming to a complete stop or even rolling uphill a little. I think the way I have implemented the bounce is a little jerry-rigged, and I would like to ask how you would go about it?
I’m using is_on_floor() twice to check if the player hits the ground. Once before the move_and_slide() method and once after. Here is the movement code that goes in my player’s script, which is getting called every frame in the _physics_process(delta) (I’ve removed some of the code that’s only relevant for bouncing on the walls and ceiling):

# global variables in player script:
const GRAVITY_VEC = Vector2(0, 900)
const FLOOR_NORMAL = Vector2(0, -1)
const SLOPE_SLIDE_STOP = 25.0     

# bouncing
var last_vel = Vector2(0, 0)
var BOUNCE_VEL_REQUIRED_DOWN = 0.01
var bounce_coefficient = 0.75

func movement(delta: float) -> void: # called every frame
	# Find collision status, move, then find collision status again.
	var on_floor_0 = is_on_floor()
	linear_vel += delta * GRAVITY_VEC
	linear_vel = move_and_slide(linear_vel, FLOOR_NORMAL, SLOPE_SLIDE_STOP)
	var on_floor_1 = is_on_floor()
	
	""" Bounce on floor with S """
	# bounce on floor
	if Input.is_action_pressed("s"):
		if last_vel.y >= BOUNCE_VEL_REQUIRED_DOWN: 
			if on_floor_0 != on_floor_1: # If you've just hit the floor
				linear_vel.y = last_vel.y * -bounce_coefficient

    # Movement controls (removed parts irrelevant for this post, such as jumping)
var target_speed = 0
if not viewing_UI:
	if Input.is_action_pressed("move_left"):
		target_speed -= 1
	if Input.is_action_pressed("move_right"):
		target_speed += 1

    # movement
target_speed *= WALK_SPEED # == 1550
linear_vel.x = lerp(linear_vel.x, target_speed, 0.016)

#store info for next frame
last_vel = linear_vel

Edit: I can reproduce the bug by falling straight down, not moving along the x-axis at all.

Example: Imgur: The magic of the Internet

In this example (image), the player falls straight down and when he tries to bounce, he actually rolls up the slope a little bit. However if he moves a little on the x-axis before hitting the slope under the arrow, he will bounce properly - a little up and to the left, like he is supposed to.

:bust_in_silhouette: Reply From: Fernando Brandt

You can apply the velocity according to the normal of the collision: velocity += get_slide_collision(0).normal * force

Thank you.
Couldn’t get it to work, and I suspect it’s because of this line: linear_vel = move_and_slide(linear_vel, FLOOR_NORMAL, SLOPE_SLIDE_STOP). I probably have to rewrite the entire move method to use move_and_collide() rather than move_and_slide() and then adjust the player’s behaviour according to different normals, rather than sort of plastering it on as the game develops.

asgerregsa | 2020-12-19 21:21