Kart Bouncing against walls. Vector3.bounce return unexpected high value

Godot Version

Godot 4.3

Question

I’m making a kart like game, I was testing some barriers, then I implemented that when the kart collided with a barrier it should bounce a bit, at first it worked, but the bounce was really small, so, I multiply it by 25, now the bounce is fine. However, when I bounce in not a straight direction it sometimes makes me fly away XD, I found that sometimes Vector3.bounce() returned an unusual high value, I don’t know why, it is my first time using 3D physics, what is happening? is it a bug? What I’m making wrong?

The code is:

func _physics_process(delta):
if ray_cast_3d.is_colliding():
var with = ray_cast_3d.get_collider()

	if is_instance_valid(with):
		with.get_parent().queue_free()
		set_helmet()

# Adds the gravity.
if not is_on_floor():
	velocity += get_gravity() * 5 * delta

# Handles jump.
if Input.is_action_just_pressed("ui_accept") and is_on_floor() and can_move:
	velocity.y = JUMP_VELOCITY

# Impulse
if Input.is_action_pressed("X") and can_move:
	direction = -transform.basis.z.normalized()
	
	velocity = velocity.move_toward(direction * SPEED, ACELERATION * delta)
	
	turning_velocity = move_toward(turning_velocity, TURNING_SPEED, TURNING_ACELERATION * delta)
else:
	velocity = velocity.move_toward(Vector3.ZERO, DERRAPE * delta)
	
	turning_velocity = move_toward(turning_velocity, 0, TURNING_DERRAPE * delta)

# Turning
if velocity != Vector3.ZERO and can_move:
	if Input.is_action_pressed("ui_left"):
		rotate_y(deg_to_rad(turning_velocity * delta))
		
	elif Input.is_action_pressed("ui_right"):
		rotate_y(deg_to_rad(-turning_velocity * delta))

#print(velocity)

var col = get_last_slide_collision()
if col:
	if col.get_collider().get_owner().is_in_group("SolidObject"):
		velocity = velocity.bounce(col.get_normal()) * 25
		print("bounce community ",col.get_normal()," / ",velocity)

move_and_slide()

here is a video where the bug occurs:


(ignore that the print says “bounce community” to show the results)

The reason for your problem is that the velocity of a colliding body is post-contact. This means that “straight collisions”, where the physics body is at a near stand-still, produce velocities that are small. On the contrary, oblique collisions barely affect the velocity of a moving physics body so the velocity maintains most of its length.

If you wish to bounce your vehicle based on the entry velocity, not the post-contact velocity, you have to store a velocity history.

var previous_velocity = Vector3.ZERO

func _physics_process(delta):
	# =================
	# === Your code ===
	# =================

	# [End of function]
	previous_velocity = velocity

It’s not bounce() returning a high value – it’s you multiplying the value by 25. Besides, bounce() doesn’t manipulate the length of the vector, it just computes its reflection vector for a given plane normal. Just don’t multiply by 25. You will find that using the previous_velocity, as outlined above, will yield better results.

In cases of inaccurate bounce directions

Depending on your turning speed and delta time of your physics, you may start to notice that previous_velocity does not correctly represent the body’s direction one tick later. That is, of course, because it’s an “old” value.

You can fix this by extrapolating the previous_velocity vector towards the current state of your body. An example of such extrapolation can be found here.


I hope that helps. Let me know if you have additional questions.

2 Likes

Thanks! for your comment, sorry for responding too late. I couldn’t test it yet, but now I understand what is happening. I will let you know when I implement it.

1 Like

I’ve tested it and finally works! Also was necessary to change the order or my collision code, now it is calculated before move_and_slide(). Thanks so much! UwU

	move_and_slide()
	
	var col = get_last_slide_collision()
	if col:
		if col.get_collider().get_owner().is_in_group("SolidObject"):
			velocity = last_velocity.bounce(col.get_normal())
	
	last_velocity = velocity
1 Like