The player passes through the Area2D when falling from a great height

Godot Version

v4.6.1.stable.arch_linux [14d19694e]

Question

I want to create trampoline. As the player jumps on its top, his jump becomes higher each time. At the bottom of trampoline the player should stand as if on the ground

Here are the nodes

Code of trampoline

extends 'res://scripts/box.gd'

@export var k: float = 1.2
@export var min_bounce: float = 300.0

func _on_area_2d_body_entered(body: Node2D) -> void:
	body.velocity.y = -max(abs(body.velocity.y) * k + 60, min_bounce)

Code of player’s _physics_process

func _physics_process(delta: float) -> void:
	var direction := Input.get_axis('move_left', 'move_right')

	if is_on_floor() and not $CollisionShape2D.disabled:
		if Input.is_action_just_pressed('jump'):
			velocity.y = jump_velocity
		else:
			n_air_jumps = 0
		if direction == 0:
			$AnimatedSprite2D.play('idle')
		else:
			$AnimatedSprite2D.play('run')
	else:
		if Input.is_action_just_pressed('jump') && n_air_jumps < 0:
			n_air_jumps += 1
			velocity.y = jump_velocity
		velocity += get_gravity() * delta
		$AnimatedSprite2D.play(fall_anim)

	if direction > 0:
		$AnimatedSprite2D.flip_h = false
		$Skins.scale.x = 1
	elif direction < 0:
		$AnimatedSprite2D.flip_h = true
		$Skins.scale.x = -1

	if direction:
		velocity.x = direction * speed
	else:
		velocity.x = move_toward(velocity.x, 0, speed)

	move_and_slide()

	for i in range(get_slide_collision_count()):
		var collision := get_slide_collision(i)
		if collision.get_collider() is RigidBody2D:
			collision.get_collider().apply_central_impulse(-collision.get_normal() * PUSH_FORCE)
	queue_redraw()

The problem is that when the player falls from a great height he passes through the Area2D. This is how it works: https://drive.google.com/file/d/1c690eCuhNXknoPvUBUyjgSj2RHTwEKBs/view?usp=drive_link

At the bottom the player should stand as if on the ground: https://drive.google.com/file/d/1EEGNJhSuZPGTYlgUBXmOVoU_5q9C88iP/view?usp=drive_link

Also the problem is that Area2D ignores inanimate objects but they also should jump

Player collision: Layer 2, Mask 1

Trampoline and Box collision: Layer 1, Mask 1, 3

How can I prevent the player from falling through Area2D and make inanimate objects jump too?

The player is probably falling fast enough to touch the RigidBody on the same frame as they touch the area, when they hit another body their velocity will stop. You may have to store the player’s velocity.y before move_and_slide, then use that in your calculation. Setting a maximum fall speed could also help if you want to limit the trampoline’s potential height.

func _on_area_2d_body_entered(body: Node2D) -> void:
	if body is Player:
		body.velocity.y = -max(abs(body.pre_slide_velocity.y) * k + 60, min_bounce)

Depends on how you create inanimate objects. If they are RigidBodies then you will need to use linear_velocity or apply_central_impulse

func _on_area_2d_body_entered(body: Node2D) -> void:
	if body is Player:
		body.velocity.y = -max(abs(body.pre_slide_velocity.y) * k + 60, min_bounce)
	elif body is RigidBody2D:
		body.linear_velocity.y =  -max(abs(body.linear_velocity.y) * k + 60, min_bounce)
1 Like

The way with pre_slide_velocity significantly increased the max height of jump but player still falls through the Area2D. Is there the solution with 100% guarantee that player will not fall?

Yes, Box is RigidBody2D, player is CharacterBody2D. The linear_velocity or apply_central_impulsedont work because Area2D completely ignores RigidBody2D. And this is strange because Box’s Layer is 1 and Trampoline’s Mask is 1 and 3. Maybe I should use for i in range(get_slide_collision_count()): in trampoline too?

Areas do not stop collision so there is always a possibility the player “falls through” it, either meaning being full enclosed or actually falling through it by one frame step, typically you will try to set reasonable speed limits and if you need some objects to move faster use larger-than-you’d-think collision shapes to cover any physics step gaps.


How are you testing the the box ignores the trampoline? It’s worth nothing the area is a child of your box so it will always transform with the other, if they aren’t overlapping at the start then they will never overlap, and if they are overlapping at the start then it will only apply such an impulse once at the start of the game.
If you are placing two separate box/trampoline instances then they should collide and bounce on each other.

get_slide_collision_count and other get_slide_ functions do not exist on RigidBodies only CharacterBody.

1 Like

Use an AnimatibleBody2D for your trampoline. It will handle all your collision issues.

How are you testing the the box ignores the trampoline?

Print does not work with a box:

func _on_area_2d_body_entered(body: Node2D) → void:
print(‘DETECTED’)

If I put box on trampoline it fall through the upper part like on the screen from last link

Try running with the “Debug → Visible Collision Shapes” option on. I don’t see two trampolines in your second screen shot.
Does “DETECTED” print for the player? It may help to print more information such as what is being detected

func _on_area_2d_body_entered(body: Node2D) -> void:
    print("DETECTED: ", body)

For player the print is “DETECTED Squirrel:<CharacterBody2D#50214209092>”

But for box nothing prints and with “Debug → Visible Collision Shapes” the Box collides only with the bottom CollisionShape and ignores the upper CollisionShape

1 Like

Can you post a screenshot of the collision layer/mask settings for the box and the trampoline’s area?

They have same layer/mask

Squirrel has layer 2 mask 1

The second screenshot is of the trampoline’s RigidBody (also named Box which is a little confusing), not it’s Area

Sorry

But still box ignores upper part

I changed to

func _on_area_2d_body_entered(body: Node2D) → void:
print('DETECTED ', body)
body.linear_velocity.y = -max(abs(body.linear_velocity.y) * k + 60, min_bounce)

And now it works with the Box. I needed to change masks in Area2D, not in RigidBody2D. Thank you very much for your help

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.