Open fuzhouch opened 10 months ago
In one sentence: Area doesn't monitor Bodies with a mask on the corresponding Area layer, it is a regression to v3.x in both 2D and 3D. EDIT: As mentioned below, it was intentionally broken aka fixed in https://github.com/godotengine/godot/pull/51801...
Actually, this seems to be the fixed behavior in Godot 4, as by #51801. The 3.x behavior was a source of a lot of confusion, I've even found my own old comments on the subject as long as #15243, and there were plenty of similar issues (and actual bugs too).
The proposal (and the related fixes) which addresses this can be found here: https://github.com/godotengine/godot-proposals/issues/2775
Documentation definitely needs a clear paragraph with examples about how layers and masks are expected to interact, for sure.
AFAIK, this is not a bug tho.
Thanks @voidexp for clarification! So it is indeed a quirk in Godot 3. Yes a documentation will be helpful. Though my workaround mentioned in bug report (seems we should not call it workaround anymore...) has its side-effect, it's not a showstopper.
A crazy thought quickly jumps in my mind: I'm thinking a comparison project btw 3 and 4 may be helpful to game developers who work on migration projects like me. I'm happy to contribute my comparison project if Godot team think it useful.
There’s a doc about migration from Godot 3 to 4, and perhaps you can PR about things not mentioned there that you’ve found and reproduced.
Also, the automatic project conversion tool can benefit from a PR too, if there’s a clear one-way fix for some of the scenarios in your demo project, although I’m not really familiar with its workings.
Thanks Ivan @voidexp, could you guide me how to do PR on the documentation?
Actually, I opened around 5 issues in the past 2 weeks. 2 have been confirmed to be undocumented design change while some are really bugs. Unfortunately, I did read migration doc before migrating, but didn't find corresponding answer, that's why I chose to open issues.
@fuzhouch - there's documentation about... how to contribute to documentation :) https://docs.godotengine.org/en/latest/contributing/documentation/contributing_to_the_documentation.html
I think the proper way to do what you want is to tell the dead enemy to add a collision exception with the player when it is dead, and then remove the exception if it gets reactivated by the flame. There's no need to mess around with collision layers and masks at runtime in this example.
EDIT: I thought this worked for all CollisionObject2D
s, but I see now that function only applies to PhysicsBody2D
s, but not Area2D
s. So I don't think it will work for your purposes after all.
Thanks @Deozaan for the suggestion! I didn't notice an API since 3.5. New things is learned.
Unfortunately this API may not be a good solution just as you pointed out. In my game, the dead enemy does not respond only to player, but also almost all enemies. Think about an idea: You see a Pumpkin Head is defeated by a ghost hunter. It should be harmless to everyone, but fire can bring it back to life. This is a typical scenario for collision masks, that I assign a mask to all fire-related Area2D, like fireballs, explosions or bonfires. These effects can be triggerred from different characters, but not a single Node.
And thank you @voidexp for pointing out the link. I may need some time to finish my current project this month. Then I will summrize all my findings and request updating the doc. Hopefully it can minimize the destraction I made to team. :)
Godot version
4.1.2.stable.flathub [399c9dc39] (regressed: v3.5.3.stable.flathub [6c814135b])
System information
Godot v4.1.2.stable (399c9dc39) - Freedesktop SDK 23.08 (Flatpak runtime) - Wayland - Vulkan (Compatibility) - Mesa Intel(R) Xe Graphics (TGL GT2) () - 11th Gen Intel(R) Core(TM) i5-11320H @ 3.20GHz (8 Threads)
Issue description
I noticed an inconsistent behavior between CharacterBody2D (Godot 4) and KinematicBody2D (Godot 3).
In Godot 3, KinematicBody2D actively detects an Area2D, if Area2D sets a collision bit and KinematicBody2D sets a corresponding mask. The detection can only be prevented by setting Area2D.monitoring = false (Note: it's not .monitable property.).
The behavior is different in Godot 4, that when Area2D sets a collision bit and CharacterBody2D sets a corresponding mask, the detection does not happen anymore.
Note before continuing
This issue can have two interpretations, that a) it's a designd change that Godot 4 intentionally prevent CharacterBody2D detecting Area2D, just didn't explain in documentation, or b) it can be a Godot 3 quirk because .monitoring property does not seems to affect this behavior.
Given there's a clear workaround (described below), it should be a low priority issue, but a confirmation of expected behavior is needed.
Use case scenario
I've developed an action game with a reactivatable enemy. The enemy supports two behaviors:
In Godot 3, this feature is implemented by settings collision bits. A brief setup looks like below:
With setup above, player does not collide with a defeated enemy (it has no collision layer bit). The defeated enemy responds to flame (KinematicBody2D sets mask bit while Area2D sets layer bit). Requirements are met.
However, when migrating my game to Godot 4 with same setup, the defeated enemy does not respond to flame. When I calls $CharacterBody2D.set_collision_layer_value(ENEMY, true) and $Area2D.set_collision_mask_value(ENEMY, true), the defeated enemy responds to both flame and player. Neither is expected.
Workaround in Godot 4
A workaround is to add a new collision layer, DEFEATED_ENEMY.
When an enemy is defeated, player does not collide with enemy (because player collides with ENEMY but not DEFEATED_ENEMY), while flame can reactivate enemy (because it detects DEFEATED_ENEMY).
Side effect of the workaround: The layer/mask bits are limited to 32 slots. It needs to be shared between terrain, characters and players. Adding more complicated interactions for enemies with different skills can exhaust the slots.
Steps to reproduce
Use my minimal comparison project repository. https://github.com/fuzhouch/godot-3to4-comparison/
Both scenes define two Area2Ds, a Blue area and a Red area. Every area is equipted with a corresponding character (a big gold). Every gold repeats 2-sec cycle of moving into the area, then moving out. Every area has a body_entered signal handler, printing a line on called.
Blue area represents a good case (area-detect-body mode), that characeter sets collision layer as PLAYER (value == 1), and area sets collision mask as PLAYER (values == 1). Red area represents a bug-reproduce case (body-detect-area mode), that character sets collision mask as EFFECT (value == 4), and area sets collision layer as EFFECT(value == 4).
Expected results
When running scene, we expect both Godot 3 and Godot 4 prints 2 lines every 2 seconds:
Actual results
Only Godot 3 prints 2 lines every 2 seconds
In Godot 4, it prints 1 line every 2 seconds. The Red area do not trigger callback.
Minimal reproduction project