Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document part of bevy_ecs::Commands #976

Merged
merged 6 commits into from
Dec 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/bevy_ecs/src/core/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ use std::{
};

/// A dynamically typed collection of components
///
/// See [Bundle]
pub trait DynamicBundle {
/// Invoke a callback on the fields' type IDs, sorted by descending alignment then id
#[doc(hidden)]
Expand All @@ -38,6 +40,8 @@ pub trait DynamicBundle {
}

/// A statically typed collection of components
///
/// See [DynamicBundle]
pub trait Bundle: DynamicBundle {
#[doc(hidden)]
fn with_static_ids<T>(f: impl FnOnce(&[TypeId]) -> T) -> T;
Expand Down
16 changes: 8 additions & 8 deletions crates/bevy_ecs/src/core/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,16 @@ impl World {
/// let a = world.spawn((123, "abc"));
/// let b = world.spawn((456, true));
/// ```
pub fn spawn(&mut self, components: impl DynamicBundle) -> Entity {
pub fn spawn(&mut self, bundle: impl DynamicBundle) -> Entity {
// Ensure all entity allocations are accounted for so `self.entities` can realloc if
// necessary
self.flush();

let entity = self.entities.alloc();
let archetype_id = components.with_ids(|ids| {
let archetype_id = bundle.with_ids(|ids| {
self.index.get(ids).copied().unwrap_or_else(|| {
let x = self.archetypes.len() as u32;
self.archetypes.push(Archetype::new(components.type_info()));
self.archetypes.push(Archetype::new(bundle.type_info()));
self.index.insert(ids.to_vec(), x);
self.archetype_generation += 1;
x
Expand All @@ -95,7 +95,7 @@ impl World {
let archetype = &mut self.archetypes[archetype_id as usize];
unsafe {
let index = archetype.allocate(entity);
components.put(|ptr, ty, size| {
bundle.put(|ptr, ty, size| {
archetype.put_dynamic(ptr, ty, size, index, ComponentFlags::ADDED);
true
});
Expand Down Expand Up @@ -566,7 +566,7 @@ impl World {
pub fn insert(
&mut self,
entity: Entity,
components: impl DynamicBundle,
bundle: impl DynamicBundle,
) -> Result<(), NoSuchEntity> {
use std::collections::hash_map::Entry;

Expand All @@ -576,7 +576,7 @@ impl World {
// Assemble Vec<TypeInfo> for the final entity
let arch = &mut self.archetypes[loc.archetype as usize];
let mut info = arch.types().to_vec();
for ty in components.type_info() {
for ty in bundle.type_info() {
if let Some(ptr) = arch.get_dynamic(ty.id(), ty.layout().size(), loc.index) {
ty.drop(ptr.as_ptr());
} else {
Expand All @@ -601,7 +601,7 @@ impl World {
if target == loc.archetype {
// Update components in the current archetype
let arch = &mut self.archetypes[loc.archetype as usize];
components.put(|ptr, ty, size| {
bundle.put(|ptr, ty, size| {
arch.put_dynamic(ptr, ty, size, loc.index, ComponentFlags::MUTATED);
true
});
Expand All @@ -625,7 +625,7 @@ impl World {
self.entities.get_mut(moved).unwrap().index = old_index;
}

components.put(|ptr, ty, size| {
bundle.put(|ptr, ty, size| {
let had_component = source_arch.has_dynamic(ty);
let flags = if had_component {
ComponentFlags::MUTATED
Expand Down
126 changes: 106 additions & 20 deletions crates/bevy_ecs/src/system/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ pub(crate) struct Spawn<T>
where
T: DynamicBundle + Send + Sync + 'static,
{
components: T,
bundle: T,
}

impl<T> Command for Spawn<T>
where
T: DynamicBundle + Send + Sync + 'static,
{
fn write(self: Box<Self>, world: &mut World, _resources: &mut Resources) {
world.spawn(self.components);
world.spawn(self.bundle);
}
}

Expand All @@ -33,7 +33,7 @@ where
I: IntoIterator,
I::Item: Bundle,
{
components_iter: I,
bundles_iter: I,
}

impl<I> Command for SpawnBatch<I>
Expand All @@ -42,7 +42,7 @@ where
I::Item: Bundle,
{
fn write(self: Box<Self>, world: &mut World, _resources: &mut Resources) {
world.spawn_batch(self.components_iter);
world.spawn_batch(self.bundles_iter);
}
}

Expand All @@ -64,15 +64,15 @@ where
T: DynamicBundle + Send + Sync + 'static,
{
entity: Entity,
components: T,
bundle: T,
}

impl<T> Command for Insert<T>
where
T: DynamicBundle + Send + Sync + 'static,
{
fn write(self: Box<Self>, world: &mut World, _resources: &mut Resources) {
world.insert(self.entity, self.components).unwrap();
world.insert(self.entity, self.bundle).unwrap();
}
}

Expand Down Expand Up @@ -181,6 +181,7 @@ impl<T: Resource> Command for InsertLocalResource<T> {
}
}

/// A list of commands that will be run to populate a `World` and `Resources`.
#[derive(Default)]
pub struct Commands {
commands: Vec<Box<dyn Command>>,
Expand All @@ -189,38 +190,78 @@ pub struct Commands {
}

impl Commands {
pub fn spawn(&mut self, components: impl DynamicBundle + Send + Sync + 'static) -> &mut Self {
/// Creates a new entity with the components contained in `bundle`.
///
/// Note that `bundle` is a [DynamicBundle], which is a collection of components. [DynamicBundle] is automatically implemented for tuples of components. You can also create your own bundle types by deriving [`derive@Bundle`]. If you would like to spawn an entity with a single component, consider wrapping the component in a tuple (which [DynamicBundle] is implemented for).
///
/// See [`Self::set_current_entity`], [`Self::insert`].
///
/// # Example
///
/// ```
/// use bevy_ecs::prelude::*;
///
/// struct Component1;
/// struct Component2;
///
/// #[derive(Bundle)]
/// struct ExampleBundle {
/// a: Component1,
/// b: Component2,
/// }
///
/// fn example_system(mut commands: Commands) {
/// // Create a new entity with a component bundle.
/// commands.spawn(ExampleBundle {
/// a: Component1,
/// b: Component2,
/// });
///
/// // Create a new entity with a single component.
/// commands.spawn((Component1,));
/// // Create a new entity with two components.
/// commands.spawn((Component1, Component2));
/// }
/// ```
pub fn spawn(&mut self, bundle: impl DynamicBundle + Send + Sync + 'static) -> &mut Self {
let entity = self
.entity_reserver
.as_ref()
.expect("entity reserver has not been set")
.reserve_entity();
self.current_entity = Some(entity);
self.commands.push(Box::new(Insert { entity, components }));
self.set_current_entity(entity);
self.insert(entity, bundle);
self
}

pub fn spawn_batch<I>(&mut self, components_iter: I) -> &mut Self
/// Equivalent to iterating `bundles_iter` and calling [`Self::spawn`] on each bundle, but slightly more performant.
pub fn spawn_batch<I>(&mut self, bundles_iter: I) -> &mut Self
where
I: IntoIterator + Send + Sync + 'static,
I::Item: Bundle,
{
self.add_command(SpawnBatch { components_iter })
self.add_command(SpawnBatch { bundles_iter })
}

/// Despawns only the specified entity, ignoring any other consideration.
/// Despawns only the specified entity, not including its children.
pub fn despawn(&mut self, entity: Entity) -> &mut Self {
self.add_command(Despawn { entity })
}

/// Inserts a bundle of components into `entity`.
///
/// See [`World::insert`].
pub fn insert(
&mut self,
entity: Entity,
components: impl DynamicBundle + Send + Sync + 'static,
bundle: impl DynamicBundle + Send + Sync + 'static,
) -> &mut Self {
self.add_command(Insert { entity, components })
self.add_command(Insert { entity, bundle })
}

/// Inserts a single component into `entity`.
///
/// See [`World::insert_one`].
pub fn insert_one(&mut self, entity: Entity, component: impl Component) -> &mut Self {
self.add_command(InsertOne { entity, component })
}
Expand All @@ -229,6 +270,9 @@ impl Commands {
self.add_command(InsertResource { resource })
}

/// Insert a resource that is local to a specific system.
///
/// See [`crate::System::id`].
pub fn insert_local_resource<T: Resource>(
&mut self,
system_id: SystemId,
Expand All @@ -240,6 +284,7 @@ impl Commands {
})
}

/// See [`World::remove_one`].
pub fn remove_one<T>(&mut self, entity: Entity) -> &mut Self
where
T: Component,
Expand All @@ -250,6 +295,7 @@ impl Commands {
})
}

/// See [`World::remove`].
pub fn remove<T>(&mut self, entity: Entity) -> &mut Self
where
T: Bundle + Send + Sync + 'static,
Expand All @@ -260,18 +306,54 @@ impl Commands {
})
}

pub fn with_bundle(
&mut self,
components: impl DynamicBundle + Send + Sync + 'static,
) -> &mut Self {
let current_entity = self.current_entity.expect("Cannot add components because the 'current entity' is not set. You should spawn an entity first.");
/// Adds a bundle of components to the current entity.
///
/// See [`Self::with`], [`Self::current_entity`].
pub fn with_bundle(&mut self, bundle: impl DynamicBundle + Send + Sync + 'static) -> &mut Self {
let current_entity = self.current_entity.expect("Cannot add bundle because the 'current entity' is not set. You should spawn an entity first.");
self.commands.push(Box::new(Insert {
entity: current_entity,
components,
bundle,
}));
self
}

/// Adds a single component to the current entity.
///
/// See [`Self::with_bundle`], [`Self::current_entity`].
///
/// # Warning
///
/// It's possible to call this with a bundle, but this is likely not intended and [`Self::with_bundle`] should be used instead. If `with` is called with a bundle, the bundle itself will be added as a component instead of the bundles' inner components each being added.
///
/// # Example
///
/// `with` can be chained with [`Self::spawn`].
///
/// ```
/// use bevy_ecs::prelude::*;
///
/// struct Component1;
/// struct Component2;
///
/// fn example_system(mut commands: Commands) {
/// // Create a new entity with a `Component1` and `Component2`.
/// commands.spawn((Component1,)).with(Component2);
///
/// // Psst! These are also equivalent to the line above!
/// commands.spawn((Component1, Component2));
/// commands.spawn(()).with(Component1).with(Component2);
/// #[derive(Bundle)]
/// struct ExampleBundle {
/// a: Component1,
/// b: Component2,
/// }
/// commands.spawn(()).with_bundle(ExampleBundle {
/// a: Component1,
/// b: Component2,
/// });
/// }
/// ```
pub fn with(&mut self, component: impl Component) -> &mut Self {
let current_entity = self.current_entity.expect("Cannot add component because the 'current entity' is not set. You should spawn an entity first.");
self.commands.push(Box::new(InsertOne {
Expand All @@ -281,22 +363,26 @@ impl Commands {
self
}

/// Adds a command directly to the command list. Prefer this to [`Self::add_command_boxed`] if the type of `command` is statically known.
pub fn add_command<C: Command + 'static>(&mut self, command: C) -> &mut Self {
self.commands.push(Box::new(command));
self
}

/// See [`Self::add_command`].
pub fn add_command_boxed(&mut self, command: Box<dyn Command>) -> &mut Self {
self.commands.push(command);
self
}

/// Runs all the stored commands on `world` and `resources`. The command buffer is emptied as a part of this call.
pub fn apply(&mut self, world: &mut World, resources: &mut Resources) {
for command in self.commands.drain(..) {
command.write(world, resources);
}
}

/// Returns the current entity, set by [`Self::spawn`] or with [`Self::set_current_entity`].
pub fn current_entity(&self) -> Option<Entity> {
self.current_entity
}
Expand Down