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`
 | ||||
| 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)] | ||||
| pub struct ComponentHooks { | ||||
|     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
 | ||||
|     /// adding all of its components.
 | ||||
|     ///
 | ||||
|     /// # Panics
 | ||||
|     ///
 | ||||
|     /// Will panic if the component already has an `on_add` hook
 | ||||
|     pub fn on_add(&mut self, hook: ComponentHook) -> &mut Self { | ||||
|         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`)
 | ||||
|     /// 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).
 | ||||
|     ///
 | ||||
|     /// # 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
 | ||||
|     pub fn on_insert(&mut self, hook: ComponentHook) -> &mut Self { | ||||
|         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.
 | ||||
|     /// Despawning an entity counts as removing all of its components.
 | ||||
|     ///
 | ||||
|     /// # Panics
 | ||||
|     ///
 | ||||
|     /// Will panic if the component already has an `on_remove` hook
 | ||||
|     pub fn on_remove(&mut self, hook: ComponentHook) -> &mut Self { | ||||
|         self.try_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.
 | ||||
|     pub fn try_on_add(&mut self, hook: ComponentHook) -> Option<&mut Self> { | ||||
|         if self.on_add.is_some() { | ||||
| @ -230,7 +293,10 @@ impl ComponentHooks { | ||||
|         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.
 | ||||
|     pub fn try_on_insert(&mut self, hook: ComponentHook) -> Option<&mut Self> { | ||||
|         if self.on_insert.is_some() { | ||||
| @ -240,7 +306,10 @@ impl ComponentHooks { | ||||
|         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.
 | ||||
|     pub fn try_on_remove(&mut self, hook: ComponentHook) -> Option<&mut Self> { | ||||
|         if self.on_remove.is_some() { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Alice Cecile
						Alice Cecile