Implement and require #[derive(Component)] on all component structs (#2254)

This implements the most minimal variant of #1843 - a derive for marker trait. This is a prerequisite to more complicated features like statically defined storage type or opt-out component reflection.

In order to make component struct's purpose explicit and avoid misuse, it must be annotated with `#[derive(Component)]` (manual impl is discouraged for compatibility). Right now this is just a marker trait, but in the future it might be expanded. Making this change early allows us to make further changes later without breaking backward compatibility for derive macro users.

This already prevents a lot of issues, like using bundles in `insert` calls. Primitive types are no longer valid components as well. This can be easily worked around by adding newtype wrappers and deriving `Component` for them.

One funny example of prevented bad code (from our own tests) is when an newtype struct or enum variant is used. Previously, it was possible to write `insert(Newtype)` instead of `insert(Newtype(value))`. That code compiled, because function pointers (in this case newtype struct constructor) implement `Send + Sync + 'static`, so we allowed them to be used as components. This is no longer the case and such invalid code will trigger a compile error.


Co-authored-by: = <=>
Co-authored-by: TheRawMeatball <therawmeatball@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
Paweł Grabarz 2021-10-03 19:23:44 +00:00
parent 5ba2b9adcf
commit 07ed1d053e
91 changed files with 1529 additions and 1082 deletions

View file

@ -46,7 +46,7 @@ bevy_audio = ["bevy_internal/bevy_audio"]
bevy_dynamic_plugin = ["bevy_internal/bevy_dynamic_plugin"] bevy_dynamic_plugin = ["bevy_internal/bevy_dynamic_plugin"]
bevy_gilrs = ["bevy_internal/bevy_gilrs"] bevy_gilrs = ["bevy_internal/bevy_gilrs"]
bevy_gltf = ["bevy_internal/bevy_gltf"] bevy_gltf = ["bevy_internal/bevy_gltf"]
bevy_wgpu = ["bevy_internal/bevy_wgpu"] bevy_wgpu = ["bevy_internal/bevy_wgpu"]
bevy_winit = ["bevy_internal/bevy_winit"] bevy_winit = ["bevy_internal/bevy_winit"]
trace_chrome = ["bevy_internal/trace_chrome"] trace_chrome = ["bevy_internal/trace_chrome"]

View file

@ -1,4 +1,5 @@
use bevy::ecs::{ use bevy::ecs::{
component::Component,
entity::Entity, entity::Entity,
system::{Command, CommandQueue, Commands}, system::{Command, CommandQueue, Commands},
world::World, world::World,
@ -18,8 +19,11 @@ criterion_group!(
); );
criterion_main!(benches); criterion_main!(benches);
#[derive(Component)]
struct A; struct A;
#[derive(Component)]
struct B; struct B;
#[derive(Component)]
struct C; struct C;
fn empty_commands(criterion: &mut Criterion) { fn empty_commands(criterion: &mut Criterion) {
@ -79,10 +83,10 @@ fn spawn_commands(criterion: &mut Criterion) {
group.finish(); group.finish();
} }
#[derive(Default)] #[derive(Default, Component)]
struct Matrix([[f32; 4]; 4]); struct Matrix([[f32; 4]; 4]);
#[derive(Default)] #[derive(Default, Component)]
struct Vec3([f32; 3]); struct Vec3([f32; 3]);
fn insert_commands(criterion: &mut Criterion) { fn insert_commands(criterion: &mut Criterion) {
@ -95,14 +99,16 @@ fn insert_commands(criterion: &mut Criterion) {
let mut world = World::default(); let mut world = World::default();
let mut command_queue = CommandQueue::default(); let mut command_queue = CommandQueue::default();
let mut entities = Vec::new(); let mut entities = Vec::new();
for i in 0..entity_count { for _ in 0..entity_count {
entities.push(world.spawn().id()); entities.push(world.spawn().id());
} }
bencher.iter(|| { bencher.iter(|| {
let mut commands = Commands::new(&mut command_queue, &world); let mut commands = Commands::new(&mut command_queue, &world);
for entity in entities.iter() { for entity in entities.iter() {
commands.entity(*entity).insert_bundle((Matrix::default(), Vec3::default())); commands
.entity(*entity)
.insert_bundle((Matrix::default(), Vec3::default()));
} }
drop(commands); drop(commands);
command_queue.apply(&mut world); command_queue.apply(&mut world);
@ -112,7 +118,7 @@ fn insert_commands(criterion: &mut Criterion) {
let mut world = World::default(); let mut world = World::default();
let mut command_queue = CommandQueue::default(); let mut command_queue = CommandQueue::default();
let mut entities = Vec::new(); let mut entities = Vec::new();
for i in 0..entity_count { for _ in 0..entity_count {
entities.push(world.spawn().id()); entities.push(world.spawn().id());
} }

View file

@ -1,6 +1,7 @@
use bevy::ecs::{ use bevy::ecs::{
component::Component,
schedule::{Stage, SystemStage}, schedule::{Stage, SystemStage},
system::{IntoSystem, Query}, system::Query,
world::World, world::World,
}; };
use criterion::{criterion_group, criterion_main, Criterion}; use criterion::{criterion_group, criterion_main, Criterion};
@ -12,10 +13,15 @@ fn run_stage(stage: &mut SystemStage, world: &mut World) {
stage.run(world); stage.run(world);
} }
#[derive(Component)]
struct A(f32); struct A(f32);
#[derive(Component)]
struct B(f32); struct B(f32);
#[derive(Component)]
struct C(f32); struct C(f32);
#[derive(Component)]
struct D(f32); struct D(f32);
#[derive(Component)]
struct E(f32); struct E(f32);
const ENTITY_BUNCH: usize = 5000; const ENTITY_BUNCH: usize = 5000;

View file

@ -1,8 +1,4 @@
use bevy::ecs::{ use bevy::ecs::{component::Component, entity::Entity, world::World};
component::{ComponentDescriptor, StorageType},
entity::Entity,
world::World,
};
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
criterion_group!( criterion_group!(
@ -15,16 +11,18 @@ criterion_group!(
); );
criterion_main!(benches); criterion_main!(benches);
struct A(f32); #[derive(Component, Default)]
#[component(storage = "Table")]
struct Table(f32);
#[derive(Component, Default)]
#[component(storage = "SparseSet")]
struct Sparse(f32);
const RANGE: std::ops::Range<u32> = 5..6; const RANGE: std::ops::Range<u32> = 5..6;
fn setup(entity_count: u32, storage: StorageType) -> World { fn setup<T: Component + Default>(entity_count: u32) -> World {
let mut world = World::default(); let mut world = World::default();
world world.spawn_batch((0..entity_count).map(|_| (T::default(),)));
.register_component(ComponentDescriptor::new::<A>(storage))
.unwrap();
world.spawn_batch((0..entity_count).map(|_| (A(0.0),)));
world world
} }
@ -35,7 +33,7 @@ fn world_entity(criterion: &mut Criterion) {
for entity_count in RANGE.map(|i| i * 10_000) { for entity_count in RANGE.map(|i| i * 10_000) {
group.bench_function(format!("{}_entities", entity_count), |bencher| { group.bench_function(format!("{}_entities", entity_count), |bencher| {
let world = setup(entity_count, StorageType::Table); let world = setup::<Table>(entity_count);
bencher.iter(|| { bencher.iter(|| {
for i in 0..entity_count { for i in 0..entity_count {
@ -55,21 +53,26 @@ fn world_get(criterion: &mut Criterion) {
group.measurement_time(std::time::Duration::from_secs(4)); group.measurement_time(std::time::Duration::from_secs(4));
for entity_count in RANGE.map(|i| i * 10_000) { for entity_count in RANGE.map(|i| i * 10_000) {
for storage in [StorageType::Table, StorageType::SparseSet] { group.bench_function(format!("{}_entities_table", entity_count), |bencher| {
group.bench_function( let world = setup::<Table>(entity_count);
format!("{}_entities_{:?}", entity_count, storage),
|bencher| {
let world = setup(entity_count, storage);
bencher.iter(|| { bencher.iter(|| {
for i in 0..entity_count { for i in 0..entity_count {
let entity = Entity::new(i); let entity = Entity::new(i);
assert!(world.get::<A>(entity).is_some()); assert!(world.get::<Table>(entity).is_some());
} }
}); });
}, });
); group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| {
} let world = setup::<Sparse>(entity_count);
bencher.iter(|| {
for i in 0..entity_count {
let entity = Entity::new(i);
assert!(world.get::<Sparse>(entity).is_some());
}
});
});
} }
group.finish(); group.finish();
@ -81,22 +84,28 @@ fn world_query_get(criterion: &mut Criterion) {
group.measurement_time(std::time::Duration::from_secs(4)); group.measurement_time(std::time::Duration::from_secs(4));
for entity_count in RANGE.map(|i| i * 10_000) { for entity_count in RANGE.map(|i| i * 10_000) {
for storage in [StorageType::Table, StorageType::SparseSet] { group.bench_function(format!("{}_entities_table", entity_count), |bencher| {
group.bench_function( let mut world = setup::<Table>(entity_count);
format!("{}_entities_{:?}", entity_count, storage), let mut query = world.query::<&Table>();
|bencher| {
let mut world = setup(entity_count, storage);
let mut query = world.query::<&A>();
bencher.iter(|| { bencher.iter(|| {
for i in 0..entity_count { for i in 0..entity_count {
let entity = Entity::new(i); let entity = Entity::new(i);
assert!(query.get(&world, entity).is_ok()); assert!(query.get(&world, entity).is_ok());
} }
}); });
}, });
); group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| {
} let mut world = setup::<Sparse>(entity_count);
let mut query = world.query::<&Sparse>();
bencher.iter(|| {
for i in 0..entity_count {
let entity = Entity::new(i);
assert!(query.get(&world, entity).is_ok());
}
});
});
} }
group.finish(); group.finish();
@ -108,24 +117,32 @@ fn world_query_iter(criterion: &mut Criterion) {
group.measurement_time(std::time::Duration::from_secs(4)); group.measurement_time(std::time::Duration::from_secs(4));
for entity_count in RANGE.map(|i| i * 10_000) { for entity_count in RANGE.map(|i| i * 10_000) {
for storage in [StorageType::Table, StorageType::SparseSet] { group.bench_function(format!("{}_entities_table", entity_count), |bencher| {
group.bench_function( let mut world = setup::<Table>(entity_count);
format!("{}_entities_{:?}", entity_count, storage), let mut query = world.query::<&Table>();
|bencher| {
let mut world = setup(entity_count, storage);
let mut query = world.query::<&A>();
bencher.iter(|| { bencher.iter(|| {
let mut count = 0; let mut count = 0;
for comp in query.iter(&world) { for comp in query.iter(&world) {
black_box(comp); black_box(comp);
count += 1; count += 1;
} }
assert_eq!(black_box(count), entity_count); assert_eq!(black_box(count), entity_count);
}); });
}, });
); group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| {
} let mut world = setup::<Sparse>(entity_count);
let mut query = world.query::<&Sparse>();
bencher.iter(|| {
let mut count = 0;
for comp in query.iter(&world) {
black_box(comp);
count += 1;
}
assert_eq!(black_box(count), entity_count);
});
});
} }
group.finish(); group.finish();
@ -137,24 +154,32 @@ fn world_query_for_each(criterion: &mut Criterion) {
group.measurement_time(std::time::Duration::from_secs(4)); group.measurement_time(std::time::Duration::from_secs(4));
for entity_count in RANGE.map(|i| i * 10_000) { for entity_count in RANGE.map(|i| i * 10_000) {
for storage in [StorageType::Table, StorageType::SparseSet] { group.bench_function(format!("{}_entities_table", entity_count), |bencher| {
group.bench_function( let mut world = setup::<Table>(entity_count);
format!("{}_entities_{:?}", entity_count, storage), let mut query = world.query::<&Table>();
|bencher| {
let mut world = setup(entity_count, storage);
let mut query = world.query::<&A>();
bencher.iter(|| { bencher.iter(|| {
let mut count = 0; let mut count = 0;
query.for_each(&world, |comp| { query.for_each(&world, |comp| {
black_box(comp); black_box(comp);
count += 1; count += 1;
}); });
assert_eq!(black_box(count), entity_count); assert_eq!(black_box(count), entity_count);
}); });
}, });
); group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| {
} let mut world = setup::<Sparse>(entity_count);
let mut query = world.query::<&Sparse>();
bencher.iter(|| {
let mut count = 0;
query.for_each(&world, |comp| {
black_box(comp);
count += 1;
});
assert_eq!(black_box(count), entity_count);
});
});
} }
group.finish(); group.finish();

View file

@ -1,14 +1,15 @@
use crate::{CoreStage, Events, Plugin, PluginGroup, PluginGroupBuilder, StartupStage}; use crate::{CoreStage, Events, Plugin, PluginGroup, PluginGroupBuilder, StartupStage};
use bevy_ecs::{ use bevy_ecs::{
component::{Component, ComponentDescriptor},
prelude::{FromWorld, IntoExclusiveSystem}, prelude::{FromWorld, IntoExclusiveSystem},
schedule::{ schedule::{
IntoSystemDescriptor, RunOnce, Schedule, Stage, StageLabel, State, SystemSet, SystemStage, IntoSystemDescriptor, RunOnce, Schedule, Stage, StageLabel, State, StateData, SystemSet,
SystemStage,
}, },
system::Resource,
world::World, world::World,
}; };
use bevy_utils::tracing::debug; use bevy_utils::tracing::debug;
use std::{fmt::Debug, hash::Hash}; use std::fmt::Debug;
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
use bevy_utils::tracing::info_span; use bevy_utils::tracing::info_span;
@ -493,7 +494,7 @@ impl App {
/// adding [State::get_driver] to additional stages you need it in. /// adding [State::get_driver] to additional stages you need it in.
pub fn add_state<T>(&mut self, initial: T) -> &mut Self pub fn add_state<T>(&mut self, initial: T) -> &mut Self
where where
T: Component + Debug + Clone + Eq + Hash, T: StateData,
{ {
self.add_state_to_stage(CoreStage::Update, initial) self.add_state_to_stage(CoreStage::Update, initial)
} }
@ -505,7 +506,7 @@ impl App {
/// stages you need it in. /// stages you need it in.
pub fn add_state_to_stage<T>(&mut self, stage: impl StageLabel, initial: T) -> &mut Self pub fn add_state_to_stage<T>(&mut self, stage: impl StageLabel, initial: T) -> &mut Self
where where
T: Component + Debug + Clone + Eq + Hash, T: StateData,
{ {
self.insert_resource(State::new(initial)) self.insert_resource(State::new(initial))
.add_system_set_to_stage(stage, State::<T>::get_driver()) .add_system_set_to_stage(stage, State::<T>::get_driver())
@ -582,7 +583,7 @@ impl App {
/// ``` /// ```
pub fn add_event<T>(&mut self) -> &mut Self pub fn add_event<T>(&mut self) -> &mut Self
where where
T: Component, T: Resource,
{ {
self.init_resource::<Events<T>>() self.init_resource::<Events<T>>()
.add_system_to_stage(CoreStage::First, Events::<T>::update_system) .add_system_to_stage(CoreStage::First, Events::<T>::update_system)
@ -608,7 +609,7 @@ impl App {
/// ``` /// ```
pub fn insert_resource<T>(&mut self, resource: T) -> &mut Self pub fn insert_resource<T>(&mut self, resource: T) -> &mut Self
where where
T: Component, T: Resource,
{ {
self.world.insert_resource(resource); self.world.insert_resource(resource);
self self
@ -810,18 +811,6 @@ impl App {
self self
} }
/// Registers a new component using the given [ComponentDescriptor].
///
/// Components do not need to be manually registered. This just provides a way to
/// override default configuration. Attempting to register a component with a type
/// that has already been used by [World] will result in an error.
///
/// See [World::register_component]
pub fn register_component(&mut self, descriptor: ComponentDescriptor) -> &mut Self {
self.world.register_component(descriptor).unwrap();
self
}
/// Adds the type `T` to the type registry resource. /// Adds the type `T` to the type registry resource.
#[cfg(feature = "bevy_reflect")] #[cfg(feature = "bevy_reflect")]
pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self { pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {

View file

@ -9,7 +9,7 @@ use crate::{
path::{AssetPath, AssetPathId}, path::{AssetPath, AssetPathId},
Asset, Assets, Asset, Assets,
}; };
use bevy_ecs::reflect::ReflectComponent; use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_reflect::{Reflect, ReflectDeserialize}; use bevy_reflect::{Reflect, ReflectDeserialize};
use bevy_utils::Uuid; use bevy_utils::Uuid;
use crossbeam_channel::{Receiver, Sender}; use crossbeam_channel::{Receiver, Sender};
@ -58,7 +58,7 @@ impl HandleId {
/// ///
/// Handles contain a unique id that corresponds to a specific asset in the [Assets](crate::Assets) /// Handles contain a unique id that corresponds to a specific asset in the [Assets](crate::Assets)
/// collection. /// collection.
#[derive(Reflect)] #[derive(Component, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct Handle<T> pub struct Handle<T>
where where

View file

@ -3,10 +3,7 @@ use crate::{
RefChangeChannel, RefChangeChannel,
}; };
use anyhow::Result; use anyhow::Result;
use bevy_ecs::{ use bevy_ecs::system::{Res, ResMut};
component::Component,
system::{Res, ResMut},
};
use bevy_reflect::{TypeUuid, TypeUuidDynamic}; use bevy_reflect::{TypeUuid, TypeUuidDynamic};
use bevy_tasks::TaskPool; use bevy_tasks::TaskPool;
use bevy_utils::{BoxedFuture, HashMap}; use bevy_utils::{BoxedFuture, HashMap};
@ -146,7 +143,7 @@ impl<'a> LoadContext<'a> {
/// The result of loading an asset of type `T` /// The result of loading an asset of type `T`
#[derive(Debug)] #[derive(Debug)]
pub struct AssetResult<T: Component> { pub struct AssetResult<T> {
pub asset: Box<T>, pub asset: Box<T>,
pub id: HandleId, pub id: HandleId,
pub version: usize, pub version: usize,
@ -154,12 +151,12 @@ pub struct AssetResult<T: Component> {
/// A channel to send and receive [AssetResult]s /// A channel to send and receive [AssetResult]s
#[derive(Debug)] #[derive(Debug)]
pub struct AssetLifecycleChannel<T: Component> { pub struct AssetLifecycleChannel<T> {
pub sender: Sender<AssetLifecycleEvent<T>>, pub sender: Sender<AssetLifecycleEvent<T>>,
pub receiver: Receiver<AssetLifecycleEvent<T>>, pub receiver: Receiver<AssetLifecycleEvent<T>>,
} }
pub enum AssetLifecycleEvent<T: Component> { pub enum AssetLifecycleEvent<T> {
Create(AssetResult<T>), Create(AssetResult<T>),
Free(HandleId), Free(HandleId),
} }
@ -193,7 +190,7 @@ impl<T: AssetDynamic> AssetLifecycle for AssetLifecycleChannel<T> {
} }
} }
impl<T: Component> Default for AssetLifecycleChannel<T> { impl<T> Default for AssetLifecycleChannel<T> {
fn default() -> Self { fn default() -> Self {
let (sender, receiver) = crossbeam_channel::unbounded(); let (sender, receiver) = crossbeam_channel::unbounded();
AssetLifecycleChannel { sender, receiver } AssetLifecycleChannel { sender, receiver }

View file

@ -1,4 +1,5 @@
use bevy_ecs::{ use bevy_ecs::{
component::Component,
entity::Entity, entity::Entity,
query::Changed, query::Changed,
reflect::ReflectComponent, reflect::ReflectComponent,
@ -13,7 +14,7 @@ use std::{
}; };
/// A collection of labels /// A collection of labels
#[derive(Default, Reflect)] #[derive(Component, Default, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct Labels { pub struct Labels {
labels: HashSet<Cow<'static, str>>, labels: HashSet<Cow<'static, str>>,

View file

@ -1,4 +1,4 @@
use bevy_ecs::reflect::ReflectComponent; use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_utils::AHasher; use bevy_utils::AHasher;
use std::{ use std::{
@ -8,7 +8,7 @@ use std::{
}; };
/// Component used to identify an entity. Stores a hash for faster comparisons /// Component used to identify an entity. Stores a hash for faster comparisons
#[derive(Debug, Clone, Reflect)] #[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct Name { pub struct Name {
hash: u64, // TODO: Shouldn't be serialized hash: u64, // TODO: Shouldn't be serialized

View file

@ -1,4 +1,4 @@
use bevy_ecs::reflect::ReflectComponent; use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_utils::Duration; use bevy_utils::Duration;
@ -23,7 +23,7 @@ use bevy_utils::Duration;
/// assert!(stopwatch.paused()); /// assert!(stopwatch.paused());
/// assert_eq!(stopwatch.elapsed_secs(), 0.0); /// assert_eq!(stopwatch.elapsed_secs(), 0.0);
/// ``` /// ```
#[derive(Clone, Debug, Default, Reflect)] #[derive(Component, Clone, Debug, Default, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct Stopwatch { pub struct Stopwatch {
elapsed: Duration, elapsed: Duration,

View file

@ -1,5 +1,5 @@
use crate::Stopwatch; use crate::Stopwatch;
use bevy_ecs::reflect::ReflectComponent; use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_utils::Duration; use bevy_utils::Duration;
@ -10,7 +10,7 @@ use bevy_utils::Duration;
/// exceeded, and can still be reset at any given point. /// exceeded, and can still be reset at any given point.
/// ///
/// Paused timers will not have elapsed time increased. /// Paused timers will not have elapsed time increased.
#[derive(Clone, Debug, Default, Reflect)] #[derive(Component, Clone, Debug, Default, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct Timer { pub struct Timer {
stopwatch: Stopwatch, stopwatch: Stopwatch,

View file

@ -34,10 +34,6 @@ rand = "0.8"
name = "events" name = "events"
path = "examples/events.rs" path = "examples/events.rs"
[[example]]
name = "component_storage"
path = "examples/component_storage.rs"
[[example]] [[example]]
name = "resources" name = "resources"
path = "examples/resources.rs" path = "examples/resources.rs"

View file

@ -193,11 +193,15 @@ Components can be stored in:
* **Tables**: Fast and cache friendly iteration, but slower adding and removing of components. This is the default storage type. * **Tables**: Fast and cache friendly iteration, but slower adding and removing of components. This is the default storage type.
* **Sparse Sets**: Fast adding and removing of components, but slower iteration. * **Sparse Sets**: Fast adding and removing of components, but slower iteration.
Component storage types are configurable, and they default to table storage if the storage is not manually defined. The [`component_storage.rs`](examples/component_storage.rs) example shows how to configure the storage type for a component. Component storage types are configurable, and they default to table storage if the storage is not manually defined.
```rust ```rs
// store Position components in Sparse Sets #[derive(Component)]
world.register_component(ComponentDescriptor::new::<Position>(StorageType::SparseSet)); struct TableStoredComponent;
#[derive(Component)]
#[component(storage = "SparseSet")]
struct SparseStoredComponent;
``` ```
### Component Bundles ### Component Bundles

View file

@ -46,7 +46,7 @@ struct EntityCounter {
} }
// This struct represents a Component and holds the age in frames of the entity it gets assigned to // This struct represents a Component and holds the age in frames of the entity it gets assigned to
#[derive(Default, Debug)] #[derive(Component, Default, Debug)]
struct Age { struct Age {
frames: i32, frames: i32,
} }

View file

@ -1,39 +0,0 @@
use bevy_ecs::{
component::{ComponentDescriptor, StorageType},
prelude::*,
};
// This example shows how to configure the storage of Components.
// A system demonstrates that querying for components is independent of their storage type.
fn main() {
let mut world = World::new();
// Store components of type `i32` in a Sparse set
world
.register_component(ComponentDescriptor::new::<i32>(StorageType::SparseSet))
.expect("The component of type i32 is already in use");
// Components of type i32 will have the above configured Sparse set storage,
// while f64 components will have the default table storage
world.spawn().insert(1).insert(0.1);
world.spawn().insert(2);
world.spawn().insert(0.2);
// Setup a schedule and stage to add a system querying for the just spawned entities
let mut schedule = Schedule::default();
let mut update = SystemStage::parallel();
update.add_system(query_entities);
schedule.add_stage("update", update);
schedule.run(&mut world);
}
// The storage type does not matter for how to query in systems
fn query_entities(entities_with_i32: Query<&i32>, entities_with_f64: Query<&f64>) {
for value in entities_with_i32.iter() {
println!("Got entity with i32: {}", value);
}
for value in entities_with_f64.iter() {
println!("Got entity with f64: {}", value);
}
}

View file

@ -0,0 +1,102 @@
use bevy_macro_utils::{get_lit_str, Symbol};
use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{quote, ToTokens};
use syn::{parse_macro_input, parse_quote, DeriveInput, Error, Ident, Path, Result};
pub fn derive_component(input: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(input as DeriveInput);
let bevy_ecs_path: Path = crate::bevy_ecs_path();
let attrs = match parse_component_attr(&ast) {
Ok(attrs) => attrs,
Err(e) => return e.into_compile_error().into(),
};
let storage = storage_path(bevy_ecs_path.clone(), attrs.storage);
ast.generics
.make_where_clause()
.predicates
.push(parse_quote! { Self: Send + Sync + 'static });
let struct_name = &ast.ident;
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
TokenStream::from(quote! {
impl #impl_generics #bevy_ecs_path::component::Component for #struct_name #type_generics #where_clause {
type Storage = #storage;
}
})
}
pub const COMPONENT: Symbol = Symbol("component");
pub const STORAGE: Symbol = Symbol("storage");
struct Attrs {
storage: StorageTy,
}
#[derive(Clone, Copy)]
enum StorageTy {
Table,
SparseSet,
}
fn parse_component_attr(ast: &DeriveInput) -> Result<Attrs> {
let meta_items = bevy_macro_utils::parse_attrs(ast, COMPONENT)?;
let mut attrs = Attrs {
storage: StorageTy::Table,
};
for meta in meta_items {
use syn::{
Meta::NameValue,
NestedMeta::{Lit, Meta},
};
match meta {
Meta(NameValue(m)) if m.path == STORAGE => {
attrs.storage = match get_lit_str(STORAGE, &m.lit)?.value().as_str() {
"Table" => StorageTy::Table,
"SparseSet" => StorageTy::SparseSet,
s => {
return Err(Error::new_spanned(
m.lit,
format!(
"Invalid storage type `{}`, expected 'table' or 'sparse'.",
s
),
))
}
};
}
Meta(meta_item) => {
return Err(Error::new_spanned(
meta_item.path(),
format!(
"unknown component attribute `{}`",
meta_item.path().into_token_stream().to_string()
),
));
}
Lit(lit) => {
return Err(Error::new_spanned(
lit,
"unexpected literal in component attribute",
))
}
}
}
Ok(attrs)
}
fn storage_path(bevy_ecs_path: Path, ty: StorageTy) -> TokenStream2 {
let typename = match ty {
StorageTy::Table => Ident::new("TableStorage", Span::call_site()),
StorageTy::SparseSet => Ident::new("SparseStorage", Span::call_site()),
};
quote! { #bevy_ecs_path::component::#typename }
}

View file

@ -1,5 +1,7 @@
extern crate proc_macro; extern crate proc_macro;
mod component;
use bevy_macro_utils::BevyManifest; use bevy_macro_utils::BevyManifest;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2}; use proc_macro2::{Span, TokenStream as TokenStream2};
@ -118,7 +120,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
{ {
if *is_bundle { if *is_bundle {
field_component_ids.push(quote! { field_component_ids.push(quote! {
component_ids.extend(<#field_type as #ecs_path::bundle::Bundle>::component_ids(components)); component_ids.extend(<#field_type as #ecs_path::bundle::Bundle>::component_ids(components, storages));
}); });
field_get_components.push(quote! { field_get_components.push(quote! {
self.#field.get_components(&mut func); self.#field.get_components(&mut func);
@ -128,7 +130,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
}); });
} else { } else {
field_component_ids.push(quote! { field_component_ids.push(quote! {
component_ids.push(components.get_or_insert_id::<#field_type>()); component_ids.push(components.init_component::<#field_type>(storages));
}); });
field_get_components.push(quote! { field_get_components.push(quote! {
func((&mut self.#field as *mut #field_type).cast::<u8>()); func((&mut self.#field as *mut #field_type).cast::<u8>());
@ -145,10 +147,11 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
let struct_name = &ast.ident; let struct_name = &ast.ident;
TokenStream::from(quote! { TokenStream::from(quote! {
/// SAFE: TypeInfo is returned in field-definition-order. [from_components] and [get_components] use field-definition-order /// SAFE: ComponentId is returned in field-definition-order. [from_components] and [get_components] use field-definition-order
unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name#ty_generics #where_clause { unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name#ty_generics #where_clause {
fn component_ids( fn component_ids(
components: &mut #ecs_path::component::Components, components: &mut #ecs_path::component::Components,
storages: &mut #ecs_path::storage::Storages,
) -> Vec<#ecs_path::component::ComponentId> { ) -> Vec<#ecs_path::component::ComponentId> {
let mut component_ids = Vec::with_capacity(#field_len); let mut component_ids = Vec::with_capacity(#field_len);
#(#field_component_ids)* #(#field_component_ids)*
@ -468,6 +471,11 @@ fn derive_label(input: DeriveInput, label_type: Ident) -> TokenStream2 {
} }
} }
fn bevy_ecs_path() -> syn::Path { pub(crate) fn bevy_ecs_path() -> syn::Path {
BevyManifest::default().get_path("bevy_ecs") BevyManifest::default().get_path("bevy_ecs")
} }
#[proc_macro_derive(Component, attributes(component))]
pub fn derive_component(input: TokenStream) -> TokenStream {
component::derive_component(input)
}

View file

@ -29,8 +29,11 @@ use std::{any::TypeId, collections::HashMap};
/// ///
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # #[derive(Component)]
/// # struct ComponentA; /// # struct ComponentA;
/// # #[derive(Component)]
/// # struct ComponentB; /// # struct ComponentB;
/// # #[derive(Component)]
/// # struct ComponentC; /// # struct ComponentC;
/// # /// #
/// #[derive(Bundle)] /// #[derive(Bundle)]
@ -43,19 +46,26 @@ use std::{any::TypeId, collections::HashMap};
/// ///
/// You can nest bundles using the `#[bundle]` attribute: /// You can nest bundles using the `#[bundle]` attribute:
/// ``` /// ```
/// # use bevy_ecs::bundle::Bundle; /// # use bevy_ecs::{component::Component, bundle::Bundle};
///
/// #[derive(Component)]
/// struct X(i32);
/// #[derive(Component)]
/// struct Y(u64);
/// #[derive(Component)]
/// struct Z(String);
/// ///
/// #[derive(Bundle)] /// #[derive(Bundle)]
/// struct A { /// struct A {
/// x: i32, /// x: X,
/// y: u64, /// y: Y,
/// } /// }
/// ///
/// #[derive(Bundle)] /// #[derive(Bundle)]
/// struct B { /// struct B {
/// #[bundle] /// #[bundle]
/// a: A, /// a: A,
/// z: String, /// z: Z,
/// } /// }
/// ``` /// ```
/// ///
@ -67,7 +77,7 @@ use std::{any::TypeId, collections::HashMap};
/// [Bundle::component_ids]. /// [Bundle::component_ids].
pub unsafe trait Bundle: Send + Sync + 'static { pub unsafe trait Bundle: Send + Sync + 'static {
/// Gets this [Bundle]'s component ids, in the order of this bundle's Components /// Gets this [Bundle]'s component ids, in the order of this bundle's Components
fn component_ids(components: &mut Components) -> Vec<ComponentId>; fn component_ids(components: &mut Components, storages: &mut Storages) -> Vec<ComponentId>;
/// Calls `func`, which should return data for each component in the bundle, in the order of /// Calls `func`, which should return data for each component in the bundle, in the order of
/// this bundle's Components /// this bundle's Components
@ -87,11 +97,11 @@ pub unsafe trait Bundle: Send + Sync + 'static {
macro_rules! tuple_impl { macro_rules! tuple_impl {
($($name: ident),*) => { ($($name: ident),*) => {
/// SAFE: TypeInfo is returned in tuple-order. [Bundle::from_components] and [Bundle::get_components] use tuple-order /// SAFE: Component is returned in tuple-order. [Bundle::from_components] and [Bundle::get_components] use tuple-order
unsafe impl<$($name: Component),*> Bundle for ($($name,)*) { unsafe impl<$($name: Component),*> Bundle for ($($name,)*) {
#[allow(unused_variables)] #[allow(unused_variables)]
fn component_ids(components: &mut Components) -> Vec<ComponentId> { fn component_ids(components: &mut Components, storages: &mut Storages) -> Vec<ComponentId> {
vec![$(components.get_or_insert_id::<$name>()),*] vec![$(components.init_component::<$name>(storages)),*]
} }
#[allow(unused_variables, unused_mut)] #[allow(unused_variables, unused_mut)]
@ -318,10 +328,7 @@ impl BundleInfo {
let component_info = unsafe { components.get_info_unchecked(component_id) }; let component_info = unsafe { components.get_info_unchecked(component_id) };
match component_info.storage_type() { match component_info.storage_type() {
StorageType::Table => new_table_components.push(component_id), StorageType::Table => new_table_components.push(component_id),
StorageType::SparseSet => { StorageType::SparseSet => new_sparse_set_components.push(component_id),
storages.sparse_sets.get_or_insert(component_info);
new_sparse_set_components.push(component_id)
}
} }
} }
} }
@ -585,10 +592,11 @@ impl Bundles {
pub(crate) fn init_info<'a, T: Bundle>( pub(crate) fn init_info<'a, T: Bundle>(
&'a mut self, &'a mut self,
components: &mut Components, components: &mut Components,
storages: &mut Storages,
) -> &'a BundleInfo { ) -> &'a BundleInfo {
let bundle_infos = &mut self.bundle_infos; let bundle_infos = &mut self.bundle_infos;
let id = self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| { let id = self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {
let component_ids = T::component_ids(components); let component_ids = T::component_ids(components, storages);
let id = BundleId(bundle_infos.len()); let id = BundleId(bundle_infos.len());
// SAFE: T::component_id ensures info was created // SAFE: T::component_id ensures info was created
let bundle_info = unsafe { let bundle_info = unsafe {

View file

@ -1,6 +1,6 @@
//! Types that detect when their internal data mutate. //! Types that detect when their internal data mutate.
use crate::component::{Component, ComponentTicks}; use crate::{component::ComponentTicks, system::Resource};
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
@ -147,14 +147,14 @@ pub(crate) struct Ticks<'a> {
/// Panics when used as a [`SystemParam`](crate::system::SystemParam) if the resource does not exist. /// Panics when used as a [`SystemParam`](crate::system::SystemParam) if the resource does not exist.
/// ///
/// Use `Option<ResMut<T>>` instead if the resource might not always exist. /// Use `Option<ResMut<T>>` instead if the resource might not always exist.
pub struct ResMut<'a, T: Component> { pub struct ResMut<'a, T: Resource> {
pub(crate) value: &'a mut T, pub(crate) value: &'a mut T,
pub(crate) ticks: Ticks<'a>, pub(crate) ticks: Ticks<'a>,
} }
change_detection_impl!(ResMut<'a, T>, T, Component); change_detection_impl!(ResMut<'a, T>, T, Resource);
impl_into_inner!(ResMut<'a, T>, T, Component); impl_into_inner!(ResMut<'a, T>, T, Resource);
impl_debug!(ResMut<'a, T>, Component); impl_debug!(ResMut<'a, T>, Resource);
/// Unique borrow of a non-[`Send`] resource. /// Unique borrow of a non-[`Send`] resource.
/// ///

View file

@ -1,18 +1,28 @@
//! Types for declaring and storing [`Component`]s. //! Types for declaring and storing [`Component`]s.
use crate::storage::SparseSetIndex; use crate::{
storage::{SparseSetIndex, Storages},
system::Resource,
};
pub use bevy_ecs_macros::Component;
use std::{ use std::{
alloc::Layout, alloc::Layout,
any::{Any, TypeId}, any::{Any, TypeId},
collections::hash_map::Entry,
}; };
use thiserror::Error; use thiserror::Error;
/// A component is data associated with an [`Entity`](crate::entity::Entity). Each entity can have /// 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. /// multiple different types of components, but only one of them per type.
/// ///
/// Any type that is `Send + Sync + 'static` automatically implements `Component`. /// 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.
/// ```
/// # use bevy_ecs::component::Component;
/// use std::time::Duration;
/// #[derive(Component)]
/// struct Cooldown(Duration);
/// ```
/// Components are added with new entities using [`Commands::spawn`](crate::system::Commands::spawn), /// 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 to existing entities with [`EntityCommands::insert`](crate::system::EntityCommands::insert),
/// or their [`World`](crate::world::World) equivalents. /// or their [`World`](crate::world::World) equivalents.
@ -21,21 +31,49 @@ use thiserror::Error;
/// as one of the arguments. /// as one of the arguments.
/// ///
/// Components can be grouped together into a [`Bundle`](crate::bundle::Bundle). /// Components can be grouped together into a [`Bundle`](crate::bundle::Bundle).
pub trait Component: Send + Sync + 'static {} pub trait Component: Send + Sync + 'static {
impl<T: Send + Sync + 'static> Component for T {} type Storage: ComponentStorage;
}
pub struct TableStorage;
pub struct SparseStorage;
pub trait ComponentStorage: sealed::Sealed {
// because the trait is selaed, those items are private API.
const STORAGE_TYPE: StorageType;
}
impl ComponentStorage for TableStorage {
const STORAGE_TYPE: StorageType = StorageType::Table;
}
impl ComponentStorage for SparseStorage {
const STORAGE_TYPE: StorageType = StorageType::SparseSet;
}
mod sealed {
pub trait Sealed {}
impl Sealed for super::TableStorage {}
impl Sealed for super::SparseStorage {}
}
// ECS dependencies cannot derive Component, so we must implement it manually for relevant structs.
impl<T> Component for bevy_tasks::Task<T>
where
Self: Send + Sync + 'static,
{
type Storage = TableStorage;
}
/// The storage used for a specific component type. /// The storage used for a specific component type.
/// ///
/// # Examples /// # Examples
/// The [`StorageType`] for a component is normally configured via `World::register_component`. /// The [`StorageType`] for a component is configured via the derive attribute
/// ///
/// ``` /// ```
/// # use bevy_ecs::{prelude::*, component::*}; /// # use bevy_ecs::{prelude::*, component::*};
/// /// #[derive(Component)]
/// #[component(storage = "SparseSet")]
/// struct A; /// struct A;
///
/// let mut world = World::default();
/// world.register_component(ComponentDescriptor::new::<A>(StorageType::SparseSet));
/// ``` /// ```
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum StorageType { pub enum StorageType {
@ -128,6 +166,8 @@ impl SparseSetIndex for ComponentId {
#[derive(Debug)] #[derive(Debug)]
pub struct ComponentDescriptor { pub struct ComponentDescriptor {
name: String, name: String,
// SAFETY: This must remain private. It must match the statically known StorageType of the
// associated rust component type if one exists.
storage_type: StorageType, storage_type: StorageType,
// SAFETY: This must remain private. It must only be set to "true" if this component is // SAFETY: This must remain private. It must only be set to "true" if this component is
// actually Send + Sync // actually Send + Sync
@ -143,7 +183,18 @@ impl ComponentDescriptor {
x.cast::<T>().drop_in_place() x.cast::<T>().drop_in_place()
} }
pub fn new<T: Component>(storage_type: StorageType) -> Self { pub fn new<T: Component>() -> Self {
Self {
name: std::any::type_name::<T>().to_string(),
storage_type: T::Storage::STORAGE_TYPE,
is_send_and_sync: true,
type_id: Some(TypeId::of::<T>()),
layout: Layout::new::<T>(),
drop: Self::drop_ptr::<T>,
}
}
pub fn new_resource<T: Resource>(storage_type: StorageType) -> Self {
Self { Self {
name: std::any::type_name::<T>().to_string(), name: std::any::type_name::<T>().to_string(),
storage_type, storage_type,
@ -191,46 +242,29 @@ pub struct Components {
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum ComponentsError { pub enum ComponentsError {
#[error("A component of type {name:?} ({type_id:?}) already exists")] #[error("A component of type {name:?} ({type_id:?}) already exists")]
ComponentAlreadyExists { type_id: TypeId, name: String }, ComponentAlreadyExists {
type_id: TypeId,
name: String,
existing_id: ComponentId,
},
} }
impl Components { impl Components {
pub(crate) fn add( #[inline]
&mut self, pub fn init_component<T: Component>(&mut self, storages: &mut Storages) -> ComponentId {
descriptor: ComponentDescriptor, let type_id = TypeId::of::<T>();
) -> Result<ComponentId, ComponentsError> { let components = &mut self.components;
let index = self.components.len(); let index = self.indices.entry(type_id).or_insert_with(|| {
if let Some(type_id) = descriptor.type_id { let index = components.len();
let index_entry = self.indices.entry(type_id); let descriptor = ComponentDescriptor::new::<T>();
if let Entry::Occupied(_) = index_entry { let info = ComponentInfo::new(ComponentId(index), descriptor);
return Err(ComponentsError::ComponentAlreadyExists { if T::Storage::STORAGE_TYPE == StorageType::SparseSet {
type_id, storages.sparse_sets.get_or_insert(&info);
name: descriptor.name,
});
} }
self.indices.insert(type_id, index); components.push(info);
} index
self.components });
.push(ComponentInfo::new(ComponentId(index), descriptor)); ComponentId(*index)
Ok(ComponentId(index))
}
#[inline]
pub fn get_or_insert_id<T: Component>(&mut self) -> ComponentId {
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
unsafe {
self.get_or_insert_with(TypeId::of::<T>(), || {
ComponentDescriptor::new::<T>(StorageType::default())
})
}
}
#[inline]
pub fn get_or_insert_info<T: Component>(&mut self) -> &ComponentInfo {
let id = self.get_or_insert_id::<T>();
// SAFE: component_info with the given `id` initialized above
unsafe { self.get_info_unchecked(id) }
} }
#[inline] #[inline]
@ -270,17 +304,17 @@ impl Components {
} }
#[inline] #[inline]
pub fn get_or_insert_resource_id<T: Component>(&mut self) -> ComponentId { pub fn init_resource<T: Resource>(&mut self) -> ComponentId {
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
unsafe { unsafe {
self.get_or_insert_resource_with(TypeId::of::<T>(), || { self.get_or_insert_resource_with(TypeId::of::<T>(), || {
ComponentDescriptor::new::<T>(StorageType::default()) ComponentDescriptor::new_resource::<T>(StorageType::default())
}) })
} }
} }
#[inline] #[inline]
pub fn get_or_insert_non_send_resource_id<T: Any>(&mut self) -> ComponentId { pub fn init_non_send<T: Any>(&mut self) -> ComponentId {
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
unsafe { unsafe {
self.get_or_insert_resource_with(TypeId::of::<T>(), || { self.get_or_insert_resource_with(TypeId::of::<T>(), || {
@ -308,26 +342,6 @@ impl Components {
ComponentId(*index) ComponentId(*index)
} }
/// # Safety
///
/// The [`ComponentDescriptor`] must match the [`TypeId`]
#[inline]
pub(crate) unsafe fn get_or_insert_with(
&mut self,
type_id: TypeId,
func: impl FnOnce() -> ComponentDescriptor,
) -> ComponentId {
let components = &mut self.components;
let index = self.indices.entry(type_id).or_insert_with(|| {
let descriptor = func();
let index = components.len();
components.push(ComponentInfo::new(ComponentId(index), descriptor));
index
});
ComponentId(*index)
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View file

@ -1,10 +1,7 @@
//! Event handling types. //! Event handling types.
use crate as bevy_ecs; use crate::system::{Local, Res, ResMut, SystemParam};
use crate::{ use crate::{self as bevy_ecs, system::Resource};
component::Component,
system::{Local, Res, ResMut, SystemParam},
};
use bevy_utils::tracing::trace; use bevy_utils::tracing::trace;
use std::{ use std::{
fmt::{self}, fmt::{self},
@ -153,20 +150,20 @@ fn map_instance_event<T>(event_instance: &EventInstance<T>) -> &T {
/// Reads events of type `T` in order and tracks which events have already been read. /// Reads events of type `T` in order and tracks which events have already been read.
#[derive(SystemParam)] #[derive(SystemParam)]
pub struct EventReader<'w, 's, T: Component> { pub struct EventReader<'w, 's, T: Resource> {
last_event_count: Local<'s, (usize, PhantomData<T>)>, last_event_count: Local<'s, (usize, PhantomData<T>)>,
events: Res<'w, Events<T>>, events: Res<'w, Events<T>>,
} }
/// Sends events of type `T`. /// Sends events of type `T`.
#[derive(SystemParam)] #[derive(SystemParam)]
pub struct EventWriter<'w, 's, T: Component> { pub struct EventWriter<'w, 's, T: Resource> {
events: ResMut<'w, Events<T>>, events: ResMut<'w, Events<T>>,
#[system_param(ignore)] #[system_param(ignore)]
marker: PhantomData<&'s usize>, marker: PhantomData<&'s usize>,
} }
impl<'w, 's, T: Component> EventWriter<'w, 's, T> { impl<'w, 's, T: Resource> EventWriter<'w, 's, T> {
pub fn send(&mut self, event: T) { pub fn send(&mut self, event: T) {
self.events.send(event); self.events.send(event);
} }
@ -256,7 +253,7 @@ fn internal_event_reader<'a, T>(
} }
} }
impl<'w, 's, T: Component> EventReader<'w, 's, T> { impl<'w, 's, T: Resource> EventReader<'w, 's, T> {
/// Iterates over the events this EventReader has not seen yet. This updates the EventReader's /// Iterates over the events this EventReader has not seen yet. This updates the EventReader's
/// event counter, which means subsequent event reads will not include events that happened /// event counter, which means subsequent event reads will not include events that happened
/// before now. /// before now.
@ -273,7 +270,7 @@ impl<'w, 's, T: Component> EventReader<'w, 's, T> {
} }
} }
impl<T: Component> Events<T> { impl<T: Resource> Events<T> {
/// "Sends" an `event` by writing it to the current event buffer. [EventReader]s can then read /// "Sends" an `event` by writing it to the current event buffer. [EventReader]s can then read
/// the event. /// the event.
pub fn send(&mut self, event: T) { pub fn send(&mut self, event: T) {

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
archetype::{Archetype, ArchetypeComponentId}, archetype::{Archetype, ArchetypeComponentId},
change_detection::Ticks, change_detection::Ticks,
component::{Component, ComponentId, ComponentTicks, StorageType}, component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType},
entity::Entity, entity::Entity,
query::{Access, FilteredAccess}, query::{Access, FilteredAccess},
storage::{ComponentSparseSet, Table, Tables}, storage::{ComponentSparseSet, Table, Tables},
@ -67,7 +67,7 @@ pub trait Fetch<'world, 'state>: Sized {
/// for "dense" queries. If this returns true, [`Fetch::set_table`] and [`Fetch::table_fetch`] /// for "dense" queries. If this returns true, [`Fetch::set_table`] and [`Fetch::table_fetch`]
/// will be called for iterators. If this returns false, [`Fetch::set_archetype`] and /// will be called for iterators. If this returns false, [`Fetch::set_archetype`] and
/// [`Fetch::archetype_fetch`] will be called for iterators. /// [`Fetch::archetype_fetch`] will be called for iterators.
fn is_dense(&self) -> bool; const IS_DENSE: bool;
/// Adjusts internal state to account for the next [`Archetype`]. This will always be called on /// Adjusts internal state to account for the next [`Archetype`]. This will always be called on
/// archetypes that match this [`Fetch`]. /// archetypes that match this [`Fetch`].
@ -178,10 +178,7 @@ impl<'w, 's> Fetch<'w, 's> for EntityFetch {
type Item = Entity; type Item = Entity;
type State = EntityState; type State = EntityState;
#[inline] const IS_DENSE: bool = true;
fn is_dense(&self) -> bool {
true
}
unsafe fn init( unsafe fn init(
_world: &World, _world: &World,
@ -228,7 +225,6 @@ impl<T: Component> WorldQuery for &T {
/// The [`FetchState`] of `&T`. /// The [`FetchState`] of `&T`.
pub struct ReadState<T> { pub struct ReadState<T> {
component_id: ComponentId, component_id: ComponentId,
storage_type: StorageType,
marker: PhantomData<T>, marker: PhantomData<T>,
} }
@ -236,10 +232,9 @@ pub struct ReadState<T> {
// read // read
unsafe impl<T: Component> FetchState for ReadState<T> { unsafe impl<T: Component> FetchState for ReadState<T> {
fn init(world: &mut World) -> Self { fn init(world: &mut World) -> Self {
let component_info = world.components.get_or_insert_info::<T>(); let component_id = world.init_component::<T>();
ReadState { ReadState {
component_id: component_info.id(), component_id,
storage_type: component_info.storage_type(),
marker: PhantomData, marker: PhantomData,
} }
} }
@ -275,7 +270,6 @@ unsafe impl<T: Component> FetchState for ReadState<T> {
/// The [`Fetch`] of `&T`. /// The [`Fetch`] of `&T`.
pub struct ReadFetch<T> { pub struct ReadFetch<T> {
storage_type: StorageType,
table_components: NonNull<T>, table_components: NonNull<T>,
entity_table_rows: *const usize, entity_table_rows: *const usize,
entities: *const Entity, entities: *const Entity,
@ -285,7 +279,6 @@ pub struct ReadFetch<T> {
impl<T> Clone for ReadFetch<T> { impl<T> Clone for ReadFetch<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
storage_type: self.storage_type,
table_components: self.table_components, table_components: self.table_components,
entity_table_rows: self.entity_table_rows, entity_table_rows: self.entity_table_rows,
entities: self.entities, entities: self.entities,
@ -301,13 +294,12 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch<T> {
type Item = &'w T; type Item = &'w T;
type State = ReadState<T>; type State = ReadState<T>;
#[inline] const IS_DENSE: bool = {
fn is_dense(&self) -> bool { match T::Storage::STORAGE_TYPE {
match self.storage_type {
StorageType::Table => true, StorageType::Table => true,
StorageType::SparseSet => false, StorageType::SparseSet => false,
} }
} };
unsafe fn init( unsafe fn init(
world: &World, world: &World,
@ -316,13 +308,12 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch<T> {
_change_tick: u32, _change_tick: u32,
) -> Self { ) -> Self {
let mut value = Self { let mut value = Self {
storage_type: state.storage_type,
table_components: NonNull::dangling(), table_components: NonNull::dangling(),
entities: ptr::null::<Entity>(), entities: ptr::null::<Entity>(),
entity_table_rows: ptr::null::<usize>(), entity_table_rows: ptr::null::<usize>(),
sparse_set: ptr::null::<ComponentSparseSet>(), sparse_set: ptr::null::<ComponentSparseSet>(),
}; };
if state.storage_type == StorageType::SparseSet { if T::Storage::STORAGE_TYPE == StorageType::SparseSet {
value.sparse_set = world value.sparse_set = world
.storages() .storages()
.sparse_sets .sparse_sets
@ -339,7 +330,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch<T> {
archetype: &Archetype, archetype: &Archetype,
tables: &Tables, tables: &Tables,
) { ) {
match state.storage_type { match T::Storage::STORAGE_TYPE {
StorageType::Table => { StorageType::Table => {
self.entity_table_rows = archetype.entity_table_rows().as_ptr(); self.entity_table_rows = archetype.entity_table_rows().as_ptr();
let column = tables[archetype.table_id()] let column = tables[archetype.table_id()]
@ -362,7 +353,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch<T> {
#[inline] #[inline]
unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item {
match self.storage_type { match T::Storage::STORAGE_TYPE {
StorageType::Table => { StorageType::Table => {
let table_row = *self.entity_table_rows.add(archetype_index); let table_row = *self.entity_table_rows.add(archetype_index);
&*self.table_components.as_ptr().add(table_row) &*self.table_components.as_ptr().add(table_row)
@ -387,7 +378,6 @@ impl<T: Component> WorldQuery for &mut T {
/// The [`Fetch`] of `&mut T`. /// The [`Fetch`] of `&mut T`.
pub struct WriteFetch<T> { pub struct WriteFetch<T> {
storage_type: StorageType,
table_components: NonNull<T>, table_components: NonNull<T>,
table_ticks: *const UnsafeCell<ComponentTicks>, table_ticks: *const UnsafeCell<ComponentTicks>,
entities: *const Entity, entities: *const Entity,
@ -400,7 +390,6 @@ pub struct WriteFetch<T> {
impl<T> Clone for WriteFetch<T> { impl<T> Clone for WriteFetch<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
storage_type: self.storage_type,
table_components: self.table_components, table_components: self.table_components,
table_ticks: self.table_ticks, table_ticks: self.table_ticks,
entities: self.entities, entities: self.entities,
@ -415,7 +404,6 @@ impl<T> Clone for WriteFetch<T> {
/// The [`FetchState`] of `&mut T`. /// The [`FetchState`] of `&mut T`.
pub struct WriteState<T> { pub struct WriteState<T> {
component_id: ComponentId, component_id: ComponentId,
storage_type: StorageType,
marker: PhantomData<T>, marker: PhantomData<T>,
} }
@ -423,10 +411,9 @@ pub struct WriteState<T> {
// written // written
unsafe impl<T: Component> FetchState for WriteState<T> { unsafe impl<T: Component> FetchState for WriteState<T> {
fn init(world: &mut World) -> Self { fn init(world: &mut World) -> Self {
let component_info = world.components.get_or_insert_info::<T>(); let component_id = world.init_component::<T>();
WriteState { WriteState {
component_id: component_info.id(), component_id,
storage_type: component_info.storage_type(),
marker: PhantomData, marker: PhantomData,
} }
} }
@ -464,13 +451,12 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch<T> {
type Item = Mut<'w, T>; type Item = Mut<'w, T>;
type State = WriteState<T>; type State = WriteState<T>;
#[inline] const IS_DENSE: bool = {
fn is_dense(&self) -> bool { match T::Storage::STORAGE_TYPE {
match self.storage_type {
StorageType::Table => true, StorageType::Table => true,
StorageType::SparseSet => false, StorageType::SparseSet => false,
} }
} };
unsafe fn init( unsafe fn init(
world: &World, world: &World,
@ -479,7 +465,6 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch<T> {
change_tick: u32, change_tick: u32,
) -> Self { ) -> Self {
let mut value = Self { let mut value = Self {
storage_type: state.storage_type,
table_components: NonNull::dangling(), table_components: NonNull::dangling(),
entities: ptr::null::<Entity>(), entities: ptr::null::<Entity>(),
entity_table_rows: ptr::null::<usize>(), entity_table_rows: ptr::null::<usize>(),
@ -488,7 +473,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch<T> {
last_change_tick, last_change_tick,
change_tick, change_tick,
}; };
if state.storage_type == StorageType::SparseSet { if T::Storage::STORAGE_TYPE == StorageType::SparseSet {
value.sparse_set = world value.sparse_set = world
.storages() .storages()
.sparse_sets .sparse_sets
@ -505,7 +490,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch<T> {
archetype: &Archetype, archetype: &Archetype,
tables: &Tables, tables: &Tables,
) { ) {
match state.storage_type { match T::Storage::STORAGE_TYPE {
StorageType::Table => { StorageType::Table => {
self.entity_table_rows = archetype.entity_table_rows().as_ptr(); self.entity_table_rows = archetype.entity_table_rows().as_ptr();
let column = tables[archetype.table_id()] let column = tables[archetype.table_id()]
@ -527,7 +512,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch<T> {
#[inline] #[inline]
unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item {
match self.storage_type { match T::Storage::STORAGE_TYPE {
StorageType::Table => { StorageType::Table => {
let table_row = *self.entity_table_rows.add(archetype_index); let table_row = *self.entity_table_rows.add(archetype_index);
Mut { Mut {
@ -625,10 +610,7 @@ impl<'w, 's, T: Fetch<'w, 's>> Fetch<'w, 's> for OptionFetch<T> {
type Item = Option<T::Item>; type Item = Option<T::Item>;
type State = OptionState<T::State>; type State = OptionState<T::State>;
#[inline] const IS_DENSE: bool = T::IS_DENSE;
fn is_dense(&self) -> bool {
self.fetch.is_dense()
}
unsafe fn init( unsafe fn init(
world: &World, world: &World,
@ -693,12 +675,14 @@ impl<'w, 's, T: Fetch<'w, 's>> Fetch<'w, 's> for OptionFetch<T> {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # use bevy_ecs::system::Query; /// # use bevy_ecs::component::Component;
/// # use bevy_ecs::query::ChangeTrackers; /// # use bevy_ecs::query::ChangeTrackers;
/// # use bevy_ecs::system::IntoSystem; /// # use bevy_ecs::system::IntoSystem;
/// # use bevy_ecs::system::Query;
/// # /// #
/// # #[derive(Debug)] /// # #[derive(Component, Debug)]
/// # struct Name {}; /// # struct Name {};
/// # #[derive(Component)]
/// # struct Transform {}; /// # struct Transform {};
/// # /// #
/// fn print_moving_objects_system(query: Query<(&Name, ChangeTrackers<Transform>)>) { /// fn print_moving_objects_system(query: Query<(&Name, ChangeTrackers<Transform>)>) {
@ -752,7 +736,6 @@ impl<T: Component> WorldQuery for ChangeTrackers<T> {
/// The [`FetchState`] of [`ChangeTrackers`]. /// The [`FetchState`] of [`ChangeTrackers`].
pub struct ChangeTrackersState<T> { pub struct ChangeTrackersState<T> {
component_id: ComponentId, component_id: ComponentId,
storage_type: StorageType,
marker: PhantomData<T>, marker: PhantomData<T>,
} }
@ -760,10 +743,9 @@ pub struct ChangeTrackersState<T> {
// read // read
unsafe impl<T: Component> FetchState for ChangeTrackersState<T> { unsafe impl<T: Component> FetchState for ChangeTrackersState<T> {
fn init(world: &mut World) -> Self { fn init(world: &mut World) -> Self {
let component_info = world.components.get_or_insert_info::<T>(); let component_id = world.init_component::<T>();
Self { Self {
component_id: component_info.id(), component_id,
storage_type: component_info.storage_type(),
marker: PhantomData, marker: PhantomData,
} }
} }
@ -799,7 +781,6 @@ unsafe impl<T: Component> FetchState for ChangeTrackersState<T> {
/// The [`Fetch`] of [`ChangeTrackers`]. /// The [`Fetch`] of [`ChangeTrackers`].
pub struct ChangeTrackersFetch<T> { pub struct ChangeTrackersFetch<T> {
storage_type: StorageType,
table_ticks: *const ComponentTicks, table_ticks: *const ComponentTicks,
entity_table_rows: *const usize, entity_table_rows: *const usize,
entities: *const Entity, entities: *const Entity,
@ -812,7 +793,6 @@ pub struct ChangeTrackersFetch<T> {
impl<T> Clone for ChangeTrackersFetch<T> { impl<T> Clone for ChangeTrackersFetch<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
storage_type: self.storage_type,
table_ticks: self.table_ticks, table_ticks: self.table_ticks,
entity_table_rows: self.entity_table_rows, entity_table_rows: self.entity_table_rows,
entities: self.entities, entities: self.entities,
@ -831,13 +811,12 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch<T> {
type Item = ChangeTrackers<T>; type Item = ChangeTrackers<T>;
type State = ChangeTrackersState<T>; type State = ChangeTrackersState<T>;
#[inline] const IS_DENSE: bool = {
fn is_dense(&self) -> bool { match T::Storage::STORAGE_TYPE {
match self.storage_type {
StorageType::Table => true, StorageType::Table => true,
StorageType::SparseSet => false, StorageType::SparseSet => false,
} }
} };
unsafe fn init( unsafe fn init(
world: &World, world: &World,
@ -846,7 +825,6 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch<T> {
change_tick: u32, change_tick: u32,
) -> Self { ) -> Self {
let mut value = Self { let mut value = Self {
storage_type: state.storage_type,
table_ticks: ptr::null::<ComponentTicks>(), table_ticks: ptr::null::<ComponentTicks>(),
entities: ptr::null::<Entity>(), entities: ptr::null::<Entity>(),
entity_table_rows: ptr::null::<usize>(), entity_table_rows: ptr::null::<usize>(),
@ -855,7 +833,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch<T> {
last_change_tick, last_change_tick,
change_tick, change_tick,
}; };
if state.storage_type == StorageType::SparseSet { if T::Storage::STORAGE_TYPE == StorageType::SparseSet {
value.sparse_set = world value.sparse_set = world
.storages() .storages()
.sparse_sets .sparse_sets
@ -872,7 +850,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch<T> {
archetype: &Archetype, archetype: &Archetype,
tables: &Tables, tables: &Tables,
) { ) {
match state.storage_type { match T::Storage::STORAGE_TYPE {
StorageType::Table => { StorageType::Table => {
self.entity_table_rows = archetype.entity_table_rows().as_ptr(); self.entity_table_rows = archetype.entity_table_rows().as_ptr();
let column = tables[archetype.table_id()] let column = tables[archetype.table_id()]
@ -894,7 +872,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch<T> {
#[inline] #[inline]
unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item {
match self.storage_type { match T::Storage::STORAGE_TYPE {
StorageType::Table => { StorageType::Table => {
let table_row = *self.entity_table_rows.add(archetype_index); let table_row = *self.entity_table_rows.add(archetype_index);
ChangeTrackers { ChangeTrackers {
@ -940,12 +918,7 @@ macro_rules! impl_tuple_fetch {
($($name::init(_world, $name, _last_change_tick, _change_tick),)*) ($($name::init(_world, $name, _last_change_tick, _change_tick),)*)
} }
const IS_DENSE: bool = true $(&& $name::IS_DENSE)*;
#[inline]
fn is_dense(&self) -> bool {
let ($($name,)*) = self;
true $(&& $name.is_dense())*
}
#[inline] #[inline]
unsafe fn set_archetype(&mut self, _state: &Self::State, _archetype: &Archetype, _tables: &Tables) { unsafe fn set_archetype(&mut self, _state: &Self::State, _archetype: &Archetype, _tables: &Tables) {

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
archetype::{Archetype, ArchetypeComponentId}, archetype::{Archetype, ArchetypeComponentId},
component::{Component, ComponentId, ComponentTicks, StorageType}, component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType},
entity::Entity, entity::Entity,
query::{Access, Fetch, FetchState, FilteredAccess, WorldQuery}, query::{Access, Fetch, FetchState, FilteredAccess, WorldQuery},
storage::{ComponentSparseSet, Table, Tables}, storage::{ComponentSparseSet, Table, Tables},
@ -50,12 +50,14 @@ where
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # use bevy_ecs::system::Query; /// # use bevy_ecs::component::Component;
/// # use bevy_ecs::query::With; /// # use bevy_ecs::query::With;
/// # use bevy_ecs::system::IntoSystem; /// # use bevy_ecs::system::IntoSystem;
/// # use bevy_ecs::system::Query;
/// # /// #
/// # #[derive(Debug)] /// # #[derive(Component)]
/// # struct IsBeautiful {}; /// # struct IsBeautiful;
/// # #[derive(Component)]
/// # struct Name { name: &'static str }; /// # struct Name { name: &'static str };
/// # /// #
/// fn compliment_entity_system(query: Query<&Name, With<IsBeautiful>>) { /// fn compliment_entity_system(query: Query<&Name, With<IsBeautiful>>) {
@ -74,24 +76,21 @@ impl<T: Component> WorldQuery for With<T> {
/// The [`Fetch`] of [`With`]. /// The [`Fetch`] of [`With`].
pub struct WithFetch<T> { pub struct WithFetch<T> {
storage_type: StorageType,
marker: PhantomData<T>, marker: PhantomData<T>,
} }
/// The [`FetchState`] of [`With`]. /// The [`FetchState`] of [`With`].
pub struct WithState<T> { pub struct WithState<T> {
component_id: ComponentId, component_id: ComponentId,
storage_type: StorageType,
marker: PhantomData<T>, marker: PhantomData<T>,
} }
// SAFETY: no component access or archetype component access // SAFETY: no component access or archetype component access
unsafe impl<T: Component> FetchState for WithState<T> { unsafe impl<T: Component> FetchState for WithState<T> {
fn init(world: &mut World) -> Self { fn init(world: &mut World) -> Self {
let component_info = world.components.get_or_insert_info::<T>(); let component_id = world.init_component::<T>();
Self { Self {
component_id: component_info.id(), component_id,
storage_type: component_info.storage_type(),
marker: PhantomData, marker: PhantomData,
} }
} }
@ -124,20 +123,21 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithFetch<T> {
unsafe fn init( unsafe fn init(
_world: &World, _world: &World,
state: &Self::State, _state: &Self::State,
_last_change_tick: u32, _last_change_tick: u32,
_change_tick: u32, _change_tick: u32,
) -> Self { ) -> Self {
Self { Self {
storage_type: state.storage_type,
marker: PhantomData, marker: PhantomData,
} }
} }
#[inline] const IS_DENSE: bool = {
fn is_dense(&self) -> bool { match T::Storage::STORAGE_TYPE {
self.storage_type == StorageType::Table StorageType::Table => true,
} StorageType::SparseSet => false,
}
};
#[inline] #[inline]
unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {} unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {}
@ -169,12 +169,14 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithFetch<T> {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # use bevy_ecs::system::Query; /// # use bevy_ecs::component::Component;
/// # use bevy_ecs::query::Without; /// # use bevy_ecs::query::Without;
/// # use bevy_ecs::system::IntoSystem; /// # use bevy_ecs::system::IntoSystem;
/// # use bevy_ecs::system::Query;
/// # /// #
/// # #[derive(Debug)] /// # #[derive(Component)]
/// # struct Permit; /// # struct Permit;
/// # #[derive(Component)]
/// # struct Name { name: &'static str }; /// # struct Name { name: &'static str };
/// # /// #
/// fn no_permit_system(query: Query<&Name, Without<Permit>>) { /// fn no_permit_system(query: Query<&Name, Without<Permit>>) {
@ -193,24 +195,21 @@ impl<T: Component> WorldQuery for Without<T> {
/// The [`Fetch`] of [`Without`]. /// The [`Fetch`] of [`Without`].
pub struct WithoutFetch<T> { pub struct WithoutFetch<T> {
storage_type: StorageType,
marker: PhantomData<T>, marker: PhantomData<T>,
} }
/// The [`FetchState`] of [`Without`]. /// The [`FetchState`] of [`Without`].
pub struct WithoutState<T> { pub struct WithoutState<T> {
component_id: ComponentId, component_id: ComponentId,
storage_type: StorageType,
marker: PhantomData<T>, marker: PhantomData<T>,
} }
// SAFETY: no component access or archetype component access // SAFETY: no component access or archetype component access
unsafe impl<T: Component> FetchState for WithoutState<T> { unsafe impl<T: Component> FetchState for WithoutState<T> {
fn init(world: &mut World) -> Self { fn init(world: &mut World) -> Self {
let component_info = world.components.get_or_insert_info::<T>(); let component_id = world.init_component::<T>();
Self { Self {
component_id: component_info.id(), component_id,
storage_type: component_info.storage_type(),
marker: PhantomData, marker: PhantomData,
} }
} }
@ -243,20 +242,21 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithoutFetch<T> {
unsafe fn init( unsafe fn init(
_world: &World, _world: &World,
state: &Self::State, _state: &Self::State,
_last_change_tick: u32, _last_change_tick: u32,
_change_tick: u32, _change_tick: u32,
) -> Self { ) -> Self {
Self { Self {
storage_type: state.storage_type,
marker: PhantomData, marker: PhantomData,
} }
} }
#[inline] const IS_DENSE: bool = {
fn is_dense(&self) -> bool { match T::Storage::STORAGE_TYPE {
self.storage_type == StorageType::Table StorageType::Table => true,
} StorageType::SparseSet => false,
}
};
#[inline] #[inline]
unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {} unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {}
@ -292,14 +292,16 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithoutFetch<T> {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # use bevy_ecs::component::Component;
/// # use bevy_ecs::entity::Entity; /// # use bevy_ecs::entity::Entity;
/// # use bevy_ecs::system::Query;
/// # use bevy_ecs::system::IntoSystem;
/// # use bevy_ecs::query::Changed; /// # use bevy_ecs::query::Changed;
/// # use bevy_ecs::query::Or; /// # use bevy_ecs::query::Or;
/// # use bevy_ecs::system::IntoSystem;
/// # use bevy_ecs::system::Query;
/// # /// #
/// # #[derive(Debug)] /// # #[derive(Component, Debug)]
/// # struct Color {}; /// # struct Color {};
/// # #[derive(Component)]
/// # struct Style {}; /// # struct Style {};
/// # /// #
/// fn print_cool_entity_system(query: Query<Entity, Or<(Changed<Color>, Changed<Style>)>>) { /// fn print_cool_entity_system(query: Query<Entity, Or<(Changed<Color>, Changed<Style>)>>) {
@ -357,11 +359,7 @@ macro_rules! impl_query_filter_tuple {
},)*)) },)*))
} }
#[inline] const IS_DENSE: bool = true $(&& $filter::IS_DENSE)*;
fn is_dense(&self) -> bool {
let ($($filter,)*) = &self.0;
true $(&& $filter.fetch.is_dense())*
}
#[inline] #[inline]
unsafe fn set_table(&mut self, state: &Self::State, table: &Table) { unsafe fn set_table(&mut self, state: &Self::State, table: &Table) {
@ -448,7 +446,6 @@ macro_rules! impl_tick_filter {
$(#[$fetch_meta])* $(#[$fetch_meta])*
pub struct $fetch_name<T> { pub struct $fetch_name<T> {
storage_type: StorageType,
table_ticks: *const UnsafeCell<ComponentTicks>, table_ticks: *const UnsafeCell<ComponentTicks>,
entity_table_rows: *const usize, entity_table_rows: *const usize,
marker: PhantomData<T>, marker: PhantomData<T>,
@ -461,7 +458,6 @@ macro_rules! impl_tick_filter {
$(#[$state_meta])* $(#[$state_meta])*
pub struct $state_name<T> { pub struct $state_name<T> {
component_id: ComponentId, component_id: ComponentId,
storage_type: StorageType,
marker: PhantomData<T>, marker: PhantomData<T>,
} }
@ -474,10 +470,8 @@ macro_rules! impl_tick_filter {
// SAFETY: this reads the T component. archetype component access and component access are updated to reflect that // SAFETY: this reads the T component. archetype component access and component access are updated to reflect that
unsafe impl<T: Component> FetchState for $state_name<T> { unsafe impl<T: Component> FetchState for $state_name<T> {
fn init(world: &mut World) -> Self { fn init(world: &mut World) -> Self {
let component_info = world.components.get_or_insert_info::<T>();
Self { Self {
component_id: component_info.id(), component_id: world.init_component::<T>(),
storage_type: component_info.storage_type(),
marker: PhantomData, marker: PhantomData,
} }
} }
@ -517,7 +511,6 @@ macro_rules! impl_tick_filter {
unsafe fn init(world: &World, state: &Self::State, last_change_tick: u32, change_tick: u32) -> Self { unsafe fn init(world: &World, state: &Self::State, last_change_tick: u32, change_tick: u32) -> Self {
let mut value = Self { let mut value = Self {
storage_type: state.storage_type,
table_ticks: ptr::null::<UnsafeCell<ComponentTicks>>(), table_ticks: ptr::null::<UnsafeCell<ComponentTicks>>(),
entities: ptr::null::<Entity>(), entities: ptr::null::<Entity>(),
entity_table_rows: ptr::null::<usize>(), entity_table_rows: ptr::null::<usize>(),
@ -526,7 +519,7 @@ macro_rules! impl_tick_filter {
last_change_tick, last_change_tick,
change_tick, change_tick,
}; };
if state.storage_type == StorageType::SparseSet { if T::Storage::STORAGE_TYPE == StorageType::SparseSet {
value.sparse_set = world value.sparse_set = world
.storages() .storages()
.sparse_sets .sparse_sets
@ -535,10 +528,12 @@ macro_rules! impl_tick_filter {
value value
} }
#[inline] const IS_DENSE: bool = {
fn is_dense(&self) -> bool { match T::Storage::STORAGE_TYPE {
self.storage_type == StorageType::Table StorageType::Table => true,
} StorageType::SparseSet => false,
}
};
unsafe fn set_table(&mut self, state: &Self::State, table: &Table) { unsafe fn set_table(&mut self, state: &Self::State, table: &Table) {
self.table_ticks = table self.table_ticks = table
@ -547,7 +542,7 @@ macro_rules! impl_tick_filter {
} }
unsafe fn set_archetype(&mut self, state: &Self::State, archetype: &Archetype, tables: &Tables) { unsafe fn set_archetype(&mut self, state: &Self::State, archetype: &Archetype, tables: &Tables) {
match state.storage_type { match T::Storage::STORAGE_TYPE {
StorageType::Table => { StorageType::Table => {
self.entity_table_rows = archetype.entity_table_rows().as_ptr(); self.entity_table_rows = archetype.entity_table_rows().as_ptr();
let table = &tables[archetype.table_id()]; let table = &tables[archetype.table_id()];
@ -564,7 +559,7 @@ macro_rules! impl_tick_filter {
} }
unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> bool { unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> bool {
match self.storage_type { match T::Storage::STORAGE_TYPE {
StorageType::Table => { StorageType::Table => {
let table_row = *self.entity_table_rows.add(archetype_index); let table_row = *self.entity_table_rows.add(archetype_index);
$is_detected(&*(&*self.table_ticks.add(table_row)).get(), self.last_change_tick, self.change_tick) $is_detected(&*(&*self.table_ticks.add(table_row)).get(), self.last_change_tick, self.change_tick)
@ -596,13 +591,13 @@ impl_tick_filter!(
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # use bevy_ecs::component::Component;
/// # use bevy_ecs::query::Added;
/// # use bevy_ecs::system::IntoSystem; /// # use bevy_ecs::system::IntoSystem;
/// # use bevy_ecs::system::Query; /// # use bevy_ecs::system::Query;
/// # use bevy_ecs::query::Added;
/// # /// #
/// # #[derive(Debug)] /// # #[derive(Component, Debug)]
/// # struct Name {}; /// # struct Name {};
/// # struct Transform {};
/// ///
/// fn print_add_name_component(query: Query<&Name, Added<Name>>) { /// fn print_add_name_component(query: Query<&Name, Added<Name>>) {
/// for name in query.iter() { /// for name in query.iter() {
@ -637,12 +632,14 @@ impl_tick_filter!(
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # use bevy_ecs::component::Component;
/// # use bevy_ecs::query::Changed;
/// # use bevy_ecs::system::IntoSystem; /// # use bevy_ecs::system::IntoSystem;
/// # use bevy_ecs::system::Query; /// # use bevy_ecs::system::Query;
/// # use bevy_ecs::query::Changed;
/// # /// #
/// # #[derive(Debug)] /// # #[derive(Component, Debug)]
/// # struct Name {}; /// # struct Name {};
/// # #[derive(Component)]
/// # struct Transform {}; /// # struct Transform {};
/// ///
/// fn print_moving_objects_system(query: Query<&Name, Changed<Transform>>) { /// fn print_moving_objects_system(query: Query<&Name, Changed<Transform>>) {

View file

@ -24,7 +24,6 @@ where
filter: F::Fetch, filter: F::Fetch,
current_len: usize, current_len: usize,
current_index: usize, current_index: usize,
is_dense: bool,
} }
impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIter<'w, 's, Q, F> impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIter<'w, 's, Q, F>
@ -60,7 +59,6 @@ where
query_state, query_state,
tables: &world.storages().tables, tables: &world.storages().tables,
archetypes: &world.archetypes, archetypes: &world.archetypes,
is_dense: fetch.is_dense() && filter.is_dense(),
fetch, fetch,
filter, filter,
table_id_iter: query_state.matched_table_ids.iter(), table_id_iter: query_state.matched_table_ids.iter(),
@ -76,7 +74,7 @@ where
// NOTE: this mimics the behavior of `QueryIter::next()`, except that it // NOTE: this mimics the behavior of `QueryIter::next()`, except that it
// never gets a `Self::Item`. // never gets a `Self::Item`.
unsafe { unsafe {
if self.is_dense { if Q::Fetch::IS_DENSE && F::Fetch::IS_DENSE {
loop { loop {
if self.current_index == self.current_len { if self.current_index == self.current_len {
let table_id = match self.table_id_iter.next() { let table_id = match self.table_id_iter.next() {
@ -139,7 +137,7 @@ where
#[inline(always)] #[inline(always)]
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
unsafe { unsafe {
if self.is_dense { if Q::Fetch::IS_DENSE && F::Fetch::IS_DENSE {
loop { loop {
if self.current_index == self.current_len { if self.current_index == self.current_len {
let table_id = self.table_id_iter.next()?; let table_id = self.table_id_iter.next()?;
@ -407,7 +405,6 @@ struct QueryIterationCursor<'s, Q: WorldQuery, F: WorldQuery> {
filter: F::Fetch, filter: F::Fetch,
current_len: usize, current_len: usize,
current_index: usize, current_index: usize,
is_dense: bool,
} }
impl<'s, Q: WorldQuery, F: WorldQuery> Clone for QueryIterationCursor<'s, Q, F> impl<'s, Q: WorldQuery, F: WorldQuery> Clone for QueryIterationCursor<'s, Q, F>
@ -423,7 +420,6 @@ where
filter: self.filter.clone(), filter: self.filter.clone(),
current_len: self.current_len, current_len: self.current_len,
current_index: self.current_index, current_index: self.current_index,
is_dense: self.is_dense,
} }
} }
} }
@ -464,7 +460,6 @@ where
change_tick, change_tick,
); );
QueryIterationCursor { QueryIterationCursor {
is_dense: fetch.is_dense() && filter.is_dense(),
fetch, fetch,
filter, filter,
table_id_iter: query_state.matched_table_ids.iter(), table_id_iter: query_state.matched_table_ids.iter(),
@ -478,7 +473,7 @@ where
#[inline] #[inline]
unsafe fn peek_last<'w>(&mut self) -> Option<<Q::Fetch as Fetch<'w, 's>>::Item> { unsafe fn peek_last<'w>(&mut self) -> Option<<Q::Fetch as Fetch<'w, 's>>::Item> {
if self.current_index > 0 { if self.current_index > 0 {
if self.is_dense { if Q::Fetch::IS_DENSE && F::Fetch::IS_DENSE {
Some(self.fetch.table_fetch(self.current_index - 1)) Some(self.fetch.table_fetch(self.current_index - 1))
} else { } else {
Some(self.fetch.archetype_fetch(self.current_index - 1)) Some(self.fetch.archetype_fetch(self.current_index - 1))
@ -498,7 +493,7 @@ where
archetypes: &'w Archetypes, archetypes: &'w Archetypes,
query_state: &'s QueryState<Q, F>, query_state: &'s QueryState<Q, F>,
) -> Option<<Q::Fetch as Fetch<'w, 's>>::Item> { ) -> Option<<Q::Fetch as Fetch<'w, 's>>::Item> {
if self.is_dense { if Q::Fetch::IS_DENSE && F::Fetch::IS_DENSE {
loop { loop {
if self.current_index == self.current_len { if self.current_index == self.current_len {
let table_id = self.table_id_iter.next()?; let table_id = self.table_id_iter.next()?;

View file

@ -12,16 +12,17 @@ pub use state::*;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{self as bevy_ecs, component::Component, world::World};
component::{ComponentDescriptor, StorageType},
world::World,
};
#[derive(Debug, Eq, PartialEq)] #[derive(Component, Debug, Eq, PartialEq)]
struct A(usize); struct A(usize);
#[derive(Debug, Eq, PartialEq)] #[derive(Component, Debug, Eq, PartialEq)]
struct B(usize); struct B(usize);
#[derive(Component, Debug, Eq, PartialEq)]
#[component(storage = "SparseSet")]
struct Sparse(usize);
#[test] #[test]
fn query() { fn query() {
let mut world = World::new(); let mut world = World::new();
@ -139,13 +140,10 @@ mod tests {
#[test] #[test]
fn query_iter_combinations_sparse() { fn query_iter_combinations_sparse() {
let mut world = World::new(); let mut world = World::new();
world
.register_component(ComponentDescriptor::new::<A>(StorageType::SparseSet))
.unwrap();
world.spawn_batch((1..=4).map(|i| (A(i),))); world.spawn_batch((1..=4).map(|i| (Sparse(i),)));
let mut query = world.query::<&mut A>(); let mut query = world.query::<&mut Sparse>();
let mut combinations = query.iter_combinations_mut(&mut world); let mut combinations = query.iter_combinations_mut(&mut world);
while let Some([mut a, mut b, mut c]) = combinations.fetch_next() { while let Some([mut a, mut b, mut c]) = combinations.fetch_next() {
a.0 += 10; a.0 += 10;
@ -153,15 +151,15 @@ mod tests {
c.0 += 1000; c.0 += 1000;
} }
let mut query = world.query::<&A>(); let mut query = world.query::<&Sparse>();
let values: Vec<[&A; 3]> = query.iter_combinations(&world).collect(); let values: Vec<[&Sparse; 3]> = query.iter_combinations(&world).collect();
assert_eq!( assert_eq!(
values, values,
vec![ vec![
[&A(31), &A(212), &A(1203)], [&Sparse(31), &Sparse(212), &Sparse(1203)],
[&A(31), &A(212), &A(3004)], [&Sparse(31), &Sparse(212), &Sparse(3004)],
[&A(31), &A(1203), &A(3004)], [&Sparse(31), &Sparse(1203), &Sparse(3004)],
[&A(212), &A(1203), &A(3004)] [&Sparse(212), &Sparse(1203), &Sparse(3004)]
] ]
); );
} }
@ -169,17 +167,17 @@ mod tests {
#[test] #[test]
fn multi_storage_query() { fn multi_storage_query() {
let mut world = World::new(); let mut world = World::new();
world
.register_component(ComponentDescriptor::new::<A>(StorageType::SparseSet))
.unwrap();
world.spawn().insert_bundle((A(1), B(2))); world.spawn().insert_bundle((Sparse(1), B(2)));
world.spawn().insert_bundle((A(2),)); world.spawn().insert_bundle((Sparse(2),));
let values = world.query::<&A>().iter(&world).collect::<Vec<&A>>(); let values = world
assert_eq!(values, vec![&A(1), &A(2)]); .query::<&Sparse>()
.iter(&world)
.collect::<Vec<&Sparse>>();
assert_eq!(values, vec![&Sparse(1), &Sparse(2)]);
for (_a, mut b) in world.query::<(&A, &mut B)>().iter_mut(&mut world) { for (_a, mut b) in world.query::<(&Sparse, &mut B)>().iter_mut(&mut world) {
b.0 = 3; b.0 = 3;
} }

View file

@ -486,7 +486,7 @@ where
<Q::Fetch as Fetch>::init(world, &self.fetch_state, last_change_tick, change_tick); <Q::Fetch as Fetch>::init(world, &self.fetch_state, last_change_tick, change_tick);
let mut filter = let mut filter =
<F::Fetch as Fetch>::init(world, &self.filter_state, last_change_tick, change_tick); <F::Fetch as Fetch>::init(world, &self.filter_state, last_change_tick, change_tick);
if fetch.is_dense() && filter.is_dense() { if Q::Fetch::IS_DENSE && F::Fetch::IS_DENSE {
let tables = &world.storages().tables; let tables = &world.storages().tables;
for table_id in self.matched_table_ids.iter() { for table_id in self.matched_table_ids.iter() {
let table = &tables[*table_id]; let table = &tables[*table_id];
@ -541,12 +541,7 @@ where
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant: // NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual // QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
task_pool.scope(|scope| { task_pool.scope(|scope| {
let fetch = if Q::Fetch::IS_DENSE && F::Fetch::IS_DENSE {
<Q::Fetch as Fetch>::init(world, &self.fetch_state, last_change_tick, change_tick);
let filter =
<F::Fetch as Fetch>::init(world, &self.filter_state, last_change_tick, change_tick);
if fetch.is_dense() && filter.is_dense() {
let tables = &world.storages().tables; let tables = &world.storages().tables;
for table_id in self.matched_table_ids.iter() { for table_id in self.matched_table_ids.iter() {
let table = &tables[*table_id]; let table = &tables[*table_id];

View file

@ -321,6 +321,11 @@ mod tests {
}; };
use async_channel::Receiver; use async_channel::Receiver;
use crate as bevy_ecs;
use crate::component::Component;
#[derive(Component)]
struct W<T>(T);
fn receive_events(world: &World) -> Vec<SchedulingEvent> { fn receive_events(world: &World) -> Vec<SchedulingEvent> {
let mut events = Vec::new(); let mut events = Vec::new();
while let Ok(event) = world while let Ok(event) = world
@ -381,9 +386,9 @@ mod tests {
#[test] #[test]
fn queries() { fn queries() {
let mut world = World::new(); let mut world = World::new();
world.spawn().insert(0usize); world.spawn().insert(W(0usize));
fn wants_mut(_: Query<&mut usize>) {} fn wants_mut(_: Query<&mut W<usize>>) {}
fn wants_ref(_: Query<&usize>) {} fn wants_ref(_: Query<&W<usize>>) {}
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(wants_mut) .with_system(wants_mut)
.with_system(wants_mut); .with_system(wants_mut);
@ -406,9 +411,9 @@ mod tests {
stage.run(&mut world); stage.run(&mut world);
assert_eq!(receive_events(&world), vec![StartedSystems(2),]); assert_eq!(receive_events(&world), vec![StartedSystems(2),]);
let mut world = World::new(); let mut world = World::new();
world.spawn().insert_bundle((0usize, 0u32, 0f32)); world.spawn().insert_bundle((W(0usize), W(0u32), W(0f32)));
fn wants_mut_usize(_: Query<(&mut usize, &f32)>) {} fn wants_mut_usize(_: Query<(&mut W<usize>, &W<f32>)>) {}
fn wants_mut_u32(_: Query<(&mut u32, &f32)>) {} fn wants_mut_u32(_: Query<(&mut W<u32>, &W<f32>)>) {}
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(wants_mut_usize) .with_system(wants_mut_usize)
.with_system(wants_mut_u32); .with_system(wants_mut_u32);

View file

@ -905,6 +905,11 @@ mod tests {
world::World, world::World,
}; };
use crate as bevy_ecs;
use crate::component::Component;
#[derive(Component)]
struct W<T>(T);
fn make_exclusive(tag: usize) -> impl FnMut(&mut World) { fn make_exclusive(tag: usize) -> impl FnMut(&mut World) {
move |world| world.get_resource_mut::<Vec<usize>>().unwrap().push(tag) move |world| world.get_resource_mut::<Vec<usize>>().unwrap().push(tag)
} }
@ -1573,7 +1578,7 @@ mod tests {
fn empty() {} fn empty() {}
fn resource(_: ResMut<usize>) {} fn resource(_: ResMut<usize>) {}
fn component(_: Query<&mut f32>) {} fn component(_: Query<&mut W<f32>>) {}
let mut world = World::new(); let mut world = World::new();
@ -1957,7 +1962,7 @@ mod tests {
stage.run(&mut world); stage.run(&mut world);
assert_eq!(*world.get_resource::<usize>().unwrap(), 1); assert_eq!(*world.get_resource::<usize>().unwrap(), 1);
world.get_entity_mut(entity).unwrap().insert(1); world.get_entity_mut(entity).unwrap().insert(W(1));
stage.run(&mut world); stage.run(&mut world);
assert_eq!(*world.get_resource::<usize>().unwrap(), 1); assert_eq!(*world.get_resource::<usize>().unwrap(), 1);
} }
@ -1980,7 +1985,7 @@ mod tests {
stage.run(&mut world); stage.run(&mut world);
assert_eq!(*world.get_resource::<usize>().unwrap(), 1); assert_eq!(*world.get_resource::<usize>().unwrap(), 1);
world.get_entity_mut(entity).unwrap().insert(1); world.get_entity_mut(entity).unwrap().insert(W(1));
stage.run(&mut world); stage.run(&mut world);
assert_eq!(*world.get_resource::<usize>().unwrap(), 1); assert_eq!(*world.get_resource::<usize>().unwrap(), 1);
} }
@ -1991,7 +1996,7 @@ mod tests {
const MAX_DELTA: u32 = (u32::MAX / 4) * 3; const MAX_DELTA: u32 = (u32::MAX / 4) * 3;
let mut world = World::new(); let mut world = World::new();
world.spawn().insert(0usize); world.spawn().insert(W(0usize));
*world.change_tick.get_mut() += MAX_DELTA + 1; *world.change_tick.get_mut() += MAX_DELTA + 1;
let mut stage = SystemStage::parallel(); let mut stage = SystemStage::parallel();
@ -2001,7 +2006,7 @@ mod tests {
// Overflow twice // Overflow twice
for _ in 0..10 { for _ in 0..10 {
stage.run(&mut world); stage.run(&mut world);
for tracker in world.query::<ChangeTrackers<usize>>().iter(&world) { for tracker in world.query::<ChangeTrackers<W<usize>>>().iter(&world) {
let time_since_last_check = tracker let time_since_last_check = tracker
.change_tick .change_tick
.wrapping_sub(tracker.component_ticks.added); .wrapping_sub(tracker.component_ticks.added);
@ -2018,6 +2023,9 @@ mod tests {
#[test] #[test]
fn change_query_wrapover() { fn change_query_wrapover() {
use crate::{self as bevy_ecs, component::Component};
#[derive(Component)]
struct C; struct C;
let mut world = World::new(); let mut world = World::new();
@ -2053,6 +2061,9 @@ mod tests {
#[test] #[test]
fn run_criteria_with_query() { fn run_criteria_with_query() {
use crate::{self as bevy_ecs, component::Component};
#[derive(Component)]
struct Foo; struct Foo;
fn even_number_of_entities_critiera(query: Query<&Foo>) -> ShouldRun { fn even_number_of_entities_critiera(query: Query<&Foo>) -> ShouldRun {
@ -2089,6 +2100,9 @@ mod tests {
#[test] #[test]
fn stage_run_criteria_with_query() { fn stage_run_criteria_with_query() {
use crate::{self as bevy_ecs, component::Component};
#[derive(Component)]
struct Foo; struct Foo;
fn even_number_of_entities_critiera(query: Query<&Foo>) -> ShouldRun { fn even_number_of_entities_critiera(query: Query<&Foo>) -> ShouldRun {

View file

@ -1,5 +1,4 @@
use crate::{ use crate::{
component::Component,
schedule::{ schedule::{
RunCriteriaDescriptor, RunCriteriaDescriptorCoercion, RunCriteriaLabel, ShouldRun, RunCriteriaDescriptor, RunCriteriaDescriptorCoercion, RunCriteriaLabel, ShouldRun,
SystemSet, SystemSet,
@ -9,6 +8,9 @@ use crate::{
use std::{any::TypeId, fmt::Debug, hash::Hash}; use std::{any::TypeId, fmt::Debug, hash::Hash};
use thiserror::Error; use thiserror::Error;
pub trait StateData: Send + Sync + Clone + Eq + Debug + Hash + 'static {}
impl<T> StateData for T where T: Send + Sync + Clone + Eq + Debug + Hash + 'static {}
/// ### Stack based state machine /// ### Stack based state machine
/// ///
/// This state machine has four operations: Push, Pop, Set and Replace. /// This state machine has four operations: Push, Pop, Set and Replace.
@ -17,7 +19,7 @@ use thiserror::Error;
/// * Set replaces the active state with a new one /// * Set replaces the active state with a new one
/// * Replace unwinds the state stack, and replaces the entire stack with a single new state /// * Replace unwinds the state stack, and replaces the entire stack with a single new state
#[derive(Debug)] #[derive(Debug)]
pub struct State<T: Component + Clone + Eq> { pub struct State<T: StateData> {
transition: Option<StateTransition<T>>, transition: Option<StateTransition<T>>,
stack: Vec<T>, stack: Vec<T>,
scheduled: Option<ScheduledOperation<T>>, scheduled: Option<ScheduledOperation<T>>,
@ -25,7 +27,7 @@ pub struct State<T: Component + Clone + Eq> {
} }
#[derive(Debug)] #[derive(Debug)]
enum StateTransition<T: Component + Clone + Eq> { enum StateTransition<T: StateData> {
PreStartup, PreStartup,
Startup, Startup,
// The parameter order is always (leaving, entering) // The parameter order is always (leaving, entering)
@ -37,7 +39,7 @@ enum StateTransition<T: Component + Clone + Eq> {
} }
#[derive(Debug)] #[derive(Debug)]
enum ScheduledOperation<T: Component + Clone + Eq> { enum ScheduledOperation<T: StateData> {
Set(T), Set(T),
Replace(T), Replace(T),
Pop, Pop,
@ -58,7 +60,7 @@ enum StateCallback {
impl StateCallback { impl StateCallback {
fn into_label<T>(self, state: T) -> StateRunCriteriaLabel<T> fn into_label<T>(self, state: T) -> StateRunCriteriaLabel<T>
where where
T: Component + Debug + Clone + Eq + Hash, T: StateData,
{ {
StateRunCriteriaLabel(state, self) StateRunCriteriaLabel(state, self)
} }
@ -68,7 +70,7 @@ impl StateCallback {
struct StateRunCriteriaLabel<T>(T, StateCallback); struct StateRunCriteriaLabel<T>(T, StateCallback);
impl<T> RunCriteriaLabel for StateRunCriteriaLabel<T> impl<T> RunCriteriaLabel for StateRunCriteriaLabel<T>
where where
T: Component + Debug + Clone + Eq + Hash, T: StateData,
{ {
fn dyn_clone(&self) -> Box<dyn RunCriteriaLabel> { fn dyn_clone(&self) -> Box<dyn RunCriteriaLabel> {
Box::new(self.clone()) Box::new(self.clone())
@ -91,7 +93,7 @@ impl DriverLabel {
impl<T> State<T> impl<T> State<T>
where where
T: Component + Debug + Clone + Eq + Hash, T: StateData,
{ {
pub fn on_update(s: T) -> RunCriteriaDescriptor { pub fn on_update(s: T) -> RunCriteriaDescriptor {
(|state: Res<State<T>>, pred: Local<Option<T>>| { (|state: Res<State<T>>, pred: Local<Option<T>>| {
@ -386,10 +388,7 @@ pub enum StateError {
StackEmpty, StackEmpty,
} }
fn should_run_adapter<T: Component + Clone + Eq>( fn should_run_adapter<T: StateData>(In(cmp_result): In<bool>, state: Res<State<T>>) -> ShouldRun {
In(cmp_result): In<bool>,
state: Res<State<T>>,
) -> ShouldRun {
if state.end_next_loop { if state.end_next_loop {
return ShouldRun::No; return ShouldRun::No;
} }
@ -400,7 +399,7 @@ fn should_run_adapter<T: Component + Clone + Eq>(
} }
} }
fn state_cleaner<T: Component + Clone + Eq>( fn state_cleaner<T: StateData>(
mut state: ResMut<State<T>>, mut state: ResMut<State<T>>,
mut prep_exit: Local<bool>, mut prep_exit: Local<bool>,
) -> ShouldRun { ) -> ShouldRun {

View file

@ -1,11 +1,7 @@
use crate::{ use crate::schedule::{
component::Component, AmbiguitySetLabel, BoxedAmbiguitySetLabel, BoxedSystemLabel, IntoRunCriteria,
schedule::{ RunCriteriaDescriptorOrLabel, State, StateData, SystemDescriptor, SystemLabel,
AmbiguitySetLabel, BoxedAmbiguitySetLabel, BoxedSystemLabel, IntoRunCriteria,
RunCriteriaDescriptorOrLabel, State, SystemDescriptor, SystemLabel,
},
}; };
use std::{fmt::Debug, hash::Hash};
use super::IntoSystemDescriptor; use super::IntoSystemDescriptor;
@ -39,49 +35,49 @@ impl SystemSet {
pub fn on_update<T>(s: T) -> SystemSet pub fn on_update<T>(s: T) -> SystemSet
where where
T: Component + Debug + Clone + Eq + Hash, T: StateData,
{ {
Self::new().with_run_criteria(State::<T>::on_update(s)) Self::new().with_run_criteria(State::<T>::on_update(s))
} }
pub fn on_inactive_update<T>(s: T) -> SystemSet pub fn on_inactive_update<T>(s: T) -> SystemSet
where where
T: Component + Debug + Clone + Eq + Hash, T: StateData,
{ {
Self::new().with_run_criteria(State::<T>::on_inactive_update(s)) Self::new().with_run_criteria(State::<T>::on_inactive_update(s))
} }
pub fn on_in_stack_update<T>(s: T) -> SystemSet pub fn on_in_stack_update<T>(s: T) -> SystemSet
where where
T: Component + Debug + Clone + Eq + Hash, T: StateData,
{ {
Self::new().with_run_criteria(State::<T>::on_in_stack_update(s)) Self::new().with_run_criteria(State::<T>::on_in_stack_update(s))
} }
pub fn on_enter<T>(s: T) -> SystemSet pub fn on_enter<T>(s: T) -> SystemSet
where where
T: Component + Debug + Clone + Eq + Hash, T: StateData,
{ {
Self::new().with_run_criteria(State::<T>::on_enter(s)) Self::new().with_run_criteria(State::<T>::on_enter(s))
} }
pub fn on_exit<T>(s: T) -> SystemSet pub fn on_exit<T>(s: T) -> SystemSet
where where
T: Component + Debug + Clone + Eq + Hash, T: StateData,
{ {
Self::new().with_run_criteria(State::<T>::on_exit(s)) Self::new().with_run_criteria(State::<T>::on_exit(s))
} }
pub fn on_pause<T>(s: T) -> SystemSet pub fn on_pause<T>(s: T) -> SystemSet
where where
T: Component + Debug + Clone + Eq + Hash, T: StateData,
{ {
Self::new().with_run_criteria(State::<T>::on_pause(s)) Self::new().with_run_criteria(State::<T>::on_pause(s))
} }
pub fn on_resume<T>(s: T) -> SystemSet pub fn on_resume<T>(s: T) -> SystemSet
where where
T: Component + Debug + Clone + Eq + Hash, T: StateData,
{ {
Self::new().with_run_criteria(State::<T>::on_resume(s)) Self::new().with_run_criteria(State::<T>::on_resume(s))
} }

View file

@ -522,12 +522,18 @@ impl IndexMut<TableId> for Tables {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate as bevy_ecs;
use crate::component::Component;
use crate::storage::Storages;
use crate::{component::Components, entity::Entity, storage::Table}; use crate::{component::Components, entity::Entity, storage::Table};
#[derive(Component)]
struct W<T>(T);
#[test] #[test]
fn table() { fn table() {
let mut components = Components::default(); let mut components = Components::default();
let component_id = components.get_or_insert_id::<usize>(); let mut storages = Storages::default();
let component_id = components.init_component::<W<usize>>(&mut storages);
let columns = &[component_id]; let columns = &[component_id];
let mut table = Table::with_capacity(0, columns.len()); let mut table = Table::with_capacity(0, columns.len());
table.add_column(components.get_info(component_id).unwrap()); table.add_column(components.get_info(component_id).unwrap());

View file

@ -10,6 +10,8 @@ use bevy_utils::tracing::{error, warn};
pub use command_queue::CommandQueue; pub use command_queue::CommandQueue;
use std::marker::PhantomData; use std::marker::PhantomData;
use super::Resource;
/// A [`World`] mutation. /// A [`World`] mutation.
pub trait Command: Send + Sync + 'static { pub trait Command: Send + Sync + 'static {
fn write(self, world: &mut World); fn write(self, world: &mut World);
@ -58,6 +60,13 @@ impl<'w, 's> Commands<'w, 's> {
/// ``` /// ```
/// use bevy_ecs::prelude::*; /// use bevy_ecs::prelude::*;
/// ///
/// #[derive(Component)]
/// struct Label(&'static str);
/// #[derive(Component)]
/// struct Strength(u32);
/// #[derive(Component)]
/// struct Agility(u32);
///
/// fn example_system(mut commands: Commands) { /// fn example_system(mut commands: Commands) {
/// // Create a new empty entity and retrieve its id. /// // Create a new empty entity and retrieve its id.
/// let empty_entity = commands.spawn().id(); /// let empty_entity = commands.spawn().id();
@ -65,9 +74,9 @@ impl<'w, 's> Commands<'w, 's> {
/// // Create another empty entity, then add some component to it /// // Create another empty entity, then add some component to it
/// commands.spawn() /// commands.spawn()
/// // adds a new component bundle to the entity /// // adds a new component bundle to the entity
/// .insert_bundle((1usize, 2u32)) /// .insert_bundle((Strength(1), Agility(2)))
/// // adds a single component to the entity /// // adds a single component to the entity
/// .insert("hello world"); /// .insert(Label("hello world"));
/// } /// }
/// # example_system.system(); /// # example_system.system();
/// ``` /// ```
@ -114,8 +123,16 @@ impl<'w, 's> Commands<'w, 's> {
/// ``` /// ```
/// use bevy_ecs::prelude::*; /// use bevy_ecs::prelude::*;
/// ///
/// #[derive(Component)]
/// struct Component1; /// struct Component1;
/// #[derive(Component)]
/// struct Component2; /// struct Component2;
/// #[derive(Component)]
/// struct Label(&'static str);
/// #[derive(Component)]
/// struct Strength(u32);
/// #[derive(Component)]
/// struct Agility(u32);
/// ///
/// #[derive(Bundle)] /// #[derive(Bundle)]
/// struct ExampleBundle { /// struct ExampleBundle {
@ -134,9 +151,9 @@ impl<'w, 's> Commands<'w, 's> {
/// // Create a new entity with two components using a "tuple bundle". /// // Create a new entity with two components using a "tuple bundle".
/// .spawn_bundle((Component1, Component2)) /// .spawn_bundle((Component1, Component2))
/// // spawn_bundle returns a builder, so you can insert more bundles like this: /// // spawn_bundle returns a builder, so you can insert more bundles like this:
/// .insert_bundle((1usize, 2u32)) /// .insert_bundle((Strength(1), Agility(2)))
/// // or insert single components like this: /// // or insert single components like this:
/// .insert("hello world"); /// .insert(Label("hello world"));
/// } /// }
/// # example_system.system(); /// # example_system.system();
/// ``` /// ```
@ -153,15 +170,22 @@ impl<'w, 's> Commands<'w, 's> {
/// ``` /// ```
/// use bevy_ecs::prelude::*; /// use bevy_ecs::prelude::*;
/// ///
/// #[derive(Component)]
/// struct Label(&'static str);
/// #[derive(Component)]
/// struct Strength(u32);
/// #[derive(Component)]
/// struct Agility(u32);
/// fn example_system(mut commands: Commands) { /// fn example_system(mut commands: Commands) {
/// // Create a new, empty entity /// // Create a new, empty entity
/// let entity = commands.spawn().id(); /// let entity = commands.spawn().id();
/// ///
/// commands.entity(entity) /// commands.entity(entity)
/// // adds a new component bundle to the entity /// // adds a new component bundle to the entity
/// .insert_bundle((1usize, 2u32)) /// .insert_bundle((Strength(1), Agility(2)))
/// // adds a single component to the entity /// // adds a single component to the entity
/// .insert("hello world"); /// .insert(Label("hello world"));
/// } /// }
/// # example_system.system(); /// # example_system.system();
/// ``` /// ```
@ -190,7 +214,9 @@ impl<'w, 's> Commands<'w, 's> {
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # /// #
/// # #[derive(Component)]
/// # struct Name(String); /// # struct Name(String);
/// # #[derive(Component)]
/// # struct Score(u32); /// # struct Score(u32);
/// # /// #
/// # fn system(mut commands: Commands) { /// # fn system(mut commands: Commands) {
@ -256,7 +282,7 @@ impl<'w, 's> Commands<'w, 's> {
/// # } /// # }
/// # system.system(); /// # system.system();
/// ``` /// ```
pub fn insert_resource<T: Component>(&mut self, resource: T) { pub fn insert_resource<T: Resource>(&mut self, resource: T) {
self.queue.push(InsertResource { resource }) self.queue.push(InsertResource { resource })
} }
@ -279,7 +305,7 @@ impl<'w, 's> Commands<'w, 's> {
/// # } /// # }
/// # system.system(); /// # system.system();
/// ``` /// ```
pub fn remove_resource<T: Component>(&mut self) { pub fn remove_resource<T: Resource>(&mut self) {
self.queue.push(RemoveResource::<T> { self.queue.push(RemoveResource::<T> {
phantom: PhantomData, phantom: PhantomData,
}); });
@ -294,8 +320,11 @@ impl<'w, 's> Commands<'w, 's> {
/// use bevy_ecs::system::InsertBundle; /// use bevy_ecs::system::InsertBundle;
/// # /// #
/// # struct PlayerEntity { entity: Entity } /// # struct PlayerEntity { entity: Entity }
/// # #[derive(Component)]
/// # struct Health(u32); /// # struct Health(u32);
/// # #[derive(Component)]
/// # struct Strength(u32); /// # struct Strength(u32);
/// # #[derive(Component)]
/// # struct Defense(u32); /// # struct Defense(u32);
/// # /// #
/// # #[derive(Bundle)] /// # #[derive(Bundle)]
@ -354,8 +383,11 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # /// #
/// # struct PlayerEntity { entity: Entity } /// # struct PlayerEntity { entity: Entity }
/// # #[derive(Component)]
/// # struct Health(u32); /// # struct Health(u32);
/// # #[derive(Component)]
/// # struct Strength(u32); /// # struct Strength(u32);
/// # #[derive(Component)]
/// # struct Defense(u32); /// # struct Defense(u32);
/// # /// #
/// # #[derive(Bundle)] /// # #[derive(Bundle)]
@ -401,7 +433,9 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # /// #
/// # #[derive(Component)]
/// # struct Component1; /// # struct Component1;
/// # #[derive(Component)]
/// # struct Component2; /// # struct Component2;
/// # /// #
/// fn example_system(mut commands: Commands) { /// fn example_system(mut commands: Commands) {
@ -436,8 +470,10 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
/// # /// #
/// # struct PlayerEntity { entity: Entity } /// # struct PlayerEntity { entity: Entity }
/// # /// #
/// # #[derive(Component)]
/// struct Dummy;
/// # #[derive(Bundle)] /// # #[derive(Bundle)]
/// # struct CombatBundle { a: u32 }; // dummy field, unit bundles are not permitted. /// # struct CombatBundle { a: Dummy }; // dummy field, unit bundles are not permitted.
/// # /// #
/// fn remove_combat_stats_system(mut commands: Commands, player: Res<PlayerEntity>) { /// fn remove_combat_stats_system(mut commands: Commands, player: Res<PlayerEntity>) {
/// commands.entity(player.entity).remove_bundle::<CombatBundle>(); /// commands.entity(player.entity).remove_bundle::<CombatBundle>();
@ -465,6 +501,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # /// #
/// # struct TargetEnemy { entity: Entity } /// # struct TargetEnemy { entity: Entity }
/// # #[derive(Component)]
/// # struct Enemy; /// # struct Enemy;
/// # /// #
/// fn convert_enemy_system(mut commands: Commands, enemy: Res<TargetEnemy>) { /// fn convert_enemy_system(mut commands: Commands, enemy: Res<TargetEnemy>) {
@ -675,21 +712,21 @@ where
} }
} }
pub struct InsertResource<T: Component> { pub struct InsertResource<T: Resource> {
pub resource: T, pub resource: T,
} }
impl<T: Component> Command for InsertResource<T> { impl<T: Resource> Command for InsertResource<T> {
fn write(self, world: &mut World) { fn write(self, world: &mut World) {
world.insert_resource(self.resource); world.insert_resource(self.resource);
} }
} }
pub struct RemoveResource<T: Component> { pub struct RemoveResource<T: Resource> {
pub phantom: PhantomData<T>, pub phantom: PhantomData<T>,
} }
impl<T: Component> Command for RemoveResource<T> { impl<T: Resource> Command for RemoveResource<T> {
fn write(self, world: &mut World) { fn write(self, world: &mut World) {
world.remove_resource::<T>(); world.remove_resource::<T>();
} }
@ -699,7 +736,8 @@ impl<T: Component> Command for RemoveResource<T> {
#[allow(clippy::float_cmp, clippy::approx_constant)] #[allow(clippy::float_cmp, clippy::approx_constant)]
mod tests { mod tests {
use crate::{ use crate::{
component::{ComponentDescriptor, StorageType}, self as bevy_ecs,
component::Component,
system::{CommandQueue, Commands}, system::{CommandQueue, Commands},
world::World, world::World,
}; };
@ -708,7 +746,11 @@ mod tests {
Arc, Arc,
}; };
#[derive(Clone, Debug)] #[derive(Component)]
#[component(storage = "SparseSet")]
struct SparseDropCk(DropCk);
#[derive(Component)]
struct DropCk(Arc<AtomicUsize>); struct DropCk(Arc<AtomicUsize>);
impl DropCk { impl DropCk {
fn new_pair() -> (Self, Arc<AtomicUsize>) { fn new_pair() -> (Self, Arc<AtomicUsize>) {
@ -723,19 +765,22 @@ mod tests {
} }
} }
#[derive(Component)]
struct W<T>(T);
#[test] #[test]
fn commands() { fn commands() {
let mut world = World::default(); let mut world = World::default();
let mut command_queue = CommandQueue::default(); let mut command_queue = CommandQueue::default();
let entity = Commands::new(&mut command_queue, &world) let entity = Commands::new(&mut command_queue, &world)
.spawn_bundle((1u32, 2u64)) .spawn_bundle((W(1u32), W(2u64)))
.id(); .id();
command_queue.apply(&mut world); command_queue.apply(&mut world);
assert!(world.entities().len() == 1); assert!(world.entities().len() == 1);
let results = world let results = world
.query::<(&u32, &u64)>() .query::<(&W<u32>, &W<u64>)>()
.iter(&world) .iter(&world)
.map(|(a, b)| (*a, *b)) .map(|(a, b)| (a.0, b.0))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
assert_eq!(results, vec![(1u32, 2u64)]); assert_eq!(results, vec![(1u32, 2u64)]);
// test entity despawn // test entity despawn
@ -746,9 +791,9 @@ mod tests {
} }
command_queue.apply(&mut world); command_queue.apply(&mut world);
let results2 = world let results2 = world
.query::<(&u32, &u64)>() .query::<(&W<u32>, &W<u64>)>()
.iter(&world) .iter(&world)
.map(|(a, b)| (*a, *b)) .map(|(a, b)| (a.0, b.0))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
assert_eq!(results2, vec![]); assert_eq!(results2, vec![]);
} }
@ -757,33 +802,28 @@ mod tests {
fn remove_components() { fn remove_components() {
let mut world = World::default(); let mut world = World::default();
struct DenseDropCk(DropCk);
world
.register_component(ComponentDescriptor::new::<DropCk>(StorageType::SparseSet))
.unwrap();
let mut command_queue = CommandQueue::default(); let mut command_queue = CommandQueue::default();
let (dense_dropck, dense_is_dropped) = DropCk::new_pair(); let (dense_dropck, dense_is_dropped) = DropCk::new_pair();
let dense_dropck = DenseDropCk(dense_dropck);
let (sparse_dropck, sparse_is_dropped) = DropCk::new_pair(); let (sparse_dropck, sparse_is_dropped) = DropCk::new_pair();
let sparse_dropck = SparseDropCk(sparse_dropck);
let entity = Commands::new(&mut command_queue, &world) let entity = Commands::new(&mut command_queue, &world)
.spawn() .spawn()
.insert_bundle((1u32, 2u64, dense_dropck, sparse_dropck)) .insert_bundle((W(1u32), W(2u64), dense_dropck, sparse_dropck))
.id(); .id();
command_queue.apply(&mut world); command_queue.apply(&mut world);
let results_before = world let results_before = world
.query::<(&u32, &u64)>() .query::<(&W<u32>, &W<u64>)>()
.iter(&world) .iter(&world)
.map(|(a, b)| (*a, *b)) .map(|(a, b)| (a.0, b.0))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
assert_eq!(results_before, vec![(1u32, 2u64)]); assert_eq!(results_before, vec![(1u32, 2u64)]);
// test component removal // test component removal
Commands::new(&mut command_queue, &world) Commands::new(&mut command_queue, &world)
.entity(entity) .entity(entity)
.remove::<u32>() .remove::<W<u32>>()
.remove_bundle::<(u32, u64, DenseDropCk, DropCk)>(); .remove_bundle::<(W<u32>, W<u64>, SparseDropCk, DropCk)>();
assert_eq!(dense_is_dropped.load(Ordering::Relaxed), 0); assert_eq!(dense_is_dropped.load(Ordering::Relaxed), 0);
assert_eq!(sparse_is_dropped.load(Ordering::Relaxed), 0); assert_eq!(sparse_is_dropped.load(Ordering::Relaxed), 0);
@ -792,15 +832,15 @@ mod tests {
assert_eq!(sparse_is_dropped.load(Ordering::Relaxed), 1); assert_eq!(sparse_is_dropped.load(Ordering::Relaxed), 1);
let results_after = world let results_after = world
.query::<(&u32, &u64)>() .query::<(&W<u32>, &W<u64>)>()
.iter(&world) .iter(&world)
.map(|(a, b)| (*a, *b)) .map(|(a, b)| (a.0, b.0))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
assert_eq!(results_after, vec![]); assert_eq!(results_after, vec![]);
let results_after_u64 = world let results_after_u64 = world
.query::<&u64>() .query::<&W<u64>>()
.iter(&world) .iter(&world)
.copied() .map(|v| v.0)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
assert_eq!(results_after_u64, vec![]); assert_eq!(results_after_u64, vec![]);
} }

View file

@ -113,36 +113,42 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
self as bevy_ecs,
component::Component,
entity::Entity, entity::Entity,
query::With, query::With,
schedule::{Stage, SystemStage}, schedule::{Stage, SystemStage},
system::{Commands, IntoExclusiveSystem, Query, ResMut}, system::{Commands, IntoExclusiveSystem, Query, ResMut},
world::World, world::World,
}; };
#[derive(Component)]
struct Foo(f32);
#[test] #[test]
fn parallel_with_commands_as_exclusive() { fn parallel_with_commands_as_exclusive() {
let mut world = World::new(); let mut world = World::new();
fn removal( fn removal(
mut commands: Commands, mut commands: Commands,
query: Query<Entity, With<f32>>, query: Query<Entity, With<Foo>>,
mut counter: ResMut<usize>, mut counter: ResMut<usize>,
) { ) {
for entity in query.iter() { for entity in query.iter() {
*counter += 1; *counter += 1;
commands.entity(entity).remove::<f32>(); commands.entity(entity).remove::<Foo>();
} }
} }
let mut stage = SystemStage::parallel().with_system(removal); let mut stage = SystemStage::parallel().with_system(removal);
world.spawn().insert(0.0f32); world.spawn().insert(Foo(0.0f32));
world.insert_resource(0usize); world.insert_resource(0usize);
stage.run(&mut world); stage.run(&mut world);
stage.run(&mut world); stage.run(&mut world);
assert_eq!(*world.get_resource::<usize>().unwrap(), 1); assert_eq!(*world.get_resource::<usize>().unwrap(), 1);
let mut stage = SystemStage::parallel().with_system(removal.exclusive_system()); let mut stage = SystemStage::parallel().with_system(removal.exclusive_system());
world.spawn().insert(0.0f32); world.spawn().insert(Foo(0.0f32));
world.insert_resource(0usize); world.insert_resource(0usize);
stage.run(&mut world); stage.run(&mut world);
stage.run(&mut world); stage.run(&mut world);
@ -151,10 +157,8 @@ mod tests {
#[test] #[test]
fn update_archetype_for_exclusive_system_coerced() { fn update_archetype_for_exclusive_system_coerced() {
struct Foo;
fn spawn_entity(mut commands: crate::prelude::Commands) { fn spawn_entity(mut commands: crate::prelude::Commands) {
commands.spawn().insert(Foo); commands.spawn().insert(Foo(0.0));
} }
fn count_entities(query: Query<&Foo>, mut res: ResMut<Vec<usize>>) { fn count_entities(query: Query<&Foo>, mut res: ResMut<Vec<usize>>) {

View file

@ -13,7 +13,9 @@
//! ``` //! ```
//! # use bevy_ecs::prelude::*; //! # use bevy_ecs::prelude::*;
//! # //! #
//! # #[derive(Component)]
//! # struct Player { alive: bool } //! # struct Player { alive: bool }
//! # #[derive(Component)]
//! # struct Score(u32); //! # struct Score(u32);
//! # struct Round(u32); //! # struct Round(u32);
//! # //! #
@ -85,9 +87,10 @@ mod tests {
use std::any::TypeId; use std::any::TypeId;
use crate::{ use crate::{
self as bevy_ecs,
archetype::Archetypes, archetype::Archetypes,
bundle::Bundles, bundle::Bundles,
component::Components, component::{Component, Components},
entity::{Entities, Entity}, entity::{Entities, Entity},
query::{Added, Changed, Or, QueryState, With, Without}, query::{Added, Changed, Or, QueryState, With, Without},
schedule::{Schedule, Stage, SystemStage}, schedule::{Schedule, Stage, SystemStage},
@ -98,14 +101,22 @@ mod tests {
world::{FromWorld, World}, world::{FromWorld, World},
}; };
#[derive(Debug, Eq, PartialEq, Default)] #[derive(Component, Debug, Eq, PartialEq, Default)]
struct A; struct A;
#[derive(Component)]
struct B; struct B;
#[derive(Component)]
struct C; struct C;
#[derive(Component)]
struct D; struct D;
#[derive(Component)]
struct E; struct E;
#[derive(Component)]
struct F; struct F;
#[derive(Component)]
struct W<T>(T);
#[test] #[test]
fn simple_system() { fn simple_system() {
fn sys(query: Query<&A>) { fn sys(query: Query<&A>) {
@ -442,15 +453,15 @@ mod tests {
fn remove_tracking() { fn remove_tracking() {
let mut world = World::new(); let mut world = World::new();
struct Despawned(Entity); struct Despawned(Entity);
let a = world.spawn().insert_bundle(("abc", 123)).id(); let a = world.spawn().insert_bundle((W("abc"), W(123))).id();
world.spawn().insert_bundle(("abc", 123)); world.spawn().insert_bundle((W("abc"), W(123)));
world.insert_resource(false); world.insert_resource(false);
world.insert_resource(Despawned(a)); world.insert_resource(Despawned(a));
world.entity_mut(a).despawn(); world.entity_mut(a).despawn();
fn validate_removed( fn validate_removed(
removed_i32: RemovedComponents<i32>, removed_i32: RemovedComponents<W<i32>>,
despawned: Res<Despawned>, despawned: Res<Despawned>,
mut ran: ResMut<bool>, mut ran: ResMut<bool>,
) { ) {
@ -486,13 +497,13 @@ mod tests {
fn world_collections_system() { fn world_collections_system() {
let mut world = World::default(); let mut world = World::default();
world.insert_resource(false); world.insert_resource(false);
world.spawn().insert_bundle((42, true)); world.spawn().insert_bundle((W(42), W(true)));
fn sys( fn sys(
archetypes: &Archetypes, archetypes: &Archetypes,
components: &Components, components: &Components,
entities: &Entities, entities: &Entities,
bundles: &Bundles, bundles: &Bundles,
query: Query<Entity, With<i32>>, query: Query<Entity, With<W<i32>>>,
mut modified: ResMut<bool>, mut modified: ResMut<bool>,
) { ) {
assert_eq!(query.iter().count(), 1, "entity exists"); assert_eq!(query.iter().count(), 1, "entity exists");
@ -501,7 +512,7 @@ mod tests {
let archetype = archetypes.get(location.archetype_id).unwrap(); let archetype = archetypes.get(location.archetype_id).unwrap();
let archetype_components = archetype.components().collect::<Vec<_>>(); let archetype_components = archetype.components().collect::<Vec<_>>();
let bundle_id = bundles let bundle_id = bundles
.get_id(std::any::TypeId::of::<(i32, bool)>()) .get_id(std::any::TypeId::of::<(W<i32>, W<bool>)>())
.expect("Bundle used to spawn entity should exist"); .expect("Bundle used to spawn entity should exist");
let bundle_info = bundles.get(bundle_id).unwrap(); let bundle_info = bundles.get(bundle_id).unwrap();
let mut bundle_components = bundle_info.components().to_vec(); let mut bundle_components = bundle_info.components().to_vec();
@ -624,7 +635,7 @@ mod tests {
#[derive(Eq, PartialEq, Debug)] #[derive(Eq, PartialEq, Debug)]
struct A(usize); struct A(usize);
#[derive(Eq, PartialEq, Debug)] #[derive(Component, Eq, PartialEq, Debug)]
struct B(usize); struct B(usize);
let mut world = World::default(); let mut world = World::default();
@ -650,7 +661,7 @@ mod tests {
#[derive(Eq, PartialEq, Debug)] #[derive(Eq, PartialEq, Debug)]
struct A(usize); struct A(usize);
#[derive(Eq, PartialEq, Debug)] #[derive(Component, Eq, PartialEq, Debug)]
struct B(usize); struct B(usize);
let mut world = World::default(); let mut world = World::default();
@ -674,7 +685,7 @@ mod tests {
#[test] #[test]
fn system_state_change_detection() { fn system_state_change_detection() {
#[derive(Eq, PartialEq, Debug)] #[derive(Component, Eq, PartialEq, Debug)]
struct A(usize); struct A(usize);
let mut world = World::default(); let mut world = World::default();
@ -709,10 +720,10 @@ mod tests {
#[test] #[test]
fn system_state_archetype_update() { fn system_state_archetype_update() {
#[derive(Eq, PartialEq, Debug)] #[derive(Component, Eq, PartialEq, Debug)]
struct A(usize); struct A(usize);
#[derive(Eq, PartialEq, Debug)] #[derive(Component, Eq, PartialEq, Debug)]
struct B(usize); struct B(usize);
let mut world = World::default(); let mut world = World::default();

View file

@ -43,9 +43,12 @@ use thiserror::Error;
/// immutably helps system parallelization. /// immutably helps system parallelization.
/// ///
/// ``` /// ```
/// # use bevy_ecs::component::Component;
/// # use bevy_ecs::system::IntoSystem; /// # use bevy_ecs::system::IntoSystem;
/// # use bevy_ecs::system::Query; /// # use bevy_ecs::system::Query;
/// # #[derive(Component)]
/// # struct ComponentA; /// # struct ComponentA;
/// # #[derive(Component)]
/// # struct ComponentB; /// # struct ComponentB;
/// # fn system( /// # fn system(
/// query: Query<(&ComponentA, &ComponentB)> /// query: Query<(&ComponentA, &ComponentB)>
@ -60,9 +63,12 @@ use thiserror::Error;
/// in the same query. /// in the same query.
/// ///
/// ``` /// ```
/// # use bevy_ecs::component::Component;
/// # use bevy_ecs::system::IntoSystem; /// # use bevy_ecs::system::IntoSystem;
/// # use bevy_ecs::system::Query; /// # use bevy_ecs::system::Query;
/// # #[derive(Component)]
/// # struct ComponentA; /// # struct ComponentA;
/// # #[derive(Component)]
/// # struct ComponentB; /// # struct ComponentB;
/// # fn system( /// # fn system(
/// // `ComponentA` is accessed mutably, while `ComponentB` is accessed immutably. /// // `ComponentA` is accessed mutably, while `ComponentB` is accessed immutably.
@ -85,10 +91,10 @@ use thiserror::Error;
/// will give access to the entity ID. /// will give access to the entity ID.
/// ///
/// ``` /// ```
/// # use bevy_ecs::entity::Entity; /// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::system::IntoSystem; /// # #[derive(Component)]
/// # use bevy_ecs::system::Query;
/// # struct ComponentA; /// # struct ComponentA;
/// # #[derive(Component)]
/// # struct ComponentB; /// # struct ComponentB;
/// # fn system( /// # fn system(
/// query: Query<(Entity, &ComponentA, &ComponentB)> /// query: Query<(Entity, &ComponentA, &ComponentB)>
@ -102,11 +108,12 @@ use thiserror::Error;
/// out the query results that don't satisfy the given condition. /// out the query results that don't satisfy the given condition.
/// ///
/// ``` /// ```
/// # use bevy_ecs::query::With; /// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::system::IntoSystem; /// # #[derive(Component)]
/// # use bevy_ecs::system::Query;
/// # struct ComponentA; /// # struct ComponentA;
/// # #[derive(Component)]
/// # struct ComponentB; /// # struct ComponentB;
/// # #[derive(Component)]
/// # struct ComponentC; /// # struct ComponentC;
/// # fn system( /// # fn system(
/// // `ComponentC` data won't be accessed, but only entities that contain it will be queried. /// // `ComponentC` data won't be accessed, but only entities that contain it will be queried.
@ -118,11 +125,12 @@ use thiserror::Error;
/// If you need to apply more filters in a single query, group them into a tuple: /// If you need to apply more filters in a single query, group them into a tuple:
/// ///
/// ``` /// ```
/// # use bevy_ecs::query::{Changed, With}; /// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::system::IntoSystem; /// # #[derive(Component)]
/// # use bevy_ecs::system::Query;
/// # struct ComponentA; /// # struct ComponentA;
/// # #[derive(Component)]
/// # struct ComponentB; /// # struct ComponentB;
/// # #[derive(Component)]
/// # struct ComponentC; /// # struct ComponentC;
/// # fn system( /// # fn system(
/// // Similar to the previous query, but with the addition of a `Changed` filter. /// // Similar to the previous query, but with the addition of a `Changed` filter.
@ -146,9 +154,10 @@ use thiserror::Error;
/// `ComponentA` and `ComponentB`, and entities that contain `ComponentA` but not `ComponentB`. /// `ComponentA` and `ComponentB`, and entities that contain `ComponentA` but not `ComponentB`.
/// ///
/// ``` /// ```
/// # use bevy_ecs::system::IntoSystem; /// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::system::Query; /// # #[derive(Component)]
/// # struct ComponentA; /// # struct ComponentA;
/// # #[derive(Component)]
/// # struct ComponentB; /// # struct ComponentB;
/// # fn system( /// # fn system(
/// query: Query<(&ComponentA, Option<&ComponentB>)> /// query: Query<(&ComponentA, Option<&ComponentB>)>
@ -168,8 +177,8 @@ use thiserror::Error;
/// of `Query` can be omitted. /// of `Query` can be omitted.
/// ///
/// ``` /// ```
/// # use bevy_ecs::system::IntoSystem; /// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::system::Query; /// # #[derive(Component)]
/// # struct MyComponent; /// # struct MyComponent;
/// # fn tuple_system( /// # fn tuple_system(
/// // This is correct, but can be avoided. /// // This is correct, but can be avoided.
@ -197,9 +206,10 @@ use thiserror::Error;
/// for advanced iterator usage. /// for advanced iterator usage.
/// ///
/// ``` /// ```
/// # use bevy_ecs::system::IntoSystem; /// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::system::Query; /// # #[derive(Component)]
/// # struct ComponentA; /// # struct ComponentA;
/// # #[derive(Component)]
/// # struct ComponentB; /// # struct ComponentB;
/// fn immutable_query_system(mut query: Query<(&ComponentA, &ComponentB)>) { /// fn immutable_query_system(mut query: Query<(&ComponentA, &ComponentB)>) {
/// for (a, b) in query.iter() { /// for (a, b) in query.iter() {
@ -278,6 +288,7 @@ where
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # /// #
/// # #[derive(Component)]
/// # struct Player { name: String } /// # struct Player { name: String }
/// # /// #
/// fn report_names_system(query: Query<&Player>) { /// fn report_names_system(query: Query<&Player>) {
@ -310,6 +321,7 @@ where
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # /// #
/// # #[derive(Component)]
/// # struct Velocity { x: f32, y: f32, z: f32 } /// # struct Velocity { x: f32, y: f32, z: f32 }
/// fn gravity_system(mut query: Query<&mut Velocity>) { /// fn gravity_system(mut query: Query<&mut Velocity>) {
/// const DELTA: f32 = 1.0 / 60.0; /// const DELTA: f32 = 1.0 / 60.0;
@ -358,8 +370,9 @@ where
/// In order to iterate it, use `fetch_next` method with `while let Some(..)` loop pattern. /// In order to iterate it, use `fetch_next` method with `while let Some(..)` loop pattern.
/// ///
/// ``` /// ```
/// # struct A;
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// # struct A;
/// # fn some_system(mut query: Query<&mut A>) { /// # fn some_system(mut query: Query<&mut A>) {
/// // iterate using `fetch_next` in while loop /// // iterate using `fetch_next` in while loop
/// let mut combinations = query.iter_combinations_mut(); /// let mut combinations = query.iter_combinations_mut();
@ -434,6 +447,7 @@ where
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # /// #
/// # #[derive(Component)]
/// # struct Player { name: String } /// # struct Player { name: String }
/// # /// #
/// fn report_names_system(query: Query<&Player>) { /// fn report_names_system(query: Query<&Player>) {
@ -471,6 +485,7 @@ where
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # /// #
/// # #[derive(Component)]
/// # struct Velocity { x: f32, y: f32, z: f32 } /// # struct Velocity { x: f32, y: f32, z: f32 }
/// fn gravity_system(mut query: Query<&mut Velocity>) { /// fn gravity_system(mut query: Query<&mut Velocity>) {
/// const DELTA: f32 = 1.0 / 60.0; /// const DELTA: f32 = 1.0 / 60.0;
@ -560,6 +575,7 @@ where
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # /// #
/// # struct SelectedCharacter { entity: Entity } /// # struct SelectedCharacter { entity: Entity }
/// # #[derive(Component)]
/// # struct Character { name: String } /// # struct Character { name: String }
/// # /// #
/// fn print_selected_character_name_system( /// fn print_selected_character_name_system(
@ -607,6 +623,7 @@ where
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # /// #
/// # struct PoisonedCharacter { character_id: Entity } /// # struct PoisonedCharacter { character_id: Entity }
/// # #[derive(Component)]
/// # struct Health(u32); /// # struct Health(u32);
/// # /// #
/// fn poison_system(mut query: Query<&mut Health>, poisoned: Res<PoisonedCharacter>) { /// fn poison_system(mut query: Query<&mut Health>, poisoned: Res<PoisonedCharacter>) {
@ -667,6 +684,7 @@ where
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # /// #
/// # struct SelectedCharacter { entity: Entity } /// # struct SelectedCharacter { entity: Entity }
/// # #[derive(Component)]
/// # struct Character { name: String } /// # struct Character { name: String }
/// # /// #
/// fn print_selected_character_name_system( /// fn print_selected_character_name_system(
@ -724,6 +742,7 @@ where
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # /// #
/// # struct PoisonedCharacter { character_id: Entity } /// # struct PoisonedCharacter { character_id: Entity }
/// # #[derive(Component)]
/// # struct Health(u32); /// # struct Health(u32);
/// # /// #
/// fn poison_system(mut query: Query<&mut Health>, poisoned: Res<PoisonedCharacter>) { /// fn poison_system(mut query: Query<&mut Health>, poisoned: Res<PoisonedCharacter>) {
@ -791,8 +810,10 @@ where
/// # Example /// # Example
/// ///
/// ``` /// ```
/// # use bevy_ecs::prelude::{IntoSystem, Query, With}; /// # use bevy_ecs::prelude::*;
/// # #[derive(Component)]
/// # struct Player; /// # struct Player;
/// # #[derive(Component)]
/// # struct Position(f32, f32); /// # struct Position(f32, f32);
/// fn player_system(query: Query<&Position, With<Player>>) { /// fn player_system(query: Query<&Position, With<Player>>) {
/// let player_position = query.single(); /// let player_position = query.single();
@ -826,9 +847,10 @@ where
/// # Example /// # Example
/// ///
/// ``` /// ```
/// # use bevy_ecs::system::{Query, QuerySingleError}; /// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::prelude::IntoSystem; /// # use bevy_ecs::system::QuerySingleError;
/// # struct PlayerScore(i32); /// # #[derive(Component)]
/// # struct PlayerScore(i32);
/// fn player_scoring_system(query: Query<&PlayerScore>) { /// fn player_scoring_system(query: Query<&PlayerScore>) {
/// match query.get_single() { /// match query.get_single() {
/// Ok(PlayerScore(score)) => { /// Ok(PlayerScore(score)) => {
@ -869,7 +891,9 @@ where
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # /// #
/// # #[derive(Component)]
/// # struct Player; /// # struct Player;
/// # #[derive(Component)]
/// # struct Health(u32); /// # struct Health(u32);
/// # /// #
/// fn regenerate_player_health_system(mut query: Query<&mut Health, With<Player>>) { /// fn regenerate_player_health_system(mut query: Query<&mut Health, With<Player>>) {
@ -899,7 +923,9 @@ where
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # /// #
/// # #[derive(Component)]
/// # struct Player; /// # struct Player;
/// # #[derive(Component)]
/// # struct Health(u32); /// # struct Health(u32);
/// # /// #
/// fn regenerate_player_health_system(mut query: Query<&mut Health, With<Player>>) { /// fn regenerate_player_health_system(mut query: Query<&mut Health, With<Player>>) {
@ -934,7 +960,9 @@ where
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # /// #
/// # #[derive(Component)]
/// # struct Player; /// # struct Player;
/// # #[derive(Component)]
/// # struct Score(u32); /// # struct Score(u32);
/// fn update_score_system(query: Query<(), With<Player>>, mut score: ResMut<Score>) { /// fn update_score_system(query: Query<(), With<Player>>, mut score: ResMut<Score>) {
/// if !query.is_empty() { /// if !query.is_empty() {

View file

@ -200,6 +200,9 @@ pub struct QuerySetState<T>(T);
impl_query_set!(); impl_query_set!();
pub trait Resource: Send + Sync + 'static {}
impl<T> Resource for T where T: Send + Sync + 'static {}
/// Shared borrow of a resource. /// Shared borrow of a resource.
/// ///
/// See the [`World`] documentation to see the usage of a resource. /// See the [`World`] documentation to see the usage of a resource.
@ -211,7 +214,7 @@ impl_query_set!();
/// Panics when used as a [`SystemParameter`](SystemParam) if the resource does not exist. /// Panics when used as a [`SystemParameter`](SystemParam) if the resource does not exist.
/// ///
/// Use `Option<Res<T>>` instead if the resource might not always exist. /// Use `Option<Res<T>>` instead if the resource might not always exist.
pub struct Res<'w, T: Component> { pub struct Res<'w, T: Resource> {
value: &'w T, value: &'w T,
ticks: &'w ComponentTicks, ticks: &'w ComponentTicks,
last_change_tick: u32, last_change_tick: u32,
@ -219,9 +222,9 @@ pub struct Res<'w, T: Component> {
} }
// SAFE: Res only reads a single World resource // SAFE: Res only reads a single World resource
unsafe impl<T: Component> ReadOnlySystemParamFetch for ResState<T> {} unsafe impl<T: Resource> ReadOnlySystemParamFetch for ResState<T> {}
impl<'w, T: Component> Debug for Res<'w, T> impl<'w, T: Resource> Debug for Res<'w, T>
where where
T: Debug, T: Debug,
{ {
@ -230,7 +233,7 @@ where
} }
} }
impl<'w, T: Component> Res<'w, T> { impl<'w, T: Resource> Res<'w, T> {
/// Returns true if (and only if) this resource been added since the last execution of this /// Returns true if (and only if) this resource been added since the last execution of this
/// system. /// system.
pub fn is_added(&self) -> bool { pub fn is_added(&self) -> bool {
@ -249,7 +252,7 @@ impl<'w, T: Component> Res<'w, T> {
} }
} }
impl<'w, T: Component> Deref for Res<'w, T> { impl<'w, T: Resource> Deref for Res<'w, T> {
type Target = T; type Target = T;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@ -257,7 +260,7 @@ impl<'w, T: Component> Deref for Res<'w, T> {
} }
} }
impl<'w, T: Component> AsRef<T> for Res<'w, T> { impl<'w, T: Resource> AsRef<T> for Res<'w, T> {
#[inline] #[inline]
fn as_ref(&self) -> &T { fn as_ref(&self) -> &T {
self.deref() self.deref()
@ -270,13 +273,13 @@ pub struct ResState<T> {
marker: PhantomData<T>, marker: PhantomData<T>,
} }
impl<'a, T: Component> SystemParam for Res<'a, T> { impl<'a, T: Resource> SystemParam for Res<'a, T> {
type Fetch = ResState<T>; type Fetch = ResState<T>;
} }
// SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res // SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res
// conflicts with any prior access, a panic will occur. // conflicts with any prior access, a panic will occur.
unsafe impl<T: Component> SystemParamState for ResState<T> { unsafe impl<T: Resource> SystemParamState for ResState<T> {
type Config = (); type Config = ();
fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self { fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
@ -305,7 +308,7 @@ unsafe impl<T: Component> SystemParamState for ResState<T> {
fn default_config() {} fn default_config() {}
} }
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResState<T> { impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for ResState<T> {
type Item = Res<'w, T>; type Item = Res<'w, T>;
#[inline] #[inline]
@ -337,14 +340,14 @@ impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResState<T> {
/// See: [`Res<T>`] /// See: [`Res<T>`]
pub struct OptionResState<T>(ResState<T>); pub struct OptionResState<T>(ResState<T>);
impl<'a, T: Component> SystemParam for Option<Res<'a, T>> { impl<'a, T: Resource> SystemParam for Option<Res<'a, T>> {
type Fetch = OptionResState<T>; type Fetch = OptionResState<T>;
} }
// SAFE: Only reads a single World resource // SAFE: Only reads a single World resource
unsafe impl<T: Component> ReadOnlySystemParamFetch for OptionResState<T> {} unsafe impl<T: Resource> ReadOnlySystemParamFetch for OptionResState<T> {}
unsafe impl<T: Component> SystemParamState for OptionResState<T> { unsafe impl<T: Resource> SystemParamState for OptionResState<T> {
type Config = (); type Config = ();
fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self { fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
@ -354,7 +357,7 @@ unsafe impl<T: Component> SystemParamState for OptionResState<T> {
fn default_config() {} fn default_config() {}
} }
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for OptionResState<T> { impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for OptionResState<T> {
type Item = Option<Res<'w, T>>; type Item = Option<Res<'w, T>>;
#[inline] #[inline]
@ -381,13 +384,13 @@ pub struct ResMutState<T> {
marker: PhantomData<T>, marker: PhantomData<T>,
} }
impl<'a, T: Component> SystemParam for ResMut<'a, T> { impl<'a, T: Resource> SystemParam for ResMut<'a, T> {
type Fetch = ResMutState<T>; type Fetch = ResMutState<T>;
} }
// SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res // SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res
// conflicts with any prior access, a panic will occur. // conflicts with any prior access, a panic will occur.
unsafe impl<T: Component> SystemParamState for ResMutState<T> { unsafe impl<T: Resource> SystemParamState for ResMutState<T> {
type Config = (); type Config = ();
fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self { fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
@ -420,7 +423,7 @@ unsafe impl<T: Component> SystemParamState for ResMutState<T> {
fn default_config() {} fn default_config() {}
} }
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResMutState<T> { impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for ResMutState<T> {
type Item = ResMut<'w, T>; type Item = ResMut<'w, T>;
#[inline] #[inline]
@ -454,11 +457,11 @@ impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResMutState<T> {
/// See: [`ResMut<T>`] /// See: [`ResMut<T>`]
pub struct OptionResMutState<T>(ResMutState<T>); pub struct OptionResMutState<T>(ResMutState<T>);
impl<'a, T: Component> SystemParam for Option<ResMut<'a, T>> { impl<'a, T: Resource> SystemParam for Option<ResMut<'a, T>> {
type Fetch = OptionResMutState<T>; type Fetch = OptionResMutState<T>;
} }
unsafe impl<T: Component> SystemParamState for OptionResMutState<T> { unsafe impl<T: Resource> SystemParamState for OptionResMutState<T> {
type Config = (); type Config = ();
fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self { fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
@ -468,7 +471,7 @@ unsafe impl<T: Component> SystemParamState for OptionResMutState<T> {
fn default_config() {} fn default_config() {}
} }
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for OptionResMutState<T> { impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for OptionResMutState<T> {
type Item = Option<ResMut<'w, T>>; type Item = Option<ResMut<'w, T>>;
#[inline] #[inline]
@ -553,12 +556,12 @@ impl<'w, 's> SystemParamFetch<'w, 's> for CommandQueue {
/// // Note how the read local is still 0 due to the locals not being shared. /// // Note how the read local is still 0 due to the locals not being shared.
/// assert_eq!(read_system.run((), world), 0); /// assert_eq!(read_system.run((), world), 0);
/// ``` /// ```
pub struct Local<'a, T: Component>(&'a mut T); pub struct Local<'a, T: Resource>(&'a mut T);
// SAFE: Local only accesses internal state // SAFE: Local only accesses internal state
unsafe impl<T: Component> ReadOnlySystemParamFetch for LocalState<T> {} unsafe impl<T: Resource> ReadOnlySystemParamFetch for LocalState<T> {}
impl<'a, T: Component> Debug for Local<'a, T> impl<'a, T: Resource> Debug for Local<'a, T>
where where
T: Debug, T: Debug,
{ {
@ -567,7 +570,7 @@ where
} }
} }
impl<'a, T: Component> Deref for Local<'a, T> { impl<'a, T: Resource> Deref for Local<'a, T> {
type Target = T; type Target = T;
#[inline] #[inline]
@ -576,7 +579,7 @@ impl<'a, T: Component> Deref for Local<'a, T> {
} }
} }
impl<'a, T: Component> DerefMut for Local<'a, T> { impl<'a, T: Resource> DerefMut for Local<'a, T> {
#[inline] #[inline]
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
self.0 self.0
@ -584,14 +587,14 @@ impl<'a, T: Component> DerefMut for Local<'a, T> {
} }
/// The [`SystemParamState`] of [`Local<T>`]. /// The [`SystemParamState`] of [`Local<T>`].
pub struct LocalState<T: Component>(T); pub struct LocalState<T: Resource>(T);
impl<'a, T: Component + FromWorld> SystemParam for Local<'a, T> { impl<'a, T: Resource + FromWorld> SystemParam for Local<'a, T> {
type Fetch = LocalState<T>; type Fetch = LocalState<T>;
} }
// SAFE: only local state is accessed // SAFE: only local state is accessed
unsafe impl<T: Component + FromWorld> SystemParamState for LocalState<T> { unsafe impl<T: Resource + FromWorld> SystemParamState for LocalState<T> {
type Config = Option<T>; type Config = Option<T>;
fn init(world: &mut World, _system_meta: &mut SystemMeta, config: Self::Config) -> Self { fn init(world: &mut World, _system_meta: &mut SystemMeta, config: Self::Config) -> Self {
@ -603,7 +606,7 @@ unsafe impl<T: Component + FromWorld> SystemParamState for LocalState<T> {
} }
} }
impl<'w, 's, T: Component + FromWorld> SystemParamFetch<'w, 's> for LocalState<T> { impl<'w, 's, T: Resource + FromWorld> SystemParamFetch<'w, 's> for LocalState<T> {
type Item = Local<'s, T>; type Item = Local<'s, T>;
#[inline] #[inline]
@ -624,9 +627,11 @@ impl<'w, 's, T: Component + FromWorld> SystemParamFetch<'w, 's> for LocalState<T
/// Basic usage: /// Basic usage:
/// ///
/// ``` /// ```
/// # use bevy_ecs::component::Component;
/// # use bevy_ecs::system::IntoSystem; /// # use bevy_ecs::system::IntoSystem;
/// # use bevy_ecs::system::RemovedComponents; /// # use bevy_ecs::system::RemovedComponents;
/// # /// #
/// # #[derive(Component)]
/// # struct MyComponent; /// # struct MyComponent;
/// ///
/// fn react_on_removal(removed: RemovedComponents<MyComponent>) { /// fn react_on_removal(removed: RemovedComponents<MyComponent>) {
@ -635,13 +640,13 @@ impl<'w, 's, T: Component + FromWorld> SystemParamFetch<'w, 's> for LocalState<T
/// ///
/// # react_on_removal.system(); /// # react_on_removal.system();
/// ``` /// ```
pub struct RemovedComponents<'a, T> { pub struct RemovedComponents<'a, T: Component> {
world: &'a World, world: &'a World,
component_id: ComponentId, component_id: ComponentId,
marker: PhantomData<T>, marker: PhantomData<T>,
} }
impl<'a, T> RemovedComponents<'a, T> { impl<'a, T: Component> RemovedComponents<'a, T> {
/// Returns an iterator over the entities that had their `T` [`Component`] removed. /// Returns an iterator over the entities that had their `T` [`Component`] removed.
pub fn iter(&self) -> std::iter::Cloned<std::slice::Iter<'_, Entity>> { pub fn iter(&self) -> std::iter::Cloned<std::slice::Iter<'_, Entity>> {
self.world.removed_with_id(self.component_id) self.world.removed_with_id(self.component_id)
@ -668,7 +673,7 @@ unsafe impl<T: Component> SystemParamState for RemovedComponentsState<T> {
fn init(world: &mut World, _system_meta: &mut SystemMeta, _config: Self::Config) -> Self { fn init(world: &mut World, _system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
Self { Self {
component_id: world.components.get_or_insert_id::<T>(), component_id: world.init_component::<T>(),
marker: PhantomData, marker: PhantomData,
} }
} }
@ -740,7 +745,7 @@ impl<'w, T: 'static> NonSend<'w, T> {
} }
} }
impl<'w, T: 'static> Deref for NonSend<'w, T> { impl<'w, T> Deref for NonSend<'w, T> {
type Target = T; type Target = T;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {

View file

@ -194,7 +194,7 @@ impl<'w> EntityMut<'w> {
let bundle_info = self let bundle_info = self
.world .world
.bundles .bundles
.init_info::<T>(&mut self.world.components); .init_info::<T>(&mut self.world.components, &mut self.world.storages);
let mut bundle_inserter = bundle_info.get_bundle_inserter( let mut bundle_inserter = bundle_info.get_bundle_inserter(
&mut self.world.entities, &mut self.world.entities,
&mut self.world.archetypes, &mut self.world.archetypes,
@ -219,7 +219,7 @@ impl<'w> EntityMut<'w> {
let entities = &mut self.world.entities; let entities = &mut self.world.entities;
let removed_components = &mut self.world.removed_components; let removed_components = &mut self.world.removed_components;
let bundle_info = self.world.bundles.init_info::<T>(components); let bundle_info = self.world.bundles.init_info::<T>(components, storages);
let old_location = self.location; let old_location = self.location;
let new_archetype_id = unsafe { let new_archetype_id = unsafe {
remove_bundle_from_archetype( remove_bundle_from_archetype(
@ -341,7 +341,7 @@ impl<'w> EntityMut<'w> {
let entities = &mut self.world.entities; let entities = &mut self.world.entities;
let removed_components = &mut self.world.removed_components; let removed_components = &mut self.world.removed_components;
let bundle_info = self.world.bundles.init_info::<T>(components); let bundle_info = self.world.bundles.init_info::<T>(components, storages);
let old_location = self.location; let old_location = self.location;
let new_archetype_id = unsafe { let new_archetype_id = unsafe {
remove_bundle_from_archetype( remove_bundle_from_archetype(

View file

@ -11,13 +11,11 @@ use crate::{
archetype::{ArchetypeComponentId, ArchetypeComponentInfo, ArchetypeId, Archetypes}, archetype::{ArchetypeComponentId, ArchetypeComponentInfo, ArchetypeId, Archetypes},
bundle::{Bundle, BundleInserter, BundleSpawner, Bundles}, bundle::{Bundle, BundleInserter, BundleSpawner, Bundles},
change_detection::Ticks, change_detection::Ticks,
component::{ component::{Component, ComponentId, ComponentTicks, Components, StorageType},
Component, ComponentDescriptor, ComponentId, ComponentTicks, Components, ComponentsError,
StorageType,
},
entity::{AllocAtWithoutReplacement, Entities, Entity}, entity::{AllocAtWithoutReplacement, Entities, Entity},
query::{FilterFetch, QueryState, WorldQuery}, query::{FilterFetch, QueryState, WorldQuery},
storage::{Column, SparseSet, Storages}, storage::{Column, SparseSet, Storages},
system::Resource,
}; };
use std::{ use std::{
any::TypeId, any::TypeId,
@ -172,38 +170,8 @@ impl World {
WorldCell::new(self) WorldCell::new(self)
} }
/// Registers a new component using the given [ComponentDescriptor]. Components do not need to pub fn init_component<T: Component>(&mut self) -> ComponentId {
/// be manually registered. This just provides a way to override default configuration. self.components.init_component::<T>(&mut self.storages)
/// Attempting to register a component with a type that has already been used by [World]
/// will result in an error.
///
/// The default component storage type can be overridden like this:
///
/// ```
/// use bevy_ecs::{component::{ComponentDescriptor, StorageType}, world::World};
///
/// struct Position {
/// x: f32,
/// y: f32,
/// }
///
/// let mut world = World::new();
/// world.register_component(ComponentDescriptor::new::<Position>(StorageType::SparseSet)).unwrap();
/// ```
pub fn register_component(
&mut self,
descriptor: ComponentDescriptor,
) -> Result<ComponentId, ComponentsError> {
let storage_type = descriptor.storage_type();
let component_id = self.components.add(descriptor)?;
// ensure sparse set is created for SparseSet components
if storage_type == StorageType::SparseSet {
// SAFE: just created
let info = unsafe { self.components.get_info_unchecked(component_id) };
self.storages.sparse_sets.get_or_insert(info);
}
Ok(component_id)
} }
/// Retrieves an [EntityRef] that exposes read-only operations for the given `entity`. /// Retrieves an [EntityRef] that exposes read-only operations for the given `entity`.
@ -211,8 +179,9 @@ impl World {
/// to check for entity existence instead of implicitly panic-ing. /// to check for entity existence instead of implicitly panic-ing.
/// ///
/// ``` /// ```
/// use bevy_ecs::world::World; /// use bevy_ecs::{component::Component, world::World};
/// ///
/// #[derive(Component)]
/// struct Position { /// struct Position {
/// x: f32, /// x: f32,
/// y: f32, /// y: f32,
@ -236,8 +205,9 @@ impl World {
/// to check for entity existence instead of implicitly panic-ing. /// to check for entity existence instead of implicitly panic-ing.
/// ///
/// ``` /// ```
/// use bevy_ecs::world::World; /// use bevy_ecs::{component::Component, world::World};
/// ///
/// #[derive(Component)]
/// struct Position { /// struct Position {
/// x: f32, /// x: f32,
/// y: f32, /// y: f32,
@ -284,8 +254,9 @@ impl World {
/// to unwrap the [EntityRef] yourself. /// to unwrap the [EntityRef] yourself.
/// ///
/// ``` /// ```
/// use bevy_ecs::world::World; /// use bevy_ecs::{component::Component, world::World};
/// ///
/// #[derive(Component)]
/// struct Position { /// struct Position {
/// x: f32, /// x: f32,
/// y: f32, /// y: f32,
@ -311,8 +282,9 @@ impl World {
/// to unwrap the [EntityMut] yourself. /// to unwrap the [EntityMut] yourself.
/// ///
/// ``` /// ```
/// use bevy_ecs::world::World; /// use bevy_ecs::{component::Component, world::World};
/// ///
/// #[derive(Component)]
/// struct Position { /// struct Position {
/// x: f32, /// x: f32,
/// y: f32, /// y: f32,
@ -338,17 +310,22 @@ impl World {
/// to add components to the entity or retrieve its id. /// to add components to the entity or retrieve its id.
/// ///
/// ``` /// ```
/// use bevy_ecs::world::World; /// use bevy_ecs::{component::Component, world::World};
/// ///
/// #[derive(Component)]
/// struct Position { /// struct Position {
/// x: f32, /// x: f32,
/// y: f32, /// y: f32,
/// } /// }
/// #[derive(Component)]
/// struct Label(&'static str);
/// #[derive(Component)]
/// struct Num(u32);
/// ///
/// let mut world = World::new(); /// let mut world = World::new();
/// let entity = world.spawn() /// let entity = world.spawn()
/// .insert(Position { x: 0.0, y: 0.0 }) // add a single component /// .insert(Position { x: 0.0, y: 0.0 }) // add a single component
/// .insert_bundle((1, 2.0, "hello")) // add a bundle of components /// .insert_bundle((Num(1), Label("hello"))) // add a bundle of components
/// .id(); /// .id();
/// ///
/// let position = world.entity(entity).get::<Position>().unwrap(); /// let position = world.entity(entity).get::<Position>().unwrap();
@ -385,12 +362,17 @@ impl World {
/// individually is more flexible. /// individually is more flexible.
/// ///
/// ``` /// ```
/// use bevy_ecs::{entity::Entity, world::World}; /// use bevy_ecs::{component::Component, entity::Entity, world::World};
///
/// #[derive(Component)]
/// struct Str(&'static str);
/// #[derive(Component)]
/// struct Num(u32);
/// ///
/// let mut world = World::new(); /// let mut world = World::new();
/// let entities = world.spawn_batch(vec![ /// let entities = world.spawn_batch(vec![
/// ("a", 0.0), // the first entity /// (Str("a"), Num(0)), // the first entity
/// ("b", 1.0), // the second entity /// (Str("b"), Num(1)), // the second entity
/// ]).collect::<Vec<Entity>>(); /// ]).collect::<Vec<Entity>>();
/// ///
/// assert_eq!(entities.len(), 2); /// assert_eq!(entities.len(), 2);
@ -406,8 +388,9 @@ impl World {
/// Retrieves a reference to the given `entity`'s [Component] of the given type. /// Retrieves a reference to the given `entity`'s [Component] of the given type.
/// Returns [None] if the `entity` does not have a [Component] of the given type. /// Returns [None] if the `entity` does not have a [Component] of the given type.
/// ``` /// ```
/// use bevy_ecs::world::World; /// use bevy_ecs::{component::Component, world::World};
/// ///
/// #[derive(Component)]
/// struct Position { /// struct Position {
/// x: f32, /// x: f32,
/// y: f32, /// y: f32,
@ -428,8 +411,9 @@ impl World {
/// Retrieves a mutable reference to the given `entity`'s [Component] of the given type. /// Retrieves a mutable reference to the given `entity`'s [Component] of the given type.
/// Returns [None] if the `entity` does not have a [Component] of the given type. /// Returns [None] if the `entity` does not have a [Component] of the given type.
/// ``` /// ```
/// use bevy_ecs::world::World; /// use bevy_ecs::{component::Component, world::World};
/// ///
/// #[derive(Component)]
/// struct Position { /// struct Position {
/// x: f32, /// x: f32,
/// y: f32, /// y: f32,
@ -451,8 +435,9 @@ impl World {
/// [Component]s. Returns `true` if the `entity` is successfully despawned and `false` if /// [Component]s. Returns `true` if the `entity` is successfully despawned and `false` if
/// the `entity` does not exist. /// the `entity` does not exist.
/// ``` /// ```
/// use bevy_ecs::world::World; /// use bevy_ecs::{component::Component, world::World};
/// ///
/// #[derive(Component)]
/// struct Position { /// struct Position {
/// x: f32, /// x: f32,
/// y: f32, /// y: f32,
@ -488,14 +473,15 @@ impl World {
/// Returns [QueryState] for the given [WorldQuery], which is used to efficiently /// Returns [QueryState] for the given [WorldQuery], which is used to efficiently
/// run queries on the [World] by storing and reusing the [QueryState]. /// run queries on the [World] by storing and reusing the [QueryState].
/// ``` /// ```
/// use bevy_ecs::{entity::Entity, world::World}; /// use bevy_ecs::{component::Component, entity::Entity, world::World};
/// ///
/// #[derive(Debug, PartialEq)] /// #[derive(Component, Debug, PartialEq)]
/// struct Position { /// struct Position {
/// x: f32, /// x: f32,
/// y: f32, /// y: f32,
/// } /// }
/// ///
/// #[derive(Component)]
/// struct Velocity { /// struct Velocity {
/// x: f32, /// x: f32,
/// y: f32, /// y: f32,
@ -523,18 +509,28 @@ impl World {
/// and allocation of a [Vec] to store it. /// and allocation of a [Vec] to store it.
/// ///
/// ``` /// ```
/// use bevy_ecs::{entity::Entity, world::World}; /// use bevy_ecs::{component::Component, entity::Entity, world::World};
///
/// #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug)]
/// struct Order(i32);
/// #[derive(Component, PartialEq, Debug)]
/// struct Label(&'static str);
///
/// let mut world = World::new(); /// let mut world = World::new();
/// let a = world.spawn().insert_bundle((2, 4.0)).id(); /// let a = world.spawn().insert_bundle((Order(2), Label("second"))).id();
/// let b = world.spawn().insert_bundle((3, 5.0)).id(); /// let b = world.spawn().insert_bundle((Order(3), Label("third"))).id();
/// let c = world.spawn().insert_bundle((1, 6.0)).id(); /// let c = world.spawn().insert_bundle((Order(1), Label("first"))).id();
/// let mut entities = world.query::<(Entity, &i32, &f64)>() /// let mut entities = world.query::<(Entity, &Order, &Label)>()
/// .iter(&world) /// .iter(&world)
/// .collect::<Vec<_>>(); /// .collect::<Vec<_>>();
/// // Sort the query results by their `i32` component before comparing /// // Sort the query results by their `Order` component before comparing
/// // to expected results. Query iteration order should not be relied on. /// // to expected results. Query iteration order should not be relied on.
/// entities.sort_by_key(|e| e.1); /// entities.sort_by_key(|e| e.1);
/// assert_eq!(entities, vec![(c, &1, &6.0), (a, &2, &4.0), (b, &3, &5.0)]); /// assert_eq!(entities, vec![
/// (c, &Order(1), &Label("first")),
/// (a, &Order(2), &Label("second")),
/// (b, &Order(3), &Label("third")),
/// ]);
/// ``` /// ```
#[inline] #[inline]
pub fn query<Q: WorldQuery>(&mut self) -> QueryState<Q, ()> { pub fn query<Q: WorldQuery>(&mut self) -> QueryState<Q, ()> {
@ -544,9 +540,11 @@ impl World {
/// Returns [QueryState] for the given filtered [WorldQuery], which is used to efficiently /// Returns [QueryState] for the given filtered [WorldQuery], which is used to efficiently
/// run queries on the [World] by storing and reusing the [QueryState]. /// run queries on the [World] by storing and reusing the [QueryState].
/// ``` /// ```
/// use bevy_ecs::{entity::Entity, world::World, query::With}; /// use bevy_ecs::{component::Component, entity::Entity, world::World, query::With};
/// ///
/// #[derive(Component)]
/// struct A; /// struct A;
/// #[derive(Component)]
/// struct B; /// struct B;
/// ///
/// let mut world = World::new(); /// let mut world = World::new();
@ -592,8 +590,8 @@ impl World {
/// Inserts a new resource with the given `value`. /// Inserts a new resource with the given `value`.
/// Resources are "unique" data of a given type. /// Resources are "unique" data of a given type.
#[inline] #[inline]
pub fn insert_resource<T: Component>(&mut self, value: T) { pub fn insert_resource<T: Resource>(&mut self, value: T) {
let component_id = self.components.get_or_insert_resource_id::<T>(); let component_id = self.components.init_resource::<T>();
// SAFE: component_id just initialized and corresponds to resource of type T // SAFE: component_id just initialized and corresponds to resource of type T
unsafe { self.insert_resource_with_id(component_id, value) }; unsafe { self.insert_resource_with_id(component_id, value) };
} }
@ -603,7 +601,7 @@ impl World {
#[inline] #[inline]
pub fn insert_non_send<T: 'static>(&mut self, value: T) { pub fn insert_non_send<T: 'static>(&mut self, value: T) {
self.validate_non_send_access::<T>(); self.validate_non_send_access::<T>();
let component_id = self.components.get_or_insert_non_send_resource_id::<T>(); let component_id = self.components.init_non_send::<T>();
// SAFE: component_id just initialized and corresponds to resource of type T // SAFE: component_id just initialized and corresponds to resource of type T
unsafe { self.insert_resource_with_id(component_id, value) }; unsafe { self.insert_resource_with_id(component_id, value) };
} }
@ -611,7 +609,7 @@ impl World {
/// Removes the resource of a given type and returns it, if it exists. Otherwise returns [None]. /// Removes the resource of a given type and returns it, if it exists. Otherwise returns [None].
/// Resources are "unique" data of a given type. /// Resources are "unique" data of a given type.
#[inline] #[inline]
pub fn remove_resource<T: Component>(&mut self) -> Option<T> { pub fn remove_resource<T: Resource>(&mut self) -> Option<T> {
// SAFE: T is Send + Sync // SAFE: T is Send + Sync
unsafe { self.remove_resource_unchecked() } unsafe { self.remove_resource_unchecked() }
} }
@ -644,7 +642,7 @@ impl World {
/// Returns `true` if a resource of type `T` exists. Otherwise returns `false`. /// Returns `true` if a resource of type `T` exists. Otherwise returns `false`.
#[inline] #[inline]
pub fn contains_resource<T: Component>(&self) -> bool { pub fn contains_resource<T: Resource>(&self) -> bool {
let component_id = let component_id =
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) { if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) {
component_id component_id
@ -657,12 +655,12 @@ impl World {
/// Gets a reference to the resource of the given type, if it exists. Otherwise returns [None] /// Gets a reference to the resource of the given type, if it exists. Otherwise returns [None]
/// Resources are "unique" data of a given type. /// Resources are "unique" data of a given type.
#[inline] #[inline]
pub fn get_resource<T: Component>(&self) -> Option<&T> { pub fn get_resource<T: Resource>(&self) -> Option<&T> {
let component_id = self.components.get_resource_id(TypeId::of::<T>())?; let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
unsafe { self.get_resource_with_id(component_id) } unsafe { self.get_resource_with_id(component_id) }
} }
pub fn is_resource_added<T: Component>(&self) -> bool { pub fn is_resource_added<T: Resource>(&self) -> bool {
let component_id = let component_id =
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) { if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) {
component_id component_id
@ -679,7 +677,7 @@ impl World {
ticks.is_added(self.last_change_tick(), self.read_change_tick()) ticks.is_added(self.last_change_tick(), self.read_change_tick())
} }
pub fn is_resource_changed<T: Component>(&self) -> bool { pub fn is_resource_changed<T: Resource>(&self) -> bool {
let component_id = let component_id =
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) { if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) {
component_id component_id
@ -699,7 +697,7 @@ impl World {
/// Gets a mutable reference to the resource of the given type, if it exists. Otherwise returns /// Gets a mutable reference to the resource of the given type, if it exists. Otherwise returns
/// [None] Resources are "unique" data of a given type. /// [None] Resources are "unique" data of a given type.
#[inline] #[inline]
pub fn get_resource_mut<T: Component>(&mut self) -> Option<Mut<'_, T>> { pub fn get_resource_mut<T: Resource>(&mut self) -> Option<Mut<'_, T>> {
// SAFE: unique world access // SAFE: unique world access
unsafe { self.get_resource_unchecked_mut() } unsafe { self.get_resource_unchecked_mut() }
} }
@ -708,7 +706,7 @@ impl World {
/// Gets a resource of type `T` if it exists, otherwise inserts the resource using the result of /// Gets a resource of type `T` if it exists, otherwise inserts the resource using the result of
/// calling `func`. /// calling `func`.
#[inline] #[inline]
pub fn get_resource_or_insert_with<T: Component>( pub fn get_resource_or_insert_with<T: Resource>(
&mut self, &mut self,
func: impl FnOnce() -> T, func: impl FnOnce() -> T,
) -> Mut<'_, T> { ) -> Mut<'_, T> {
@ -725,7 +723,7 @@ impl World {
/// This will allow aliased mutable access to the given resource type. The caller must ensure /// This will allow aliased mutable access to the given resource type. The caller must ensure
/// that only one mutable access exists at a time. /// that only one mutable access exists at a time.
#[inline] #[inline]
pub unsafe fn get_resource_unchecked_mut<T: Component>(&self) -> Option<Mut<'_, T>> { pub unsafe fn get_resource_unchecked_mut<T: Resource>(&self) -> Option<Mut<'_, T>> {
let component_id = self.components.get_resource_id(TypeId::of::<T>())?; let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
self.get_resource_unchecked_mut_with_id(component_id) self.get_resource_unchecked_mut_with_id(component_id)
} }
@ -772,17 +770,21 @@ impl World {
/// worked out to share an ID space (which doesn't happen by default). /// worked out to share an ID space (which doesn't happen by default).
/// ///
/// ``` /// ```
/// use bevy_ecs::{entity::Entity, world::World}; /// use bevy_ecs::{entity::Entity, world::World, component::Component};
/// #[derive(Component)]
/// struct A(&'static str);
/// #[derive(Component, PartialEq, Debug)]
/// struct B(f32);
/// ///
/// let mut world = World::new(); /// let mut world = World::new();
/// let e0 = world.spawn().id(); /// let e0 = world.spawn().id();
/// let e1 = world.spawn().id(); /// let e1 = world.spawn().id();
/// world.insert_or_spawn_batch(vec![ /// world.insert_or_spawn_batch(vec![
/// (e0, ("a", 0.0)), // the first entity /// (e0, (A("a"), B(0.0))), // the first entity
/// (e1, ("b", 1.0)), // the second entity /// (e1, (A("b"), B(1.0))), // the second entity
/// ]); /// ]);
/// ///
/// assert_eq!(world.get::<f64>(e0), Some(&0.0)); /// assert_eq!(world.get::<B>(e0), Some(&B(0.0)));
/// ``` /// ```
pub fn insert_or_spawn_batch<I, B>(&mut self, iter: I) -> Result<(), Vec<Entity>> pub fn insert_or_spawn_batch<I, B>(&mut self, iter: I) -> Result<(), Vec<Entity>>
where where
@ -795,7 +797,9 @@ impl World {
let iter = iter.into_iter(); let iter = iter.into_iter();
let change_tick = *self.change_tick.get_mut(); let change_tick = *self.change_tick.get_mut();
let bundle_info = self.bundles.init_info::<B>(&mut self.components); let bundle_info = self
.bundles
.init_info::<B>(&mut self.components, &mut self.storages);
enum SpawnOrInsert<'a, 'b> { enum SpawnOrInsert<'a, 'b> {
Spawn(BundleSpawner<'a, 'b>), Spawn(BundleSpawner<'a, 'b>),
Insert(BundleInserter<'a, 'b>, ArchetypeId), Insert(BundleInserter<'a, 'b>, ArchetypeId),
@ -884,8 +888,10 @@ impl World {
/// returning. This enables safe mutable access to a resource while still providing mutable /// returning. This enables safe mutable access to a resource while still providing mutable
/// world access /// world access
/// ``` /// ```
/// use bevy_ecs::world::{World, Mut}; /// use bevy_ecs::{component::Component, world::{World, Mut}};
/// #[derive(Component)]
/// struct A(u32); /// struct A(u32);
/// #[derive(Component)]
/// struct B(u32); /// struct B(u32);
/// let mut world = World::new(); /// let mut world = World::new();
/// world.insert_resource(A(1)); /// world.insert_resource(A(1));
@ -897,10 +903,7 @@ impl World {
/// }); /// });
/// assert_eq!(world.get_resource::<A>().unwrap().0, 2); /// assert_eq!(world.get_resource::<A>().unwrap().0, 2);
/// ``` /// ```
pub fn resource_scope<T: Component, U>( pub fn resource_scope<T: Resource, U>(&mut self, f: impl FnOnce(&mut World, Mut<T>) -> U) -> U {
&mut self,
f: impl FnOnce(&mut World, Mut<T>) -> U,
) -> U {
let component_id = self let component_id = self
.components .components
.get_resource_id(TypeId::of::<T>()) .get_resource_id(TypeId::of::<T>())
@ -1041,15 +1044,15 @@ impl World {
}) })
} }
pub(crate) fn initialize_resource<T: Component>(&mut self) -> ComponentId { pub(crate) fn initialize_resource<T: Resource>(&mut self) -> ComponentId {
let component_id = self.components.get_or_insert_resource_id::<T>(); let component_id = self.components.init_resource::<T>();
// SAFE: resource initialized above // SAFE: resource initialized above
unsafe { self.initialize_resource_internal(component_id) }; unsafe { self.initialize_resource_internal(component_id) };
component_id component_id
} }
pub(crate) fn initialize_non_send_resource<T: 'static>(&mut self) -> ComponentId { pub(crate) fn initialize_non_send_resource<T: 'static>(&mut self) -> ComponentId {
let component_id = self.components.get_or_insert_non_send_resource_id::<T>(); let component_id = self.components.init_non_send::<T>();
// SAFE: resource initialized above // SAFE: resource initialized above
unsafe { self.initialize_resource_internal(component_id) }; unsafe { self.initialize_resource_internal(component_id) };
component_id component_id

View file

@ -27,7 +27,9 @@ where
let (lower, upper) = iter.size_hint(); let (lower, upper) = iter.size_hint();
let length = upper.unwrap_or(lower); let length = upper.unwrap_or(lower);
let bundle_info = world.bundles.init_info::<I::Item>(&mut world.components); let bundle_info = world
.bundles
.init_info::<I::Item>(&mut world.components, &mut world.storages);
world.entities.reserve(length as u32); world.entities.reserve(length as u32);
let mut spawner = bundle_info.get_bundle_spawner( let mut spawner = bundle_info.get_bundle_spawner(
&mut world.entities, &mut world.entities,

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
archetype::ArchetypeComponentId, archetype::ArchetypeComponentId,
component::Component,
storage::SparseSet, storage::SparseSet,
system::Resource,
world::{Mut, World}, world::{Mut, World},
}; };
use std::{ use std::{
@ -182,7 +182,7 @@ impl<'w> WorldCell<'w> {
} }
} }
pub fn get_resource<T: Component>(&self) -> Option<WorldBorrow<'_, T>> { pub fn get_resource<T: Resource>(&self) -> Option<WorldBorrow<'_, T>> {
let component_id = self.world.components.get_resource_id(TypeId::of::<T>())?; let component_id = self.world.components.get_resource_id(TypeId::of::<T>())?;
let resource_archetype = self.world.archetypes.resource(); let resource_archetype = self.world.archetypes.resource();
let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?; let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
@ -194,7 +194,7 @@ impl<'w> WorldCell<'w> {
)) ))
} }
pub fn get_resource_mut<T: Component>(&self) -> Option<WorldBorrowMut<'_, T>> { pub fn get_resource_mut<T: Resource>(&self) -> Option<WorldBorrowMut<'_, T>> {
let component_id = self.world.components.get_resource_id(TypeId::of::<T>())?; let component_id = self.world.components.get_resource_id(TypeId::of::<T>())?;
let resource_archetype = self.world.archetypes.resource(); let resource_archetype = self.world.archetypes.resource();
let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?; let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;

View file

@ -0,0 +1,50 @@
use syn::DeriveInput;
use crate::Symbol;
pub fn get_attr_meta_items(
attr: &syn::Attribute,
attr_name: &'static str,
) -> syn::Result<Vec<syn::NestedMeta>> {
if !attr.path.is_ident(attr_name) {
return Ok(Vec::new());
}
match attr.parse_meta()? {
syn::Meta::List(meta) => Ok(meta.nested.into_iter().collect()),
other => Err(syn::Error::new_spanned(
other,
format!("expected #[{}(...)]", attr_name),
)),
}
}
pub fn parse_attrs(ast: &DeriveInput, attr_name: Symbol) -> syn::Result<Vec<syn::NestedMeta>> {
let mut list = Vec::new();
for attr in ast.attrs.iter().filter(|a| a.path == attr_name) {
match attr.parse_meta()? {
syn::Meta::List(meta) => list.extend(meta.nested.into_iter()),
other => {
return Err(syn::Error::new_spanned(
other,
format!("expected #[{}(...)]", attr_name),
))
}
}
}
Ok(list)
}
pub fn get_lit_str(attr_name: Symbol, lit: &syn::Lit) -> syn::Result<&syn::LitStr> {
if let syn::Lit::Str(lit) = lit {
Ok(lit)
} else {
Err(syn::Error::new_spanned(
lit,
format!(
"expected {} attribute to be a string: `{} = \"...\"`",
attr_name, attr_name
),
))
}
}

View file

@ -1,5 +1,11 @@
extern crate proc_macro; extern crate proc_macro;
mod attrs;
mod symbol;
pub use attrs::*;
pub use symbol::*;
use cargo_manifest::{DepsSet, Manifest}; use cargo_manifest::{DepsSet, Manifest};
use proc_macro::TokenStream; use proc_macro::TokenStream;
use std::{env, path::PathBuf}; use std::{env, path::PathBuf};

View file

@ -0,0 +1,35 @@
use std::fmt::{self, Display};
use syn::{Ident, Path};
#[derive(Copy, Clone)]
pub struct Symbol(pub &'static str);
impl PartialEq<Symbol> for Ident {
fn eq(&self, word: &Symbol) -> bool {
self == word.0
}
}
impl<'a> PartialEq<Symbol> for &'a Ident {
fn eq(&self, word: &Symbol) -> bool {
*self == word.0
}
}
impl PartialEq<Symbol> for Path {
fn eq(&self, word: &Symbol) -> bool {
self.is_ident(word.0)
}
}
impl<'a> PartialEq<Symbol> for &'a Path {
fn eq(&self, word: &Symbol) -> bool {
self.is_ident(word.0)
}
}
impl Display for Symbol {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(self.0)
}
}

View file

@ -1,12 +1,12 @@
use bevy_core::{Pod, Zeroable}; use bevy_core::{Pod, Zeroable};
use bevy_ecs::reflect::ReflectComponent; use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_math::Vec3; use bevy_math::Vec3;
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_render::color::Color; use bevy_render::color::Color;
use bevy_transform::components::GlobalTransform; use bevy_transform::components::GlobalTransform;
/// A point light /// A point light
#[derive(Debug, Clone, Copy, Reflect)] #[derive(Component, Debug, Clone, Copy, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct PointLight { pub struct PointLight {
pub color: Color, pub color: Color,
@ -77,7 +77,7 @@ impl PointLightUniform {
/// | 32,000100,000 | Direct sunlight | /// | 32,000100,000 | Direct sunlight |
/// ///
/// Source: [Wikipedia](https://en.wikipedia.org/wiki/Lux) /// Source: [Wikipedia](https://en.wikipedia.org/wiki/Lux)
#[derive(Debug, Clone, Copy, Reflect)] #[derive(Component, Debug, Clone, Copy, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct DirectionalLight { pub struct DirectionalLight {
pub color: Color, pub color: Color,

View file

@ -2,12 +2,13 @@ use crate::renderer::RenderResourceBindings;
use super::Camera; use super::Camera;
use bevy_ecs::{ use bevy_ecs::{
component::Component,
entity::Entity, entity::Entity,
system::{Query, ResMut}, system::{Query, ResMut},
}; };
use bevy_utils::HashMap; use bevy_utils::HashMap;
#[derive(Debug, Default)] #[derive(Component, Debug, Default)]
pub struct ActiveCamera { pub struct ActiveCamera {
pub name: String, pub name: String,
pub entity: Option<Entity>, pub entity: Option<Entity>,

View file

@ -15,7 +15,7 @@ use bevy_transform::components::GlobalTransform;
use bevy_window::{WindowCreated, WindowId, WindowResized, Windows}; use bevy_window::{WindowCreated, WindowId, WindowResized, Windows};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Default, Debug, Reflect)] #[derive(Component, Default, Debug, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct Camera { pub struct Camera {
pub projection_matrix: Mat4, pub projection_matrix: Mat4,

View file

@ -1,5 +1,5 @@
use super::DepthCalculation; use super::DepthCalculation;
use bevy_ecs::reflect::ReflectComponent; use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_math::Mat4; use bevy_math::Mat4;
use bevy_reflect::{Reflect, ReflectDeserialize}; use bevy_reflect::{Reflect, ReflectDeserialize};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -10,7 +10,7 @@ pub trait CameraProjection {
fn depth_calculation(&self) -> DepthCalculation; fn depth_calculation(&self) -> DepthCalculation;
} }
#[derive(Debug, Clone, Reflect)] #[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct PerspectiveProjection { pub struct PerspectiveProjection {
pub fov: f32, pub fov: f32,
@ -66,7 +66,7 @@ pub enum ScalingMode {
FixedHorizontal, FixedHorizontal,
} }
#[derive(Debug, Clone, Reflect)] #[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct OrthographicProjection { pub struct OrthographicProjection {
pub left: f32, pub left: f32,

View file

@ -1,7 +1,9 @@
use super::{Camera, DepthCalculation}; use super::{Camera, DepthCalculation};
use crate::{draw::OutsideFrustum, prelude::Visible}; use crate::{draw::OutsideFrustum, prelude::Visible};
use bevy_core::FloatOrd; use bevy_core::FloatOrd;
use bevy_ecs::{entity::Entity, query::Without, reflect::ReflectComponent, system::Query}; use bevy_ecs::{
component::Component, entity::Entity, query::Without, reflect::ReflectComponent, system::Query,
};
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_transform::prelude::GlobalTransform; use bevy_transform::prelude::GlobalTransform;
@ -11,7 +13,7 @@ pub struct VisibleEntity {
pub order: FloatOrd, pub order: FloatOrd,
} }
#[derive(Default, Debug, Reflect)] #[derive(Component, Default, Debug, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct VisibleEntities { pub struct VisibleEntities {
#[reflect(ignore)] #[reflect(ignore)]
@ -42,7 +44,7 @@ pub type Layer = u8;
/// An entity with this component without any layers is invisible. /// An entity with this component without any layers is invisible.
/// ///
/// Entities without this component belong to layer `0`. /// Entities without this component belong to layer `0`.
#[derive(Copy, Clone, Reflect, PartialEq, Eq, PartialOrd, Ord)] #[derive(Component, Copy, Clone, Reflect, PartialEq, Eq, PartialOrd, Ord)]
#[reflect(Component, PartialEq)] #[reflect(Component, PartialEq)]
pub struct RenderLayers(LayerMask); pub struct RenderLayers(LayerMask);

View file

@ -10,6 +10,7 @@ use crate::{
}; };
use bevy_asset::{Asset, Assets, Handle}; use bevy_asset::{Asset, Assets, Handle};
use bevy_ecs::{ use bevy_ecs::{
component::Component,
reflect::ReflectComponent, reflect::ReflectComponent,
system::{Query, Res, ResMut, SystemParam}, system::{Query, Res, ResMut, SystemParam},
}; };
@ -49,7 +50,7 @@ pub enum RenderCommand {
}, },
} }
#[derive(Debug, Clone, Reflect)] #[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct Visible { pub struct Visible {
pub is_visible: bool, pub is_visible: bool,
@ -73,12 +74,13 @@ impl Default for Visible {
/// This does not handle multiple "views" properly as it is a "global" filter. /// This does not handle multiple "views" properly as it is a "global" filter.
/// This will be resolved in the future. For now, disable frustum culling if you /// This will be resolved in the future. For now, disable frustum culling if you
/// need to support multiple views (ex: set the `SpriteSettings::frustum_culling_enabled` resource). /// need to support multiple views (ex: set the `SpriteSettings::frustum_culling_enabled` resource).
#[derive(Debug, Default, Clone, Reflect)] #[derive(Component, Debug, Default, Clone, Reflect)]
#[reflect(Component)] #[reflect(Component)]
#[component(storage = "SparseSet")]
pub struct OutsideFrustum; pub struct OutsideFrustum;
/// A component that indicates how to draw an entity. /// A component that indicates how to draw an entity.
#[derive(Debug, Clone, Reflect)] #[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct Draw { pub struct Draw {
#[reflect(ignore)] #[reflect(ignore)]

View file

@ -7,6 +7,7 @@ use crate::{
}; };
use bevy_asset::{Assets, Handle}; use bevy_asset::{Assets, Handle};
use bevy_ecs::{ use bevy_ecs::{
component::Component,
query::Without, query::Without,
reflect::ReflectComponent, reflect::ReflectComponent,
system::{Query, Res, ResMut}, system::{Query, Res, ResMut},
@ -44,7 +45,7 @@ impl RenderPipeline {
} }
} }
#[derive(Debug, Clone, Reflect)] #[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct RenderPipelines { pub struct RenderPipelines {
pub pipelines: Vec<RenderPipeline>, pub pipelines: Vec<RenderPipeline>,

View file

@ -10,12 +10,12 @@ use crate::{
texture::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage}, texture::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage},
Color, Color,
}; };
use bevy_ecs::{reflect::ReflectComponent, world::World}; use bevy_ecs::{component::Component, reflect::ReflectComponent, world::World};
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_window::WindowId; use bevy_window::WindowId;
/// A component that indicates that an entity should be drawn in the "main pass" /// A component that indicates that an entity should be drawn in the "main pass"
#[derive(Clone, Debug, Default, Reflect)] #[derive(Component, Clone, Debug, Default, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct MainPass; pub struct MainPass;

View file

@ -12,6 +12,7 @@ use crate::{
use bevy_app::EventReader; use bevy_app::EventReader;
use bevy_asset::{Asset, AssetEvent, Assets, Handle, HandleId}; use bevy_asset::{Asset, AssetEvent, Assets, Handle, HandleId};
use bevy_ecs::{ use bevy_ecs::{
component::Component,
entity::Entity, entity::Entity,
prelude::QueryState, prelude::QueryState,
query::{Changed, Or, With}, query::{Changed, Or, With},
@ -398,7 +399,7 @@ where
impl<T> SystemNode for RenderResourcesNode<T> impl<T> SystemNode for RenderResourcesNode<T>
where where
T: renderer::RenderResources, T: renderer::RenderResources + Component,
{ {
fn get_system(&self) -> BoxedSystem { fn get_system(&self) -> BoxedSystem {
let system = render_resources_node_system::<T>.config(|config| { let system = render_resources_node_system::<T>.config(|config| {
@ -430,7 +431,7 @@ impl<I, T: RenderResources> Default for RenderResourcesNodeState<I, T> {
} }
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
fn render_resources_node_system<T: RenderResources>( fn render_resources_node_system<T: RenderResources + Component>(
mut state: Local<RenderResourcesNodeState<Entity, T>>, mut state: Local<RenderResourcesNodeState<Entity, T>>,
mut entities_waiting_for_textures: Local<Vec<Entity>>, mut entities_waiting_for_textures: Local<Vec<Entity>>,
render_resource_context: Res<Box<dyn RenderResourceContext>>, render_resource_context: Res<Box<dyn RenderResourceContext>>,

View file

@ -3,6 +3,7 @@ use bevy_asset::{Asset, Assets, Handle};
use crate::{draw::OutsideFrustum, pipeline::RenderPipelines, Texture}; use crate::{draw::OutsideFrustum, pipeline::RenderPipelines, Texture};
pub use bevy_derive::ShaderDefs; pub use bevy_derive::ShaderDefs;
use bevy_ecs::{ use bevy_ecs::{
prelude::Component,
query::Without, query::Without,
system::{Query, Res}, system::{Query, Res},
}; };
@ -70,7 +71,7 @@ impl ShaderDef for Option<Handle<Texture>> {
/// Updates [RenderPipelines] with the latest [ShaderDefs] /// Updates [RenderPipelines] with the latest [ShaderDefs]
pub fn shader_defs_system<T>(mut query: Query<(&T, &mut RenderPipelines), Without<OutsideFrustum>>) pub fn shader_defs_system<T>(mut query: Query<(&T, &mut RenderPipelines), Without<OutsideFrustum>>)
where where
T: ShaderDefs + Send + Sync + 'static, T: ShaderDefs + Component,
{ {
for (shader_defs, mut render_pipelines) in query.iter_mut() { for (shader_defs, mut render_pipelines) in query.iter_mut() {
for shader_def in shader_defs.iter_shader_defs() { for shader_def in shader_defs.iter_shader_defs() {

View file

@ -8,6 +8,7 @@ use crate::{
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_asset::{Assets, Handle, HandleUntyped}; use bevy_asset::{Assets, Handle, HandleUntyped};
use bevy_ecs::{ use bevy_ecs::{
component::Component,
query::{QueryState, With}, query::{QueryState, With},
reflect::ReflectComponent, reflect::ReflectComponent,
system::{QuerySet, Res}, system::{QuerySet, Res},
@ -40,7 +41,7 @@ impl Plugin for WireframePlugin {
} }
} }
#[derive(Debug, Clone, Reflect, Default)] #[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)] #[reflect(Component)]
pub struct Wireframe; pub struct Wireframe;

View file

@ -28,11 +28,9 @@ pub use texture_atlas_builder::*;
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_asset::{AddAsset, Assets, Handle, HandleUntyped}; use bevy_asset::{AddAsset, Assets, Handle, HandleUntyped};
use bevy_ecs::component::{ComponentDescriptor, StorageType};
use bevy_math::Vec2; use bevy_math::Vec2;
use bevy_reflect::TypeUuid; use bevy_reflect::TypeUuid;
use bevy_render::{ use bevy_render::{
draw::OutsideFrustum,
mesh::{shape, Mesh}, mesh::{shape, Mesh},
pipeline::PipelineDescriptor, pipeline::PipelineDescriptor,
render_graph::RenderGraph, render_graph::RenderGraph,
@ -90,12 +88,6 @@ impl Plugin for SpritePlugin {
frustum_culling::atlas_frustum_culling_system, frustum_culling::atlas_frustum_culling_system,
); );
} }
app.world
.register_component(ComponentDescriptor::new::<OutsideFrustum>(
StorageType::SparseSet,
))
.unwrap();
let world_cell = app.world.cell(); let world_cell = app.world.cell();
let mut render_graph = world_cell.get_resource_mut::<RenderGraph>().unwrap(); let mut render_graph = world_cell.get_resource_mut::<RenderGraph>().unwrap();
let mut pipelines = world_cell let mut pipelines = world_cell

View file

@ -2,6 +2,7 @@ use crate::ColorMaterial;
use bevy_asset::{Assets, Handle}; use bevy_asset::{Assets, Handle};
use bevy_core::Bytes; use bevy_core::Bytes;
use bevy_ecs::{ use bevy_ecs::{
component::Component,
query::Without, query::Without,
system::{Query, Res}, system::{Query, Res},
}; };
@ -15,7 +16,7 @@ use bevy_render::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// General Sprite Examples: [Link](https://github.com/bevyengine/bevy/tree/latest/examples/2d) /// General Sprite Examples: [Link](https://github.com/bevyengine/bevy/tree/latest/examples/2d)
#[derive(Debug, Default, Clone, TypeUuid, Reflect, RenderResources)] #[derive(Component, Debug, Default, Clone, TypeUuid, Reflect, RenderResources)]
#[render_resources(from_self)] #[render_resources(from_self)]
#[uuid = "7233c597-ccfa-411f-bd59-9af349432ada"] #[uuid = "7233c597-ccfa-411f-bd59-9af349432ada"]
#[repr(C)] #[repr(C)]

View file

@ -1,6 +1,7 @@
use crate::Rect; use crate::Rect;
use bevy_asset::Handle; use bevy_asset::Handle;
use bevy_core::Bytes; use bevy_core::Bytes;
use bevy_ecs::component::Component;
use bevy_math::Vec2; use bevy_math::Vec2;
use bevy_reflect::TypeUuid; use bevy_reflect::TypeUuid;
use bevy_render::{ use bevy_render::{
@ -27,7 +28,7 @@ pub struct TextureAtlas {
pub texture_handles: Option<HashMap<Handle<Texture>, usize>>, pub texture_handles: Option<HashMap<Handle<Texture>, usize>>,
} }
#[derive(Debug, Clone, RenderResources)] #[derive(Component, Debug, Clone, RenderResources)]
#[render_resources(from_self)] #[render_resources(from_self)]
#[repr(C)] #[repr(C)]
pub struct TextureAtlasSprite { pub struct TextureAtlasSprite {

View file

@ -1,11 +1,12 @@
use bevy_asset::Handle; use bevy_asset::Handle;
use bevy_ecs::component::Component;
use bevy_math::Size; use bevy_math::Size;
use bevy_render::color::Color; use bevy_render::color::Color;
use glyph_brush_layout::{HorizontalAlign, VerticalAlign}; use glyph_brush_layout::{HorizontalAlign, VerticalAlign};
use crate::Font; use crate::Font;
#[derive(Debug, Default, Clone)] #[derive(Component, Debug, Default, Clone)]
pub struct Text { pub struct Text {
pub sections: Vec<TextSection>, pub sections: Vec<TextSection>,
pub alignment: TextAlignment, pub alignment: TextAlignment,
@ -101,7 +102,7 @@ impl Default for TextStyle {
} }
} }
#[derive(Default, Copy, Clone, Debug)] #[derive(Component, Default, Copy, Clone, Debug)]
pub struct Text2dSize { pub struct Text2dSize {
pub size: Size, pub size: Size,
} }

View file

@ -1,4 +1,5 @@
use bevy_ecs::{ use bevy_ecs::{
component::Component,
entity::{Entity, EntityMap, MapEntities, MapEntitiesError}, entity::{Entity, EntityMap, MapEntities, MapEntitiesError},
reflect::{ReflectComponent, ReflectMapEntities}, reflect::{ReflectComponent, ReflectMapEntities},
}; };
@ -6,7 +7,7 @@ use bevy_reflect::Reflect;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::ops::Deref; use std::ops::Deref;
#[derive(Default, Clone, Debug, Reflect)] #[derive(Component, Default, Clone, Debug, Reflect)]
#[reflect(Component, MapEntities)] #[reflect(Component, MapEntities)]
pub struct Children(pub(crate) SmallVec<[Entity; 8]>); pub struct Children(pub(crate) SmallVec<[Entity; 8]>);

View file

@ -1,5 +1,5 @@
use super::Transform; use super::Transform;
use bevy_ecs::reflect::ReflectComponent; use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_math::{Mat3, Mat4, Quat, Vec3}; use bevy_math::{Mat3, Mat4, Quat, Vec3};
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use std::ops::Mul; use std::ops::Mul;
@ -33,7 +33,7 @@ use std::ops::Mul;
/// This system runs in stage [`CoreStage::PostUpdate`](crate::CoreStage::PostUpdate). If you /// This system runs in stage [`CoreStage::PostUpdate`](crate::CoreStage::PostUpdate). If you
/// update the[`Transform`] of an entity in this stage or after, you will notice a 1 frame lag /// update the[`Transform`] of an entity in this stage or after, you will notice a 1 frame lag
/// before the [`GlobalTransform`] is updated. /// before the [`GlobalTransform`] is updated.
#[derive(Debug, PartialEq, Clone, Copy, Reflect)] #[derive(Component, Debug, PartialEq, Clone, Copy, Reflect)]
#[reflect(Component, PartialEq)] #[reflect(Component, PartialEq)]
pub struct GlobalTransform { pub struct GlobalTransform {
pub translation: Vec3, pub translation: Vec3,

View file

@ -1,4 +1,5 @@
use bevy_ecs::{ use bevy_ecs::{
component::Component,
entity::{Entity, EntityMap, MapEntities, MapEntitiesError}, entity::{Entity, EntityMap, MapEntities, MapEntitiesError},
reflect::{ReflectComponent, ReflectMapEntities}, reflect::{ReflectComponent, ReflectMapEntities},
world::{FromWorld, World}, world::{FromWorld, World},
@ -6,7 +7,7 @@ use bevy_ecs::{
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Reflect)] #[derive(Component, Debug, Copy, Clone, Eq, PartialEq, Reflect)]
#[reflect(Component, MapEntities, PartialEq)] #[reflect(Component, MapEntities, PartialEq)]
pub struct Parent(pub Entity); pub struct Parent(pub Entity);
@ -41,7 +42,7 @@ impl DerefMut for Parent {
} }
} }
#[derive(Debug, Copy, Clone, Eq, PartialEq, Reflect)] #[derive(Component, Debug, Copy, Clone, Eq, PartialEq, Reflect)]
#[reflect(Component, MapEntities, PartialEq)] #[reflect(Component, MapEntities, PartialEq)]
pub struct PreviousParent(pub(crate) Entity); pub struct PreviousParent(pub(crate) Entity);

View file

@ -1,5 +1,5 @@
use super::GlobalTransform; use super::GlobalTransform;
use bevy_ecs::reflect::ReflectComponent; use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_math::{Mat3, Mat4, Quat, Vec3}; use bevy_math::{Mat3, Mat4, Quat, Vec3};
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use std::ops::Mul; use std::ops::Mul;
@ -34,7 +34,7 @@ use std::ops::Mul;
/// This system runs in stage [`CoreStage::PostUpdate`](crate::CoreStage::PostUpdate). If you /// This system runs in stage [`CoreStage::PostUpdate`](crate::CoreStage::PostUpdate). If you
/// update the[`Transform`] of an entity in this stage or after, you will notice a 1 frame lag /// update the[`Transform`] of an entity in this stage or after, you will notice a 1 frame lag
/// before the [`GlobalTransform`] is updated. /// before the [`GlobalTransform`] is updated.
#[derive(Debug, PartialEq, Clone, Copy, Reflect)] #[derive(Component, Debug, PartialEq, Clone, Copy, Reflect)]
#[reflect(Component, PartialEq)] #[reflect(Component, PartialEq)]
pub struct Transform { pub struct Transform {
/// Position of the entity. In 2d, the last value of the `Vec3` is used for z-ordering. /// Position of the entity. In 2d, the last value of the `Vec3` is used for z-ordering.

View file

@ -326,12 +326,16 @@ mod tests {
use super::{BuildChildren, BuildWorldChildren}; use super::{BuildChildren, BuildWorldChildren};
use crate::prelude::{Children, Parent, PreviousParent}; use crate::prelude::{Children, Parent, PreviousParent};
use bevy_ecs::{ use bevy_ecs::{
component::Component,
entity::Entity, entity::Entity,
system::{CommandQueue, Commands}, system::{CommandQueue, Commands},
world::World, world::World,
}; };
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
#[derive(Component)]
struct C(u32);
#[test] #[test]
fn build_children() { fn build_children() {
let mut world = World::default(); let mut world = World::default();
@ -339,11 +343,11 @@ mod tests {
let mut commands = Commands::new(&mut queue, &world); let mut commands = Commands::new(&mut queue, &world);
let mut children = Vec::new(); let mut children = Vec::new();
let parent = commands.spawn().insert(1).id(); let parent = commands.spawn().insert(C(1)).id();
commands.entity(parent).with_children(|parent| { commands.entity(parent).with_children(|parent| {
children.push(parent.spawn().insert(2).id()); children.push(parent.spawn().insert(C(2)).id());
children.push(parent.spawn().insert(3).id()); children.push(parent.spawn().insert(C(3)).id());
children.push(parent.spawn().insert(4).id()); children.push(parent.spawn().insert(C(4)).id());
}); });
queue.apply(&mut world); queue.apply(&mut world);
@ -369,7 +373,7 @@ mod tests {
let mut world = World::default(); let mut world = World::default();
let entities = world let entities = world
.spawn_batch(vec![(1,), (2,), (3,), (4,), (5,)]) .spawn_batch(vec![(C(1),), (C(2),), (C(3),), (C(4),), (C(5),)])
.collect::<Vec<Entity>>(); .collect::<Vec<Entity>>();
let mut queue = CommandQueue::default(); let mut queue = CommandQueue::default();
@ -430,7 +434,7 @@ mod tests {
let mut world = World::default(); let mut world = World::default();
let entities = world let entities = world
.spawn_batch(vec![(1,), (2,), (3,), (4,), (5,)]) .spawn_batch(vec![(C(1),), (C(2),), (C(3),), (C(4),), (C(5),)])
.collect::<Vec<Entity>>(); .collect::<Vec<Entity>>();
world.entity_mut(entities[0]).push_children(&entities[1..3]); world.entity_mut(entities[0]).push_children(&entities[1..3]);

View file

@ -70,6 +70,7 @@ impl<'w> DespawnRecursiveExt for EntityMut<'w> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use bevy_ecs::{ use bevy_ecs::{
component::Component,
system::{CommandQueue, Commands}, system::{CommandQueue, Commands},
world::World, world::World,
}; };
@ -77,6 +78,12 @@ mod tests {
use super::DespawnRecursiveExt; use super::DespawnRecursiveExt;
use crate::{components::Children, hierarchy::BuildChildren}; use crate::{components::Children, hierarchy::BuildChildren};
#[derive(Component, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Debug)]
struct Idx(u32);
#[derive(Component, Clone, PartialEq, Eq, Ord, PartialOrd, Debug)]
struct N(String);
#[test] #[test]
fn despawn_recursive() { fn despawn_recursive() {
let mut world = World::default(); let mut world = World::default();
@ -86,33 +93,35 @@ mod tests {
let mut commands = Commands::new(&mut queue, &world); let mut commands = Commands::new(&mut queue, &world);
commands commands
.spawn_bundle(("Another parent".to_owned(), 0u32)) .spawn_bundle((N("Another parent".to_owned()), Idx(0)))
.with_children(|parent| { .with_children(|parent| {
parent.spawn_bundle(("Another child".to_owned(), 1u32)); parent.spawn_bundle((N("Another child".to_owned()), Idx(1)));
}); });
// Create a grandparent entity which will _not_ be deleted // Create a grandparent entity which will _not_ be deleted
grandparent_entity = commands.spawn_bundle(("Grandparent".to_owned(), 2u32)).id(); grandparent_entity = commands
.spawn_bundle((N("Grandparent".to_owned()), Idx(2)))
.id();
commands.entity(grandparent_entity).with_children(|parent| { commands.entity(grandparent_entity).with_children(|parent| {
// Add a child to the grandparent (the "parent"), which will get deleted // Add a child to the grandparent (the "parent"), which will get deleted
parent parent
.spawn_bundle(("Parent, to be deleted".to_owned(), 3u32)) .spawn_bundle((N("Parent, to be deleted".to_owned()), Idx(3)))
// All descendents of the "parent" should also be deleted. // All descendents of the "parent" should also be deleted.
.with_children(|parent| { .with_children(|parent| {
parent parent
.spawn_bundle(("First Child, to be deleted".to_owned(), 4u32)) .spawn_bundle((N("First Child, to be deleted".to_owned()), Idx(4)))
.with_children(|parent| { .with_children(|parent| {
// child // child
parent.spawn_bundle(( parent.spawn_bundle((
"First grand child, to be deleted".to_owned(), N("First grand child, to be deleted".to_owned()),
5u32, Idx(5),
)); ));
}); });
parent.spawn_bundle(("Second child, to be deleted".to_owned(), 6u32)); parent.spawn_bundle((N("Second child, to be deleted".to_owned()), Idx(6)));
}); });
}); });
commands.spawn_bundle(("An innocent bystander".to_owned(), 7u32)); commands.spawn_bundle((N("An innocent bystander".to_owned()), Idx(7)));
} }
queue.apply(&mut world); queue.apply(&mut world);
@ -127,7 +136,7 @@ mod tests {
queue.apply(&mut world); queue.apply(&mut world);
let mut results = world let mut results = world
.query::<(&String, &u32)>() .query::<(&N, &Idx)>()
.iter(&world) .iter(&world)
.map(|(a, b)| (a.clone(), *b)) .map(|(a, b)| (a.clone(), *b))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -144,10 +153,10 @@ mod tests {
assert_eq!( assert_eq!(
results, results,
vec![ vec![
("Another parent".to_owned(), 0u32), (N("Another parent".to_owned()), Idx(0)),
("Another child".to_owned(), 1u32), (N("Another child".to_owned()), Idx(1)),
("Grandparent".to_owned(), 2u32), (N("Grandparent".to_owned()), Idx(2)),
("An innocent bystander".to_owned(), 7u32) (N("An innocent bystander".to_owned()), Idx(7))
] ]
); );
} }

View file

@ -1,6 +1,7 @@
use crate::Node; use crate::Node;
use bevy_core::FloatOrd; use bevy_core::FloatOrd;
use bevy_ecs::{ use bevy_ecs::{
component::Component,
entity::Entity, entity::Entity,
system::{Local, Query, Res}, system::{Local, Query, Res},
}; };
@ -9,7 +10,7 @@ use bevy_transform::components::GlobalTransform;
use bevy_window::Windows; use bevy_window::Windows;
use smallvec::SmallVec; use smallvec::SmallVec;
#[derive(Copy, Clone, Eq, PartialEq, Debug)] #[derive(Component, Copy, Clone, Eq, PartialEq, Debug)]
pub enum Interaction { pub enum Interaction {
Clicked, Clicked,
Hovered, Hovered,
@ -22,7 +23,7 @@ impl Default for Interaction {
} }
} }
#[derive(Copy, Clone, Eq, PartialEq, Debug)] #[derive(Component, Copy, Clone, Eq, PartialEq, Debug)]
pub enum FocusPolicy { pub enum FocusPolicy {
Block, Block,
Pass, Pass,

View file

@ -1,11 +1,11 @@
use bevy_ecs::reflect::ReflectComponent; use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_math::{Rect, Size, Vec2}; use bevy_math::{Rect, Size, Vec2};
use bevy_reflect::{Reflect, ReflectDeserialize}; use bevy_reflect::{Reflect, ReflectDeserialize};
use bevy_render::renderer::RenderResources; use bevy_render::renderer::RenderResources;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::ops::{Add, AddAssign}; use std::ops::{Add, AddAssign};
#[derive(Debug, Clone, Default, RenderResources, Reflect)] #[derive(Component, Debug, Clone, Default, RenderResources, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct Node { pub struct Node {
pub size: Vec2, pub size: Vec2,
@ -49,7 +49,7 @@ impl AddAssign<f32> for Val {
} }
} }
#[derive(Clone, PartialEq, Debug, Reflect)] #[derive(Component, Clone, PartialEq, Debug, Reflect)]
#[reflect(Component, PartialEq)] #[reflect(Component, PartialEq)]
pub struct Style { pub struct Style {
pub display: Display, pub display: Display,
@ -251,7 +251,7 @@ impl Default for FlexWrap {
} }
} }
#[derive(Default, Copy, Clone, Debug)] #[derive(Component, Default, Copy, Clone, Debug)]
pub struct CalculatedSize { pub struct CalculatedSize {
pub size: Size, pub size: Size,
} }

View file

@ -53,6 +53,7 @@ fn update_hierarchy(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use bevy_ecs::{ use bevy_ecs::{
component::Component,
schedule::{Schedule, Stage, SystemStage}, schedule::{Schedule, Stage, SystemStage},
system::{CommandQueue, Commands}, system::{CommandQueue, Commands},
world::World, world::World,
@ -63,12 +64,15 @@ mod tests {
use super::{ui_z_system, UI_Z_STEP}; use super::{ui_z_system, UI_Z_STEP};
fn node_with_transform(name: &str) -> (String, Node, Transform) { #[derive(Component, PartialEq, Debug, Clone)]
(name.to_owned(), Node::default(), Transform::identity()) struct Label(&'static str);
fn node_with_transform(name: &'static str) -> (Label, Node, Transform) {
(Label(name), Node::default(), Transform::identity())
} }
fn node_without_transform(name: &str) -> (String, Node) { fn node_without_transform(name: &'static str) -> (Label, Node) {
(name.to_owned(), Node::default()) (Label(name), Node::default())
} }
fn get_steps(transform: &Transform) -> u32 { fn get_steps(transform: &Transform) -> u32 {
@ -127,29 +131,29 @@ mod tests {
schedule.run(&mut world); schedule.run(&mut world);
let mut actual_result = world let mut actual_result = world
.query::<(&String, &Transform)>() .query::<(&Label, &Transform)>()
.iter(&world) .iter(&world)
.map(|(name, transform)| (name.clone(), get_steps(transform))) .map(|(name, transform)| (name.clone(), get_steps(transform)))
.collect::<Vec<(String, u32)>>(); .collect::<Vec<(Label, u32)>>();
actual_result.sort_unstable_by_key(|(name, _)| name.clone()); actual_result.sort_unstable_by_key(|(name, _)| name.0);
let expected_result = vec![ let expected_result = vec![
("0".to_owned(), 1), (Label("0"), 1),
("1".to_owned(), 1), (Label("1"), 1),
("1-0".to_owned(), 1), (Label("1-0"), 1),
("1-0-0".to_owned(), 1), (Label("1-0-0"), 1),
// 1-0-1 has no transform // 1-0-1 has no transform
("1-0-2".to_owned(), 3), (Label("1-0-2"), 3),
("1-1".to_owned(), 5), (Label("1-1"), 5),
// 1-2 has no transform // 1-2 has no transform
("1-2-0".to_owned(), 1), (Label("1-2-0"), 1),
("1-2-1".to_owned(), 2), (Label("1-2-1"), 2),
("1-2-2".to_owned(), 3), (Label("1-2-2"), 3),
("1-2-3".to_owned(), 4), (Label("1-2-3"), 4),
("1-3".to_owned(), 11), (Label("1-3"), 11),
// 2 has no transform // 2 has no transform
("2-0".to_owned(), 1), (Label("2-0"), 1),
("2-1".to_owned(), 2), (Label("2-1"), 2),
("2-1-0".to_owned(), 1), (Label("2-1-0"), 1),
]; ];
assert_eq!(actual_result, expected_result); assert_eq!(actual_result, expected_result);
} }

View file

@ -1,2 +1,4 @@
#[derive(Debug, Clone)] use bevy_ecs::component::Component;
#[derive(Component, Debug, Clone)]
pub struct Button; pub struct Button;

View file

@ -1,6 +1,7 @@
use crate::CalculatedSize; use crate::CalculatedSize;
use bevy_asset::{Assets, Handle}; use bevy_asset::{Assets, Handle};
use bevy_ecs::{ use bevy_ecs::{
component::Component,
query::With, query::With,
system::{Query, Res}, system::{Query, Res},
}; };
@ -8,7 +9,7 @@ use bevy_math::Size;
use bevy_render::texture::Texture; use bevy_render::texture::Texture;
use bevy_sprite::ColorMaterial; use bevy_sprite::ColorMaterial;
#[derive(Debug, Clone)] #[derive(Component, Debug, Clone)]
pub enum Image { pub enum Image {
KeepAspect, KeepAspect,
} }

View file

@ -24,14 +24,18 @@ struct ContributorSelection {
idx: usize, idx: usize,
} }
#[derive(Component)]
struct SelectTimer; struct SelectTimer;
#[derive(Component)]
struct ContributorDisplay; struct ContributorDisplay;
#[derive(Component)]
struct Contributor { struct Contributor {
hue: f32, hue: f32,
} }
#[derive(Component)]
struct Velocity { struct Velocity {
translation: Vec3, translation: Vec3,
rotation: f32, rotation: f32,

View file

@ -10,8 +10,11 @@ fn main() {
.run(); .run();
} }
#[derive(Component)]
struct AnimateTranslation; struct AnimateTranslation;
#[derive(Component)]
struct AnimateRotation; struct AnimateRotation;
#[derive(Component)]
struct AnimateScale; struct AnimateScale;
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {

View file

@ -28,6 +28,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
} }
/// this component indicates what entities should rotate /// this component indicates what entities should rotate
#[derive(Component)]
struct Rotates; struct Rotates;
fn rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<Rotates>>) { fn rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<Rotates>>) {

View file

@ -12,6 +12,7 @@ fn main() {
} }
/// this component indicates what entities should rotate /// this component indicates what entities should rotate
#[derive(Component)]
struct Rotator; struct Rotator;
/// rotates the parent, which will result in the child also rotating /// rotates the parent, which will result in the child also rotating

View file

@ -19,6 +19,7 @@ use bevy::{
window::WindowId, window::WindowId,
}; };
#[derive(Component)]
pub struct FirstPass; pub struct FirstPass;
pub const RENDER_TEXTURE_HANDLE: HandleUntyped = pub const RENDER_TEXTURE_HANDLE: HandleUntyped =
@ -109,7 +110,9 @@ fn add_render_to_texture_graph(graph: &mut RenderGraph, size: Extent3d) {
graph.add_node_edge("transform", FIRST_PASS).unwrap(); graph.add_node_edge("transform", FIRST_PASS).unwrap();
} }
#[derive(Component)]
struct FirstPassCube; struct FirstPassCube;
#[derive(Component)]
struct MainPassCube; struct MainPassCube;
/// rotates the inner cube (first pass) /// rotates the inner cube (first pass)

View file

@ -16,6 +16,7 @@ fn main() {
struct SceneInstance(Option<InstanceId>); struct SceneInstance(Option<InstanceId>);
// Component that will be used to tag entities in the scene // Component that will be used to tag entities in the scene
#[derive(Component)]
struct EntityInMyScene; struct EntityInMyScene;
fn setup( fn setup(

View file

@ -17,6 +17,7 @@ fn main() {
.run(); .run();
} }
#[derive(Component)]
struct Rotator; struct Rotator;
/// rotates the parent, which will result in the child also rotating /// rotates the parent, which will result in the child also rotating

View file

@ -12,7 +12,7 @@ fn main() {
.run(); .run();
} }
#[derive(Debug)] #[derive(Component, Debug)]
struct MyComponent(f64); struct MyComponent(f64);
fn setup(mut commands: Commands) { fn setup(mut commands: Commands) {

View file

@ -37,11 +37,13 @@ use rand::random;
// //
// Our game will have a number of "players". Each player has a name that identifies them // Our game will have a number of "players". Each player has a name that identifies them
#[derive(Component)]
struct Player { struct Player {
name: String, name: String,
} }
// Each player also has a score. This component holds on to that score // Each player also has a score. This component holds on to that score
#[derive(Component)]
struct Score { struct Score {
value: usize, value: usize,
} }

View file

@ -26,11 +26,11 @@ const GRAVITY_CONSTANT: f32 = 0.001;
const SOFTENING: f32 = 0.01; const SOFTENING: f32 = 0.01;
const NUM_BODIES: usize = 100; const NUM_BODIES: usize = 100;
#[derive(Default)] #[derive(Component, Default)]
struct Mass(f32); struct Mass(f32);
#[derive(Default)] #[derive(Component, Default)]
struct Acceleration(Vec3); struct Acceleration(Vec3);
#[derive(Default)] #[derive(Component, Default)]
struct LastPos(Vec3); struct LastPos(Vec3);
#[derive(Bundle, Default)] #[derive(Bundle, Default)]

View file

@ -1,6 +1,7 @@
use bevy::{prelude::*, tasks::prelude::*}; use bevy::{prelude::*, tasks::prelude::*};
use rand::random; use rand::random;
#[derive(Component)]
struct Velocity(Vec2); struct Velocity(Vec2);
fn spawn_system( fn spawn_system(

View file

@ -23,6 +23,7 @@ fn main() {
// This `Struct` is just used for convenience in this example. This is the `Component` we'll be // This `Struct` is just used for convenience in this example. This is the `Component` we'll be
// giving to the `Entity` so we have a `Component` to remove in `remove_component()`. // giving to the `Entity` so we have a `Component` to remove in `remove_component()`.
#[derive(Component)]
struct MyComponent; struct MyComponent;
fn setup( fn setup(

View file

@ -9,7 +9,9 @@ fn main() {
.run(); .run();
} }
#[derive(Component)]
pub struct Player; pub struct Player;
#[derive(Component)]
pub struct PlayerCount(usize); pub struct PlayerCount(usize);
/// The SystemParam struct can contain any types that can also be included in a /// The SystemParam struct can contain any types that can also be included in a

View file

@ -25,24 +25,27 @@ fn main() {
.run(); .run();
} }
#[derive(Component)]
struct Paddle { struct Paddle {
speed: f32, speed: f32,
} }
#[derive(Component)]
struct Ball { struct Ball {
velocity: Vec3, velocity: Vec3,
} }
struct Scoreboard { #[derive(Component)]
score: usize,
}
enum Collider { enum Collider {
Solid, Solid,
Scorable, Scorable,
Paddle, Paddle,
} }
struct Scoreboard {
score: usize,
}
fn setup( fn setup(
mut commands: Commands, mut commands: Commands,
mut materials: ResMut<Assets<ColorMaterial>>, mut materials: ResMut<Assets<ColorMaterial>>,

View file

@ -18,8 +18,8 @@ fn main() {
// `Reflect` enable a bunch of cool behaviors, so its worth checking out the dedicated `reflect.rs` // `Reflect` enable a bunch of cool behaviors, so its worth checking out the dedicated `reflect.rs`
// example. The `FromWorld` trait determines how your component is constructed when it loads. // example. The `FromWorld` trait determines how your component is constructed when it loads.
// For simple use cases you can just implement the `Default` trait (which automatically implements // For simple use cases you can just implement the `Default` trait (which automatically implements
// FromWorld). The simplest registered component just needs these two derives: // FromResources). The simplest registered component just needs these two derives:
#[derive(Reflect, Default)] #[derive(Component, Reflect, Default)]
#[reflect(Component)] // this tells the reflect derive to also reflect component behaviors #[reflect(Component)] // this tells the reflect derive to also reflect component behaviors
struct ComponentA { struct ComponentA {
pub x: f32, pub x: f32,
@ -30,7 +30,7 @@ struct ComponentA {
// ignored with the #[reflect(ignore)] attribute. This is also generally where the `FromWorld` // ignored with the #[reflect(ignore)] attribute. This is also generally where the `FromWorld`
// trait comes into play. `FromWorld` gives you access to your App's current ECS `Resources` // trait comes into play. `FromWorld` gives you access to your App's current ECS `Resources`
// when you construct your component. // when you construct your component.
#[derive(Reflect)] #[derive(Component, Reflect)]
#[reflect(Component)] #[reflect(Component)]
struct ComponentB { struct ComponentB {
pub value: String, pub value: String,

View file

@ -20,7 +20,7 @@ pub fn main() {
.run(); .run();
} }
#[derive(RenderResources, Default, TypeUuid)] #[derive(Component, RenderResources, Default, TypeUuid)]
#[uuid = "463e4b8a-d555-4fc2-ba9f-4c880063ba92"] #[uuid = "463e4b8a-d555-4fc2-ba9f-4c880063ba92"]
struct TimeUniform { struct TimeUniform {
value: f32, value: f32,

View file

@ -21,7 +21,7 @@ fn main() {
.run(); .run();
} }
#[derive(RenderResources, Default, TypeUuid)] #[derive(Component, RenderResources, Default, TypeUuid)]
#[uuid = "93fb26fc-6c05-489b-9029-601edf703b6b"] #[uuid = "93fb26fc-6c05-489b-9029-601edf703b6b"]
struct MyArrayTexture { struct MyArrayTexture {
pub texture: Handle<Texture>, pub texture: Handle<Texture>,

View file

@ -15,6 +15,7 @@ struct BevyCounter {
pub count: u128, pub count: u128,
} }
#[derive(Component)]
struct Bird { struct Bird {
velocity: Vec3, velocity: Vec3,
} }

View file

@ -17,9 +17,11 @@ fn main() {
} }
// A unit struct to help identify the FPS UI component, since there may be many Text components // A unit struct to help identify the FPS UI component, since there may be many Text components
#[derive(Component)]
struct FpsText; struct FpsText;
// A unit struct to help identify the color-changing Text component // A unit struct to help identify the color-changing Text component
#[derive(Component)]
struct ColorText; struct ColorText;
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {

View file

@ -17,6 +17,7 @@ fn main() {
.run(); .run();
} }
#[derive(Component)]
struct TextChanges; struct TextChanges;
fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) { fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {

View file

@ -1,6 +1,6 @@
use bevy::prelude::*; use bevy::prelude::*;
#[derive(Default)] #[derive(Component, Default)]
struct Enemy { struct Enemy {
hit_points: u32, hit_points: u32,
} }