Skip to content

Commit

Permalink
Improve entity and component API docs (bevyengine#4767)
Browse files Browse the repository at this point in the history
# 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]: bevyengine#4767 (comment)
[Resolved]: bevyengine#4767 (comment)
  • Loading branch information
Nilirad authored and james7132 committed Jun 22, 2022
1 parent 2381ba2 commit d025d03
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 36 deletions.
98 changes: 87 additions & 11 deletions crates/bevy_ecs/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
103 changes: 78 additions & 25 deletions crates/bevy_ecs/src/entity/mod.rs
Original file line number Diff line number Diff line change
@@ -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:
//!
//! |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`]|
//!
//! - **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).
//! [`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;

Expand All @@ -35,15 +46,57 @@ use std::{
sync::atomic::{AtomicI64, Ordering},
};

/// Lightweight unique ID of an entity.
/// Lightweight identifier of an [entity](crate::entity).
///
/// 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.
///
/// [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.
///
/// 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.
/// ```
/// # 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);
/// ```
///
/// `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.
/// [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,
Expand Down

0 comments on commit d025d03

Please sign in to comment.