mirror of
https://github.com/bevyengine/bevy
synced 2025-02-16 22:18:33 +00:00
Improve entity and component API docs (#4767)
# Objective The descriptions included in the API docs of `entity` module, `Entity` struct, and `Component` trait have some issues: 1. the concept of entity is not clearly defined, 2. descriptions are a little bit out of place, 3. in a case the description leak too many details about the implementation, 4. some descriptions are not exhaustive, 5. there are not enough examples, 6. the content can be formatted in a much better way. ## Solution 1. ~~Stress the fact that entity is an abstract and elementary concept. Abstract because the concept of entity is not hardcoded into the library but emerges from the interaction of `Entity` with every other part of `bevy_ecs`, like components and world methods. Elementary because it is a fundamental concept that cannot be defined with other terms (like point in euclidean geometry, or time in classical physics).~~ We decided to omit the definition of entity in the API docs ([see why]). It is only described in its relationship with components. 2. Information has been moved to relevant places and links are used instead in the other places. 3. Implementation details about `Entity` have been reduced. 4. Descriptions have been made more exhaustive by stating how to obtain and use items. Entity operations are enriched with `World` methods. 5. Examples have been added or enriched. 6. Sections have been added to organize content. Entity operations are now laid out in a table. ### Todo list - [x] Break lines at sentence-level. ## For reviewers - ~~I added a TODO over `Component` docs, make sure to check it out and discuss it if necessary.~~ ([Resolved]) - You can easily check the rendered documentation by doing `cargo doc -p bevy_ecs --no-deps --open`. [see why]: https://github.com/bevyengine/bevy/pull/4767#discussion_r875106329 [Resolved]: https://github.com/bevyengine/bevy/pull/4767#discussion_r874127825
This commit is contained in:
parent
c4fc5d88f0
commit
511bcc9633
2 changed files with 165 additions and 36 deletions
|
@ -14,26 +14,102 @@ use std::{
|
|||
mem::needs_drop,
|
||||
};
|
||||
|
||||
/// A component is data associated with an [`Entity`](crate::entity::Entity). Each entity can have
|
||||
/// multiple different types of components, but only one of them per type.
|
||||
/// A data type that can be used to store data for an [entity].
|
||||
///
|
||||
/// Any type that is `Send + Sync + 'static` can implement `Component` using `#[derive(Component)]`.
|
||||
///
|
||||
/// In order to use foreign types as components, wrap them using a newtype pattern.
|
||||
/// `Component` is a [derivable trait]: this means that a data type can implement it by applying a `#[derive(Component)]` attribute to it.
|
||||
/// However, components must always satisfy the `Send + Sync + 'static` trait bounds.
|
||||
///
|
||||
/// [entity]: crate::entity
|
||||
/// [derivable trait]: https://doc.rust-lang.org/book/appendix-03-derivable-traits.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Components can take many forms: they are usually structs, but can also be of every other kind of data type, like enums or zero sized types.
|
||||
/// The following examples show how components are laid out in code.
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::component::Component;
|
||||
/// # struct Color;
|
||||
/// #
|
||||
/// // A component can contain data...
|
||||
/// #[derive(Component)]
|
||||
/// struct LicensePlate(String);
|
||||
///
|
||||
/// // ... but it can also be a zero-sized marker.
|
||||
/// #[derive(Component)]
|
||||
/// struct Car;
|
||||
///
|
||||
/// // Components can also be structs with named fields...
|
||||
/// #[derive(Component)]
|
||||
/// struct VehiclePerformance {
|
||||
/// acceleration: f32,
|
||||
/// top_speed: f32,
|
||||
/// handling: f32,
|
||||
/// }
|
||||
///
|
||||
/// // ... or enums.
|
||||
/// #[derive(Component)]
|
||||
/// enum WheelCount {
|
||||
/// Two,
|
||||
/// Three,
|
||||
/// Four,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Component and data access
|
||||
///
|
||||
/// See the [`entity`] module level documentation to learn how to add or remove components from an entity.
|
||||
///
|
||||
/// See the documentation for [`Query`] to learn how to access component data from a system.
|
||||
///
|
||||
/// [`entity`]: crate::entity#usage
|
||||
/// [`Query`]: crate::system::Query
|
||||
///
|
||||
/// # Choosing a storage type
|
||||
///
|
||||
/// Components can be stored in the world using different strategies with their own performance implications.
|
||||
/// By default, components are added to the [`Table`] storage, which is optimized for query iteration.
|
||||
///
|
||||
/// Alternatively, components can be added to the [`SparseSet`] storage, which is optimized for component insertion and removal.
|
||||
/// This is achieved by adding an additional `#[component(storage = "SparseSet")]` attribute to the derive one:
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::component::Component;
|
||||
/// #
|
||||
/// #[derive(Component)]
|
||||
/// #[component(storage = "SparseSet")]
|
||||
/// struct ComponentA;
|
||||
/// ```
|
||||
///
|
||||
/// [`Table`]: crate::storage::Table
|
||||
/// [`SparseSet`]: crate::storage::SparseSet
|
||||
///
|
||||
/// # Implementing the trait for foreign types
|
||||
///
|
||||
/// As a consequence of the [orphan rule], it is not possible to separate into two different crates the implementation of `Component` from the definition of a type.
|
||||
/// This means that it is not possible to directly have a type defined in a third party library as a component.
|
||||
/// This important limitation can be easily worked around using the [newtype pattern]:
|
||||
/// this makes it possible to locally define and implement `Component` for a tuple struct that wraps the foreign type.
|
||||
/// The following example gives a demonstration of this pattern.
|
||||
///
|
||||
/// ```
|
||||
/// // `Component` is defined in the `bevy_ecs` crate.
|
||||
/// use bevy_ecs::component::Component;
|
||||
///
|
||||
/// // `Duration` is defined in the `std` crate.
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// // It is not possible to implement `Component` for `Duration` from this position, as they are
|
||||
/// // both foreign items, defined in an external crate. However, nothing prevents to define a new
|
||||
/// // `Cooldown` type that wraps `Duration`. As `Cooldown` is defined in a local crate, it is
|
||||
/// // possible to implement `Component` for it.
|
||||
/// #[derive(Component)]
|
||||
/// struct Cooldown(Duration);
|
||||
/// ```
|
||||
/// Components are added with new entities using [`Commands::spawn`](crate::system::Commands::spawn),
|
||||
/// or to existing entities with [`EntityCommands::insert`](crate::system::EntityCommands::insert),
|
||||
/// or their [`World`](crate::world::World) equivalents.
|
||||
///
|
||||
/// Components can be accessed in systems by using a [`Query`](crate::system::Query)
|
||||
/// as one of the arguments.
|
||||
///
|
||||
/// Components can be grouped together into a [`Bundle`](crate::bundle::Bundle).
|
||||
/// [orphan rule]: https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type
|
||||
/// [newtype pattern]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types
|
||||
pub trait Component: Send + Sync + 'static {
|
||||
type Storage: ComponentStorage;
|
||||
}
|
||||
|
|
|
@ -1,27 +1,38 @@
|
|||
//! Entity handling types.
|
||||
//!
|
||||
//! In Bevy ECS, there is no monolithic data structure for an entity. Instead, the [`Entity`]
|
||||
//! `struct` is just a *generational index* (a combination of an ID and a generation). Then,
|
||||
//! the `Entity` maps to the specific [`Component`s](crate::component::Component). This way,
|
||||
//! entities can have meaningful data attached to it. This is a fundamental design choice
|
||||
//! that has been taken to enhance performance and usability.
|
||||
//! An **entity** exclusively owns zero or more [component] instances, all of different types, and can dynamically acquire or lose them over its lifetime.
|
||||
//!
|
||||
//! See [`Entity`] to learn more.
|
||||
//!
|
||||
//! [component]: crate::component::Component
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! Here are links to the methods used to perform common operations
|
||||
//! involving entities:
|
||||
//! Operations involving entities and their components are performed either from a system by submitting commands,
|
||||
//! or from the outside (or from an exclusive system) by directly using [`World`] methods:
|
||||
//!
|
||||
//! - **Spawning an empty entity:** use [`Commands::spawn`](crate::system::Commands::spawn).
|
||||
//! - **Spawning an entity with components:** use
|
||||
//! [`Commands::spawn_bundle`](crate::system::Commands::spawn_bundle).
|
||||
//! - **Despawning an entity:** use
|
||||
//! [`EntityCommands::despawn`](crate::system::EntityCommands::despawn).
|
||||
//! - **Inserting a component to an entity:** use
|
||||
//! [`EntityCommands::insert`](crate::system::EntityCommands::insert).
|
||||
//! - **Adding multiple components to an entity:** use
|
||||
//! [`EntityCommands::insert_bundle`](crate::system::EntityCommands::insert_bundle).
|
||||
//! - **Removing a component to an entity:** use
|
||||
//! [`EntityCommands::remove`](crate::system::EntityCommands::remove).
|
||||
//! |Operation|Command|Method|
|
||||
//! |:---:|:---:|:---:|
|
||||
//! |Spawn a new entity|[`Commands::spawn`]|[`World::spawn`]|
|
||||
//! |Spawn an entity with components|[`Commands::spawn_bundle`]|---|
|
||||
//! |Despawn an entity|[`EntityCommands::despawn`]|[`World::despawn`]|
|
||||
//! |Insert a component to an entity|[`EntityCommands::insert`]|[`EntityMut::insert`]|
|
||||
//! |Insert multiple components to an entity|[`EntityCommands::insert_bundle`]|[`EntityMut::insert_bundle`]|
|
||||
//! |Remove a component from an entity|[`EntityCommands::remove`]|[`EntityMut::remove`]|
|
||||
//!
|
||||
//! [`World`]: crate::world::World
|
||||
//! [`Commands::spawn`]: crate::system::Commands::spawn
|
||||
//! [`Commands::spawn_bundle`]: crate::system::Commands::spawn_bundle
|
||||
//! [`EntityCommands::despawn`]: crate::system::EntityCommands::despawn
|
||||
//! [`EntityCommands::insert`]: crate::system::EntityCommands::insert
|
||||
//! [`EntityCommands::insert_bundle`]: crate::system::EntityCommands::insert_bundle
|
||||
//! [`EntityCommands::remove`]: crate::system::EntityCommands::remove
|
||||
//! [`World::spawn`]: crate::world::World::spawn
|
||||
//! [`World::spawn_bundle`]: crate::world::World::spawn_bundle
|
||||
//! [`World::despawn`]: crate::world::World::despawn
|
||||
//! [`EntityMut::insert`]: crate::world::EntityMut::insert
|
||||
//! [`EntityMut::insert_bundle`]: crate::world::EntityMut::insert_bundle
|
||||
//! [`EntityMut::remove`]: crate::world::EntityMut::remove
|
||||
mod map_entities;
|
||||
mod serde;
|
||||
|
||||
|
@ -35,15 +46,57 @@ use std::{
|
|||
sync::atomic::{AtomicI64, Ordering},
|
||||
};
|
||||
|
||||
/// Lightweight unique ID of an entity.
|
||||
/// Lightweight identifier of an [entity](crate::entity).
|
||||
///
|
||||
/// Obtained from [`World::spawn`](crate::world::World::spawn), typically via
|
||||
/// [`Commands::spawn`](crate::system::Commands::spawn). Can be stored to refer to an entity in the
|
||||
/// future.
|
||||
/// The identifier is implemented using a [generational index]: a combination of an ID and a generation.
|
||||
/// This allows fast insertion after data removal in an array while minimizing loss of spatial locality.
|
||||
///
|
||||
/// `Entity` can be a part of a query, e.g. `Query<(Entity, &MyComponent)>`.
|
||||
/// Components of a specific entity can be accessed using
|
||||
/// [`Query::get`](crate::system::Query::get) and related methods.
|
||||
/// [generational index]: https://lucassardois.medium.com/generational-indices-guide-8e3c5f7fd594
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// This data type is returned by iterating a `Query` that has `Entity` as part of its query fetch type parameter ([learn more]).
|
||||
/// It can also be obtained by calling [`EntityCommands::id`] or [`EntityMut::id`].
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// #
|
||||
/// fn setup(mut commands: Commands) {
|
||||
/// // Calling `spawn` returns `EntityCommands`.
|
||||
/// let entity = commands.spawn().id();
|
||||
/// }
|
||||
///
|
||||
/// fn exclusive_system(world: &mut World) {
|
||||
/// // Calling `spawn` returns `EntityMut`.
|
||||
/// let entity = world.spawn().id();
|
||||
/// }
|
||||
/// #
|
||||
/// # bevy_ecs::system::assert_is_system(setup);
|
||||
/// # bevy_ecs::system::IntoExclusiveSystem::exclusive_system(exclusive_system);
|
||||
/// ```
|
||||
///
|
||||
/// It can be used to refer to a specific entity to apply [`EntityCommands`], or to call [`Query::get`] (or similar methods) to access its components.
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// #
|
||||
/// # #[derive(Component)]
|
||||
/// # struct Expired;
|
||||
/// #
|
||||
/// fn dispose_expired_food(mut commands: Commands, query: Query<Entity, With<Expired>>) {
|
||||
/// for food_entity in query.iter() {
|
||||
/// commands.entity(food_entity).despawn();
|
||||
/// }
|
||||
/// }
|
||||
/// #
|
||||
/// # bevy_ecs::system::assert_is_system(dispose_expired_food);
|
||||
/// ```
|
||||
///
|
||||
/// [learn more]: crate::system::Query#entity-id-access
|
||||
/// [`EntityCommands::id`]: crate::system::EntityCommands::id
|
||||
/// [`EntityMut::id`]: crate::world::EntityMut::id
|
||||
/// [`EntityCommands`]: crate::system::EntityCommands
|
||||
/// [`Query::get`]: crate::system::Query::get
|
||||
#[derive(Clone, Copy, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Entity {
|
||||
pub(crate) generation: u32,
|
||||
|
|
Loading…
Add table
Reference in a new issue