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_gilrs = ["bevy_internal/bevy_gilrs"]
bevy_gltf = ["bevy_internal/bevy_gltf"]
bevy_wgpu = ["bevy_internal/bevy_wgpu"]
bevy_wgpu = ["bevy_internal/bevy_wgpu"]
bevy_winit = ["bevy_internal/bevy_winit"]
trace_chrome = ["bevy_internal/trace_chrome"]

View file

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

View file

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

View file

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

View file

@ -1,14 +1,15 @@
use crate::{CoreStage, Events, Plugin, PluginGroup, PluginGroupBuilder, StartupStage};
use bevy_ecs::{
component::{Component, ComponentDescriptor},
prelude::{FromWorld, IntoExclusiveSystem},
schedule::{
IntoSystemDescriptor, RunOnce, Schedule, Stage, StageLabel, State, SystemSet, SystemStage,
IntoSystemDescriptor, RunOnce, Schedule, Stage, StageLabel, State, StateData, SystemSet,
SystemStage,
},
system::Resource,
world::World,
};
use bevy_utils::tracing::debug;
use std::{fmt::Debug, hash::Hash};
use std::fmt::Debug;
#[cfg(feature = "trace")]
use bevy_utils::tracing::info_span;
@ -493,7 +494,7 @@ impl App {
/// adding [State::get_driver] to additional stages you need it in.
pub fn add_state<T>(&mut self, initial: T) -> &mut Self
where
T: Component + Debug + Clone + Eq + Hash,
T: StateData,
{
self.add_state_to_stage(CoreStage::Update, initial)
}
@ -505,7 +506,7 @@ impl App {
/// stages you need it in.
pub fn add_state_to_stage<T>(&mut self, stage: impl StageLabel, initial: T) -> &mut Self
where
T: Component + Debug + Clone + Eq + Hash,
T: StateData,
{
self.insert_resource(State::new(initial))
.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
where
T: Component,
T: Resource,
{
self.init_resource::<Events<T>>()
.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
where
T: Component,
T: Resource,
{
self.world.insert_resource(resource);
self
@ -810,18 +811,6 @@ impl App {
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.
#[cfg(feature = "bevy_reflect")]
pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {

View file

@ -9,7 +9,7 @@ use crate::{
path::{AssetPath, AssetPathId},
Asset, Assets,
};
use bevy_ecs::reflect::ReflectComponent;
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_reflect::{Reflect, ReflectDeserialize};
use bevy_utils::Uuid;
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)
/// collection.
#[derive(Reflect)]
#[derive(Component, Reflect)]
#[reflect(Component)]
pub struct Handle<T>
where

View file

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

View file

@ -1,4 +1,5 @@
use bevy_ecs::{
component::Component,
entity::Entity,
query::Changed,
reflect::ReflectComponent,
@ -13,7 +14,7 @@ use std::{
};
/// A collection of labels
#[derive(Default, Reflect)]
#[derive(Component, Default, Reflect)]
#[reflect(Component)]
pub struct Labels {
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_utils::AHasher;
use std::{
@ -8,7 +8,7 @@ use std::{
};
/// Component used to identify an entity. Stores a hash for faster comparisons
#[derive(Debug, Clone, Reflect)]
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)]
pub struct Name {
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_utils::Duration;
@ -23,7 +23,7 @@ use bevy_utils::Duration;
/// assert!(stopwatch.paused());
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
/// ```
#[derive(Clone, Debug, Default, Reflect)]
#[derive(Component, Clone, Debug, Default, Reflect)]
#[reflect(Component)]
pub struct Stopwatch {
elapsed: Duration,

View file

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

View file

@ -34,10 +34,6 @@ rand = "0.8"
name = "events"
path = "examples/events.rs"
[[example]]
name = "component_storage"
path = "examples/component_storage.rs"
[[example]]
name = "resources"
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.
* **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
// store Position components in Sparse Sets
world.register_component(ComponentDescriptor::new::<Position>(StorageType::SparseSet));
```rs
#[derive(Component)]
struct TableStoredComponent;
#[derive(Component)]
#[component(storage = "SparseSet")]
struct SparseStoredComponent;
```
### 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
#[derive(Default, Debug)]
#[derive(Component, Default, Debug)]
struct Age {
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;
mod component;
use bevy_macro_utils::BevyManifest;
use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
@ -118,7 +120,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
{
if *is_bundle {
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! {
self.#field.get_components(&mut func);
@ -128,7 +130,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
});
} else {
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! {
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;
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 {
fn component_ids(
components: &mut #ecs_path::component::Components,
storages: &mut #ecs_path::storage::Storages,
) -> Vec<#ecs_path::component::ComponentId> {
let mut component_ids = Vec::with_capacity(#field_len);
#(#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")
}
#[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::*;
/// # #[derive(Component)]
/// # struct ComponentA;
/// # #[derive(Component)]
/// # struct ComponentB;
/// # #[derive(Component)]
/// # struct ComponentC;
/// #
/// #[derive(Bundle)]
@ -43,19 +46,26 @@ use std::{any::TypeId, collections::HashMap};
///
/// 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)]
/// struct A {
/// x: i32,
/// y: u64,
/// x: X,
/// y: Y,
/// }
///
/// #[derive(Bundle)]
/// struct B {
/// #[bundle]
/// a: A,
/// z: String,
/// z: Z,
/// }
/// ```
///
@ -67,7 +77,7 @@ use std::{any::TypeId, collections::HashMap};
/// [Bundle::component_ids].
pub unsafe trait Bundle: Send + Sync + 'static {
/// 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
/// this bundle's Components
@ -87,11 +97,11 @@ pub unsafe trait Bundle: Send + Sync + 'static {
macro_rules! tuple_impl {
($($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,)*) {
#[allow(unused_variables)]
fn component_ids(components: &mut Components) -> Vec<ComponentId> {
vec![$(components.get_or_insert_id::<$name>()),*]
fn component_ids(components: &mut Components, storages: &mut Storages) -> Vec<ComponentId> {
vec![$(components.init_component::<$name>(storages)),*]
}
#[allow(unused_variables, unused_mut)]
@ -318,10 +328,7 @@ impl BundleInfo {
let component_info = unsafe { components.get_info_unchecked(component_id) };
match component_info.storage_type() {
StorageType::Table => new_table_components.push(component_id),
StorageType::SparseSet => {
storages.sparse_sets.get_or_insert(component_info);
new_sparse_set_components.push(component_id)
}
StorageType::SparseSet => new_sparse_set_components.push(component_id),
}
}
}
@ -585,10 +592,11 @@ impl Bundles {
pub(crate) fn init_info<'a, T: Bundle>(
&'a mut self,
components: &mut Components,
storages: &mut Storages,
) -> &'a BundleInfo {
let bundle_infos = &mut self.bundle_infos;
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());
// SAFE: T::component_id ensures info was created
let bundle_info = unsafe {

View file

@ -1,6 +1,6 @@
//! Types that detect when their internal data mutate.
use crate::component::{Component, ComponentTicks};
use crate::{component::ComponentTicks, system::Resource};
use bevy_reflect::Reflect;
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.
///
/// 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) ticks: Ticks<'a>,
}
change_detection_impl!(ResMut<'a, T>, T, Component);
impl_into_inner!(ResMut<'a, T>, T, Component);
impl_debug!(ResMut<'a, T>, Component);
change_detection_impl!(ResMut<'a, T>, T, Resource);
impl_into_inner!(ResMut<'a, T>, T, Resource);
impl_debug!(ResMut<'a, T>, Resource);
/// Unique borrow of a non-[`Send`] resource.
///

View file

@ -1,18 +1,28 @@
//! 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::{
alloc::Layout,
any::{Any, TypeId},
collections::hash_map::Entry,
};
use thiserror::Error;
/// 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.
///
/// 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),
/// or to existing entities with [`EntityCommands::insert`](crate::system::EntityCommands::insert),
/// or their [`World`](crate::world::World) equivalents.
@ -21,21 +31,49 @@ use thiserror::Error;
/// as one of the arguments.
///
/// Components can be grouped together into a [`Bundle`](crate::bundle::Bundle).
pub trait Component: Send + Sync + 'static {}
impl<T: Send + Sync + 'static> Component for T {}
pub trait Component: Send + Sync + 'static {
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.
///
/// # 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::*};
///
/// #[derive(Component)]
/// #[component(storage = "SparseSet")]
/// struct A;
///
/// let mut world = World::default();
/// world.register_component(ComponentDescriptor::new::<A>(StorageType::SparseSet));
/// ```
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum StorageType {
@ -128,6 +166,8 @@ impl SparseSetIndex for ComponentId {
#[derive(Debug)]
pub struct ComponentDescriptor {
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,
// SAFETY: This must remain private. It must only be set to "true" if this component is
// actually Send + Sync
@ -143,7 +183,18 @@ impl ComponentDescriptor {
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 {
name: std::any::type_name::<T>().to_string(),
storage_type,
@ -191,46 +242,29 @@ pub struct Components {
#[derive(Debug, Error)]
pub enum ComponentsError {
#[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 {
pub(crate) fn add(
&mut self,
descriptor: ComponentDescriptor,
) -> Result<ComponentId, ComponentsError> {
let index = self.components.len();
if let Some(type_id) = descriptor.type_id {
let index_entry = self.indices.entry(type_id);
if let Entry::Occupied(_) = index_entry {
return Err(ComponentsError::ComponentAlreadyExists {
type_id,
name: descriptor.name,
});
#[inline]
pub fn init_component<T: Component>(&mut self, storages: &mut Storages) -> ComponentId {
let type_id = TypeId::of::<T>();
let components = &mut self.components;
let index = self.indices.entry(type_id).or_insert_with(|| {
let index = components.len();
let descriptor = ComponentDescriptor::new::<T>();
let info = ComponentInfo::new(ComponentId(index), descriptor);
if T::Storage::STORAGE_TYPE == StorageType::SparseSet {
storages.sparse_sets.get_or_insert(&info);
}
self.indices.insert(type_id, index);
}
self.components
.push(ComponentInfo::new(ComponentId(index), descriptor));
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) }
components.push(info);
index
});
ComponentId(*index)
}
#[inline]
@ -270,17 +304,17 @@ impl Components {
}
#[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`]
unsafe {
self.get_or_insert_resource_with(TypeId::of::<T>(), || {
ComponentDescriptor::new::<T>(StorageType::default())
ComponentDescriptor::new_resource::<T>(StorageType::default())
})
}
}
#[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`]
unsafe {
self.get_or_insert_resource_with(TypeId::of::<T>(), || {
@ -308,26 +342,6 @@ impl Components {
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)]

View file

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

View file

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

View file

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

View file

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

View file

@ -486,7 +486,7 @@ where
<Q::Fetch as Fetch>::init(world, &self.fetch_state, last_change_tick, change_tick);
let mut filter =
<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;
for table_id in self.matched_table_ids.iter() {
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:
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
task_pool.scope(|scope| {
let fetch =
<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() {
if Q::Fetch::IS_DENSE && F::Fetch::IS_DENSE {
let tables = &world.storages().tables;
for table_id in self.matched_table_ids.iter() {
let table = &tables[*table_id];

View file

@ -321,6 +321,11 @@ mod tests {
};
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> {
let mut events = Vec::new();
while let Ok(event) = world
@ -381,9 +386,9 @@ mod tests {
#[test]
fn queries() {
let mut world = World::new();
world.spawn().insert(0usize);
fn wants_mut(_: Query<&mut usize>) {}
fn wants_ref(_: Query<&usize>) {}
world.spawn().insert(W(0usize));
fn wants_mut(_: Query<&mut W<usize>>) {}
fn wants_ref(_: Query<&W<usize>>) {}
let mut stage = SystemStage::parallel()
.with_system(wants_mut)
.with_system(wants_mut);
@ -406,9 +411,9 @@ mod tests {
stage.run(&mut world);
assert_eq!(receive_events(&world), vec![StartedSystems(2),]);
let mut world = World::new();
world.spawn().insert_bundle((0usize, 0u32, 0f32));
fn wants_mut_usize(_: Query<(&mut usize, &f32)>) {}
fn wants_mut_u32(_: Query<(&mut u32, &f32)>) {}
world.spawn().insert_bundle((W(0usize), W(0u32), W(0f32)));
fn wants_mut_usize(_: Query<(&mut W<usize>, &W<f32>)>) {}
fn wants_mut_u32(_: Query<(&mut W<u32>, &W<f32>)>) {}
let mut stage = SystemStage::parallel()
.with_system(wants_mut_usize)
.with_system(wants_mut_u32);

View file

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

View file

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

View file

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

View file

@ -522,12 +522,18 @@ impl IndexMut<TableId> for Tables {
#[cfg(test)]
mod tests {
use crate as bevy_ecs;
use crate::component::Component;
use crate::storage::Storages;
use crate::{component::Components, entity::Entity, storage::Table};
#[derive(Component)]
struct W<T>(T);
#[test]
fn table() {
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 mut table = Table::with_capacity(0, columns.len());
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;
use std::marker::PhantomData;
use super::Resource;
/// A [`World`] mutation.
pub trait Command: Send + Sync + 'static {
fn write(self, world: &mut World);
@ -58,6 +60,13 @@ impl<'w, 's> Commands<'w, 's> {
/// ```
/// 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) {
/// // Create a new empty entity and retrieve its 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
/// commands.spawn()
/// // 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
/// .insert("hello world");
/// .insert(Label("hello world"));
/// }
/// # example_system.system();
/// ```
@ -114,8 +123,16 @@ impl<'w, 's> Commands<'w, 's> {
/// ```
/// use bevy_ecs::prelude::*;
///
/// #[derive(Component)]
/// struct Component1;
/// #[derive(Component)]
/// struct Component2;
/// #[derive(Component)]
/// struct Label(&'static str);
/// #[derive(Component)]
/// struct Strength(u32);
/// #[derive(Component)]
/// struct Agility(u32);
///
/// #[derive(Bundle)]
/// struct ExampleBundle {
@ -134,9 +151,9 @@ impl<'w, 's> Commands<'w, 's> {
/// // Create a new entity with two components using a "tuple bundle".
/// .spawn_bundle((Component1, Component2))
/// // 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:
/// .insert("hello world");
/// .insert(Label("hello world"));
/// }
/// # example_system.system();
/// ```
@ -153,15 +170,22 @@ impl<'w, 's> Commands<'w, 's> {
/// ```
/// 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) {
/// // Create a new, empty entity
/// let entity = commands.spawn().id();
///
/// commands.entity(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
/// .insert("hello world");
/// .insert(Label("hello world"));
/// }
/// # example_system.system();
/// ```
@ -190,7 +214,9 @@ impl<'w, 's> Commands<'w, 's> {
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Component)]
/// # struct Name(String);
/// # #[derive(Component)]
/// # struct Score(u32);
/// #
/// # fn system(mut commands: Commands) {
@ -256,7 +282,7 @@ impl<'w, 's> Commands<'w, 's> {
/// # }
/// # 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 })
}
@ -279,7 +305,7 @@ impl<'w, 's> Commands<'w, 's> {
/// # }
/// # system.system();
/// ```
pub fn remove_resource<T: Component>(&mut self) {
pub fn remove_resource<T: Resource>(&mut self) {
self.queue.push(RemoveResource::<T> {
phantom: PhantomData,
});
@ -294,8 +320,11 @@ impl<'w, 's> Commands<'w, 's> {
/// use bevy_ecs::system::InsertBundle;
/// #
/// # struct PlayerEntity { entity: Entity }
/// # #[derive(Component)]
/// # struct Health(u32);
/// # #[derive(Component)]
/// # struct Strength(u32);
/// # #[derive(Component)]
/// # struct Defense(u32);
/// #
/// # #[derive(Bundle)]
@ -354,8 +383,11 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
/// # use bevy_ecs::prelude::*;
/// #
/// # struct PlayerEntity { entity: Entity }
/// # #[derive(Component)]
/// # struct Health(u32);
/// # #[derive(Component)]
/// # struct Strength(u32);
/// # #[derive(Component)]
/// # struct Defense(u32);
/// #
/// # #[derive(Bundle)]
@ -401,7 +433,9 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Component)]
/// # struct Component1;
/// # #[derive(Component)]
/// # struct Component2;
/// #
/// fn example_system(mut commands: Commands) {
@ -436,8 +470,10 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
/// #
/// # struct PlayerEntity { entity: Entity }
/// #
/// # #[derive(Component)]
/// struct Dummy;
/// # #[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>) {
/// commands.entity(player.entity).remove_bundle::<CombatBundle>();
@ -465,6 +501,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
/// # use bevy_ecs::prelude::*;
/// #
/// # struct TargetEnemy { entity: Entity }
/// # #[derive(Component)]
/// # struct Enemy;
/// #
/// 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,
}
impl<T: Component> Command for InsertResource<T> {
impl<T: Resource> Command for InsertResource<T> {
fn write(self, world: &mut World) {
world.insert_resource(self.resource);
}
}
pub struct RemoveResource<T: Component> {
pub struct RemoveResource<T: Resource> {
pub phantom: PhantomData<T>,
}
impl<T: Component> Command for RemoveResource<T> {
impl<T: Resource> Command for RemoveResource<T> {
fn write(self, world: &mut World) {
world.remove_resource::<T>();
}
@ -699,7 +736,8 @@ impl<T: Component> Command for RemoveResource<T> {
#[allow(clippy::float_cmp, clippy::approx_constant)]
mod tests {
use crate::{
component::{ComponentDescriptor, StorageType},
self as bevy_ecs,
component::Component,
system::{CommandQueue, Commands},
world::World,
};
@ -708,7 +746,11 @@ mod tests {
Arc,
};
#[derive(Clone, Debug)]
#[derive(Component)]
#[component(storage = "SparseSet")]
struct SparseDropCk(DropCk);
#[derive(Component)]
struct DropCk(Arc<AtomicUsize>);
impl DropCk {
fn new_pair() -> (Self, Arc<AtomicUsize>) {
@ -723,19 +765,22 @@ mod tests {
}
}
#[derive(Component)]
struct W<T>(T);
#[test]
fn commands() {
let mut world = World::default();
let mut command_queue = CommandQueue::default();
let entity = Commands::new(&mut command_queue, &world)
.spawn_bundle((1u32, 2u64))
.spawn_bundle((W(1u32), W(2u64)))
.id();
command_queue.apply(&mut world);
assert!(world.entities().len() == 1);
let results = world
.query::<(&u32, &u64)>()
.query::<(&W<u32>, &W<u64>)>()
.iter(&world)
.map(|(a, b)| (*a, *b))
.map(|(a, b)| (a.0, b.0))
.collect::<Vec<_>>();
assert_eq!(results, vec![(1u32, 2u64)]);
// test entity despawn
@ -746,9 +791,9 @@ mod tests {
}
command_queue.apply(&mut world);
let results2 = world
.query::<(&u32, &u64)>()
.query::<(&W<u32>, &W<u64>)>()
.iter(&world)
.map(|(a, b)| (*a, *b))
.map(|(a, b)| (a.0, b.0))
.collect::<Vec<_>>();
assert_eq!(results2, vec![]);
}
@ -757,33 +802,28 @@ mod tests {
fn remove_components() {
let mut world = World::default();
struct DenseDropCk(DropCk);
world
.register_component(ComponentDescriptor::new::<DropCk>(StorageType::SparseSet))
.unwrap();
let mut command_queue = CommandQueue::default();
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 = SparseDropCk(sparse_dropck);
let entity = Commands::new(&mut command_queue, &world)
.spawn()
.insert_bundle((1u32, 2u64, dense_dropck, sparse_dropck))
.insert_bundle((W(1u32), W(2u64), dense_dropck, sparse_dropck))
.id();
command_queue.apply(&mut world);
let results_before = world
.query::<(&u32, &u64)>()
.query::<(&W<u32>, &W<u64>)>()
.iter(&world)
.map(|(a, b)| (*a, *b))
.map(|(a, b)| (a.0, b.0))
.collect::<Vec<_>>();
assert_eq!(results_before, vec![(1u32, 2u64)]);
// test component removal
Commands::new(&mut command_queue, &world)
.entity(entity)
.remove::<u32>()
.remove_bundle::<(u32, u64, DenseDropCk, DropCk)>();
.remove::<W<u32>>()
.remove_bundle::<(W<u32>, W<u64>, SparseDropCk, DropCk)>();
assert_eq!(dense_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);
let results_after = world
.query::<(&u32, &u64)>()
.query::<(&W<u32>, &W<u64>)>()
.iter(&world)
.map(|(a, b)| (*a, *b))
.map(|(a, b)| (a.0, b.0))
.collect::<Vec<_>>();
assert_eq!(results_after, vec![]);
let results_after_u64 = world
.query::<&u64>()
.query::<&W<u64>>()
.iter(&world)
.copied()
.map(|v| v.0)
.collect::<Vec<_>>();
assert_eq!(results_after_u64, vec![]);
}

View file

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

View file

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

View file

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

View file

@ -200,6 +200,9 @@ pub struct QuerySetState<T>(T);
impl_query_set!();
pub trait Resource: Send + Sync + 'static {}
impl<T> Resource for T where T: Send + Sync + 'static {}
/// Shared borrow 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.
///
/// 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,
ticks: &'w ComponentTicks,
last_change_tick: u32,
@ -219,9 +222,9 @@ pub struct Res<'w, T: Component> {
}
// 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
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
/// system.
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;
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]
fn as_ref(&self) -> &T {
self.deref()
@ -270,13 +273,13 @@ pub struct ResState<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>;
}
// SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res
// 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 = ();
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() {}
}
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>;
#[inline]
@ -337,14 +340,14 @@ impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResState<T> {
/// See: [`Res<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>;
}
// 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 = ();
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() {}
}
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>>;
#[inline]
@ -381,13 +384,13 @@ pub struct ResMutState<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>;
}
// SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res
// 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 = ();
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() {}
}
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>;
#[inline]
@ -454,11 +457,11 @@ impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResMutState<T> {
/// See: [`ResMut<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>;
}
unsafe impl<T: Component> SystemParamState for OptionResMutState<T> {
unsafe impl<T: Resource> SystemParamState for OptionResMutState<T> {
type Config = ();
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() {}
}
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>>;
#[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.
/// 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
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
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;
#[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]
fn deref_mut(&mut self) -> &mut Self::Target {
self.0
@ -584,14 +587,14 @@ impl<'a, T: Component> DerefMut for Local<'a, 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>;
}
// 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>;
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>;
#[inline]
@ -624,9 +627,11 @@ impl<'w, 's, T: Component + FromWorld> SystemParamFetch<'w, 's> for LocalState<T
/// Basic usage:
///
/// ```
/// # use bevy_ecs::component::Component;
/// # use bevy_ecs::system::IntoSystem;
/// # use bevy_ecs::system::RemovedComponents;
/// #
/// # #[derive(Component)]
/// # struct 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();
/// ```
pub struct RemovedComponents<'a, T> {
pub struct RemovedComponents<'a, T: Component> {
world: &'a World,
component_id: ComponentId,
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.
pub fn iter(&self) -> std::iter::Cloned<std::slice::Iter<'_, Entity>> {
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 {
Self {
component_id: world.components.get_or_insert_id::<T>(),
component_id: world.init_component::<T>(),
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;
fn deref(&self) -> &Self::Target {

View file

@ -194,7 +194,7 @@ impl<'w> EntityMut<'w> {
let bundle_info = self
.world
.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(
&mut self.world.entities,
&mut self.world.archetypes,
@ -219,7 +219,7 @@ impl<'w> EntityMut<'w> {
let entities = &mut self.world.entities;
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 new_archetype_id = unsafe {
remove_bundle_from_archetype(
@ -341,7 +341,7 @@ impl<'w> EntityMut<'w> {
let entities = &mut self.world.entities;
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 new_archetype_id = unsafe {
remove_bundle_from_archetype(

View file

@ -11,13 +11,11 @@ use crate::{
archetype::{ArchetypeComponentId, ArchetypeComponentInfo, ArchetypeId, Archetypes},
bundle::{Bundle, BundleInserter, BundleSpawner, Bundles},
change_detection::Ticks,
component::{
Component, ComponentDescriptor, ComponentId, ComponentTicks, Components, ComponentsError,
StorageType,
},
component::{Component, ComponentId, ComponentTicks, Components, StorageType},
entity::{AllocAtWithoutReplacement, Entities, Entity},
query::{FilterFetch, QueryState, WorldQuery},
storage::{Column, SparseSet, Storages},
system::Resource,
};
use std::{
any::TypeId,
@ -172,38 +170,8 @@ impl World {
WorldCell::new(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.
///
/// 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)
pub fn init_component<T: Component>(&mut self) -> ComponentId {
self.components.init_component::<T>(&mut self.storages)
}
/// 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.
///
/// ```
/// use bevy_ecs::world::World;
/// use bevy_ecs::{component::Component, world::World};
///
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
@ -236,8 +205,9 @@ impl World {
/// 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 {
/// x: f32,
/// y: f32,
@ -284,8 +254,9 @@ impl World {
/// to unwrap the [EntityRef] yourself.
///
/// ```
/// use bevy_ecs::world::World;
/// use bevy_ecs::{component::Component, world::World};
///
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
@ -311,8 +282,9 @@ impl World {
/// to unwrap the [EntityMut] yourself.
///
/// ```
/// use bevy_ecs::world::World;
/// use bevy_ecs::{component::Component, world::World};
///
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
@ -338,17 +310,22 @@ impl World {
/// 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 {
/// x: f32,
/// y: f32,
/// }
/// #[derive(Component)]
/// struct Label(&'static str);
/// #[derive(Component)]
/// struct Num(u32);
///
/// let mut world = World::new();
/// let entity = world.spawn()
/// .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();
///
/// let position = world.entity(entity).get::<Position>().unwrap();
@ -385,12 +362,17 @@ impl World {
/// 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 entities = world.spawn_batch(vec![
/// ("a", 0.0), // the first entity
/// ("b", 1.0), // the second entity
/// (Str("a"), Num(0)), // the first entity
/// (Str("b"), Num(1)), // the second entity
/// ]).collect::<Vec<Entity>>();
///
/// assert_eq!(entities.len(), 2);
@ -406,8 +388,9 @@ impl World {
/// 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.
/// ```
/// use bevy_ecs::world::World;
/// use bevy_ecs::{component::Component, world::World};
///
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
@ -428,8 +411,9 @@ impl World {
/// 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.
/// ```
/// use bevy_ecs::world::World;
/// use bevy_ecs::{component::Component, world::World};
///
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
@ -451,8 +435,9 @@ impl World {
/// [Component]s. Returns `true` if the `entity` is successfully despawned and `false` if
/// the `entity` does not exist.
/// ```
/// use bevy_ecs::world::World;
/// use bevy_ecs::{component::Component, world::World};
///
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
@ -488,14 +473,15 @@ impl World {
/// Returns [QueryState] for the given [WorldQuery], which is used to efficiently
/// 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 {
/// x: f32,
/// y: f32,
/// }
///
/// #[derive(Component)]
/// struct Velocity {
/// x: f32,
/// y: f32,
@ -523,18 +509,28 @@ impl World {
/// 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 a = world.spawn().insert_bundle((2, 4.0)).id();
/// let b = world.spawn().insert_bundle((3, 5.0)).id();
/// let c = world.spawn().insert_bundle((1, 6.0)).id();
/// let mut entities = world.query::<(Entity, &i32, &f64)>()
/// let a = world.spawn().insert_bundle((Order(2), Label("second"))).id();
/// let b = world.spawn().insert_bundle((Order(3), Label("third"))).id();
/// let c = world.spawn().insert_bundle((Order(1), Label("first"))).id();
/// let mut entities = world.query::<(Entity, &Order, &Label)>()
/// .iter(&world)
/// .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.
/// 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]
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
/// 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;
/// #[derive(Component)]
/// struct B;
///
/// let mut world = World::new();
@ -592,8 +590,8 @@ impl World {
/// Inserts a new resource with the given `value`.
/// Resources are "unique" data of a given type.
#[inline]
pub fn insert_resource<T: Component>(&mut self, value: T) {
let component_id = self.components.get_or_insert_resource_id::<T>();
pub fn insert_resource<T: Resource>(&mut self, value: T) {
let component_id = self.components.init_resource::<T>();
// SAFE: component_id just initialized and corresponds to resource of type T
unsafe { self.insert_resource_with_id(component_id, value) };
}
@ -603,7 +601,7 @@ impl World {
#[inline]
pub fn insert_non_send<T: 'static>(&mut self, value: 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
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].
/// Resources are "unique" data of a given type.
#[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
unsafe { self.remove_resource_unchecked() }
}
@ -644,7 +642,7 @@ impl World {
/// Returns `true` if a resource of type `T` exists. Otherwise returns `false`.
#[inline]
pub fn contains_resource<T: Component>(&self) -> bool {
pub fn contains_resource<T: Resource>(&self) -> bool {
let component_id =
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) {
component_id
@ -657,12 +655,12 @@ impl World {
/// Gets a reference to the resource of the given type, if it exists. Otherwise returns [None]
/// Resources are "unique" data of a given type.
#[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>())?;
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 =
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) {
component_id
@ -679,7 +677,7 @@ impl World {
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 =
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) {
component_id
@ -699,7 +697,7 @@ impl World {
/// 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.
#[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
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
/// calling `func`.
#[inline]
pub fn get_resource_or_insert_with<T: Component>(
pub fn get_resource_or_insert_with<T: Resource>(
&mut self,
func: impl FnOnce() -> T,
) -> Mut<'_, T> {
@ -725,7 +723,7 @@ impl World {
/// This will allow aliased mutable access to the given resource type. The caller must ensure
/// that only one mutable access exists at a time.
#[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>())?;
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).
///
/// ```
/// 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 e0 = world.spawn().id();
/// let e1 = world.spawn().id();
/// world.insert_or_spawn_batch(vec![
/// (e0, ("a", 0.0)), // the first entity
/// (e1, ("b", 1.0)), // the second entity
/// (e0, (A("a"), B(0.0))), // the first 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>>
where
@ -795,7 +797,9 @@ impl World {
let iter = iter.into_iter();
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> {
Spawn(BundleSpawner<'a, 'b>),
Insert(BundleInserter<'a, 'b>, ArchetypeId),
@ -884,8 +888,10 @@ impl World {
/// returning. This enables safe mutable access to a resource while still providing mutable
/// world access
/// ```
/// use bevy_ecs::world::{World, Mut};
/// use bevy_ecs::{component::Component, world::{World, Mut}};
/// #[derive(Component)]
/// struct A(u32);
/// #[derive(Component)]
/// struct B(u32);
/// let mut world = World::new();
/// world.insert_resource(A(1));
@ -897,10 +903,7 @@ impl World {
/// });
/// assert_eq!(world.get_resource::<A>().unwrap().0, 2);
/// ```
pub fn resource_scope<T: Component, U>(
&mut self,
f: impl FnOnce(&mut World, Mut<T>) -> U,
) -> U {
pub fn resource_scope<T: Resource, U>(&mut self, f: impl FnOnce(&mut World, Mut<T>) -> U) -> U {
let component_id = self
.components
.get_resource_id(TypeId::of::<T>())
@ -1041,15 +1044,15 @@ impl World {
})
}
pub(crate) fn initialize_resource<T: Component>(&mut self) -> ComponentId {
let component_id = self.components.get_or_insert_resource_id::<T>();
pub(crate) fn initialize_resource<T: Resource>(&mut self) -> ComponentId {
let component_id = self.components.init_resource::<T>();
// SAFE: resource initialized above
unsafe { self.initialize_resource_internal(component_id) };
component_id
}
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
unsafe { self.initialize_resource_internal(component_id) };
component_id

View file

@ -27,7 +27,9 @@ where
let (lower, upper) = iter.size_hint();
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);
let mut spawner = bundle_info.get_bundle_spawner(
&mut world.entities,

View file

@ -1,7 +1,7 @@
use crate::{
archetype::ArchetypeComponentId,
component::Component,
storage::SparseSet,
system::Resource,
world::{Mut, World},
};
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 resource_archetype = self.world.archetypes.resource();
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 resource_archetype = self.world.archetypes.resource();
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;
mod attrs;
mod symbol;
pub use attrs::*;
pub use symbol::*;
use cargo_manifest::{DepsSet, Manifest};
use proc_macro::TokenStream;
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_ecs::reflect::ReflectComponent;
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_math::Vec3;
use bevy_reflect::Reflect;
use bevy_render::color::Color;
use bevy_transform::components::GlobalTransform;
/// A point light
#[derive(Debug, Clone, Copy, Reflect)]
#[derive(Component, Debug, Clone, Copy, Reflect)]
#[reflect(Component)]
pub struct PointLight {
pub color: Color,
@ -77,7 +77,7 @@ impl PointLightUniform {
/// | 32,000100,000 | Direct sunlight |
///
/// Source: [Wikipedia](https://en.wikipedia.org/wiki/Lux)
#[derive(Debug, Clone, Copy, Reflect)]
#[derive(Component, Debug, Clone, Copy, Reflect)]
#[reflect(Component)]
pub struct DirectionalLight {
pub color: Color,

View file

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

View file

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

View file

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

View file

@ -1,7 +1,9 @@
use super::{Camera, DepthCalculation};
use crate::{draw::OutsideFrustum, prelude::Visible};
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_transform::prelude::GlobalTransform;
@ -11,7 +13,7 @@ pub struct VisibleEntity {
pub order: FloatOrd,
}
#[derive(Default, Debug, Reflect)]
#[derive(Component, Default, Debug, Reflect)]
#[reflect(Component)]
pub struct VisibleEntities {
#[reflect(ignore)]
@ -42,7 +44,7 @@ pub type Layer = u8;
/// An entity with this component without any layers is invisible.
///
/// 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)]
pub struct RenderLayers(LayerMask);

View file

@ -10,6 +10,7 @@ use crate::{
};
use bevy_asset::{Asset, Assets, Handle};
use bevy_ecs::{
component::Component,
reflect::ReflectComponent,
system::{Query, Res, ResMut, SystemParam},
};
@ -49,7 +50,7 @@ pub enum RenderCommand {
},
}
#[derive(Debug, Clone, Reflect)]
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)]
pub struct Visible {
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 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).
#[derive(Debug, Default, Clone, Reflect)]
#[derive(Component, Debug, Default, Clone, Reflect)]
#[reflect(Component)]
#[component(storage = "SparseSet")]
pub struct OutsideFrustum;
/// A component that indicates how to draw an entity.
#[derive(Debug, Clone, Reflect)]
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)]
pub struct Draw {
#[reflect(ignore)]

View file

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

View file

@ -10,12 +10,12 @@ use crate::{
texture::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage},
Color,
};
use bevy_ecs::{reflect::ReflectComponent, world::World};
use bevy_ecs::{component::Component, reflect::ReflectComponent, world::World};
use bevy_reflect::Reflect;
use bevy_window::WindowId;
/// 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)]
pub struct MainPass;

View file

@ -12,6 +12,7 @@ use crate::{
use bevy_app::EventReader;
use bevy_asset::{Asset, AssetEvent, Assets, Handle, HandleId};
use bevy_ecs::{
component::Component,
entity::Entity,
prelude::QueryState,
query::{Changed, Or, With},
@ -398,7 +399,7 @@ where
impl<T> SystemNode for RenderResourcesNode<T>
where
T: renderer::RenderResources,
T: renderer::RenderResources + Component,
{
fn get_system(&self) -> BoxedSystem {
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)]
fn render_resources_node_system<T: RenderResources>(
fn render_resources_node_system<T: RenderResources + Component>(
mut state: Local<RenderResourcesNodeState<Entity, T>>,
mut entities_waiting_for_textures: Local<Vec<Entity>>,
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};
pub use bevy_derive::ShaderDefs;
use bevy_ecs::{
prelude::Component,
query::Without,
system::{Query, Res},
};
@ -70,7 +71,7 @@ impl ShaderDef for Option<Handle<Texture>> {
/// Updates [RenderPipelines] with the latest [ShaderDefs]
pub fn shader_defs_system<T>(mut query: Query<(&T, &mut RenderPipelines), Without<OutsideFrustum>>)
where
T: ShaderDefs + Send + Sync + 'static,
T: ShaderDefs + Component,
{
for (shader_defs, mut render_pipelines) in query.iter_mut() {
for shader_def in shader_defs.iter_shader_defs() {

View file

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

View file

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

View file

@ -2,6 +2,7 @@ use crate::ColorMaterial;
use bevy_asset::{Assets, Handle};
use bevy_core::Bytes;
use bevy_ecs::{
component::Component,
query::Without,
system::{Query, Res},
};
@ -15,7 +16,7 @@ use bevy_render::{
use serde::{Deserialize, Serialize};
/// 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)]
#[uuid = "7233c597-ccfa-411f-bd59-9af349432ada"]
#[repr(C)]

View file

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

View file

@ -1,11 +1,12 @@
use bevy_asset::Handle;
use bevy_ecs::component::Component;
use bevy_math::Size;
use bevy_render::color::Color;
use glyph_brush_layout::{HorizontalAlign, VerticalAlign};
use crate::Font;
#[derive(Debug, Default, Clone)]
#[derive(Component, Debug, Default, Clone)]
pub struct Text {
pub sections: Vec<TextSection>,
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 size: Size,
}

View file

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

View file

@ -1,5 +1,5 @@
use super::Transform;
use bevy_ecs::reflect::ReflectComponent;
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_math::{Mat3, Mat4, Quat, Vec3};
use bevy_reflect::Reflect;
use std::ops::Mul;
@ -33,7 +33,7 @@ use std::ops::Mul;
/// 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
/// before the [`GlobalTransform`] is updated.
#[derive(Debug, PartialEq, Clone, Copy, Reflect)]
#[derive(Component, Debug, PartialEq, Clone, Copy, Reflect)]
#[reflect(Component, PartialEq)]
pub struct GlobalTransform {
pub translation: Vec3,

View file

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

View file

@ -1,5 +1,5 @@
use super::GlobalTransform;
use bevy_ecs::reflect::ReflectComponent;
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_math::{Mat3, Mat4, Quat, Vec3};
use bevy_reflect::Reflect;
use std::ops::Mul;
@ -34,7 +34,7 @@ use std::ops::Mul;
/// 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
/// before the [`GlobalTransform`] is updated.
#[derive(Debug, PartialEq, Clone, Copy, Reflect)]
#[derive(Component, Debug, PartialEq, Clone, Copy, Reflect)]
#[reflect(Component, PartialEq)]
pub struct Transform {
/// 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 crate::prelude::{Children, Parent, PreviousParent};
use bevy_ecs::{
component::Component,
entity::Entity,
system::{CommandQueue, Commands},
world::World,
};
use smallvec::{smallvec, SmallVec};
#[derive(Component)]
struct C(u32);
#[test]
fn build_children() {
let mut world = World::default();
@ -339,11 +343,11 @@ mod tests {
let mut commands = Commands::new(&mut queue, &world);
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| {
children.push(parent.spawn().insert(2).id());
children.push(parent.spawn().insert(3).id());
children.push(parent.spawn().insert(4).id());
children.push(parent.spawn().insert(C(2)).id());
children.push(parent.spawn().insert(C(3)).id());
children.push(parent.spawn().insert(C(4)).id());
});
queue.apply(&mut world);
@ -369,7 +373,7 @@ mod tests {
let mut world = World::default();
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>>();
let mut queue = CommandQueue::default();
@ -430,7 +434,7 @@ mod tests {
let mut world = World::default();
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>>();
world.entity_mut(entities[0]).push_children(&entities[1..3]);

View file

@ -70,6 +70,7 @@ impl<'w> DespawnRecursiveExt for EntityMut<'w> {
#[cfg(test)]
mod tests {
use bevy_ecs::{
component::Component,
system::{CommandQueue, Commands},
world::World,
};
@ -77,6 +78,12 @@ mod tests {
use super::DespawnRecursiveExt;
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]
fn despawn_recursive() {
let mut world = World::default();
@ -86,33 +93,35 @@ mod tests {
let mut commands = Commands::new(&mut queue, &world);
commands
.spawn_bundle(("Another parent".to_owned(), 0u32))
.spawn_bundle((N("Another parent".to_owned()), Idx(0)))
.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
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| {
// Add a child to the grandparent (the "parent"), which will get deleted
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.
.with_children(|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| {
// child
parent.spawn_bundle((
"First grand child, to be deleted".to_owned(),
5u32,
N("First grand child, to be deleted".to_owned()),
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);
@ -127,7 +136,7 @@ mod tests {
queue.apply(&mut world);
let mut results = world
.query::<(&String, &u32)>()
.query::<(&N, &Idx)>()
.iter(&world)
.map(|(a, b)| (a.clone(), *b))
.collect::<Vec<_>>();
@ -144,10 +153,10 @@ mod tests {
assert_eq!(
results,
vec![
("Another parent".to_owned(), 0u32),
("Another child".to_owned(), 1u32),
("Grandparent".to_owned(), 2u32),
("An innocent bystander".to_owned(), 7u32)
(N("Another parent".to_owned()), Idx(0)),
(N("Another child".to_owned()), Idx(1)),
(N("Grandparent".to_owned()), Idx(2)),
(N("An innocent bystander".to_owned()), Idx(7))
]
);
}

View file

@ -1,6 +1,7 @@
use crate::Node;
use bevy_core::FloatOrd;
use bevy_ecs::{
component::Component,
entity::Entity,
system::{Local, Query, Res},
};
@ -9,7 +10,7 @@ use bevy_transform::components::GlobalTransform;
use bevy_window::Windows;
use smallvec::SmallVec;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[derive(Component, Copy, Clone, Eq, PartialEq, Debug)]
pub enum Interaction {
Clicked,
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 {
Block,
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_reflect::{Reflect, ReflectDeserialize};
use bevy_render::renderer::RenderResources;
use serde::{Deserialize, Serialize};
use std::ops::{Add, AddAssign};
#[derive(Debug, Clone, Default, RenderResources, Reflect)]
#[derive(Component, Debug, Clone, Default, RenderResources, Reflect)]
#[reflect(Component)]
pub struct Node {
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)]
pub struct Style {
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 size: Size,
}

View file

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

View file

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

View file

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

View file

@ -10,8 +10,11 @@ fn main() {
.run();
}
#[derive(Component)]
struct AnimateTranslation;
#[derive(Component)]
struct AnimateRotation;
#[derive(Component)]
struct AnimateScale;
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
#[derive(Component)]
struct 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
#[derive(Component)]
struct Rotator;
/// rotates the parent, which will result in the child also rotating

View file

@ -19,6 +19,7 @@ use bevy::{
window::WindowId,
};
#[derive(Component)]
pub struct FirstPass;
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();
}
#[derive(Component)]
struct FirstPassCube;
#[derive(Component)]
struct MainPassCube;
/// rotates the inner cube (first pass)

View file

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

View file

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

View file

@ -12,7 +12,7 @@ fn main() {
.run();
}
#[derive(Debug)]
#[derive(Component, Debug)]
struct MyComponent(f64);
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
#[derive(Component)]
struct Player {
name: String,
}
// Each player also has a score. This component holds on to that score
#[derive(Component)]
struct Score {
value: usize,
}

View file

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

View file

@ -1,6 +1,7 @@
use bevy::{prelude::*, tasks::prelude::*};
use rand::random;
#[derive(Component)]
struct Velocity(Vec2);
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
// giving to the `Entity` so we have a `Component` to remove in `remove_component()`.
#[derive(Component)]
struct MyComponent;
fn setup(

View file

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

View file

@ -25,24 +25,27 @@ fn main() {
.run();
}
#[derive(Component)]
struct Paddle {
speed: f32,
}
#[derive(Component)]
struct Ball {
velocity: Vec3,
}
struct Scoreboard {
score: usize,
}
#[derive(Component)]
enum Collider {
Solid,
Scorable,
Paddle,
}
struct Scoreboard {
score: usize,
}
fn setup(
mut commands: Commands,
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`
// 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
// FromWorld). The simplest registered component just needs these two derives:
#[derive(Reflect, Default)]
// FromResources). The simplest registered component just needs these two derives:
#[derive(Component, Reflect, Default)]
#[reflect(Component)] // this tells the reflect derive to also reflect component behaviors
struct ComponentA {
pub x: f32,
@ -30,7 +30,7 @@ struct ComponentA {
// 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`
// when you construct your component.
#[derive(Reflect)]
#[derive(Component, Reflect)]
#[reflect(Component)]
struct ComponentB {
pub value: String,

View file

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

View file

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

View file

@ -15,6 +15,7 @@ struct BevyCounter {
pub count: u128,
}
#[derive(Component)]
struct Bird {
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
#[derive(Component)]
struct FpsText;
// A unit struct to help identify the color-changing Text component
#[derive(Component)]
struct ColorText;
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {

View file

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

View file

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