-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Description
Bevy version
0.16
What you did
#[derive(Component, PartialEq, Debug, Default, Copy, Clone)]
#[require(Required)]
struct Explicit(u8);
#[derive(Component, PartialEq, Debug, Default, Copy, Clone)]
struct Required(u8);
let mut world = World::new();
let target = world.spawn(Required(10)).id();
world.spawn((Explicit(20), Required(20))).clone_components::<Explicit>(target);
assert_eq!(world.get(target), Some(&Required(10)));What went wrong
The assertion fails, the target now contains Required(20).
This is surprising, since I did not explicitly ask for that to be cloned and it should not happen if the target already contains it.
The issue here is that EntityCloner has no information of the target entity at the time of configuration. You can either tell it to move Explicit with or without Required, the latter by using EntityCloner::without_required_components. This is not comfortable because you would have to check the target archetype first to pick the correct configuration. There is no automatism.
To make the issue more clear, these are the possible scenarios if I wanted to clone Explicit with or without Required:
use without_required_components |
target before | target after |
|---|---|---|
| no | Explicit(10), Required(10) |
Explicit(20), Required(20) ❌ |
| no | Required(10) |
Explicit(20), Required(20) ❌ |
| no | Explicit(20), Required(20) ✅ |
|
| yes | Explicit(10), Required(10) |
Explicit(20), Required(10) ✅ |
| yes | Required(10) |
Explicit(20), Required(10) ✅ |
| yes | Explicit(20), Required(0) ❌ |
As you can see, if you only want to clone Required if it does not exist at the target yet, like insert behaves where it only constructs a default Required when needed, you need to check the target archetype first and pick the correct cloner configuration based on that.
This makes this API uncomfortable.
A proper fix, if we want to keep EntityCloner agnostic to the target archetype (since it may be used on multiple source/target pairs as I saw in the code), it should at least know which ComponentId were given as explicit or implicit.