| Crates.io | bevy_mod_erased_component_registry |
| lib.rs | bevy_mod_erased_component_registry |
| version | 0.3.0 |
| created_at | 2025-11-08 21:13:46.574842+00 |
| updated_at | 2026-01-24 00:06:19.723176+00 |
| description | Insert components into entities by their `TypeId`! |
| homepage | https://github.com/AlexAegis/bevy_mod_erased_component_registry |
| repository | https://github.com/AlexAegis/bevy_mod_erased_component_registry |
| max_upload_size | |
| id | 1923281 |
| size | 144,474 |
Insert components into entities by their TypeId!
The type must implement either
FromWorldorDefault, as new instances will be created usingfrom_world.
Register your Component + FromWorld type in a plugin:
app.register_erased_component::<GenericFlagComponent<A>>();
Create a TypeId of your type, and save it somewhere: into a resource, or in a
component.
let flag_a_type_id = TypeId::of::<GenericFlagComponent<A>>()
Then insert it into an entity using this type_id
If you find yourself creating the
TypeIdright where you would call this insert function, then you definitely do not need to use this function. You know the type at the time of insertion, just insert it normally.
commands
.spawn_empty()
.insert_component_by_type_id(flag_a_type_id);
This example creates a randomly selected generic variant of a component every
time you press Space. The system that spawns this entity has no idea of the
actual type of this component!
cargo run -p bevy_mod_erased_component_registry --example erased_component_registry_example --features example
With required components, if you know that the entity where you use Thing<T>
will only ever have one kind of Thing<T> on it, you can require another
component that will hold the previous components TypeId! Giving you something
you can query without T, and create a new instance of the original
component without even knowing which kind of Thing<T> it was!
use core::marker::PhantomData;
use std::any::TypeId;
use bevy_ecs::component::Component;
#[derive(Component)]
#[require(ThingTypeId::new::<T>())]
pub struct Thing<T> {
_phantom_data: PhantomData<T>,
}
#[derive(Component)]
pub struct ThingTypeId {
type_id: TypeId,
}
impl ThingTypeId {
fn new<T: 'static>() -> Self {
Self {
type_id: TypeId::of::<Thing<T>>(),
}
}
}
Bevy can insert components by their ComponentId into an entity, but it
requires you to construct the component. But there's no api to try that with
a possible FromWorld implementation, which would require an internal registry
of constructors. This crate does that, with the registry being a resource.
A "quick" user-space proof of concept can make a better case for upstreaming a feature into bevy and makes it easy for users to try it out without having to use nightly bevy!
If that happens, migration would be very easy as the entire api is just 2 functions, one to register a component, and one to insert it. The register function would just disappear and the insert function would at worst get renamed.
TypeId instead of ComponentId?To get a ComponentId you need world access, but a TypeId can be acquired
anywhere. And when you retrieve the ComponentId from the world, it uses
TypeId anyway, so it's fine to use TypeId in the user facing api.
Internally when interacting with the World it will use the ComponentId
associated with that TypeId in the World.
You want consistency when inserting new components from the registry, to be
sure that if you insert a component now vs 10 minutes later, it will be created
the same way. Therefore changing the implementation of the constructor during
runtime must be prohibited. And if it's prohibited then your only option to
define it is during App build inside a plugin. At that point, using FromWorld
is the perfect solution do define that custom constuctor.
| Bevy | bevy_mod_erased_component_registry |
|---|---|
| 0.18 | 0.3 |
| 0.17 | 0.2 |
| 0.16 | 0.1 |
See contributing.md