Description
Bevy version
Bevy 0.16
What you did
This issue is somewhat related to: #19300
While I do believe that Required Components with higher specificity should precede those with lower specificity, I also believe there has been a regression from Bevy 0.15 to 0.16 in component requirements. (I haven't tested for a regression specifically, but I don't recall having issues like this in Bevy 0.15 as I use such patterns heavily in my project. It could just be that the upgrade exposes the issue).
Consider the following example, using Avian 0.3.0 and Bevy 0.16.0:
use avian3d::prelude::*;
use bevy_ecs::prelude::*;
#[derive(Default, Component)]
#[require(CollisionLayers::NONE, Collider::default())]
struct A;
#[derive(Default, Component)]
#[require(A, Collider::default())] // ((CollisionLayers::NONE, Collider::default())
struct B;
#[derive(Default, Component)]
#[require(B, Collider::default())] // (CollisionLayers::NONE, Collider::default())
struct C;
#[test]
fn case_b() {
let mut w = World::new();
let a = w.spawn(B);
// Passes!
assert_eq!(
a.get::<CollisionLayers>().unwrap().memberships,
LayerMask::NONE
);
}
#[test]
fn case_c() {
let mut w = World::new();
let a = w.spawn(C);
// Fails?!
assert_eq!(
a.get::<CollisionLayers>().unwrap().memberships,
LayerMask::NONE
);
}
In this example, case_b
PASSES, while case_c
FAILS.
Both C
and B
should have the same exact components in the same exact order.
Neither B
, nor C
, nor Collider
explicitly declare CollisionLayers::default()
as a requirement, yet that's what we end up with in case_c
but not in case_b
.
I don't see how this behavior matches the documented behavior:
From a user perspective, just think about this as the following:
Specifying a required component constructor for Foo directly on a spawned component Bar will result in that constructor being used (and overriding existing constructors lower in the inheritance tree). This is the classic “inheritance override” behavior people expect.
For cases where “multiple inheritance” results in constructor clashes, Components should be listed in “importance order”. List a component earlier in the requirement list to initialize its inheritance tree earlier.
In all cases, A
and B
are required before Collider
, which implies CollisionLayers::NONE
should be required before Collider
, and we're never explicitly inserting a Collider
on spawn either.
What went wrong
There are inconsistencies between the expected and actual override behavior of required components.
Additional information
I believe the solution proposed in #19300 would automatically solve this problem in a more generalized way. However, the issue raised here seems like a bug to me.