Intermittent Collision Layer Issue

Hi everyone! I’m encountering an issue in my Godot (4.2.2) project related to collision layers and input handling.

I’m trying to change the collision layer and mask of my player character when the player presses and releases the ui_down action while standing on a platform. The logic works correctly sometimes but fails intermittently, especially after trying a few times. Sometimes it’s possible to get back on the platform 3 times, sometimes 2, sometimes even just once.

The platform consists of a Sprite2D and a CollisionShape2D with an extra Area2D (which parents a CollisionShape2D) on top to detect if the player is standing by the means of a body_entered signal.

Here is the code for the platform:

extends AnimatableBody2D

var standing_on_platform = false

func _on_area_2d_body_entered(body):
	standing_on_platform = true


func _on_area_2d_body_exited(body):
	standing_on_platform = false


func _input(event):
	if standing_on_platform:
		if event.is_action_pressed("ui_down"):
			set_collision_layer_value(2, false)

		elif event.is_action_released("ui_down"):
			set_collision_layer_value(2, true)

And I’m also attaching a picture of the platform itself.

I tried changing from _input(event) to something like _process and even _physics_process but nothing really helped. The collision layers and masks are working so I’m thinking this is a more deeper problem, like on how Godot draws each frame or something.

A small update: I managed to replicate the bug. Apparently, tapping ui_down very fast won’t cause the bug. The problem occurs if the key is hold for a while (even a very short period of time will make it stop working). I tried changing the code to .is_action_just_pressed with no success.

Anyway, any idea of what could be happening here?

You aren’t checking that the player is the body entering and exiting the Area2D so any PhysicsBody2D that enters or exits the Area2D will change the standing_on_platform variable.

You should always check if the ui_down is released and enable the layer back. Right now in your code if the user releases it after leaving the Area2D it won’t enable the layer back because standing_on_platform will be false.

You could also use a Timer with a small wait time instead and start it just after disabling the layer so the player won’t snap back to the top of the platform if the user releases the ui_down action too fast.

Try also moving the code to _physics_process()

For example:

Your CharacterBody2D script

extends CharacterBody2D

# This is the default CharacterBody2D template

# ...

var can_fallthrough = false

func _ready() -> void:
	add_to_group("Player")

func _physics_process(delta: float) -> void:

	# ...
	
	if can_fallthrough:
		if Input.is_action_just_pressed("ui_down"):
			set_collision_mask_value(1, false)
			get_tree().create_timer(0.1).timeout.connect(func():
				set_collision_mask_value(1, true)
			)

The detection Area2D:

extends Area2D


func _on_body_entered(body: Node2D) -> void:
	if body.is_in_group("Player"):
		body.can_fallthrough = true


func _on_body_exited(body: Node2D) -> void:
	if body.is_in_group("Player"):
		body.can_fallthrough = false