mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
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
1 changed files with 74 additions and 5 deletions
|
@ -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 a new issue