Further improve docs for component hooks (#13475)
# Objective While reviewing the other open hooks-related PRs, I found that the docs on the `ComponentHooks` struct itself didn't give enough information about how and why the feature could be used. ## Solution 1. Clean up the docs to add additional context. 2. Add a doc test demonstrating simple usage. ## Testing The doc test passes locally. --------- Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com>
This commit is contained in:
parent
1ec5cdf3f2
commit
dda7a744cf
@ -182,7 +182,55 @@ pub enum StorageType {
|
|||||||
/// The type used for [`Component`] lifecycle hooks such as `on_add`, `on_insert` or `on_remove`
|
/// The type used for [`Component`] lifecycle hooks such as `on_add`, `on_insert` or `on_remove`
|
||||||
pub type ComponentHook = for<'w> fn(DeferredWorld<'w>, Entity, ComponentId);
|
pub type ComponentHook = for<'w> fn(DeferredWorld<'w>, Entity, ComponentId);
|
||||||
|
|
||||||
/// Lifecycle hooks for a given [`Component`], stored in its [`ComponentInfo`]
|
/// [`World`]-mutating functions that run as part of lifecycle events of a [`Component`].
|
||||||
|
///
|
||||||
|
/// Hooks are functions that run when a component is added, overwritten, or removed from an entity.
|
||||||
|
/// These are intended to be used for structural side effects that need to happen when a component is added or removed,
|
||||||
|
/// and are not intended for general-purpose logic.
|
||||||
|
///
|
||||||
|
/// For example, you might use a hook to update a cached index when a component is added,
|
||||||
|
/// to clean up resources when a component is removed,
|
||||||
|
/// or to keep hierarchical data structures across entities in sync.
|
||||||
|
///
|
||||||
|
/// This information is stored in the [`ComponentInfo`] of the associated component.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use bevy_ecs::prelude::*;
|
||||||
|
/// use bevy_utils::HashSet;
|
||||||
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct MyTrackedComponent;
|
||||||
|
///
|
||||||
|
/// #[derive(Resource, Default)]
|
||||||
|
/// struct TrackedEntities(HashSet<Entity>);
|
||||||
|
///
|
||||||
|
/// let mut world = World::new();
|
||||||
|
/// world.init_resource::<TrackedEntities>();
|
||||||
|
///
|
||||||
|
/// // No entities with `MyTrackedComponent` have been added yet, so we can safely add component hooks
|
||||||
|
/// let mut tracked_component_query = world.query::<&MyTrackedComponent>();
|
||||||
|
/// assert!(tracked_component_query.iter(&world).next().is_none());
|
||||||
|
///
|
||||||
|
/// world.register_component_hooks::<MyTrackedComponent>().on_add(|mut world, entity, _component_id| {
|
||||||
|
/// let mut tracked_entities = world.resource_mut::<TrackedEntities>();
|
||||||
|
/// tracked_entities.0.insert(entity);
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// world.register_component_hooks::<MyTrackedComponent>().on_remove(|mut world, entity, _component_id| {
|
||||||
|
/// let mut tracked_entities = world.resource_mut::<TrackedEntities>();
|
||||||
|
/// tracked_entities.0.remove(&entity);
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// let entity = world.spawn(MyTrackedComponent).id();
|
||||||
|
/// let tracked_entities = world.resource::<TrackedEntities>();
|
||||||
|
/// assert!(tracked_entities.0.contains(&entity));
|
||||||
|
///
|
||||||
|
/// world.despawn(entity);
|
||||||
|
/// let tracked_entities = world.resource::<TrackedEntities>();
|
||||||
|
/// assert!(!tracked_entities.0.contains(&entity));
|
||||||
|
/// ```
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct ComponentHooks {
|
pub struct ComponentHooks {
|
||||||
pub(crate) on_add: Option<ComponentHook>,
|
pub(crate) on_add: Option<ComponentHook>,
|
||||||
@ -195,6 +243,8 @@ impl ComponentHooks {
|
|||||||
/// An `on_add` hook will always run before `on_insert` hooks. Spawning an entity counts as
|
/// An `on_add` hook will always run before `on_insert` hooks. Spawning an entity counts as
|
||||||
/// adding all of its components.
|
/// adding all of its components.
|
||||||
///
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
/// Will panic if the component already has an `on_add` hook
|
/// Will panic if the component already has an `on_add` hook
|
||||||
pub fn on_add(&mut self, hook: ComponentHook) -> &mut Self {
|
pub fn on_add(&mut self, hook: ComponentHook) -> &mut Self {
|
||||||
self.try_on_add(hook)
|
self.try_on_add(hook)
|
||||||
@ -202,9 +252,17 @@ impl ComponentHooks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Register a [`ComponentHook`] that will be run when this component is added (with `.insert`)
|
/// Register a [`ComponentHook`] that will be run when this component is added (with `.insert`)
|
||||||
/// or replaced. The hook won't run if the component is already present and is only mutated.
|
/// or replaced.
|
||||||
|
///
|
||||||
/// An `on_insert` hook always runs after any `on_add` hooks (if the entity didn't already have the component).
|
/// An `on_insert` hook always runs after any `on_add` hooks (if the entity didn't already have the component).
|
||||||
///
|
///
|
||||||
|
/// # Warning
|
||||||
|
///
|
||||||
|
/// The hook won't run if the component is already present and is only mutated, such as in a system via a query.
|
||||||
|
/// As a result, this is *not* an appropriate mechanism for reliably updating indexes and other caches.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
/// Will panic if the component already has an `on_insert` hook
|
/// Will panic if the component already has an `on_insert` hook
|
||||||
pub fn on_insert(&mut self, hook: ComponentHook) -> &mut Self {
|
pub fn on_insert(&mut self, hook: ComponentHook) -> &mut Self {
|
||||||
self.try_on_insert(hook)
|
self.try_on_insert(hook)
|
||||||
@ -214,13 +272,18 @@ impl ComponentHooks {
|
|||||||
/// Register a [`ComponentHook`] that will be run when this component is removed from an entity.
|
/// Register a [`ComponentHook`] that will be run when this component is removed from an entity.
|
||||||
/// Despawning an entity counts as removing all of its components.
|
/// Despawning an entity counts as removing all of its components.
|
||||||
///
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
/// Will panic if the component already has an `on_remove` hook
|
/// Will panic if the component already has an `on_remove` hook
|
||||||
pub fn on_remove(&mut self, hook: ComponentHook) -> &mut Self {
|
pub fn on_remove(&mut self, hook: ComponentHook) -> &mut Self {
|
||||||
self.try_on_remove(hook)
|
self.try_on_remove(hook)
|
||||||
.expect("Component id: {:?}, already has an on_remove hook")
|
.expect("Component id: {:?}, already has an on_remove hook")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fallible version of [`Self::on_add`].
|
/// Attempt to register a [`ComponentHook`] that will be run when this component is added to an entity.
|
||||||
|
///
|
||||||
|
/// This is a fallible version of [`Self::on_add`].
|
||||||
|
///
|
||||||
/// Returns `None` if the component already has an `on_add` hook.
|
/// Returns `None` if the component already has an `on_add` hook.
|
||||||
pub fn try_on_add(&mut self, hook: ComponentHook) -> Option<&mut Self> {
|
pub fn try_on_add(&mut self, hook: ComponentHook) -> Option<&mut Self> {
|
||||||
if self.on_add.is_some() {
|
if self.on_add.is_some() {
|
||||||
@ -230,7 +293,10 @@ impl ComponentHooks {
|
|||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fallible version of [`Self::on_insert`].
|
/// Attempt to register a [`ComponentHook`] that will be run when this component is added (with `.insert`)
|
||||||
|
///
|
||||||
|
/// This is a fallible version of [`Self::on_insert`].
|
||||||
|
///
|
||||||
/// Returns `None` if the component already has an `on_insert` hook.
|
/// Returns `None` if the component already has an `on_insert` hook.
|
||||||
pub fn try_on_insert(&mut self, hook: ComponentHook) -> Option<&mut Self> {
|
pub fn try_on_insert(&mut self, hook: ComponentHook) -> Option<&mut Self> {
|
||||||
if self.on_insert.is_some() {
|
if self.on_insert.is_some() {
|
||||||
@ -240,7 +306,10 @@ impl ComponentHooks {
|
|||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fallible version of [`Self::on_remove`].
|
/// Attempt to register a [`ComponentHook`] that will be run when this component is removed from an entity.
|
||||||
|
///
|
||||||
|
/// This is a fallible version of [`Self::on_remove`].
|
||||||
|
///
|
||||||
/// Returns `None` if the component already has an `on_remove` hook.
|
/// Returns `None` if the component already has an `on_remove` hook.
|
||||||
pub fn try_on_remove(&mut self, hook: ComponentHook) -> Option<&mut Self> {
|
pub fn try_on_remove(&mut self, hook: ComponentHook) -> Option<&mut Self> {
|
||||||
if self.on_remove.is_some() {
|
if self.on_remove.is_some() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user