DynamicScene::write_to_world does not trigger ChildOf on_insert hooks, leaving parent Children component unpopulated
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 22h
- Merged PRs (30d)
- 161
Description
## Bevy version and features
- Bevy 0.18.0, default features
## Relevant system information
- Rust 1.86 (stable)
- Windows 11 Home 10.0.26200
- `AdapterInfo { name: "NVIDIA GeForce RTX 3060", vendor: 4318, device: 9351, device_type: DiscreteGpu, driver: "NVIDIA", driver_info: "595.71", backend:
Vulkan }`
## What you did
Saved a scene containing a parent-child hierarchy using `DynamicSceneBuilder`. The parent entity ("Group") has `Name` + `Transform`. Children have
`ChildOf(parent)` + `Name` + `Transform` + custom components. Loaded the scene back using `DynamicScene::write_to_world`.
## What went wrong
**Expected:** After `write_to_world`, the parent entity should have a `Children` component populated by the `ChildOf` on_insert hook, and Bevy's
`propagate_transforms` system should compute correct `GlobalTransform` values for children.
**Actual:** The parent entity never receives a `Children` component. `ChildOf` is present on the children and correctly references the parent, but the hook
that normally maintains `Children` does not fire. This causes:
1. `GlobalTransform` for all children stays at `(0, 0, 0)` permanently — `propagate_transforms` never walks into them because `Children` is empty/missing on
the parent.
2. Children render at the world origin regardless of their local `Transform`.
3. `InheritedVisibility` propagation also breaks (B0004 warnings).
Diagnostic output showing the problem persists across multiple frames:
```text
Frame 1 (immediately after write_to_world):
Group (33v0): local=(0,0,0) global=(0,0,0) children=0 (Children never populated)
Cone (34v0): local=(-5.35,0,0) global=(0,0,0) parent=33v0
Cube (37v0): local=(5.57,0,0) global=(0,0,0) parent=33v0
Frame 5 (several frames later):
Group (33v0): local=(0,0,0) global=(0,0,0) children=0 (still empty)
Cone (34v0): local=(-5.35,0,0) global=(0,0,0) parent=33v0 (never propagated)
Cube (37v0): local=(5.57,0,0) global=(0,0,0) parent=33v0 (never propagated)
The issue is that write_to_world inserts ChildOf via the reflection path (ReflectComponent::insert), which does not trigger the component's on_insert hook
that would call add_child on the parent.
Additional information
Workaround: After write_to_world, remove and re-insert ChildOf on each child entity to force the hooks to fire:
let children_with_parents: Vec<(Entity, Entity)> = entity_map
.values()
.filter_map(|&entity| {
world.entity(entity).get::().map(|c| (entity, c.parent()))
})
.collect();
for (child, parent) in children_with_parents {
world.entity_mut(child).remove::();
world.entity_mut(child).insert(ChildOf(parent));
}
This immediately populates Children on the parent and transform/visibility propagation works correctly.
Scope: This affects any scene with parent-child hierarchies loaded via DynamicScene::write_to_world. Flat scenes (no ChildOf) are unaffected.
```
Contributor guide
Assessment
This issue has not been assessed yet.