Skip to content

Commit fa56a5c

Browse files
committed
Add component_id function to World and Components (#5066)
# Objective - Simplify the process of obtaining a `ComponentId` instance corresponding to a `Component`. - Resolves #5060. ## Solution - Add a `component_id::<T: Component>(&self)` function to both `World` and `Components` to retrieve the `ComponentId` associated with `T` from a immutable reference. --- ## Changelog - Added `World::component_id::<C>()` and `Components::component_id::<C>()` to retrieve a `Component`'s corresponding `ComponentId` if it exists.
1 parent f8fa229 commit fa56a5c

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

crates/bevy_ecs/src/component.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,23 @@ impl ComponentInfo {
214214
}
215215
}
216216

217+
/// A semi-opaque value which uniquely identifies the type of a [`Component`] within a
218+
/// [`World`](crate::world::World).
219+
///
220+
/// Each time a new `Component` type is registered within a `World` using
221+
/// [`World::init_component`](crate::world::World::init_component) or
222+
/// [`World::init_component_with_descriptor`](crate::world::World::init_component_with_descriptor),
223+
/// a corresponding `ComponentId` is created to track it.
224+
///
225+
/// While the distinction between `ComponentId` and [`TypeId`] may seem superficial, breaking them
226+
/// into two separate but related concepts allows components to exist outside of Rust's type system.
227+
/// Each Rust type registered as a `Component` will have a corresponding `ComponentId`, but additional
228+
/// `ComponentId`s may exist in a `World` to track components which cannot be
229+
/// represented as Rust types for scripting or other advanced use-cases.
230+
///
231+
/// A `ComponentId` is tightly coupled to its parent `World`. Attempting to use a `ComponentId` from
232+
/// one `World` to access the metadata of a `Component` in a different `World` is undefined behaviour
233+
/// and must not be attempted.
217234
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
218235
pub struct ComponentId(usize);
219236

@@ -422,11 +439,38 @@ impl Components {
422439
self.components.get_unchecked(id.0)
423440
}
424441

442+
/// Type-erased equivalent of [`Components::component_id`].
425443
#[inline]
426444
pub fn get_id(&self, type_id: TypeId) -> Option<ComponentId> {
427445
self.indices.get(&type_id).map(|index| ComponentId(*index))
428446
}
429447

448+
/// Returns the [`ComponentId`] of the given [`Component`] type `T`.
449+
///
450+
/// The returned `ComponentId` is specific to the `Components` instance
451+
/// it was retrieved from and should not be used with another `Components`
452+
/// instance.
453+
///
454+
/// Returns [`None`] if the `Component` type has not
455+
/// yet been initialized using [`Components::init_component`].
456+
///
457+
/// ```rust
458+
/// use bevy_ecs::prelude::*;
459+
///
460+
/// let mut world = World::new();
461+
///
462+
/// #[derive(Component)]
463+
/// struct ComponentA;
464+
///
465+
/// let component_a_id = world.init_component::<ComponentA>();
466+
///
467+
/// assert_eq!(component_a_id, world.components().component_id::<ComponentA>().unwrap())
468+
/// ```
469+
#[inline]
470+
pub fn component_id<T: Component>(&self) -> Option<ComponentId> {
471+
self.get_id(TypeId::of::<T>())
472+
}
473+
430474
#[inline]
431475
pub fn get_resource_id(&self, type_id: TypeId) -> Option<ComponentId> {
432476
self.resource_indices

crates/bevy_ecs/src/world/mod.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,20 @@ impl World {
177177
WorldCell::new(self)
178178
}
179179

180+
/// Initializes a new [`Component`] type and returns the [`ComponentId`] created for it.
180181
pub fn init_component<T: Component>(&mut self) -> ComponentId {
181182
self.components.init_component::<T>(&mut self.storages)
182183
}
183184

185+
/// Initializes a new [`Component`] type and returns the [`ComponentId`] created for it.
186+
///
187+
/// This method differs from [`World::init_component`] in that it uses a [`ComponentDescriptor`]
188+
/// to initialize the new component type instead of statically available type information. This
189+
/// enables the dynamic initialization of new component definitions at runtime for advanced use cases.
190+
///
191+
/// While the option to initialize a component from a descriptor is useful in type-erased
192+
/// contexts, the standard `World::init_component` function should always be used instead
193+
/// when type information is available at compile time.
184194
pub fn init_component_with_descriptor(
185195
&mut self,
186196
descriptor: ComponentDescriptor,
@@ -189,6 +199,31 @@ impl World {
189199
.init_component_with_descriptor(&mut self.storages, descriptor)
190200
}
191201

202+
/// Returns the [`ComponentId`] of the given [`Component`] type `T`.
203+
///
204+
/// The returned `ComponentId` is specific to the `World` instance
205+
/// it was retrieved from and should not be used with another `World` instance.
206+
///
207+
/// Returns [`None`] if the `Component` type has not yet been initialized within
208+
/// the `World` using [`World::init_component`].
209+
///
210+
/// ```rust
211+
/// use bevy_ecs::prelude::*;
212+
///
213+
/// let mut world = World::new();
214+
///
215+
/// #[derive(Component)]
216+
/// struct ComponentA;
217+
///
218+
/// let component_a_id = world.init_component::<ComponentA>();
219+
///
220+
/// assert_eq!(component_a_id, world.component_id::<ComponentA>().unwrap())
221+
/// ```
222+
#[inline]
223+
pub fn component_id<T: Component>(&self) -> Option<ComponentId> {
224+
self.components.component_id::<T>()
225+
}
226+
192227
/// Retrieves an [`EntityRef`] that exposes read-only operations for the given `entity`.
193228
/// This will panic if the `entity` does not exist. Use [`World::get_entity`] if you want
194229
/// to check for entity existence instead of implicitly panic-ing.

0 commit comments

Comments
 (0)