Why are Area2D collisions triggered 1 frame late?

I did one more test where I set up 2 different Area2D, each of them has their body_entered signal set up so they send the entering body to the position of the other Area2D.

What happens now is that whenever the CharacterBody2D enters either of them, it gets teleported indifinetely, to the opposite Area2D each tick, like this:

Tick 1:

Tick 2:

This is a demonstration that the signal process happens 1 physics tick late after it’s been triggered. I understand it’s been designed this way so it might prevent an infinite signal loop from blocking the main thread. While there might be benefits to this system, it bugs me that there’s no apparent way to make the signal be executed on the same physics tick that it’s been triggered

I haven’t read through everything here in the forum yet. Increasing the physic steps in this way is out of the question for you?

Engine.max_fps * 2 or 3 (if > 0)
or
Engine.get_frames_per_second() monitor FPS and also dynamically increase ‘physical steps’

func _ready():
	Engine.max_fps = 60
	#Engine.physics_jitter_fix = 0
	Engine.max_physics_steps_per_frame = 200
	Engine.physics_ticks_per_second = 200

	print("max_fps:", Engine.max_fps)
	print("max_physics_steps_per_frame:", Engine.max_physics_steps_per_frame)
	print("physics_jitter_fix:", Engine.physics_jitter_fix)
	print("physics_ticks_per_second:", Engine.physics_ticks_per_second)

24 fps video, in original 60 FPS Godot icon was reset a little later.

1 Like

This definitely helps a bit, but it does not solve the problem. Using the values in your example code I still can see the overlap, even if now it becomes less apparent. To make the overlap go away completely I have to use a ridiculously high value of 400 physics_ticks_per_second. This is not ideal because it could break performance later on.

I appreciate your interest though.

At this point I’m just going to accept that this is the way Godot signals were designed, but I honestly hate it.

I tried without signal, using ‘move_and_collide’, but unfortunately it doesn’t work yet, info is always null. Later I’ll experiment with GdScript first and check other projects.

func _process(delta):
	%FPS.text = "FPS: %s" % Engine.get_frames_per_second()

	var collision_info = move_and_collide(velocity * delta)
	if collision_info:
		global_position = starting_position
		print("collision_info:", collision_info)

func _physics_process(delta):

	var direction = Input.get_axis("ui_left", "ui_right")
	if direction:
		velocity.x = direction * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
	#move_and_slide()

You could try with StaticBody2D, then move_and_collide should work or something like that

	move_and_slide()
	for i in get_slide_collision_count():
		var collision = get_slide_collision(i)
		print("I collided with ", collision.get_collider().name)
		global_position = starting_position

Docs

I think it’s ok if Area is triggered a bit delayed.
I would not make the coins disappear immediately, but perform a ‘fade’ animation. :slight_smile:

StaticBody2D would be rather unsuitable for coins, could stop CharacterBody

I just had a look at the (V3.5?) ping pong beginner (?) example / mini tutorial titled: A simple Pong game. This demo shows best practices for game development in Godot, including signals.
…and the main difference is:

The pong ball is by far slower…

…so if the speed of the problematic file is reduced… for example:

const SPEED = 120.0

…then it seems to be no overlaping problem. So the overlap here might be because of the (high) speed ?

The collision detection fires when the object is colliding… but there is a “resolution” of time… not only in the detection ( physics_ticks_per_second ) but also in the rendering (actualy rendered frames per second). One frame before the collision there is obviously no collision but the next rendered frame there is a collision but the objects do then already overlap …or… are drawn overlapped.

So for the wanted speed the solution may be simply to change the position of the touching object to not overlap but “just touching” ??

Or (maybe needed for different angles of movement ) to have an additonal collision area ( with expanding size as a function of the speed) to detect when an object comes “into possible contact on the next frame”… so that the actual position could be changed to have no overlap and re-computed to the actual “touching position”.

Maybe even any associated vfx for “touching” mostly is/should be done at the position where the object do touch and not at the position where the object is detected to collide… ?

Another difference to the pong thing is: it uses CharacterBody2D… so i tried to use it also on the non-player like so:

…and it “stops” on contact and can be moved to the left again…( but i’m not sure how to reset the position on contact yet… )

Thanks for your interest
Yes, there’s a difference in collisions between a CharacterBody2D and Area2D, and collisions between 2 CharacterBody2D (or a CharacterBody2D and a StaticBody2D)

The overlap thing only seems to happens when an Area2D is involved. The other types of nodes, which are physics based, don’t produce an overlap, but can stop the CharacterBody2D when colliding if you don’t set the collision layers properly (which can be a hassle)

If you wanted to make bullets, AFAIK one should use CharacterBody2D for the bullets, as they should stop when colliding with a wall (StaticBody2D). If you made the bullets Area2D, you will see them overlap with the wall, which is undesired.

I also found out that, for there to be no visible overlap between Area2D and CharacterBody2D, 2 consecutive _physics_process must happen, (without a standard _process in between). This can be tested by lowering max_fps, which affect _process rate. This seems to indicate that drawing happens immediately after _process, and that signals are emitted first, before movement happens:

  • 1st _physics_process: the player is moved to the right with move_and_slide(), and enters the Area2D. The Area2D body_entered is NOT yet emitted.
  • _process: the engine draws the stuff. The player is drawn overlapping the Area2D
  • 2nd _physics_process: the Area2D body_entered signal is emitted, before the player movement is processed.

That order of things could explain why if you lower max_fps (or increase physics tick rate) so there are 2 consecutive _physics_process with no _process in between, the overlap is not visible.

This is just a nuance of the engine to keep in mind when developing a game, sadly I don’t think there’s much that can be done here without reinventing the wheel

EDIT: Area2D node is actually physics based too, as it inherits CollisionObject2D

EDIT 2: I also tried the pong official demo, yes the ball can be seen overlapping the paddles if you make the ball faster. Usually in pong you want the ball to go faster with every hit. The visual overlap is absolutely undesired, even if it is just one frame. In my opinion, if I were to make pong using Godot, I’d used CharacterBody2D for the paddles and ball, not Area2D

I faced a similar issue, though signals weren’t involved. A fast object was colliding with a wall and going way into it!

I fixed this by reducing the “physics jitter fix” setting. I used 0.2 but I think I could go lower.

(Physics > Common, and enable “advanced settings” toggle on the top right)

This seems to be intentional for dealing with network code and timing, but my game won’t have multiplayer so I opted out.