Add trait ComponentConstructor
What problem does this solve or what need does it fill?
For rapier and avian, Collider is not implemented with Reflect. We cannot use extra properties in GLFT to reflect to create components.
Can we add a ComponentConstructor or something else to create a component from a reflectable constructor.
What solution would you like?
Allow Component convert from another reflectable type
#[derive(Component)]
struct Foo {
....
}
impl Foo {
pub fn new_a (...) { ... }
pub fn new_b (...) { ... }
}
#[derive(Reflect, ComponentConstructor)]
enum FooConstructor {
A( ... ),
B( ... ),
}
impl ComponentConstructor for FooConstructor {
type C = Foo;
pub fn to_component(self) -> C {
....
}
}
....
// for spawn
commands.spawn(( FooConstructor::A( .... ), ))
// should same with
commands.spawn(( Foo::new_a( ... ), ))
What alternative(s) have you considered?
For avian, they use https://github.com/Jondolf/avian/blob/39a7480cd1915bc5266aa8ebd3c3436d4d0568b0/src/collision/collider/backend.rs#L358C4-L358C30 to convert Constructor to Component.
For context: https://github.com/bevyengine/bevy/discussions/14437 (section called "Construct"), this is coming but I believe it will be called 'Construct' instead of 'ComponentConstructor'
It's not clear to me what commands.spawn(( FooConstructor::A( .... ), )) would gain you over doing commands.spawn(( FooConstructor::A( .... ).to_component(), )) and directly spawning the component.
For avian, they use https://github.com/Jondolf/avian/blob/39a7480cd1915bc5266aa8ebd3c3436d4d0568b0/src/collision/collider/backend.rs#L358C4-L358C30 to convert Constructor to Component.
Avian seems to need this to use this in order to create colliders that need to read from the Mesh associated to that entity. It's not clear how your proposed ComponentConstructor could handle this, since its to_component method takes no context other than the constructor itself.
Another alternative would be using hooks to detect the insertion of the constructor and immediately replace it with the constructed component. However an issue that this has (and also Construct) is that this will break if you do .insert(constructor).insert(dependency) (or .construct::<Foo>(constructor).insert(dependency)) because the constructor will run when the dependency has not been inserted yet, and hence will fail. The current approach with a delayed query will avoid this issue, at the cost of some systems being able to observe the constructor before it gets replaced by the constructed value.