mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 06:00:20 +00:00
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:
parent
5ba2b9adcf
commit
07ed1d053e
91 changed files with 1529 additions and 1082 deletions
|
@ -1,4 +1,5 @@
|
||||||
use bevy::ecs::{
|
use bevy::ecs::{
|
||||||
|
component::Component,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
system::{Command, CommandQueue, Commands},
|
system::{Command, CommandQueue, Commands},
|
||||||
world::World,
|
world::World,
|
||||||
|
@ -18,8 +19,11 @@ criterion_group!(
|
||||||
);
|
);
|
||||||
criterion_main!(benches);
|
criterion_main!(benches);
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct A;
|
struct A;
|
||||||
|
#[derive(Component)]
|
||||||
struct B;
|
struct B;
|
||||||
|
#[derive(Component)]
|
||||||
struct C;
|
struct C;
|
||||||
|
|
||||||
fn empty_commands(criterion: &mut Criterion) {
|
fn empty_commands(criterion: &mut Criterion) {
|
||||||
|
@ -79,10 +83,10 @@ fn spawn_commands(criterion: &mut Criterion) {
|
||||||
group.finish();
|
group.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Component)]
|
||||||
struct Matrix([[f32; 4]; 4]);
|
struct Matrix([[f32; 4]; 4]);
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Component)]
|
||||||
struct Vec3([f32; 3]);
|
struct Vec3([f32; 3]);
|
||||||
|
|
||||||
fn insert_commands(criterion: &mut Criterion) {
|
fn insert_commands(criterion: &mut Criterion) {
|
||||||
|
@ -95,14 +99,16 @@ fn insert_commands(criterion: &mut Criterion) {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
let mut command_queue = CommandQueue::default();
|
let mut command_queue = CommandQueue::default();
|
||||||
let mut entities = Vec::new();
|
let mut entities = Vec::new();
|
||||||
for i in 0..entity_count {
|
for _ in 0..entity_count {
|
||||||
entities.push(world.spawn().id());
|
entities.push(world.spawn().id());
|
||||||
}
|
}
|
||||||
|
|
||||||
bencher.iter(|| {
|
bencher.iter(|| {
|
||||||
let mut commands = Commands::new(&mut command_queue, &world);
|
let mut commands = Commands::new(&mut command_queue, &world);
|
||||||
for entity in entities.iter() {
|
for entity in entities.iter() {
|
||||||
commands.entity(*entity).insert_bundle((Matrix::default(), Vec3::default()));
|
commands
|
||||||
|
.entity(*entity)
|
||||||
|
.insert_bundle((Matrix::default(), Vec3::default()));
|
||||||
}
|
}
|
||||||
drop(commands);
|
drop(commands);
|
||||||
command_queue.apply(&mut world);
|
command_queue.apply(&mut world);
|
||||||
|
@ -112,7 +118,7 @@ fn insert_commands(criterion: &mut Criterion) {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
let mut command_queue = CommandQueue::default();
|
let mut command_queue = CommandQueue::default();
|
||||||
let mut entities = Vec::new();
|
let mut entities = Vec::new();
|
||||||
for i in 0..entity_count {
|
for _ in 0..entity_count {
|
||||||
entities.push(world.spawn().id());
|
entities.push(world.spawn().id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use bevy::ecs::{
|
use bevy::ecs::{
|
||||||
|
component::Component,
|
||||||
schedule::{Stage, SystemStage},
|
schedule::{Stage, SystemStage},
|
||||||
system::{IntoSystem, Query},
|
system::Query,
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
|
@ -12,10 +13,15 @@ fn run_stage(stage: &mut SystemStage, world: &mut World) {
|
||||||
stage.run(world);
|
stage.run(world);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct A(f32);
|
struct A(f32);
|
||||||
|
#[derive(Component)]
|
||||||
struct B(f32);
|
struct B(f32);
|
||||||
|
#[derive(Component)]
|
||||||
struct C(f32);
|
struct C(f32);
|
||||||
|
#[derive(Component)]
|
||||||
struct D(f32);
|
struct D(f32);
|
||||||
|
#[derive(Component)]
|
||||||
struct E(f32);
|
struct E(f32);
|
||||||
|
|
||||||
const ENTITY_BUNCH: usize = 5000;
|
const ENTITY_BUNCH: usize = 5000;
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
use bevy::ecs::{
|
use bevy::ecs::{component::Component, entity::Entity, world::World};
|
||||||
component::{ComponentDescriptor, StorageType},
|
|
||||||
entity::Entity,
|
|
||||||
world::World,
|
|
||||||
};
|
|
||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
criterion_group!(
|
criterion_group!(
|
||||||
|
@ -15,16 +11,18 @@ criterion_group!(
|
||||||
);
|
);
|
||||||
criterion_main!(benches);
|
criterion_main!(benches);
|
||||||
|
|
||||||
struct A(f32);
|
#[derive(Component, Default)]
|
||||||
|
#[component(storage = "Table")]
|
||||||
|
struct Table(f32);
|
||||||
|
#[derive(Component, Default)]
|
||||||
|
#[component(storage = "SparseSet")]
|
||||||
|
struct Sparse(f32);
|
||||||
|
|
||||||
const RANGE: std::ops::Range<u32> = 5..6;
|
const RANGE: std::ops::Range<u32> = 5..6;
|
||||||
|
|
||||||
fn setup(entity_count: u32, storage: StorageType) -> World {
|
fn setup<T: Component + Default>(entity_count: u32) -> World {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
world
|
world.spawn_batch((0..entity_count).map(|_| (T::default(),)));
|
||||||
.register_component(ComponentDescriptor::new::<A>(storage))
|
|
||||||
.unwrap();
|
|
||||||
world.spawn_batch((0..entity_count).map(|_| (A(0.0),)));
|
|
||||||
world
|
world
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +33,7 @@ fn world_entity(criterion: &mut Criterion) {
|
||||||
|
|
||||||
for entity_count in RANGE.map(|i| i * 10_000) {
|
for entity_count in RANGE.map(|i| i * 10_000) {
|
||||||
group.bench_function(format!("{}_entities", entity_count), |bencher| {
|
group.bench_function(format!("{}_entities", entity_count), |bencher| {
|
||||||
let world = setup(entity_count, StorageType::Table);
|
let world = setup::<Table>(entity_count);
|
||||||
|
|
||||||
bencher.iter(|| {
|
bencher.iter(|| {
|
||||||
for i in 0..entity_count {
|
for i in 0..entity_count {
|
||||||
|
@ -55,21 +53,26 @@ fn world_get(criterion: &mut Criterion) {
|
||||||
group.measurement_time(std::time::Duration::from_secs(4));
|
group.measurement_time(std::time::Duration::from_secs(4));
|
||||||
|
|
||||||
for entity_count in RANGE.map(|i| i * 10_000) {
|
for entity_count in RANGE.map(|i| i * 10_000) {
|
||||||
for storage in [StorageType::Table, StorageType::SparseSet] {
|
group.bench_function(format!("{}_entities_table", entity_count), |bencher| {
|
||||||
group.bench_function(
|
let world = setup::<Table>(entity_count);
|
||||||
format!("{}_entities_{:?}", entity_count, storage),
|
|
||||||
|bencher| {
|
|
||||||
let world = setup(entity_count, storage);
|
|
||||||
|
|
||||||
bencher.iter(|| {
|
bencher.iter(|| {
|
||||||
for i in 0..entity_count {
|
for i in 0..entity_count {
|
||||||
let entity = Entity::new(i);
|
let entity = Entity::new(i);
|
||||||
assert!(world.get::<A>(entity).is_some());
|
assert!(world.get::<Table>(entity).is_some());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
});
|
||||||
);
|
group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| {
|
||||||
}
|
let world = setup::<Sparse>(entity_count);
|
||||||
|
|
||||||
|
bencher.iter(|| {
|
||||||
|
for i in 0..entity_count {
|
||||||
|
let entity = Entity::new(i);
|
||||||
|
assert!(world.get::<Sparse>(entity).is_some());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
group.finish();
|
group.finish();
|
||||||
|
@ -81,22 +84,28 @@ fn world_query_get(criterion: &mut Criterion) {
|
||||||
group.measurement_time(std::time::Duration::from_secs(4));
|
group.measurement_time(std::time::Duration::from_secs(4));
|
||||||
|
|
||||||
for entity_count in RANGE.map(|i| i * 10_000) {
|
for entity_count in RANGE.map(|i| i * 10_000) {
|
||||||
for storage in [StorageType::Table, StorageType::SparseSet] {
|
group.bench_function(format!("{}_entities_table", entity_count), |bencher| {
|
||||||
group.bench_function(
|
let mut world = setup::<Table>(entity_count);
|
||||||
format!("{}_entities_{:?}", entity_count, storage),
|
let mut query = world.query::<&Table>();
|
||||||
|bencher| {
|
|
||||||
let mut world = setup(entity_count, storage);
|
|
||||||
let mut query = world.query::<&A>();
|
|
||||||
|
|
||||||
bencher.iter(|| {
|
bencher.iter(|| {
|
||||||
for i in 0..entity_count {
|
for i in 0..entity_count {
|
||||||
let entity = Entity::new(i);
|
let entity = Entity::new(i);
|
||||||
assert!(query.get(&world, entity).is_ok());
|
assert!(query.get(&world, entity).is_ok());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
});
|
||||||
);
|
group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| {
|
||||||
}
|
let mut world = setup::<Sparse>(entity_count);
|
||||||
|
let mut query = world.query::<&Sparse>();
|
||||||
|
|
||||||
|
bencher.iter(|| {
|
||||||
|
for i in 0..entity_count {
|
||||||
|
let entity = Entity::new(i);
|
||||||
|
assert!(query.get(&world, entity).is_ok());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
group.finish();
|
group.finish();
|
||||||
|
@ -108,24 +117,32 @@ fn world_query_iter(criterion: &mut Criterion) {
|
||||||
group.measurement_time(std::time::Duration::from_secs(4));
|
group.measurement_time(std::time::Duration::from_secs(4));
|
||||||
|
|
||||||
for entity_count in RANGE.map(|i| i * 10_000) {
|
for entity_count in RANGE.map(|i| i * 10_000) {
|
||||||
for storage in [StorageType::Table, StorageType::SparseSet] {
|
group.bench_function(format!("{}_entities_table", entity_count), |bencher| {
|
||||||
group.bench_function(
|
let mut world = setup::<Table>(entity_count);
|
||||||
format!("{}_entities_{:?}", entity_count, storage),
|
let mut query = world.query::<&Table>();
|
||||||
|bencher| {
|
|
||||||
let mut world = setup(entity_count, storage);
|
|
||||||
let mut query = world.query::<&A>();
|
|
||||||
|
|
||||||
bencher.iter(|| {
|
bencher.iter(|| {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
for comp in query.iter(&world) {
|
for comp in query.iter(&world) {
|
||||||
black_box(comp);
|
black_box(comp);
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
assert_eq!(black_box(count), entity_count);
|
assert_eq!(black_box(count), entity_count);
|
||||||
});
|
});
|
||||||
},
|
});
|
||||||
);
|
group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| {
|
||||||
}
|
let mut world = setup::<Sparse>(entity_count);
|
||||||
|
let mut query = world.query::<&Sparse>();
|
||||||
|
|
||||||
|
bencher.iter(|| {
|
||||||
|
let mut count = 0;
|
||||||
|
for comp in query.iter(&world) {
|
||||||
|
black_box(comp);
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
assert_eq!(black_box(count), entity_count);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
group.finish();
|
group.finish();
|
||||||
|
@ -137,24 +154,32 @@ fn world_query_for_each(criterion: &mut Criterion) {
|
||||||
group.measurement_time(std::time::Duration::from_secs(4));
|
group.measurement_time(std::time::Duration::from_secs(4));
|
||||||
|
|
||||||
for entity_count in RANGE.map(|i| i * 10_000) {
|
for entity_count in RANGE.map(|i| i * 10_000) {
|
||||||
for storage in [StorageType::Table, StorageType::SparseSet] {
|
group.bench_function(format!("{}_entities_table", entity_count), |bencher| {
|
||||||
group.bench_function(
|
let mut world = setup::<Table>(entity_count);
|
||||||
format!("{}_entities_{:?}", entity_count, storage),
|
let mut query = world.query::<&Table>();
|
||||||
|bencher| {
|
|
||||||
let mut world = setup(entity_count, storage);
|
|
||||||
let mut query = world.query::<&A>();
|
|
||||||
|
|
||||||
bencher.iter(|| {
|
bencher.iter(|| {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
query.for_each(&world, |comp| {
|
query.for_each(&world, |comp| {
|
||||||
black_box(comp);
|
black_box(comp);
|
||||||
count += 1;
|
count += 1;
|
||||||
});
|
});
|
||||||
assert_eq!(black_box(count), entity_count);
|
assert_eq!(black_box(count), entity_count);
|
||||||
});
|
});
|
||||||
},
|
});
|
||||||
);
|
group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| {
|
||||||
}
|
let mut world = setup::<Sparse>(entity_count);
|
||||||
|
let mut query = world.query::<&Sparse>();
|
||||||
|
|
||||||
|
bencher.iter(|| {
|
||||||
|
let mut count = 0;
|
||||||
|
query.for_each(&world, |comp| {
|
||||||
|
black_box(comp);
|
||||||
|
count += 1;
|
||||||
|
});
|
||||||
|
assert_eq!(black_box(count), entity_count);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
group.finish();
|
group.finish();
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
use crate::{CoreStage, Events, Plugin, PluginGroup, PluginGroupBuilder, StartupStage};
|
use crate::{CoreStage, Events, Plugin, PluginGroup, PluginGroupBuilder, StartupStage};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
component::{Component, ComponentDescriptor},
|
|
||||||
prelude::{FromWorld, IntoExclusiveSystem},
|
prelude::{FromWorld, IntoExclusiveSystem},
|
||||||
schedule::{
|
schedule::{
|
||||||
IntoSystemDescriptor, RunOnce, Schedule, Stage, StageLabel, State, SystemSet, SystemStage,
|
IntoSystemDescriptor, RunOnce, Schedule, Stage, StageLabel, State, StateData, SystemSet,
|
||||||
|
SystemStage,
|
||||||
},
|
},
|
||||||
|
system::Resource,
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
use bevy_utils::tracing::debug;
|
use bevy_utils::tracing::debug;
|
||||||
use std::{fmt::Debug, hash::Hash};
|
use std::fmt::Debug;
|
||||||
|
|
||||||
#[cfg(feature = "trace")]
|
#[cfg(feature = "trace")]
|
||||||
use bevy_utils::tracing::info_span;
|
use bevy_utils::tracing::info_span;
|
||||||
|
@ -493,7 +494,7 @@ impl App {
|
||||||
/// adding [State::get_driver] to additional stages you need it in.
|
/// adding [State::get_driver] to additional stages you need it in.
|
||||||
pub fn add_state<T>(&mut self, initial: T) -> &mut Self
|
pub fn add_state<T>(&mut self, initial: T) -> &mut Self
|
||||||
where
|
where
|
||||||
T: Component + Debug + Clone + Eq + Hash,
|
T: StateData,
|
||||||
{
|
{
|
||||||
self.add_state_to_stage(CoreStage::Update, initial)
|
self.add_state_to_stage(CoreStage::Update, initial)
|
||||||
}
|
}
|
||||||
|
@ -505,7 +506,7 @@ impl App {
|
||||||
/// stages you need it in.
|
/// stages you need it in.
|
||||||
pub fn add_state_to_stage<T>(&mut self, stage: impl StageLabel, initial: T) -> &mut Self
|
pub fn add_state_to_stage<T>(&mut self, stage: impl StageLabel, initial: T) -> &mut Self
|
||||||
where
|
where
|
||||||
T: Component + Debug + Clone + Eq + Hash,
|
T: StateData,
|
||||||
{
|
{
|
||||||
self.insert_resource(State::new(initial))
|
self.insert_resource(State::new(initial))
|
||||||
.add_system_set_to_stage(stage, State::<T>::get_driver())
|
.add_system_set_to_stage(stage, State::<T>::get_driver())
|
||||||
|
@ -582,7 +583,7 @@ impl App {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn add_event<T>(&mut self) -> &mut Self
|
pub fn add_event<T>(&mut self) -> &mut Self
|
||||||
where
|
where
|
||||||
T: Component,
|
T: Resource,
|
||||||
{
|
{
|
||||||
self.init_resource::<Events<T>>()
|
self.init_resource::<Events<T>>()
|
||||||
.add_system_to_stage(CoreStage::First, Events::<T>::update_system)
|
.add_system_to_stage(CoreStage::First, Events::<T>::update_system)
|
||||||
|
@ -608,7 +609,7 @@ impl App {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn insert_resource<T>(&mut self, resource: T) -> &mut Self
|
pub fn insert_resource<T>(&mut self, resource: T) -> &mut Self
|
||||||
where
|
where
|
||||||
T: Component,
|
T: Resource,
|
||||||
{
|
{
|
||||||
self.world.insert_resource(resource);
|
self.world.insert_resource(resource);
|
||||||
self
|
self
|
||||||
|
@ -810,18 +811,6 @@ impl App {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers a new component using the given [ComponentDescriptor].
|
|
||||||
///
|
|
||||||
/// Components do not need to be manually registered. This just provides a way to
|
|
||||||
/// override default configuration. Attempting to register a component with a type
|
|
||||||
/// that has already been used by [World] will result in an error.
|
|
||||||
///
|
|
||||||
/// See [World::register_component]
|
|
||||||
pub fn register_component(&mut self, descriptor: ComponentDescriptor) -> &mut Self {
|
|
||||||
self.world.register_component(descriptor).unwrap();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds the type `T` to the type registry resource.
|
/// Adds the type `T` to the type registry resource.
|
||||||
#[cfg(feature = "bevy_reflect")]
|
#[cfg(feature = "bevy_reflect")]
|
||||||
pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
|
pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
path::{AssetPath, AssetPathId},
|
path::{AssetPath, AssetPathId},
|
||||||
Asset, Assets,
|
Asset, Assets,
|
||||||
};
|
};
|
||||||
use bevy_ecs::reflect::ReflectComponent;
|
use bevy_ecs::{component::Component, reflect::ReflectComponent};
|
||||||
use bevy_reflect::{Reflect, ReflectDeserialize};
|
use bevy_reflect::{Reflect, ReflectDeserialize};
|
||||||
use bevy_utils::Uuid;
|
use bevy_utils::Uuid;
|
||||||
use crossbeam_channel::{Receiver, Sender};
|
use crossbeam_channel::{Receiver, Sender};
|
||||||
|
@ -58,7 +58,7 @@ impl HandleId {
|
||||||
///
|
///
|
||||||
/// Handles contain a unique id that corresponds to a specific asset in the [Assets](crate::Assets)
|
/// Handles contain a unique id that corresponds to a specific asset in the [Assets](crate::Assets)
|
||||||
/// collection.
|
/// collection.
|
||||||
#[derive(Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct Handle<T>
|
pub struct Handle<T>
|
||||||
where
|
where
|
||||||
|
|
|
@ -3,10 +3,7 @@ use crate::{
|
||||||
RefChangeChannel,
|
RefChangeChannel,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::system::{Res, ResMut};
|
||||||
component::Component,
|
|
||||||
system::{Res, ResMut},
|
|
||||||
};
|
|
||||||
use bevy_reflect::{TypeUuid, TypeUuidDynamic};
|
use bevy_reflect::{TypeUuid, TypeUuidDynamic};
|
||||||
use bevy_tasks::TaskPool;
|
use bevy_tasks::TaskPool;
|
||||||
use bevy_utils::{BoxedFuture, HashMap};
|
use bevy_utils::{BoxedFuture, HashMap};
|
||||||
|
@ -146,7 +143,7 @@ impl<'a> LoadContext<'a> {
|
||||||
|
|
||||||
/// The result of loading an asset of type `T`
|
/// The result of loading an asset of type `T`
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AssetResult<T: Component> {
|
pub struct AssetResult<T> {
|
||||||
pub asset: Box<T>,
|
pub asset: Box<T>,
|
||||||
pub id: HandleId,
|
pub id: HandleId,
|
||||||
pub version: usize,
|
pub version: usize,
|
||||||
|
@ -154,12 +151,12 @@ pub struct AssetResult<T: Component> {
|
||||||
|
|
||||||
/// A channel to send and receive [AssetResult]s
|
/// A channel to send and receive [AssetResult]s
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AssetLifecycleChannel<T: Component> {
|
pub struct AssetLifecycleChannel<T> {
|
||||||
pub sender: Sender<AssetLifecycleEvent<T>>,
|
pub sender: Sender<AssetLifecycleEvent<T>>,
|
||||||
pub receiver: Receiver<AssetLifecycleEvent<T>>,
|
pub receiver: Receiver<AssetLifecycleEvent<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum AssetLifecycleEvent<T: Component> {
|
pub enum AssetLifecycleEvent<T> {
|
||||||
Create(AssetResult<T>),
|
Create(AssetResult<T>),
|
||||||
Free(HandleId),
|
Free(HandleId),
|
||||||
}
|
}
|
||||||
|
@ -193,7 +190,7 @@ impl<T: AssetDynamic> AssetLifecycle for AssetLifecycleChannel<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Component> Default for AssetLifecycleChannel<T> {
|
impl<T> Default for AssetLifecycleChannel<T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let (sender, receiver) = crossbeam_channel::unbounded();
|
let (sender, receiver) = crossbeam_channel::unbounded();
|
||||||
AssetLifecycleChannel { sender, receiver }
|
AssetLifecycleChannel { sender, receiver }
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
query::Changed,
|
query::Changed,
|
||||||
reflect::ReflectComponent,
|
reflect::ReflectComponent,
|
||||||
|
@ -13,7 +14,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A collection of labels
|
/// A collection of labels
|
||||||
#[derive(Default, Reflect)]
|
#[derive(Component, Default, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct Labels {
|
pub struct Labels {
|
||||||
labels: HashSet<Cow<'static, str>>,
|
labels: HashSet<Cow<'static, str>>,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use bevy_ecs::reflect::ReflectComponent;
|
use bevy_ecs::{component::Component, reflect::ReflectComponent};
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use bevy_utils::AHasher;
|
use bevy_utils::AHasher;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -8,7 +8,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Component used to identify an entity. Stores a hash for faster comparisons
|
/// Component used to identify an entity. Stores a hash for faster comparisons
|
||||||
#[derive(Debug, Clone, Reflect)]
|
#[derive(Component, Debug, Clone, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct Name {
|
pub struct Name {
|
||||||
hash: u64, // TODO: Shouldn't be serialized
|
hash: u64, // TODO: Shouldn't be serialized
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use bevy_ecs::reflect::ReflectComponent;
|
use bevy_ecs::{component::Component, reflect::ReflectComponent};
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use bevy_utils::Duration;
|
use bevy_utils::Duration;
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ use bevy_utils::Duration;
|
||||||
/// assert!(stopwatch.paused());
|
/// assert!(stopwatch.paused());
|
||||||
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
|
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, Debug, Default, Reflect)]
|
#[derive(Component, Clone, Debug, Default, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct Stopwatch {
|
pub struct Stopwatch {
|
||||||
elapsed: Duration,
|
elapsed: Duration,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::Stopwatch;
|
use crate::Stopwatch;
|
||||||
use bevy_ecs::reflect::ReflectComponent;
|
use bevy_ecs::{component::Component, reflect::ReflectComponent};
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use bevy_utils::Duration;
|
use bevy_utils::Duration;
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ use bevy_utils::Duration;
|
||||||
/// exceeded, and can still be reset at any given point.
|
/// exceeded, and can still be reset at any given point.
|
||||||
///
|
///
|
||||||
/// Paused timers will not have elapsed time increased.
|
/// Paused timers will not have elapsed time increased.
|
||||||
#[derive(Clone, Debug, Default, Reflect)]
|
#[derive(Component, Clone, Debug, Default, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct Timer {
|
pub struct Timer {
|
||||||
stopwatch: Stopwatch,
|
stopwatch: Stopwatch,
|
||||||
|
|
|
@ -34,10 +34,6 @@ rand = "0.8"
|
||||||
name = "events"
|
name = "events"
|
||||||
path = "examples/events.rs"
|
path = "examples/events.rs"
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "component_storage"
|
|
||||||
path = "examples/component_storage.rs"
|
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "resources"
|
name = "resources"
|
||||||
path = "examples/resources.rs"
|
path = "examples/resources.rs"
|
||||||
|
|
|
@ -193,11 +193,15 @@ Components can be stored in:
|
||||||
* **Tables**: Fast and cache friendly iteration, but slower adding and removing of components. This is the default storage type.
|
* **Tables**: Fast and cache friendly iteration, but slower adding and removing of components. This is the default storage type.
|
||||||
* **Sparse Sets**: Fast adding and removing of components, but slower iteration.
|
* **Sparse Sets**: Fast adding and removing of components, but slower iteration.
|
||||||
|
|
||||||
Component storage types are configurable, and they default to table storage if the storage is not manually defined. The [`component_storage.rs`](examples/component_storage.rs) example shows how to configure the storage type for a component.
|
Component storage types are configurable, and they default to table storage if the storage is not manually defined.
|
||||||
|
|
||||||
```rust
|
```rs
|
||||||
// store Position components in Sparse Sets
|
#[derive(Component)]
|
||||||
world.register_component(ComponentDescriptor::new::<Position>(StorageType::SparseSet));
|
struct TableStoredComponent;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
#[component(storage = "SparseSet")]
|
||||||
|
struct SparseStoredComponent;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Component Bundles
|
### Component Bundles
|
||||||
|
|
|
@ -46,7 +46,7 @@ struct EntityCounter {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This struct represents a Component and holds the age in frames of the entity it gets assigned to
|
// This struct represents a Component and holds the age in frames of the entity it gets assigned to
|
||||||
#[derive(Default, Debug)]
|
#[derive(Component, Default, Debug)]
|
||||||
struct Age {
|
struct Age {
|
||||||
frames: i32,
|
frames: i32,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
102
crates/bevy_ecs/macros/src/component.rs
Normal file
102
crates/bevy_ecs/macros/src/component.rs
Normal 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 }
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
mod component;
|
||||||
|
|
||||||
use bevy_macro_utils::BevyManifest;
|
use bevy_macro_utils::BevyManifest;
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||||
|
@ -118,7 +120,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
||||||
{
|
{
|
||||||
if *is_bundle {
|
if *is_bundle {
|
||||||
field_component_ids.push(quote! {
|
field_component_ids.push(quote! {
|
||||||
component_ids.extend(<#field_type as #ecs_path::bundle::Bundle>::component_ids(components));
|
component_ids.extend(<#field_type as #ecs_path::bundle::Bundle>::component_ids(components, storages));
|
||||||
});
|
});
|
||||||
field_get_components.push(quote! {
|
field_get_components.push(quote! {
|
||||||
self.#field.get_components(&mut func);
|
self.#field.get_components(&mut func);
|
||||||
|
@ -128,7 +130,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
field_component_ids.push(quote! {
|
field_component_ids.push(quote! {
|
||||||
component_ids.push(components.get_or_insert_id::<#field_type>());
|
component_ids.push(components.init_component::<#field_type>(storages));
|
||||||
});
|
});
|
||||||
field_get_components.push(quote! {
|
field_get_components.push(quote! {
|
||||||
func((&mut self.#field as *mut #field_type).cast::<u8>());
|
func((&mut self.#field as *mut #field_type).cast::<u8>());
|
||||||
|
@ -145,10 +147,11 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
||||||
let struct_name = &ast.ident;
|
let struct_name = &ast.ident;
|
||||||
|
|
||||||
TokenStream::from(quote! {
|
TokenStream::from(quote! {
|
||||||
/// SAFE: TypeInfo is returned in field-definition-order. [from_components] and [get_components] use field-definition-order
|
/// SAFE: ComponentId is returned in field-definition-order. [from_components] and [get_components] use field-definition-order
|
||||||
unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name#ty_generics #where_clause {
|
unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name#ty_generics #where_clause {
|
||||||
fn component_ids(
|
fn component_ids(
|
||||||
components: &mut #ecs_path::component::Components,
|
components: &mut #ecs_path::component::Components,
|
||||||
|
storages: &mut #ecs_path::storage::Storages,
|
||||||
) -> Vec<#ecs_path::component::ComponentId> {
|
) -> Vec<#ecs_path::component::ComponentId> {
|
||||||
let mut component_ids = Vec::with_capacity(#field_len);
|
let mut component_ids = Vec::with_capacity(#field_len);
|
||||||
#(#field_component_ids)*
|
#(#field_component_ids)*
|
||||||
|
@ -468,6 +471,11 @@ fn derive_label(input: DeriveInput, label_type: Ident) -> TokenStream2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bevy_ecs_path() -> syn::Path {
|
pub(crate) fn bevy_ecs_path() -> syn::Path {
|
||||||
BevyManifest::default().get_path("bevy_ecs")
|
BevyManifest::default().get_path("bevy_ecs")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(Component, attributes(component))]
|
||||||
|
pub fn derive_component(input: TokenStream) -> TokenStream {
|
||||||
|
component::derive_component(input)
|
||||||
|
}
|
||||||
|
|
|
@ -29,8 +29,11 @@ use std::{any::TypeId, collections::HashMap};
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct ComponentA;
|
/// # struct ComponentA;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct ComponentB;
|
/// # struct ComponentB;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct ComponentC;
|
/// # struct ComponentC;
|
||||||
/// #
|
/// #
|
||||||
/// #[derive(Bundle)]
|
/// #[derive(Bundle)]
|
||||||
|
@ -43,19 +46,26 @@ use std::{any::TypeId, collections::HashMap};
|
||||||
///
|
///
|
||||||
/// You can nest bundles using the `#[bundle]` attribute:
|
/// You can nest bundles using the `#[bundle]` attribute:
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::bundle::Bundle;
|
/// # use bevy_ecs::{component::Component, bundle::Bundle};
|
||||||
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct X(i32);
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct Y(u64);
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct Z(String);
|
||||||
///
|
///
|
||||||
/// #[derive(Bundle)]
|
/// #[derive(Bundle)]
|
||||||
/// struct A {
|
/// struct A {
|
||||||
/// x: i32,
|
/// x: X,
|
||||||
/// y: u64,
|
/// y: Y,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// #[derive(Bundle)]
|
/// #[derive(Bundle)]
|
||||||
/// struct B {
|
/// struct B {
|
||||||
/// #[bundle]
|
/// #[bundle]
|
||||||
/// a: A,
|
/// a: A,
|
||||||
/// z: String,
|
/// z: Z,
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
@ -67,7 +77,7 @@ use std::{any::TypeId, collections::HashMap};
|
||||||
/// [Bundle::component_ids].
|
/// [Bundle::component_ids].
|
||||||
pub unsafe trait Bundle: Send + Sync + 'static {
|
pub unsafe trait Bundle: Send + Sync + 'static {
|
||||||
/// Gets this [Bundle]'s component ids, in the order of this bundle's Components
|
/// Gets this [Bundle]'s component ids, in the order of this bundle's Components
|
||||||
fn component_ids(components: &mut Components) -> Vec<ComponentId>;
|
fn component_ids(components: &mut Components, storages: &mut Storages) -> Vec<ComponentId>;
|
||||||
|
|
||||||
/// Calls `func`, which should return data for each component in the bundle, in the order of
|
/// Calls `func`, which should return data for each component in the bundle, in the order of
|
||||||
/// this bundle's Components
|
/// this bundle's Components
|
||||||
|
@ -87,11 +97,11 @@ pub unsafe trait Bundle: Send + Sync + 'static {
|
||||||
|
|
||||||
macro_rules! tuple_impl {
|
macro_rules! tuple_impl {
|
||||||
($($name: ident),*) => {
|
($($name: ident),*) => {
|
||||||
/// SAFE: TypeInfo is returned in tuple-order. [Bundle::from_components] and [Bundle::get_components] use tuple-order
|
/// SAFE: Component is returned in tuple-order. [Bundle::from_components] and [Bundle::get_components] use tuple-order
|
||||||
unsafe impl<$($name: Component),*> Bundle for ($($name,)*) {
|
unsafe impl<$($name: Component),*> Bundle for ($($name,)*) {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn component_ids(components: &mut Components) -> Vec<ComponentId> {
|
fn component_ids(components: &mut Components, storages: &mut Storages) -> Vec<ComponentId> {
|
||||||
vec![$(components.get_or_insert_id::<$name>()),*]
|
vec![$(components.init_component::<$name>(storages)),*]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_variables, unused_mut)]
|
#[allow(unused_variables, unused_mut)]
|
||||||
|
@ -318,10 +328,7 @@ impl BundleInfo {
|
||||||
let component_info = unsafe { components.get_info_unchecked(component_id) };
|
let component_info = unsafe { components.get_info_unchecked(component_id) };
|
||||||
match component_info.storage_type() {
|
match component_info.storage_type() {
|
||||||
StorageType::Table => new_table_components.push(component_id),
|
StorageType::Table => new_table_components.push(component_id),
|
||||||
StorageType::SparseSet => {
|
StorageType::SparseSet => new_sparse_set_components.push(component_id),
|
||||||
storages.sparse_sets.get_or_insert(component_info);
|
|
||||||
new_sparse_set_components.push(component_id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -585,10 +592,11 @@ impl Bundles {
|
||||||
pub(crate) fn init_info<'a, T: Bundle>(
|
pub(crate) fn init_info<'a, T: Bundle>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
components: &mut Components,
|
components: &mut Components,
|
||||||
|
storages: &mut Storages,
|
||||||
) -> &'a BundleInfo {
|
) -> &'a BundleInfo {
|
||||||
let bundle_infos = &mut self.bundle_infos;
|
let bundle_infos = &mut self.bundle_infos;
|
||||||
let id = self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {
|
let id = self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {
|
||||||
let component_ids = T::component_ids(components);
|
let component_ids = T::component_ids(components, storages);
|
||||||
let id = BundleId(bundle_infos.len());
|
let id = BundleId(bundle_infos.len());
|
||||||
// SAFE: T::component_id ensures info was created
|
// SAFE: T::component_id ensures info was created
|
||||||
let bundle_info = unsafe {
|
let bundle_info = unsafe {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Types that detect when their internal data mutate.
|
//! Types that detect when their internal data mutate.
|
||||||
|
|
||||||
use crate::component::{Component, ComponentTicks};
|
use crate::{component::ComponentTicks, system::Resource};
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
@ -147,14 +147,14 @@ pub(crate) struct Ticks<'a> {
|
||||||
/// Panics when used as a [`SystemParam`](crate::system::SystemParam) if the resource does not exist.
|
/// Panics when used as a [`SystemParam`](crate::system::SystemParam) if the resource does not exist.
|
||||||
///
|
///
|
||||||
/// Use `Option<ResMut<T>>` instead if the resource might not always exist.
|
/// Use `Option<ResMut<T>>` instead if the resource might not always exist.
|
||||||
pub struct ResMut<'a, T: Component> {
|
pub struct ResMut<'a, T: Resource> {
|
||||||
pub(crate) value: &'a mut T,
|
pub(crate) value: &'a mut T,
|
||||||
pub(crate) ticks: Ticks<'a>,
|
pub(crate) ticks: Ticks<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
change_detection_impl!(ResMut<'a, T>, T, Component);
|
change_detection_impl!(ResMut<'a, T>, T, Resource);
|
||||||
impl_into_inner!(ResMut<'a, T>, T, Component);
|
impl_into_inner!(ResMut<'a, T>, T, Resource);
|
||||||
impl_debug!(ResMut<'a, T>, Component);
|
impl_debug!(ResMut<'a, T>, Resource);
|
||||||
|
|
||||||
/// Unique borrow of a non-[`Send`] resource.
|
/// Unique borrow of a non-[`Send`] resource.
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,18 +1,28 @@
|
||||||
//! Types for declaring and storing [`Component`]s.
|
//! Types for declaring and storing [`Component`]s.
|
||||||
|
|
||||||
use crate::storage::SparseSetIndex;
|
use crate::{
|
||||||
|
storage::{SparseSetIndex, Storages},
|
||||||
|
system::Resource,
|
||||||
|
};
|
||||||
|
pub use bevy_ecs_macros::Component;
|
||||||
use std::{
|
use std::{
|
||||||
alloc::Layout,
|
alloc::Layout,
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
collections::hash_map::Entry,
|
|
||||||
};
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// A component is data associated with an [`Entity`](crate::entity::Entity). Each entity can have
|
/// A component is data associated with an [`Entity`](crate::entity::Entity). Each entity can have
|
||||||
/// multiple different types of components, but only one of them per type.
|
/// multiple different types of components, but only one of them per type.
|
||||||
///
|
///
|
||||||
/// Any type that is `Send + Sync + 'static` automatically implements `Component`.
|
/// Any type that is `Send + Sync + 'static` can implement `Component` using `#[derive(Component)]`.
|
||||||
///
|
///
|
||||||
|
/// In order to use foreign types as components, wrap them using a newtype pattern.
|
||||||
|
/// ```
|
||||||
|
/// # use bevy_ecs::component::Component;
|
||||||
|
/// use std::time::Duration;
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct Cooldown(Duration);
|
||||||
|
/// ```
|
||||||
/// Components are added with new entities using [`Commands::spawn`](crate::system::Commands::spawn),
|
/// Components are added with new entities using [`Commands::spawn`](crate::system::Commands::spawn),
|
||||||
/// or to existing entities with [`EntityCommands::insert`](crate::system::EntityCommands::insert),
|
/// or to existing entities with [`EntityCommands::insert`](crate::system::EntityCommands::insert),
|
||||||
/// or their [`World`](crate::world::World) equivalents.
|
/// or their [`World`](crate::world::World) equivalents.
|
||||||
|
@ -21,21 +31,49 @@ use thiserror::Error;
|
||||||
/// as one of the arguments.
|
/// as one of the arguments.
|
||||||
///
|
///
|
||||||
/// Components can be grouped together into a [`Bundle`](crate::bundle::Bundle).
|
/// Components can be grouped together into a [`Bundle`](crate::bundle::Bundle).
|
||||||
pub trait Component: Send + Sync + 'static {}
|
pub trait Component: Send + Sync + 'static {
|
||||||
impl<T: Send + Sync + 'static> Component for T {}
|
type Storage: ComponentStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TableStorage;
|
||||||
|
pub struct SparseStorage;
|
||||||
|
|
||||||
|
pub trait ComponentStorage: sealed::Sealed {
|
||||||
|
// because the trait is selaed, those items are private API.
|
||||||
|
const STORAGE_TYPE: StorageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentStorage for TableStorage {
|
||||||
|
const STORAGE_TYPE: StorageType = StorageType::Table;
|
||||||
|
}
|
||||||
|
impl ComponentStorage for SparseStorage {
|
||||||
|
const STORAGE_TYPE: StorageType = StorageType::SparseSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
mod sealed {
|
||||||
|
pub trait Sealed {}
|
||||||
|
impl Sealed for super::TableStorage {}
|
||||||
|
impl Sealed for super::SparseStorage {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECS dependencies cannot derive Component, so we must implement it manually for relevant structs.
|
||||||
|
impl<T> Component for bevy_tasks::Task<T>
|
||||||
|
where
|
||||||
|
Self: Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
type Storage = TableStorage;
|
||||||
|
}
|
||||||
|
|
||||||
/// The storage used for a specific component type.
|
/// The storage used for a specific component type.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// The [`StorageType`] for a component is normally configured via `World::register_component`.
|
/// The [`StorageType`] for a component is configured via the derive attribute
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::{prelude::*, component::*};
|
/// # use bevy_ecs::{prelude::*, component::*};
|
||||||
///
|
/// #[derive(Component)]
|
||||||
|
/// #[component(storage = "SparseSet")]
|
||||||
/// struct A;
|
/// struct A;
|
||||||
///
|
|
||||||
/// let mut world = World::default();
|
|
||||||
/// world.register_component(ComponentDescriptor::new::<A>(StorageType::SparseSet));
|
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum StorageType {
|
pub enum StorageType {
|
||||||
|
@ -128,6 +166,8 @@ impl SparseSetIndex for ComponentId {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ComponentDescriptor {
|
pub struct ComponentDescriptor {
|
||||||
name: String,
|
name: String,
|
||||||
|
// SAFETY: This must remain private. It must match the statically known StorageType of the
|
||||||
|
// associated rust component type if one exists.
|
||||||
storage_type: StorageType,
|
storage_type: StorageType,
|
||||||
// SAFETY: This must remain private. It must only be set to "true" if this component is
|
// SAFETY: This must remain private. It must only be set to "true" if this component is
|
||||||
// actually Send + Sync
|
// actually Send + Sync
|
||||||
|
@ -143,7 +183,18 @@ impl ComponentDescriptor {
|
||||||
x.cast::<T>().drop_in_place()
|
x.cast::<T>().drop_in_place()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new<T: Component>(storage_type: StorageType) -> Self {
|
pub fn new<T: Component>() -> Self {
|
||||||
|
Self {
|
||||||
|
name: std::any::type_name::<T>().to_string(),
|
||||||
|
storage_type: T::Storage::STORAGE_TYPE,
|
||||||
|
is_send_and_sync: true,
|
||||||
|
type_id: Some(TypeId::of::<T>()),
|
||||||
|
layout: Layout::new::<T>(),
|
||||||
|
drop: Self::drop_ptr::<T>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_resource<T: Resource>(storage_type: StorageType) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: std::any::type_name::<T>().to_string(),
|
name: std::any::type_name::<T>().to_string(),
|
||||||
storage_type,
|
storage_type,
|
||||||
|
@ -191,46 +242,29 @@ pub struct Components {
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum ComponentsError {
|
pub enum ComponentsError {
|
||||||
#[error("A component of type {name:?} ({type_id:?}) already exists")]
|
#[error("A component of type {name:?} ({type_id:?}) already exists")]
|
||||||
ComponentAlreadyExists { type_id: TypeId, name: String },
|
ComponentAlreadyExists {
|
||||||
|
type_id: TypeId,
|
||||||
|
name: String,
|
||||||
|
existing_id: ComponentId,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Components {
|
impl Components {
|
||||||
pub(crate) fn add(
|
#[inline]
|
||||||
&mut self,
|
pub fn init_component<T: Component>(&mut self, storages: &mut Storages) -> ComponentId {
|
||||||
descriptor: ComponentDescriptor,
|
let type_id = TypeId::of::<T>();
|
||||||
) -> Result<ComponentId, ComponentsError> {
|
let components = &mut self.components;
|
||||||
let index = self.components.len();
|
let index = self.indices.entry(type_id).or_insert_with(|| {
|
||||||
if let Some(type_id) = descriptor.type_id {
|
let index = components.len();
|
||||||
let index_entry = self.indices.entry(type_id);
|
let descriptor = ComponentDescriptor::new::<T>();
|
||||||
if let Entry::Occupied(_) = index_entry {
|
let info = ComponentInfo::new(ComponentId(index), descriptor);
|
||||||
return Err(ComponentsError::ComponentAlreadyExists {
|
if T::Storage::STORAGE_TYPE == StorageType::SparseSet {
|
||||||
type_id,
|
storages.sparse_sets.get_or_insert(&info);
|
||||||
name: descriptor.name,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
self.indices.insert(type_id, index);
|
components.push(info);
|
||||||
}
|
index
|
||||||
self.components
|
});
|
||||||
.push(ComponentInfo::new(ComponentId(index), descriptor));
|
ComponentId(*index)
|
||||||
|
|
||||||
Ok(ComponentId(index))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_or_insert_id<T: Component>(&mut self) -> ComponentId {
|
|
||||||
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
|
|
||||||
unsafe {
|
|
||||||
self.get_or_insert_with(TypeId::of::<T>(), || {
|
|
||||||
ComponentDescriptor::new::<T>(StorageType::default())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_or_insert_info<T: Component>(&mut self) -> &ComponentInfo {
|
|
||||||
let id = self.get_or_insert_id::<T>();
|
|
||||||
// SAFE: component_info with the given `id` initialized above
|
|
||||||
unsafe { self.get_info_unchecked(id) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -270,17 +304,17 @@ impl Components {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_or_insert_resource_id<T: Component>(&mut self) -> ComponentId {
|
pub fn init_resource<T: Resource>(&mut self) -> ComponentId {
|
||||||
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
|
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
|
||||||
unsafe {
|
unsafe {
|
||||||
self.get_or_insert_resource_with(TypeId::of::<T>(), || {
|
self.get_or_insert_resource_with(TypeId::of::<T>(), || {
|
||||||
ComponentDescriptor::new::<T>(StorageType::default())
|
ComponentDescriptor::new_resource::<T>(StorageType::default())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_or_insert_non_send_resource_id<T: Any>(&mut self) -> ComponentId {
|
pub fn init_non_send<T: Any>(&mut self) -> ComponentId {
|
||||||
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
|
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
|
||||||
unsafe {
|
unsafe {
|
||||||
self.get_or_insert_resource_with(TypeId::of::<T>(), || {
|
self.get_or_insert_resource_with(TypeId::of::<T>(), || {
|
||||||
|
@ -308,26 +342,6 @@ impl Components {
|
||||||
|
|
||||||
ComponentId(*index)
|
ComponentId(*index)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The [`ComponentDescriptor`] must match the [`TypeId`]
|
|
||||||
#[inline]
|
|
||||||
pub(crate) unsafe fn get_or_insert_with(
|
|
||||||
&mut self,
|
|
||||||
type_id: TypeId,
|
|
||||||
func: impl FnOnce() -> ComponentDescriptor,
|
|
||||||
) -> ComponentId {
|
|
||||||
let components = &mut self.components;
|
|
||||||
let index = self.indices.entry(type_id).or_insert_with(|| {
|
|
||||||
let descriptor = func();
|
|
||||||
let index = components.len();
|
|
||||||
components.push(ComponentInfo::new(ComponentId(index), descriptor));
|
|
||||||
index
|
|
||||||
});
|
|
||||||
|
|
||||||
ComponentId(*index)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
//! Event handling types.
|
//! Event handling types.
|
||||||
|
|
||||||
use crate as bevy_ecs;
|
use crate::system::{Local, Res, ResMut, SystemParam};
|
||||||
use crate::{
|
use crate::{self as bevy_ecs, system::Resource};
|
||||||
component::Component,
|
|
||||||
system::{Local, Res, ResMut, SystemParam},
|
|
||||||
};
|
|
||||||
use bevy_utils::tracing::trace;
|
use bevy_utils::tracing::trace;
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{self},
|
fmt::{self},
|
||||||
|
@ -153,20 +150,20 @@ fn map_instance_event<T>(event_instance: &EventInstance<T>) -> &T {
|
||||||
|
|
||||||
/// Reads events of type `T` in order and tracks which events have already been read.
|
/// Reads events of type `T` in order and tracks which events have already been read.
|
||||||
#[derive(SystemParam)]
|
#[derive(SystemParam)]
|
||||||
pub struct EventReader<'w, 's, T: Component> {
|
pub struct EventReader<'w, 's, T: Resource> {
|
||||||
last_event_count: Local<'s, (usize, PhantomData<T>)>,
|
last_event_count: Local<'s, (usize, PhantomData<T>)>,
|
||||||
events: Res<'w, Events<T>>,
|
events: Res<'w, Events<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends events of type `T`.
|
/// Sends events of type `T`.
|
||||||
#[derive(SystemParam)]
|
#[derive(SystemParam)]
|
||||||
pub struct EventWriter<'w, 's, T: Component> {
|
pub struct EventWriter<'w, 's, T: Resource> {
|
||||||
events: ResMut<'w, Events<T>>,
|
events: ResMut<'w, Events<T>>,
|
||||||
#[system_param(ignore)]
|
#[system_param(ignore)]
|
||||||
marker: PhantomData<&'s usize>,
|
marker: PhantomData<&'s usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w, 's, T: Component> EventWriter<'w, 's, T> {
|
impl<'w, 's, T: Resource> EventWriter<'w, 's, T> {
|
||||||
pub fn send(&mut self, event: T) {
|
pub fn send(&mut self, event: T) {
|
||||||
self.events.send(event);
|
self.events.send(event);
|
||||||
}
|
}
|
||||||
|
@ -256,7 +253,7 @@ fn internal_event_reader<'a, T>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w, 's, T: Component> EventReader<'w, 's, T> {
|
impl<'w, 's, T: Resource> EventReader<'w, 's, T> {
|
||||||
/// Iterates over the events this EventReader has not seen yet. This updates the EventReader's
|
/// Iterates over the events this EventReader has not seen yet. This updates the EventReader's
|
||||||
/// event counter, which means subsequent event reads will not include events that happened
|
/// event counter, which means subsequent event reads will not include events that happened
|
||||||
/// before now.
|
/// before now.
|
||||||
|
@ -273,7 +270,7 @@ impl<'w, 's, T: Component> EventReader<'w, 's, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Component> Events<T> {
|
impl<T: Resource> Events<T> {
|
||||||
/// "Sends" an `event` by writing it to the current event buffer. [EventReader]s can then read
|
/// "Sends" an `event` by writing it to the current event buffer. [EventReader]s can then read
|
||||||
/// the event.
|
/// the event.
|
||||||
pub fn send(&mut self, event: T) {
|
pub fn send(&mut self, event: T) {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
archetype::{Archetype, ArchetypeComponentId},
|
archetype::{Archetype, ArchetypeComponentId},
|
||||||
change_detection::Ticks,
|
change_detection::Ticks,
|
||||||
component::{Component, ComponentId, ComponentTicks, StorageType},
|
component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType},
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
query::{Access, FilteredAccess},
|
query::{Access, FilteredAccess},
|
||||||
storage::{ComponentSparseSet, Table, Tables},
|
storage::{ComponentSparseSet, Table, Tables},
|
||||||
|
@ -67,7 +67,7 @@ pub trait Fetch<'world, 'state>: Sized {
|
||||||
/// for "dense" queries. If this returns true, [`Fetch::set_table`] and [`Fetch::table_fetch`]
|
/// for "dense" queries. If this returns true, [`Fetch::set_table`] and [`Fetch::table_fetch`]
|
||||||
/// will be called for iterators. If this returns false, [`Fetch::set_archetype`] and
|
/// will be called for iterators. If this returns false, [`Fetch::set_archetype`] and
|
||||||
/// [`Fetch::archetype_fetch`] will be called for iterators.
|
/// [`Fetch::archetype_fetch`] will be called for iterators.
|
||||||
fn is_dense(&self) -> bool;
|
const IS_DENSE: bool;
|
||||||
|
|
||||||
/// Adjusts internal state to account for the next [`Archetype`]. This will always be called on
|
/// Adjusts internal state to account for the next [`Archetype`]. This will always be called on
|
||||||
/// archetypes that match this [`Fetch`].
|
/// archetypes that match this [`Fetch`].
|
||||||
|
@ -178,10 +178,7 @@ impl<'w, 's> Fetch<'w, 's> for EntityFetch {
|
||||||
type Item = Entity;
|
type Item = Entity;
|
||||||
type State = EntityState;
|
type State = EntityState;
|
||||||
|
|
||||||
#[inline]
|
const IS_DENSE: bool = true;
|
||||||
fn is_dense(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn init(
|
unsafe fn init(
|
||||||
_world: &World,
|
_world: &World,
|
||||||
|
@ -228,7 +225,6 @@ impl<T: Component> WorldQuery for &T {
|
||||||
/// The [`FetchState`] of `&T`.
|
/// The [`FetchState`] of `&T`.
|
||||||
pub struct ReadState<T> {
|
pub struct ReadState<T> {
|
||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
storage_type: StorageType,
|
|
||||||
marker: PhantomData<T>,
|
marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,10 +232,9 @@ pub struct ReadState<T> {
|
||||||
// read
|
// read
|
||||||
unsafe impl<T: Component> FetchState for ReadState<T> {
|
unsafe impl<T: Component> FetchState for ReadState<T> {
|
||||||
fn init(world: &mut World) -> Self {
|
fn init(world: &mut World) -> Self {
|
||||||
let component_info = world.components.get_or_insert_info::<T>();
|
let component_id = world.init_component::<T>();
|
||||||
ReadState {
|
ReadState {
|
||||||
component_id: component_info.id(),
|
component_id,
|
||||||
storage_type: component_info.storage_type(),
|
|
||||||
marker: PhantomData,
|
marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,7 +270,6 @@ unsafe impl<T: Component> FetchState for ReadState<T> {
|
||||||
|
|
||||||
/// The [`Fetch`] of `&T`.
|
/// The [`Fetch`] of `&T`.
|
||||||
pub struct ReadFetch<T> {
|
pub struct ReadFetch<T> {
|
||||||
storage_type: StorageType,
|
|
||||||
table_components: NonNull<T>,
|
table_components: NonNull<T>,
|
||||||
entity_table_rows: *const usize,
|
entity_table_rows: *const usize,
|
||||||
entities: *const Entity,
|
entities: *const Entity,
|
||||||
|
@ -285,7 +279,6 @@ pub struct ReadFetch<T> {
|
||||||
impl<T> Clone for ReadFetch<T> {
|
impl<T> Clone for ReadFetch<T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
storage_type: self.storage_type,
|
|
||||||
table_components: self.table_components,
|
table_components: self.table_components,
|
||||||
entity_table_rows: self.entity_table_rows,
|
entity_table_rows: self.entity_table_rows,
|
||||||
entities: self.entities,
|
entities: self.entities,
|
||||||
|
@ -301,13 +294,12 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch<T> {
|
||||||
type Item = &'w T;
|
type Item = &'w T;
|
||||||
type State = ReadState<T>;
|
type State = ReadState<T>;
|
||||||
|
|
||||||
#[inline]
|
const IS_DENSE: bool = {
|
||||||
fn is_dense(&self) -> bool {
|
match T::Storage::STORAGE_TYPE {
|
||||||
match self.storage_type {
|
|
||||||
StorageType::Table => true,
|
StorageType::Table => true,
|
||||||
StorageType::SparseSet => false,
|
StorageType::SparseSet => false,
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
unsafe fn init(
|
unsafe fn init(
|
||||||
world: &World,
|
world: &World,
|
||||||
|
@ -316,13 +308,12 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch<T> {
|
||||||
_change_tick: u32,
|
_change_tick: u32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut value = Self {
|
let mut value = Self {
|
||||||
storage_type: state.storage_type,
|
|
||||||
table_components: NonNull::dangling(),
|
table_components: NonNull::dangling(),
|
||||||
entities: ptr::null::<Entity>(),
|
entities: ptr::null::<Entity>(),
|
||||||
entity_table_rows: ptr::null::<usize>(),
|
entity_table_rows: ptr::null::<usize>(),
|
||||||
sparse_set: ptr::null::<ComponentSparseSet>(),
|
sparse_set: ptr::null::<ComponentSparseSet>(),
|
||||||
};
|
};
|
||||||
if state.storage_type == StorageType::SparseSet {
|
if T::Storage::STORAGE_TYPE == StorageType::SparseSet {
|
||||||
value.sparse_set = world
|
value.sparse_set = world
|
||||||
.storages()
|
.storages()
|
||||||
.sparse_sets
|
.sparse_sets
|
||||||
|
@ -339,7 +330,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch<T> {
|
||||||
archetype: &Archetype,
|
archetype: &Archetype,
|
||||||
tables: &Tables,
|
tables: &Tables,
|
||||||
) {
|
) {
|
||||||
match state.storage_type {
|
match T::Storage::STORAGE_TYPE {
|
||||||
StorageType::Table => {
|
StorageType::Table => {
|
||||||
self.entity_table_rows = archetype.entity_table_rows().as_ptr();
|
self.entity_table_rows = archetype.entity_table_rows().as_ptr();
|
||||||
let column = tables[archetype.table_id()]
|
let column = tables[archetype.table_id()]
|
||||||
|
@ -362,7 +353,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch<T> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item {
|
unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item {
|
||||||
match self.storage_type {
|
match T::Storage::STORAGE_TYPE {
|
||||||
StorageType::Table => {
|
StorageType::Table => {
|
||||||
let table_row = *self.entity_table_rows.add(archetype_index);
|
let table_row = *self.entity_table_rows.add(archetype_index);
|
||||||
&*self.table_components.as_ptr().add(table_row)
|
&*self.table_components.as_ptr().add(table_row)
|
||||||
|
@ -387,7 +378,6 @@ impl<T: Component> WorldQuery for &mut T {
|
||||||
|
|
||||||
/// The [`Fetch`] of `&mut T`.
|
/// The [`Fetch`] of `&mut T`.
|
||||||
pub struct WriteFetch<T> {
|
pub struct WriteFetch<T> {
|
||||||
storage_type: StorageType,
|
|
||||||
table_components: NonNull<T>,
|
table_components: NonNull<T>,
|
||||||
table_ticks: *const UnsafeCell<ComponentTicks>,
|
table_ticks: *const UnsafeCell<ComponentTicks>,
|
||||||
entities: *const Entity,
|
entities: *const Entity,
|
||||||
|
@ -400,7 +390,6 @@ pub struct WriteFetch<T> {
|
||||||
impl<T> Clone for WriteFetch<T> {
|
impl<T> Clone for WriteFetch<T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
storage_type: self.storage_type,
|
|
||||||
table_components: self.table_components,
|
table_components: self.table_components,
|
||||||
table_ticks: self.table_ticks,
|
table_ticks: self.table_ticks,
|
||||||
entities: self.entities,
|
entities: self.entities,
|
||||||
|
@ -415,7 +404,6 @@ impl<T> Clone for WriteFetch<T> {
|
||||||
/// The [`FetchState`] of `&mut T`.
|
/// The [`FetchState`] of `&mut T`.
|
||||||
pub struct WriteState<T> {
|
pub struct WriteState<T> {
|
||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
storage_type: StorageType,
|
|
||||||
marker: PhantomData<T>,
|
marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,10 +411,9 @@ pub struct WriteState<T> {
|
||||||
// written
|
// written
|
||||||
unsafe impl<T: Component> FetchState for WriteState<T> {
|
unsafe impl<T: Component> FetchState for WriteState<T> {
|
||||||
fn init(world: &mut World) -> Self {
|
fn init(world: &mut World) -> Self {
|
||||||
let component_info = world.components.get_or_insert_info::<T>();
|
let component_id = world.init_component::<T>();
|
||||||
WriteState {
|
WriteState {
|
||||||
component_id: component_info.id(),
|
component_id,
|
||||||
storage_type: component_info.storage_type(),
|
|
||||||
marker: PhantomData,
|
marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -464,13 +451,12 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch<T> {
|
||||||
type Item = Mut<'w, T>;
|
type Item = Mut<'w, T>;
|
||||||
type State = WriteState<T>;
|
type State = WriteState<T>;
|
||||||
|
|
||||||
#[inline]
|
const IS_DENSE: bool = {
|
||||||
fn is_dense(&self) -> bool {
|
match T::Storage::STORAGE_TYPE {
|
||||||
match self.storage_type {
|
|
||||||
StorageType::Table => true,
|
StorageType::Table => true,
|
||||||
StorageType::SparseSet => false,
|
StorageType::SparseSet => false,
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
unsafe fn init(
|
unsafe fn init(
|
||||||
world: &World,
|
world: &World,
|
||||||
|
@ -479,7 +465,6 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch<T> {
|
||||||
change_tick: u32,
|
change_tick: u32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut value = Self {
|
let mut value = Self {
|
||||||
storage_type: state.storage_type,
|
|
||||||
table_components: NonNull::dangling(),
|
table_components: NonNull::dangling(),
|
||||||
entities: ptr::null::<Entity>(),
|
entities: ptr::null::<Entity>(),
|
||||||
entity_table_rows: ptr::null::<usize>(),
|
entity_table_rows: ptr::null::<usize>(),
|
||||||
|
@ -488,7 +473,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch<T> {
|
||||||
last_change_tick,
|
last_change_tick,
|
||||||
change_tick,
|
change_tick,
|
||||||
};
|
};
|
||||||
if state.storage_type == StorageType::SparseSet {
|
if T::Storage::STORAGE_TYPE == StorageType::SparseSet {
|
||||||
value.sparse_set = world
|
value.sparse_set = world
|
||||||
.storages()
|
.storages()
|
||||||
.sparse_sets
|
.sparse_sets
|
||||||
|
@ -505,7 +490,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch<T> {
|
||||||
archetype: &Archetype,
|
archetype: &Archetype,
|
||||||
tables: &Tables,
|
tables: &Tables,
|
||||||
) {
|
) {
|
||||||
match state.storage_type {
|
match T::Storage::STORAGE_TYPE {
|
||||||
StorageType::Table => {
|
StorageType::Table => {
|
||||||
self.entity_table_rows = archetype.entity_table_rows().as_ptr();
|
self.entity_table_rows = archetype.entity_table_rows().as_ptr();
|
||||||
let column = tables[archetype.table_id()]
|
let column = tables[archetype.table_id()]
|
||||||
|
@ -527,7 +512,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch<T> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item {
|
unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item {
|
||||||
match self.storage_type {
|
match T::Storage::STORAGE_TYPE {
|
||||||
StorageType::Table => {
|
StorageType::Table => {
|
||||||
let table_row = *self.entity_table_rows.add(archetype_index);
|
let table_row = *self.entity_table_rows.add(archetype_index);
|
||||||
Mut {
|
Mut {
|
||||||
|
@ -625,10 +610,7 @@ impl<'w, 's, T: Fetch<'w, 's>> Fetch<'w, 's> for OptionFetch<T> {
|
||||||
type Item = Option<T::Item>;
|
type Item = Option<T::Item>;
|
||||||
type State = OptionState<T::State>;
|
type State = OptionState<T::State>;
|
||||||
|
|
||||||
#[inline]
|
const IS_DENSE: bool = T::IS_DENSE;
|
||||||
fn is_dense(&self) -> bool {
|
|
||||||
self.fetch.is_dense()
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn init(
|
unsafe fn init(
|
||||||
world: &World,
|
world: &World,
|
||||||
|
@ -693,12 +675,14 @@ impl<'w, 's, T: Fetch<'w, 's>> Fetch<'w, 's> for OptionFetch<T> {
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::system::Query;
|
/// # use bevy_ecs::component::Component;
|
||||||
/// # use bevy_ecs::query::ChangeTrackers;
|
/// # use bevy_ecs::query::ChangeTrackers;
|
||||||
/// # use bevy_ecs::system::IntoSystem;
|
/// # use bevy_ecs::system::IntoSystem;
|
||||||
|
/// # use bevy_ecs::system::Query;
|
||||||
/// #
|
/// #
|
||||||
/// # #[derive(Debug)]
|
/// # #[derive(Component, Debug)]
|
||||||
/// # struct Name {};
|
/// # struct Name {};
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Transform {};
|
/// # struct Transform {};
|
||||||
/// #
|
/// #
|
||||||
/// fn print_moving_objects_system(query: Query<(&Name, ChangeTrackers<Transform>)>) {
|
/// fn print_moving_objects_system(query: Query<(&Name, ChangeTrackers<Transform>)>) {
|
||||||
|
@ -752,7 +736,6 @@ impl<T: Component> WorldQuery for ChangeTrackers<T> {
|
||||||
/// The [`FetchState`] of [`ChangeTrackers`].
|
/// The [`FetchState`] of [`ChangeTrackers`].
|
||||||
pub struct ChangeTrackersState<T> {
|
pub struct ChangeTrackersState<T> {
|
||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
storage_type: StorageType,
|
|
||||||
marker: PhantomData<T>,
|
marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -760,10 +743,9 @@ pub struct ChangeTrackersState<T> {
|
||||||
// read
|
// read
|
||||||
unsafe impl<T: Component> FetchState for ChangeTrackersState<T> {
|
unsafe impl<T: Component> FetchState for ChangeTrackersState<T> {
|
||||||
fn init(world: &mut World) -> Self {
|
fn init(world: &mut World) -> Self {
|
||||||
let component_info = world.components.get_or_insert_info::<T>();
|
let component_id = world.init_component::<T>();
|
||||||
Self {
|
Self {
|
||||||
component_id: component_info.id(),
|
component_id,
|
||||||
storage_type: component_info.storage_type(),
|
|
||||||
marker: PhantomData,
|
marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -799,7 +781,6 @@ unsafe impl<T: Component> FetchState for ChangeTrackersState<T> {
|
||||||
|
|
||||||
/// The [`Fetch`] of [`ChangeTrackers`].
|
/// The [`Fetch`] of [`ChangeTrackers`].
|
||||||
pub struct ChangeTrackersFetch<T> {
|
pub struct ChangeTrackersFetch<T> {
|
||||||
storage_type: StorageType,
|
|
||||||
table_ticks: *const ComponentTicks,
|
table_ticks: *const ComponentTicks,
|
||||||
entity_table_rows: *const usize,
|
entity_table_rows: *const usize,
|
||||||
entities: *const Entity,
|
entities: *const Entity,
|
||||||
|
@ -812,7 +793,6 @@ pub struct ChangeTrackersFetch<T> {
|
||||||
impl<T> Clone for ChangeTrackersFetch<T> {
|
impl<T> Clone for ChangeTrackersFetch<T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
storage_type: self.storage_type,
|
|
||||||
table_ticks: self.table_ticks,
|
table_ticks: self.table_ticks,
|
||||||
entity_table_rows: self.entity_table_rows,
|
entity_table_rows: self.entity_table_rows,
|
||||||
entities: self.entities,
|
entities: self.entities,
|
||||||
|
@ -831,13 +811,12 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch<T> {
|
||||||
type Item = ChangeTrackers<T>;
|
type Item = ChangeTrackers<T>;
|
||||||
type State = ChangeTrackersState<T>;
|
type State = ChangeTrackersState<T>;
|
||||||
|
|
||||||
#[inline]
|
const IS_DENSE: bool = {
|
||||||
fn is_dense(&self) -> bool {
|
match T::Storage::STORAGE_TYPE {
|
||||||
match self.storage_type {
|
|
||||||
StorageType::Table => true,
|
StorageType::Table => true,
|
||||||
StorageType::SparseSet => false,
|
StorageType::SparseSet => false,
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
unsafe fn init(
|
unsafe fn init(
|
||||||
world: &World,
|
world: &World,
|
||||||
|
@ -846,7 +825,6 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch<T> {
|
||||||
change_tick: u32,
|
change_tick: u32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut value = Self {
|
let mut value = Self {
|
||||||
storage_type: state.storage_type,
|
|
||||||
table_ticks: ptr::null::<ComponentTicks>(),
|
table_ticks: ptr::null::<ComponentTicks>(),
|
||||||
entities: ptr::null::<Entity>(),
|
entities: ptr::null::<Entity>(),
|
||||||
entity_table_rows: ptr::null::<usize>(),
|
entity_table_rows: ptr::null::<usize>(),
|
||||||
|
@ -855,7 +833,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch<T> {
|
||||||
last_change_tick,
|
last_change_tick,
|
||||||
change_tick,
|
change_tick,
|
||||||
};
|
};
|
||||||
if state.storage_type == StorageType::SparseSet {
|
if T::Storage::STORAGE_TYPE == StorageType::SparseSet {
|
||||||
value.sparse_set = world
|
value.sparse_set = world
|
||||||
.storages()
|
.storages()
|
||||||
.sparse_sets
|
.sparse_sets
|
||||||
|
@ -872,7 +850,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch<T> {
|
||||||
archetype: &Archetype,
|
archetype: &Archetype,
|
||||||
tables: &Tables,
|
tables: &Tables,
|
||||||
) {
|
) {
|
||||||
match state.storage_type {
|
match T::Storage::STORAGE_TYPE {
|
||||||
StorageType::Table => {
|
StorageType::Table => {
|
||||||
self.entity_table_rows = archetype.entity_table_rows().as_ptr();
|
self.entity_table_rows = archetype.entity_table_rows().as_ptr();
|
||||||
let column = tables[archetype.table_id()]
|
let column = tables[archetype.table_id()]
|
||||||
|
@ -894,7 +872,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch<T> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item {
|
unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item {
|
||||||
match self.storage_type {
|
match T::Storage::STORAGE_TYPE {
|
||||||
StorageType::Table => {
|
StorageType::Table => {
|
||||||
let table_row = *self.entity_table_rows.add(archetype_index);
|
let table_row = *self.entity_table_rows.add(archetype_index);
|
||||||
ChangeTrackers {
|
ChangeTrackers {
|
||||||
|
@ -940,12 +918,7 @@ macro_rules! impl_tuple_fetch {
|
||||||
($($name::init(_world, $name, _last_change_tick, _change_tick),)*)
|
($($name::init(_world, $name, _last_change_tick, _change_tick),)*)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const IS_DENSE: bool = true $(&& $name::IS_DENSE)*;
|
||||||
#[inline]
|
|
||||||
fn is_dense(&self) -> bool {
|
|
||||||
let ($($name,)*) = self;
|
|
||||||
true $(&& $name.is_dense())*
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn set_archetype(&mut self, _state: &Self::State, _archetype: &Archetype, _tables: &Tables) {
|
unsafe fn set_archetype(&mut self, _state: &Self::State, _archetype: &Archetype, _tables: &Tables) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
archetype::{Archetype, ArchetypeComponentId},
|
archetype::{Archetype, ArchetypeComponentId},
|
||||||
component::{Component, ComponentId, ComponentTicks, StorageType},
|
component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType},
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
query::{Access, Fetch, FetchState, FilteredAccess, WorldQuery},
|
query::{Access, Fetch, FetchState, FilteredAccess, WorldQuery},
|
||||||
storage::{ComponentSparseSet, Table, Tables},
|
storage::{ComponentSparseSet, Table, Tables},
|
||||||
|
@ -50,12 +50,14 @@ where
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::system::Query;
|
/// # use bevy_ecs::component::Component;
|
||||||
/// # use bevy_ecs::query::With;
|
/// # use bevy_ecs::query::With;
|
||||||
/// # use bevy_ecs::system::IntoSystem;
|
/// # use bevy_ecs::system::IntoSystem;
|
||||||
|
/// # use bevy_ecs::system::Query;
|
||||||
/// #
|
/// #
|
||||||
/// # #[derive(Debug)]
|
/// # #[derive(Component)]
|
||||||
/// # struct IsBeautiful {};
|
/// # struct IsBeautiful;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Name { name: &'static str };
|
/// # struct Name { name: &'static str };
|
||||||
/// #
|
/// #
|
||||||
/// fn compliment_entity_system(query: Query<&Name, With<IsBeautiful>>) {
|
/// fn compliment_entity_system(query: Query<&Name, With<IsBeautiful>>) {
|
||||||
|
@ -74,24 +76,21 @@ impl<T: Component> WorldQuery for With<T> {
|
||||||
|
|
||||||
/// The [`Fetch`] of [`With`].
|
/// The [`Fetch`] of [`With`].
|
||||||
pub struct WithFetch<T> {
|
pub struct WithFetch<T> {
|
||||||
storage_type: StorageType,
|
|
||||||
marker: PhantomData<T>,
|
marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`FetchState`] of [`With`].
|
/// The [`FetchState`] of [`With`].
|
||||||
pub struct WithState<T> {
|
pub struct WithState<T> {
|
||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
storage_type: StorageType,
|
|
||||||
marker: PhantomData<T>,
|
marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: no component access or archetype component access
|
// SAFETY: no component access or archetype component access
|
||||||
unsafe impl<T: Component> FetchState for WithState<T> {
|
unsafe impl<T: Component> FetchState for WithState<T> {
|
||||||
fn init(world: &mut World) -> Self {
|
fn init(world: &mut World) -> Self {
|
||||||
let component_info = world.components.get_or_insert_info::<T>();
|
let component_id = world.init_component::<T>();
|
||||||
Self {
|
Self {
|
||||||
component_id: component_info.id(),
|
component_id,
|
||||||
storage_type: component_info.storage_type(),
|
|
||||||
marker: PhantomData,
|
marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,20 +123,21 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithFetch<T> {
|
||||||
|
|
||||||
unsafe fn init(
|
unsafe fn init(
|
||||||
_world: &World,
|
_world: &World,
|
||||||
state: &Self::State,
|
_state: &Self::State,
|
||||||
_last_change_tick: u32,
|
_last_change_tick: u32,
|
||||||
_change_tick: u32,
|
_change_tick: u32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
storage_type: state.storage_type,
|
|
||||||
marker: PhantomData,
|
marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
const IS_DENSE: bool = {
|
||||||
fn is_dense(&self) -> bool {
|
match T::Storage::STORAGE_TYPE {
|
||||||
self.storage_type == StorageType::Table
|
StorageType::Table => true,
|
||||||
}
|
StorageType::SparseSet => false,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {}
|
unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {}
|
||||||
|
@ -169,12 +169,14 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithFetch<T> {
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::system::Query;
|
/// # use bevy_ecs::component::Component;
|
||||||
/// # use bevy_ecs::query::Without;
|
/// # use bevy_ecs::query::Without;
|
||||||
/// # use bevy_ecs::system::IntoSystem;
|
/// # use bevy_ecs::system::IntoSystem;
|
||||||
|
/// # use bevy_ecs::system::Query;
|
||||||
/// #
|
/// #
|
||||||
/// # #[derive(Debug)]
|
/// # #[derive(Component)]
|
||||||
/// # struct Permit;
|
/// # struct Permit;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Name { name: &'static str };
|
/// # struct Name { name: &'static str };
|
||||||
/// #
|
/// #
|
||||||
/// fn no_permit_system(query: Query<&Name, Without<Permit>>) {
|
/// fn no_permit_system(query: Query<&Name, Without<Permit>>) {
|
||||||
|
@ -193,24 +195,21 @@ impl<T: Component> WorldQuery for Without<T> {
|
||||||
|
|
||||||
/// The [`Fetch`] of [`Without`].
|
/// The [`Fetch`] of [`Without`].
|
||||||
pub struct WithoutFetch<T> {
|
pub struct WithoutFetch<T> {
|
||||||
storage_type: StorageType,
|
|
||||||
marker: PhantomData<T>,
|
marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`FetchState`] of [`Without`].
|
/// The [`FetchState`] of [`Without`].
|
||||||
pub struct WithoutState<T> {
|
pub struct WithoutState<T> {
|
||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
storage_type: StorageType,
|
|
||||||
marker: PhantomData<T>,
|
marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: no component access or archetype component access
|
// SAFETY: no component access or archetype component access
|
||||||
unsafe impl<T: Component> FetchState for WithoutState<T> {
|
unsafe impl<T: Component> FetchState for WithoutState<T> {
|
||||||
fn init(world: &mut World) -> Self {
|
fn init(world: &mut World) -> Self {
|
||||||
let component_info = world.components.get_or_insert_info::<T>();
|
let component_id = world.init_component::<T>();
|
||||||
Self {
|
Self {
|
||||||
component_id: component_info.id(),
|
component_id,
|
||||||
storage_type: component_info.storage_type(),
|
|
||||||
marker: PhantomData,
|
marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -243,20 +242,21 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithoutFetch<T> {
|
||||||
|
|
||||||
unsafe fn init(
|
unsafe fn init(
|
||||||
_world: &World,
|
_world: &World,
|
||||||
state: &Self::State,
|
_state: &Self::State,
|
||||||
_last_change_tick: u32,
|
_last_change_tick: u32,
|
||||||
_change_tick: u32,
|
_change_tick: u32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
storage_type: state.storage_type,
|
|
||||||
marker: PhantomData,
|
marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
const IS_DENSE: bool = {
|
||||||
fn is_dense(&self) -> bool {
|
match T::Storage::STORAGE_TYPE {
|
||||||
self.storage_type == StorageType::Table
|
StorageType::Table => true,
|
||||||
}
|
StorageType::SparseSet => false,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {}
|
unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {}
|
||||||
|
@ -292,14 +292,16 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithoutFetch<T> {
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
/// # use bevy_ecs::component::Component;
|
||||||
/// # use bevy_ecs::entity::Entity;
|
/// # use bevy_ecs::entity::Entity;
|
||||||
/// # use bevy_ecs::system::Query;
|
|
||||||
/// # use bevy_ecs::system::IntoSystem;
|
|
||||||
/// # use bevy_ecs::query::Changed;
|
/// # use bevy_ecs::query::Changed;
|
||||||
/// # use bevy_ecs::query::Or;
|
/// # use bevy_ecs::query::Or;
|
||||||
|
/// # use bevy_ecs::system::IntoSystem;
|
||||||
|
/// # use bevy_ecs::system::Query;
|
||||||
/// #
|
/// #
|
||||||
/// # #[derive(Debug)]
|
/// # #[derive(Component, Debug)]
|
||||||
/// # struct Color {};
|
/// # struct Color {};
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Style {};
|
/// # struct Style {};
|
||||||
/// #
|
/// #
|
||||||
/// fn print_cool_entity_system(query: Query<Entity, Or<(Changed<Color>, Changed<Style>)>>) {
|
/// fn print_cool_entity_system(query: Query<Entity, Or<(Changed<Color>, Changed<Style>)>>) {
|
||||||
|
@ -357,11 +359,7 @@ macro_rules! impl_query_filter_tuple {
|
||||||
},)*))
|
},)*))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
const IS_DENSE: bool = true $(&& $filter::IS_DENSE)*;
|
||||||
fn is_dense(&self) -> bool {
|
|
||||||
let ($($filter,)*) = &self.0;
|
|
||||||
true $(&& $filter.fetch.is_dense())*
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn set_table(&mut self, state: &Self::State, table: &Table) {
|
unsafe fn set_table(&mut self, state: &Self::State, table: &Table) {
|
||||||
|
@ -448,7 +446,6 @@ macro_rules! impl_tick_filter {
|
||||||
|
|
||||||
$(#[$fetch_meta])*
|
$(#[$fetch_meta])*
|
||||||
pub struct $fetch_name<T> {
|
pub struct $fetch_name<T> {
|
||||||
storage_type: StorageType,
|
|
||||||
table_ticks: *const UnsafeCell<ComponentTicks>,
|
table_ticks: *const UnsafeCell<ComponentTicks>,
|
||||||
entity_table_rows: *const usize,
|
entity_table_rows: *const usize,
|
||||||
marker: PhantomData<T>,
|
marker: PhantomData<T>,
|
||||||
|
@ -461,7 +458,6 @@ macro_rules! impl_tick_filter {
|
||||||
$(#[$state_meta])*
|
$(#[$state_meta])*
|
||||||
pub struct $state_name<T> {
|
pub struct $state_name<T> {
|
||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
storage_type: StorageType,
|
|
||||||
marker: PhantomData<T>,
|
marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -474,10 +470,8 @@ macro_rules! impl_tick_filter {
|
||||||
// SAFETY: this reads the T component. archetype component access and component access are updated to reflect that
|
// SAFETY: this reads the T component. archetype component access and component access are updated to reflect that
|
||||||
unsafe impl<T: Component> FetchState for $state_name<T> {
|
unsafe impl<T: Component> FetchState for $state_name<T> {
|
||||||
fn init(world: &mut World) -> Self {
|
fn init(world: &mut World) -> Self {
|
||||||
let component_info = world.components.get_or_insert_info::<T>();
|
|
||||||
Self {
|
Self {
|
||||||
component_id: component_info.id(),
|
component_id: world.init_component::<T>(),
|
||||||
storage_type: component_info.storage_type(),
|
|
||||||
marker: PhantomData,
|
marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -517,7 +511,6 @@ macro_rules! impl_tick_filter {
|
||||||
|
|
||||||
unsafe fn init(world: &World, state: &Self::State, last_change_tick: u32, change_tick: u32) -> Self {
|
unsafe fn init(world: &World, state: &Self::State, last_change_tick: u32, change_tick: u32) -> Self {
|
||||||
let mut value = Self {
|
let mut value = Self {
|
||||||
storage_type: state.storage_type,
|
|
||||||
table_ticks: ptr::null::<UnsafeCell<ComponentTicks>>(),
|
table_ticks: ptr::null::<UnsafeCell<ComponentTicks>>(),
|
||||||
entities: ptr::null::<Entity>(),
|
entities: ptr::null::<Entity>(),
|
||||||
entity_table_rows: ptr::null::<usize>(),
|
entity_table_rows: ptr::null::<usize>(),
|
||||||
|
@ -526,7 +519,7 @@ macro_rules! impl_tick_filter {
|
||||||
last_change_tick,
|
last_change_tick,
|
||||||
change_tick,
|
change_tick,
|
||||||
};
|
};
|
||||||
if state.storage_type == StorageType::SparseSet {
|
if T::Storage::STORAGE_TYPE == StorageType::SparseSet {
|
||||||
value.sparse_set = world
|
value.sparse_set = world
|
||||||
.storages()
|
.storages()
|
||||||
.sparse_sets
|
.sparse_sets
|
||||||
|
@ -535,10 +528,12 @@ macro_rules! impl_tick_filter {
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
const IS_DENSE: bool = {
|
||||||
fn is_dense(&self) -> bool {
|
match T::Storage::STORAGE_TYPE {
|
||||||
self.storage_type == StorageType::Table
|
StorageType::Table => true,
|
||||||
}
|
StorageType::SparseSet => false,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
unsafe fn set_table(&mut self, state: &Self::State, table: &Table) {
|
unsafe fn set_table(&mut self, state: &Self::State, table: &Table) {
|
||||||
self.table_ticks = table
|
self.table_ticks = table
|
||||||
|
@ -547,7 +542,7 @@ macro_rules! impl_tick_filter {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn set_archetype(&mut self, state: &Self::State, archetype: &Archetype, tables: &Tables) {
|
unsafe fn set_archetype(&mut self, state: &Self::State, archetype: &Archetype, tables: &Tables) {
|
||||||
match state.storage_type {
|
match T::Storage::STORAGE_TYPE {
|
||||||
StorageType::Table => {
|
StorageType::Table => {
|
||||||
self.entity_table_rows = archetype.entity_table_rows().as_ptr();
|
self.entity_table_rows = archetype.entity_table_rows().as_ptr();
|
||||||
let table = &tables[archetype.table_id()];
|
let table = &tables[archetype.table_id()];
|
||||||
|
@ -564,7 +559,7 @@ macro_rules! impl_tick_filter {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> bool {
|
unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> bool {
|
||||||
match self.storage_type {
|
match T::Storage::STORAGE_TYPE {
|
||||||
StorageType::Table => {
|
StorageType::Table => {
|
||||||
let table_row = *self.entity_table_rows.add(archetype_index);
|
let table_row = *self.entity_table_rows.add(archetype_index);
|
||||||
$is_detected(&*(&*self.table_ticks.add(table_row)).get(), self.last_change_tick, self.change_tick)
|
$is_detected(&*(&*self.table_ticks.add(table_row)).get(), self.last_change_tick, self.change_tick)
|
||||||
|
@ -596,13 +591,13 @@ impl_tick_filter!(
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
/// # use bevy_ecs::component::Component;
|
||||||
|
/// # use bevy_ecs::query::Added;
|
||||||
/// # use bevy_ecs::system::IntoSystem;
|
/// # use bevy_ecs::system::IntoSystem;
|
||||||
/// # use bevy_ecs::system::Query;
|
/// # use bevy_ecs::system::Query;
|
||||||
/// # use bevy_ecs::query::Added;
|
|
||||||
/// #
|
/// #
|
||||||
/// # #[derive(Debug)]
|
/// # #[derive(Component, Debug)]
|
||||||
/// # struct Name {};
|
/// # struct Name {};
|
||||||
/// # struct Transform {};
|
|
||||||
///
|
///
|
||||||
/// fn print_add_name_component(query: Query<&Name, Added<Name>>) {
|
/// fn print_add_name_component(query: Query<&Name, Added<Name>>) {
|
||||||
/// for name in query.iter() {
|
/// for name in query.iter() {
|
||||||
|
@ -637,12 +632,14 @@ impl_tick_filter!(
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
/// # use bevy_ecs::component::Component;
|
||||||
|
/// # use bevy_ecs::query::Changed;
|
||||||
/// # use bevy_ecs::system::IntoSystem;
|
/// # use bevy_ecs::system::IntoSystem;
|
||||||
/// # use bevy_ecs::system::Query;
|
/// # use bevy_ecs::system::Query;
|
||||||
/// # use bevy_ecs::query::Changed;
|
|
||||||
/// #
|
/// #
|
||||||
/// # #[derive(Debug)]
|
/// # #[derive(Component, Debug)]
|
||||||
/// # struct Name {};
|
/// # struct Name {};
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Transform {};
|
/// # struct Transform {};
|
||||||
///
|
///
|
||||||
/// fn print_moving_objects_system(query: Query<&Name, Changed<Transform>>) {
|
/// fn print_moving_objects_system(query: Query<&Name, Changed<Transform>>) {
|
||||||
|
|
|
@ -24,7 +24,6 @@ where
|
||||||
filter: F::Fetch,
|
filter: F::Fetch,
|
||||||
current_len: usize,
|
current_len: usize,
|
||||||
current_index: usize,
|
current_index: usize,
|
||||||
is_dense: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIter<'w, 's, Q, F>
|
impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIter<'w, 's, Q, F>
|
||||||
|
@ -60,7 +59,6 @@ where
|
||||||
query_state,
|
query_state,
|
||||||
tables: &world.storages().tables,
|
tables: &world.storages().tables,
|
||||||
archetypes: &world.archetypes,
|
archetypes: &world.archetypes,
|
||||||
is_dense: fetch.is_dense() && filter.is_dense(),
|
|
||||||
fetch,
|
fetch,
|
||||||
filter,
|
filter,
|
||||||
table_id_iter: query_state.matched_table_ids.iter(),
|
table_id_iter: query_state.matched_table_ids.iter(),
|
||||||
|
@ -76,7 +74,7 @@ where
|
||||||
// NOTE: this mimics the behavior of `QueryIter::next()`, except that it
|
// NOTE: this mimics the behavior of `QueryIter::next()`, except that it
|
||||||
// never gets a `Self::Item`.
|
// never gets a `Self::Item`.
|
||||||
unsafe {
|
unsafe {
|
||||||
if self.is_dense {
|
if Q::Fetch::IS_DENSE && F::Fetch::IS_DENSE {
|
||||||
loop {
|
loop {
|
||||||
if self.current_index == self.current_len {
|
if self.current_index == self.current_len {
|
||||||
let table_id = match self.table_id_iter.next() {
|
let table_id = match self.table_id_iter.next() {
|
||||||
|
@ -139,7 +137,7 @@ where
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if self.is_dense {
|
if Q::Fetch::IS_DENSE && F::Fetch::IS_DENSE {
|
||||||
loop {
|
loop {
|
||||||
if self.current_index == self.current_len {
|
if self.current_index == self.current_len {
|
||||||
let table_id = self.table_id_iter.next()?;
|
let table_id = self.table_id_iter.next()?;
|
||||||
|
@ -407,7 +405,6 @@ struct QueryIterationCursor<'s, Q: WorldQuery, F: WorldQuery> {
|
||||||
filter: F::Fetch,
|
filter: F::Fetch,
|
||||||
current_len: usize,
|
current_len: usize,
|
||||||
current_index: usize,
|
current_index: usize,
|
||||||
is_dense: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s, Q: WorldQuery, F: WorldQuery> Clone for QueryIterationCursor<'s, Q, F>
|
impl<'s, Q: WorldQuery, F: WorldQuery> Clone for QueryIterationCursor<'s, Q, F>
|
||||||
|
@ -423,7 +420,6 @@ where
|
||||||
filter: self.filter.clone(),
|
filter: self.filter.clone(),
|
||||||
current_len: self.current_len,
|
current_len: self.current_len,
|
||||||
current_index: self.current_index,
|
current_index: self.current_index,
|
||||||
is_dense: self.is_dense,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -464,7 +460,6 @@ where
|
||||||
change_tick,
|
change_tick,
|
||||||
);
|
);
|
||||||
QueryIterationCursor {
|
QueryIterationCursor {
|
||||||
is_dense: fetch.is_dense() && filter.is_dense(),
|
|
||||||
fetch,
|
fetch,
|
||||||
filter,
|
filter,
|
||||||
table_id_iter: query_state.matched_table_ids.iter(),
|
table_id_iter: query_state.matched_table_ids.iter(),
|
||||||
|
@ -478,7 +473,7 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn peek_last<'w>(&mut self) -> Option<<Q::Fetch as Fetch<'w, 's>>::Item> {
|
unsafe fn peek_last<'w>(&mut self) -> Option<<Q::Fetch as Fetch<'w, 's>>::Item> {
|
||||||
if self.current_index > 0 {
|
if self.current_index > 0 {
|
||||||
if self.is_dense {
|
if Q::Fetch::IS_DENSE && F::Fetch::IS_DENSE {
|
||||||
Some(self.fetch.table_fetch(self.current_index - 1))
|
Some(self.fetch.table_fetch(self.current_index - 1))
|
||||||
} else {
|
} else {
|
||||||
Some(self.fetch.archetype_fetch(self.current_index - 1))
|
Some(self.fetch.archetype_fetch(self.current_index - 1))
|
||||||
|
@ -498,7 +493,7 @@ where
|
||||||
archetypes: &'w Archetypes,
|
archetypes: &'w Archetypes,
|
||||||
query_state: &'s QueryState<Q, F>,
|
query_state: &'s QueryState<Q, F>,
|
||||||
) -> Option<<Q::Fetch as Fetch<'w, 's>>::Item> {
|
) -> Option<<Q::Fetch as Fetch<'w, 's>>::Item> {
|
||||||
if self.is_dense {
|
if Q::Fetch::IS_DENSE && F::Fetch::IS_DENSE {
|
||||||
loop {
|
loop {
|
||||||
if self.current_index == self.current_len {
|
if self.current_index == self.current_len {
|
||||||
let table_id = self.table_id_iter.next()?;
|
let table_id = self.table_id_iter.next()?;
|
||||||
|
|
|
@ -12,16 +12,17 @@ pub use state::*;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{self as bevy_ecs, component::Component, world::World};
|
||||||
component::{ComponentDescriptor, StorageType},
|
|
||||||
world::World,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Component, Debug, Eq, PartialEq)]
|
||||||
struct A(usize);
|
struct A(usize);
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Component, Debug, Eq, PartialEq)]
|
||||||
struct B(usize);
|
struct B(usize);
|
||||||
|
|
||||||
|
#[derive(Component, Debug, Eq, PartialEq)]
|
||||||
|
#[component(storage = "SparseSet")]
|
||||||
|
struct Sparse(usize);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn query() {
|
fn query() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
@ -139,13 +140,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn query_iter_combinations_sparse() {
|
fn query_iter_combinations_sparse() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
world
|
|
||||||
.register_component(ComponentDescriptor::new::<A>(StorageType::SparseSet))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
world.spawn_batch((1..=4).map(|i| (A(i),)));
|
world.spawn_batch((1..=4).map(|i| (Sparse(i),)));
|
||||||
|
|
||||||
let mut query = world.query::<&mut A>();
|
let mut query = world.query::<&mut Sparse>();
|
||||||
let mut combinations = query.iter_combinations_mut(&mut world);
|
let mut combinations = query.iter_combinations_mut(&mut world);
|
||||||
while let Some([mut a, mut b, mut c]) = combinations.fetch_next() {
|
while let Some([mut a, mut b, mut c]) = combinations.fetch_next() {
|
||||||
a.0 += 10;
|
a.0 += 10;
|
||||||
|
@ -153,15 +151,15 @@ mod tests {
|
||||||
c.0 += 1000;
|
c.0 += 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut query = world.query::<&A>();
|
let mut query = world.query::<&Sparse>();
|
||||||
let values: Vec<[&A; 3]> = query.iter_combinations(&world).collect();
|
let values: Vec<[&Sparse; 3]> = query.iter_combinations(&world).collect();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
values,
|
values,
|
||||||
vec![
|
vec![
|
||||||
[&A(31), &A(212), &A(1203)],
|
[&Sparse(31), &Sparse(212), &Sparse(1203)],
|
||||||
[&A(31), &A(212), &A(3004)],
|
[&Sparse(31), &Sparse(212), &Sparse(3004)],
|
||||||
[&A(31), &A(1203), &A(3004)],
|
[&Sparse(31), &Sparse(1203), &Sparse(3004)],
|
||||||
[&A(212), &A(1203), &A(3004)]
|
[&Sparse(212), &Sparse(1203), &Sparse(3004)]
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -169,17 +167,17 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn multi_storage_query() {
|
fn multi_storage_query() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
world
|
|
||||||
.register_component(ComponentDescriptor::new::<A>(StorageType::SparseSet))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
world.spawn().insert_bundle((A(1), B(2)));
|
world.spawn().insert_bundle((Sparse(1), B(2)));
|
||||||
world.spawn().insert_bundle((A(2),));
|
world.spawn().insert_bundle((Sparse(2),));
|
||||||
|
|
||||||
let values = world.query::<&A>().iter(&world).collect::<Vec<&A>>();
|
let values = world
|
||||||
assert_eq!(values, vec![&A(1), &A(2)]);
|
.query::<&Sparse>()
|
||||||
|
.iter(&world)
|
||||||
|
.collect::<Vec<&Sparse>>();
|
||||||
|
assert_eq!(values, vec![&Sparse(1), &Sparse(2)]);
|
||||||
|
|
||||||
for (_a, mut b) in world.query::<(&A, &mut B)>().iter_mut(&mut world) {
|
for (_a, mut b) in world.query::<(&Sparse, &mut B)>().iter_mut(&mut world) {
|
||||||
b.0 = 3;
|
b.0 = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -486,7 +486,7 @@ where
|
||||||
<Q::Fetch as Fetch>::init(world, &self.fetch_state, last_change_tick, change_tick);
|
<Q::Fetch as Fetch>::init(world, &self.fetch_state, last_change_tick, change_tick);
|
||||||
let mut filter =
|
let mut filter =
|
||||||
<F::Fetch as Fetch>::init(world, &self.filter_state, last_change_tick, change_tick);
|
<F::Fetch as Fetch>::init(world, &self.filter_state, last_change_tick, change_tick);
|
||||||
if fetch.is_dense() && filter.is_dense() {
|
if Q::Fetch::IS_DENSE && F::Fetch::IS_DENSE {
|
||||||
let tables = &world.storages().tables;
|
let tables = &world.storages().tables;
|
||||||
for table_id in self.matched_table_ids.iter() {
|
for table_id in self.matched_table_ids.iter() {
|
||||||
let table = &tables[*table_id];
|
let table = &tables[*table_id];
|
||||||
|
@ -541,12 +541,7 @@ where
|
||||||
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
|
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
|
||||||
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
|
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
|
||||||
task_pool.scope(|scope| {
|
task_pool.scope(|scope| {
|
||||||
let fetch =
|
if Q::Fetch::IS_DENSE && F::Fetch::IS_DENSE {
|
||||||
<Q::Fetch as Fetch>::init(world, &self.fetch_state, last_change_tick, change_tick);
|
|
||||||
let filter =
|
|
||||||
<F::Fetch as Fetch>::init(world, &self.filter_state, last_change_tick, change_tick);
|
|
||||||
|
|
||||||
if fetch.is_dense() && filter.is_dense() {
|
|
||||||
let tables = &world.storages().tables;
|
let tables = &world.storages().tables;
|
||||||
for table_id in self.matched_table_ids.iter() {
|
for table_id in self.matched_table_ids.iter() {
|
||||||
let table = &tables[*table_id];
|
let table = &tables[*table_id];
|
||||||
|
|
|
@ -321,6 +321,11 @@ mod tests {
|
||||||
};
|
};
|
||||||
use async_channel::Receiver;
|
use async_channel::Receiver;
|
||||||
|
|
||||||
|
use crate as bevy_ecs;
|
||||||
|
use crate::component::Component;
|
||||||
|
#[derive(Component)]
|
||||||
|
struct W<T>(T);
|
||||||
|
|
||||||
fn receive_events(world: &World) -> Vec<SchedulingEvent> {
|
fn receive_events(world: &World) -> Vec<SchedulingEvent> {
|
||||||
let mut events = Vec::new();
|
let mut events = Vec::new();
|
||||||
while let Ok(event) = world
|
while let Ok(event) = world
|
||||||
|
@ -381,9 +386,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn queries() {
|
fn queries() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
world.spawn().insert(0usize);
|
world.spawn().insert(W(0usize));
|
||||||
fn wants_mut(_: Query<&mut usize>) {}
|
fn wants_mut(_: Query<&mut W<usize>>) {}
|
||||||
fn wants_ref(_: Query<&usize>) {}
|
fn wants_ref(_: Query<&W<usize>>) {}
|
||||||
let mut stage = SystemStage::parallel()
|
let mut stage = SystemStage::parallel()
|
||||||
.with_system(wants_mut)
|
.with_system(wants_mut)
|
||||||
.with_system(wants_mut);
|
.with_system(wants_mut);
|
||||||
|
@ -406,9 +411,9 @@ mod tests {
|
||||||
stage.run(&mut world);
|
stage.run(&mut world);
|
||||||
assert_eq!(receive_events(&world), vec![StartedSystems(2),]);
|
assert_eq!(receive_events(&world), vec![StartedSystems(2),]);
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
world.spawn().insert_bundle((0usize, 0u32, 0f32));
|
world.spawn().insert_bundle((W(0usize), W(0u32), W(0f32)));
|
||||||
fn wants_mut_usize(_: Query<(&mut usize, &f32)>) {}
|
fn wants_mut_usize(_: Query<(&mut W<usize>, &W<f32>)>) {}
|
||||||
fn wants_mut_u32(_: Query<(&mut u32, &f32)>) {}
|
fn wants_mut_u32(_: Query<(&mut W<u32>, &W<f32>)>) {}
|
||||||
let mut stage = SystemStage::parallel()
|
let mut stage = SystemStage::parallel()
|
||||||
.with_system(wants_mut_usize)
|
.with_system(wants_mut_usize)
|
||||||
.with_system(wants_mut_u32);
|
.with_system(wants_mut_u32);
|
||||||
|
|
|
@ -905,6 +905,11 @@ mod tests {
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate as bevy_ecs;
|
||||||
|
use crate::component::Component;
|
||||||
|
#[derive(Component)]
|
||||||
|
struct W<T>(T);
|
||||||
|
|
||||||
fn make_exclusive(tag: usize) -> impl FnMut(&mut World) {
|
fn make_exclusive(tag: usize) -> impl FnMut(&mut World) {
|
||||||
move |world| world.get_resource_mut::<Vec<usize>>().unwrap().push(tag)
|
move |world| world.get_resource_mut::<Vec<usize>>().unwrap().push(tag)
|
||||||
}
|
}
|
||||||
|
@ -1573,7 +1578,7 @@ mod tests {
|
||||||
|
|
||||||
fn empty() {}
|
fn empty() {}
|
||||||
fn resource(_: ResMut<usize>) {}
|
fn resource(_: ResMut<usize>) {}
|
||||||
fn component(_: Query<&mut f32>) {}
|
fn component(_: Query<&mut W<f32>>) {}
|
||||||
|
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
|
||||||
|
@ -1957,7 +1962,7 @@ mod tests {
|
||||||
stage.run(&mut world);
|
stage.run(&mut world);
|
||||||
assert_eq!(*world.get_resource::<usize>().unwrap(), 1);
|
assert_eq!(*world.get_resource::<usize>().unwrap(), 1);
|
||||||
|
|
||||||
world.get_entity_mut(entity).unwrap().insert(1);
|
world.get_entity_mut(entity).unwrap().insert(W(1));
|
||||||
stage.run(&mut world);
|
stage.run(&mut world);
|
||||||
assert_eq!(*world.get_resource::<usize>().unwrap(), 1);
|
assert_eq!(*world.get_resource::<usize>().unwrap(), 1);
|
||||||
}
|
}
|
||||||
|
@ -1980,7 +1985,7 @@ mod tests {
|
||||||
stage.run(&mut world);
|
stage.run(&mut world);
|
||||||
assert_eq!(*world.get_resource::<usize>().unwrap(), 1);
|
assert_eq!(*world.get_resource::<usize>().unwrap(), 1);
|
||||||
|
|
||||||
world.get_entity_mut(entity).unwrap().insert(1);
|
world.get_entity_mut(entity).unwrap().insert(W(1));
|
||||||
stage.run(&mut world);
|
stage.run(&mut world);
|
||||||
assert_eq!(*world.get_resource::<usize>().unwrap(), 1);
|
assert_eq!(*world.get_resource::<usize>().unwrap(), 1);
|
||||||
}
|
}
|
||||||
|
@ -1991,7 +1996,7 @@ mod tests {
|
||||||
const MAX_DELTA: u32 = (u32::MAX / 4) * 3;
|
const MAX_DELTA: u32 = (u32::MAX / 4) * 3;
|
||||||
|
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
world.spawn().insert(0usize);
|
world.spawn().insert(W(0usize));
|
||||||
*world.change_tick.get_mut() += MAX_DELTA + 1;
|
*world.change_tick.get_mut() += MAX_DELTA + 1;
|
||||||
|
|
||||||
let mut stage = SystemStage::parallel();
|
let mut stage = SystemStage::parallel();
|
||||||
|
@ -2001,7 +2006,7 @@ mod tests {
|
||||||
// Overflow twice
|
// Overflow twice
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
stage.run(&mut world);
|
stage.run(&mut world);
|
||||||
for tracker in world.query::<ChangeTrackers<usize>>().iter(&world) {
|
for tracker in world.query::<ChangeTrackers<W<usize>>>().iter(&world) {
|
||||||
let time_since_last_check = tracker
|
let time_since_last_check = tracker
|
||||||
.change_tick
|
.change_tick
|
||||||
.wrapping_sub(tracker.component_ticks.added);
|
.wrapping_sub(tracker.component_ticks.added);
|
||||||
|
@ -2018,6 +2023,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn change_query_wrapover() {
|
fn change_query_wrapover() {
|
||||||
|
use crate::{self as bevy_ecs, component::Component};
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct C;
|
struct C;
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
|
||||||
|
@ -2053,6 +2061,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn run_criteria_with_query() {
|
fn run_criteria_with_query() {
|
||||||
|
use crate::{self as bevy_ecs, component::Component};
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct Foo;
|
struct Foo;
|
||||||
|
|
||||||
fn even_number_of_entities_critiera(query: Query<&Foo>) -> ShouldRun {
|
fn even_number_of_entities_critiera(query: Query<&Foo>) -> ShouldRun {
|
||||||
|
@ -2089,6 +2100,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn stage_run_criteria_with_query() {
|
fn stage_run_criteria_with_query() {
|
||||||
|
use crate::{self as bevy_ecs, component::Component};
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct Foo;
|
struct Foo;
|
||||||
|
|
||||||
fn even_number_of_entities_critiera(query: Query<&Foo>) -> ShouldRun {
|
fn even_number_of_entities_critiera(query: Query<&Foo>) -> ShouldRun {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
component::Component,
|
|
||||||
schedule::{
|
schedule::{
|
||||||
RunCriteriaDescriptor, RunCriteriaDescriptorCoercion, RunCriteriaLabel, ShouldRun,
|
RunCriteriaDescriptor, RunCriteriaDescriptorCoercion, RunCriteriaLabel, ShouldRun,
|
||||||
SystemSet,
|
SystemSet,
|
||||||
|
@ -9,6 +8,9 @@ use crate::{
|
||||||
use std::{any::TypeId, fmt::Debug, hash::Hash};
|
use std::{any::TypeId, fmt::Debug, hash::Hash};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub trait StateData: Send + Sync + Clone + Eq + Debug + Hash + 'static {}
|
||||||
|
impl<T> StateData for T where T: Send + Sync + Clone + Eq + Debug + Hash + 'static {}
|
||||||
|
|
||||||
/// ### Stack based state machine
|
/// ### Stack based state machine
|
||||||
///
|
///
|
||||||
/// This state machine has four operations: Push, Pop, Set and Replace.
|
/// This state machine has four operations: Push, Pop, Set and Replace.
|
||||||
|
@ -17,7 +19,7 @@ use thiserror::Error;
|
||||||
/// * Set replaces the active state with a new one
|
/// * Set replaces the active state with a new one
|
||||||
/// * Replace unwinds the state stack, and replaces the entire stack with a single new state
|
/// * Replace unwinds the state stack, and replaces the entire stack with a single new state
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct State<T: Component + Clone + Eq> {
|
pub struct State<T: StateData> {
|
||||||
transition: Option<StateTransition<T>>,
|
transition: Option<StateTransition<T>>,
|
||||||
stack: Vec<T>,
|
stack: Vec<T>,
|
||||||
scheduled: Option<ScheduledOperation<T>>,
|
scheduled: Option<ScheduledOperation<T>>,
|
||||||
|
@ -25,7 +27,7 @@ pub struct State<T: Component + Clone + Eq> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum StateTransition<T: Component + Clone + Eq> {
|
enum StateTransition<T: StateData> {
|
||||||
PreStartup,
|
PreStartup,
|
||||||
Startup,
|
Startup,
|
||||||
// The parameter order is always (leaving, entering)
|
// The parameter order is always (leaving, entering)
|
||||||
|
@ -37,7 +39,7 @@ enum StateTransition<T: Component + Clone + Eq> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum ScheduledOperation<T: Component + Clone + Eq> {
|
enum ScheduledOperation<T: StateData> {
|
||||||
Set(T),
|
Set(T),
|
||||||
Replace(T),
|
Replace(T),
|
||||||
Pop,
|
Pop,
|
||||||
|
@ -58,7 +60,7 @@ enum StateCallback {
|
||||||
impl StateCallback {
|
impl StateCallback {
|
||||||
fn into_label<T>(self, state: T) -> StateRunCriteriaLabel<T>
|
fn into_label<T>(self, state: T) -> StateRunCriteriaLabel<T>
|
||||||
where
|
where
|
||||||
T: Component + Debug + Clone + Eq + Hash,
|
T: StateData,
|
||||||
{
|
{
|
||||||
StateRunCriteriaLabel(state, self)
|
StateRunCriteriaLabel(state, self)
|
||||||
}
|
}
|
||||||
|
@ -68,7 +70,7 @@ impl StateCallback {
|
||||||
struct StateRunCriteriaLabel<T>(T, StateCallback);
|
struct StateRunCriteriaLabel<T>(T, StateCallback);
|
||||||
impl<T> RunCriteriaLabel for StateRunCriteriaLabel<T>
|
impl<T> RunCriteriaLabel for StateRunCriteriaLabel<T>
|
||||||
where
|
where
|
||||||
T: Component + Debug + Clone + Eq + Hash,
|
T: StateData,
|
||||||
{
|
{
|
||||||
fn dyn_clone(&self) -> Box<dyn RunCriteriaLabel> {
|
fn dyn_clone(&self) -> Box<dyn RunCriteriaLabel> {
|
||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
|
@ -91,7 +93,7 @@ impl DriverLabel {
|
||||||
|
|
||||||
impl<T> State<T>
|
impl<T> State<T>
|
||||||
where
|
where
|
||||||
T: Component + Debug + Clone + Eq + Hash,
|
T: StateData,
|
||||||
{
|
{
|
||||||
pub fn on_update(s: T) -> RunCriteriaDescriptor {
|
pub fn on_update(s: T) -> RunCriteriaDescriptor {
|
||||||
(|state: Res<State<T>>, pred: Local<Option<T>>| {
|
(|state: Res<State<T>>, pred: Local<Option<T>>| {
|
||||||
|
@ -386,10 +388,7 @@ pub enum StateError {
|
||||||
StackEmpty,
|
StackEmpty,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_run_adapter<T: Component + Clone + Eq>(
|
fn should_run_adapter<T: StateData>(In(cmp_result): In<bool>, state: Res<State<T>>) -> ShouldRun {
|
||||||
In(cmp_result): In<bool>,
|
|
||||||
state: Res<State<T>>,
|
|
||||||
) -> ShouldRun {
|
|
||||||
if state.end_next_loop {
|
if state.end_next_loop {
|
||||||
return ShouldRun::No;
|
return ShouldRun::No;
|
||||||
}
|
}
|
||||||
|
@ -400,7 +399,7 @@ fn should_run_adapter<T: Component + Clone + Eq>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state_cleaner<T: Component + Clone + Eq>(
|
fn state_cleaner<T: StateData>(
|
||||||
mut state: ResMut<State<T>>,
|
mut state: ResMut<State<T>>,
|
||||||
mut prep_exit: Local<bool>,
|
mut prep_exit: Local<bool>,
|
||||||
) -> ShouldRun {
|
) -> ShouldRun {
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
use crate::{
|
use crate::schedule::{
|
||||||
component::Component,
|
AmbiguitySetLabel, BoxedAmbiguitySetLabel, BoxedSystemLabel, IntoRunCriteria,
|
||||||
schedule::{
|
RunCriteriaDescriptorOrLabel, State, StateData, SystemDescriptor, SystemLabel,
|
||||||
AmbiguitySetLabel, BoxedAmbiguitySetLabel, BoxedSystemLabel, IntoRunCriteria,
|
|
||||||
RunCriteriaDescriptorOrLabel, State, SystemDescriptor, SystemLabel,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use std::{fmt::Debug, hash::Hash};
|
|
||||||
|
|
||||||
use super::IntoSystemDescriptor;
|
use super::IntoSystemDescriptor;
|
||||||
|
|
||||||
|
@ -39,49 +35,49 @@ impl SystemSet {
|
||||||
|
|
||||||
pub fn on_update<T>(s: T) -> SystemSet
|
pub fn on_update<T>(s: T) -> SystemSet
|
||||||
where
|
where
|
||||||
T: Component + Debug + Clone + Eq + Hash,
|
T: StateData,
|
||||||
{
|
{
|
||||||
Self::new().with_run_criteria(State::<T>::on_update(s))
|
Self::new().with_run_criteria(State::<T>::on_update(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_inactive_update<T>(s: T) -> SystemSet
|
pub fn on_inactive_update<T>(s: T) -> SystemSet
|
||||||
where
|
where
|
||||||
T: Component + Debug + Clone + Eq + Hash,
|
T: StateData,
|
||||||
{
|
{
|
||||||
Self::new().with_run_criteria(State::<T>::on_inactive_update(s))
|
Self::new().with_run_criteria(State::<T>::on_inactive_update(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_in_stack_update<T>(s: T) -> SystemSet
|
pub fn on_in_stack_update<T>(s: T) -> SystemSet
|
||||||
where
|
where
|
||||||
T: Component + Debug + Clone + Eq + Hash,
|
T: StateData,
|
||||||
{
|
{
|
||||||
Self::new().with_run_criteria(State::<T>::on_in_stack_update(s))
|
Self::new().with_run_criteria(State::<T>::on_in_stack_update(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_enter<T>(s: T) -> SystemSet
|
pub fn on_enter<T>(s: T) -> SystemSet
|
||||||
where
|
where
|
||||||
T: Component + Debug + Clone + Eq + Hash,
|
T: StateData,
|
||||||
{
|
{
|
||||||
Self::new().with_run_criteria(State::<T>::on_enter(s))
|
Self::new().with_run_criteria(State::<T>::on_enter(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_exit<T>(s: T) -> SystemSet
|
pub fn on_exit<T>(s: T) -> SystemSet
|
||||||
where
|
where
|
||||||
T: Component + Debug + Clone + Eq + Hash,
|
T: StateData,
|
||||||
{
|
{
|
||||||
Self::new().with_run_criteria(State::<T>::on_exit(s))
|
Self::new().with_run_criteria(State::<T>::on_exit(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_pause<T>(s: T) -> SystemSet
|
pub fn on_pause<T>(s: T) -> SystemSet
|
||||||
where
|
where
|
||||||
T: Component + Debug + Clone + Eq + Hash,
|
T: StateData,
|
||||||
{
|
{
|
||||||
Self::new().with_run_criteria(State::<T>::on_pause(s))
|
Self::new().with_run_criteria(State::<T>::on_pause(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_resume<T>(s: T) -> SystemSet
|
pub fn on_resume<T>(s: T) -> SystemSet
|
||||||
where
|
where
|
||||||
T: Component + Debug + Clone + Eq + Hash,
|
T: StateData,
|
||||||
{
|
{
|
||||||
Self::new().with_run_criteria(State::<T>::on_resume(s))
|
Self::new().with_run_criteria(State::<T>::on_resume(s))
|
||||||
}
|
}
|
||||||
|
|
|
@ -522,12 +522,18 @@ impl IndexMut<TableId> for Tables {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate as bevy_ecs;
|
||||||
|
use crate::component::Component;
|
||||||
|
use crate::storage::Storages;
|
||||||
use crate::{component::Components, entity::Entity, storage::Table};
|
use crate::{component::Components, entity::Entity, storage::Table};
|
||||||
|
#[derive(Component)]
|
||||||
|
struct W<T>(T);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn table() {
|
fn table() {
|
||||||
let mut components = Components::default();
|
let mut components = Components::default();
|
||||||
let component_id = components.get_or_insert_id::<usize>();
|
let mut storages = Storages::default();
|
||||||
|
let component_id = components.init_component::<W<usize>>(&mut storages);
|
||||||
let columns = &[component_id];
|
let columns = &[component_id];
|
||||||
let mut table = Table::with_capacity(0, columns.len());
|
let mut table = Table::with_capacity(0, columns.len());
|
||||||
table.add_column(components.get_info(component_id).unwrap());
|
table.add_column(components.get_info(component_id).unwrap());
|
||||||
|
|
|
@ -10,6 +10,8 @@ use bevy_utils::tracing::{error, warn};
|
||||||
pub use command_queue::CommandQueue;
|
pub use command_queue::CommandQueue;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use super::Resource;
|
||||||
|
|
||||||
/// A [`World`] mutation.
|
/// A [`World`] mutation.
|
||||||
pub trait Command: Send + Sync + 'static {
|
pub trait Command: Send + Sync + 'static {
|
||||||
fn write(self, world: &mut World);
|
fn write(self, world: &mut World);
|
||||||
|
@ -58,6 +60,13 @@ impl<'w, 's> Commands<'w, 's> {
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::prelude::*;
|
/// use bevy_ecs::prelude::*;
|
||||||
///
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct Label(&'static str);
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct Strength(u32);
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct Agility(u32);
|
||||||
|
///
|
||||||
/// fn example_system(mut commands: Commands) {
|
/// fn example_system(mut commands: Commands) {
|
||||||
/// // Create a new empty entity and retrieve its id.
|
/// // Create a new empty entity and retrieve its id.
|
||||||
/// let empty_entity = commands.spawn().id();
|
/// let empty_entity = commands.spawn().id();
|
||||||
|
@ -65,9 +74,9 @@ impl<'w, 's> Commands<'w, 's> {
|
||||||
/// // Create another empty entity, then add some component to it
|
/// // Create another empty entity, then add some component to it
|
||||||
/// commands.spawn()
|
/// commands.spawn()
|
||||||
/// // adds a new component bundle to the entity
|
/// // adds a new component bundle to the entity
|
||||||
/// .insert_bundle((1usize, 2u32))
|
/// .insert_bundle((Strength(1), Agility(2)))
|
||||||
/// // adds a single component to the entity
|
/// // adds a single component to the entity
|
||||||
/// .insert("hello world");
|
/// .insert(Label("hello world"));
|
||||||
/// }
|
/// }
|
||||||
/// # example_system.system();
|
/// # example_system.system();
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -114,8 +123,16 @@ impl<'w, 's> Commands<'w, 's> {
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::prelude::*;
|
/// use bevy_ecs::prelude::*;
|
||||||
///
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
/// struct Component1;
|
/// struct Component1;
|
||||||
|
/// #[derive(Component)]
|
||||||
/// struct Component2;
|
/// struct Component2;
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct Label(&'static str);
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct Strength(u32);
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct Agility(u32);
|
||||||
///
|
///
|
||||||
/// #[derive(Bundle)]
|
/// #[derive(Bundle)]
|
||||||
/// struct ExampleBundle {
|
/// struct ExampleBundle {
|
||||||
|
@ -134,9 +151,9 @@ impl<'w, 's> Commands<'w, 's> {
|
||||||
/// // Create a new entity with two components using a "tuple bundle".
|
/// // Create a new entity with two components using a "tuple bundle".
|
||||||
/// .spawn_bundle((Component1, Component2))
|
/// .spawn_bundle((Component1, Component2))
|
||||||
/// // spawn_bundle returns a builder, so you can insert more bundles like this:
|
/// // spawn_bundle returns a builder, so you can insert more bundles like this:
|
||||||
/// .insert_bundle((1usize, 2u32))
|
/// .insert_bundle((Strength(1), Agility(2)))
|
||||||
/// // or insert single components like this:
|
/// // or insert single components like this:
|
||||||
/// .insert("hello world");
|
/// .insert(Label("hello world"));
|
||||||
/// }
|
/// }
|
||||||
/// # example_system.system();
|
/// # example_system.system();
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -153,15 +170,22 @@ impl<'w, 's> Commands<'w, 's> {
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::prelude::*;
|
/// use bevy_ecs::prelude::*;
|
||||||
///
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct Label(&'static str);
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct Strength(u32);
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct Agility(u32);
|
||||||
|
|
||||||
/// fn example_system(mut commands: Commands) {
|
/// fn example_system(mut commands: Commands) {
|
||||||
/// // Create a new, empty entity
|
/// // Create a new, empty entity
|
||||||
/// let entity = commands.spawn().id();
|
/// let entity = commands.spawn().id();
|
||||||
///
|
///
|
||||||
/// commands.entity(entity)
|
/// commands.entity(entity)
|
||||||
/// // adds a new component bundle to the entity
|
/// // adds a new component bundle to the entity
|
||||||
/// .insert_bundle((1usize, 2u32))
|
/// .insert_bundle((Strength(1), Agility(2)))
|
||||||
/// // adds a single component to the entity
|
/// // adds a single component to the entity
|
||||||
/// .insert("hello world");
|
/// .insert(Label("hello world"));
|
||||||
/// }
|
/// }
|
||||||
/// # example_system.system();
|
/// # example_system.system();
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -190,7 +214,9 @@ impl<'w, 's> Commands<'w, 's> {
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// #
|
/// #
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Name(String);
|
/// # struct Name(String);
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Score(u32);
|
/// # struct Score(u32);
|
||||||
/// #
|
/// #
|
||||||
/// # fn system(mut commands: Commands) {
|
/// # fn system(mut commands: Commands) {
|
||||||
|
@ -256,7 +282,7 @@ impl<'w, 's> Commands<'w, 's> {
|
||||||
/// # }
|
/// # }
|
||||||
/// # system.system();
|
/// # system.system();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn insert_resource<T: Component>(&mut self, resource: T) {
|
pub fn insert_resource<T: Resource>(&mut self, resource: T) {
|
||||||
self.queue.push(InsertResource { resource })
|
self.queue.push(InsertResource { resource })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,7 +305,7 @@ impl<'w, 's> Commands<'w, 's> {
|
||||||
/// # }
|
/// # }
|
||||||
/// # system.system();
|
/// # system.system();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn remove_resource<T: Component>(&mut self) {
|
pub fn remove_resource<T: Resource>(&mut self) {
|
||||||
self.queue.push(RemoveResource::<T> {
|
self.queue.push(RemoveResource::<T> {
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
});
|
});
|
||||||
|
@ -294,8 +320,11 @@ impl<'w, 's> Commands<'w, 's> {
|
||||||
/// use bevy_ecs::system::InsertBundle;
|
/// use bevy_ecs::system::InsertBundle;
|
||||||
/// #
|
/// #
|
||||||
/// # struct PlayerEntity { entity: Entity }
|
/// # struct PlayerEntity { entity: Entity }
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Health(u32);
|
/// # struct Health(u32);
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Strength(u32);
|
/// # struct Strength(u32);
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Defense(u32);
|
/// # struct Defense(u32);
|
||||||
/// #
|
/// #
|
||||||
/// # #[derive(Bundle)]
|
/// # #[derive(Bundle)]
|
||||||
|
@ -354,8 +383,11 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// #
|
/// #
|
||||||
/// # struct PlayerEntity { entity: Entity }
|
/// # struct PlayerEntity { entity: Entity }
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Health(u32);
|
/// # struct Health(u32);
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Strength(u32);
|
/// # struct Strength(u32);
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Defense(u32);
|
/// # struct Defense(u32);
|
||||||
/// #
|
/// #
|
||||||
/// # #[derive(Bundle)]
|
/// # #[derive(Bundle)]
|
||||||
|
@ -401,7 +433,9 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// #
|
/// #
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Component1;
|
/// # struct Component1;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Component2;
|
/// # struct Component2;
|
||||||
/// #
|
/// #
|
||||||
/// fn example_system(mut commands: Commands) {
|
/// fn example_system(mut commands: Commands) {
|
||||||
|
@ -436,8 +470,10 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
|
||||||
/// #
|
/// #
|
||||||
/// # struct PlayerEntity { entity: Entity }
|
/// # struct PlayerEntity { entity: Entity }
|
||||||
/// #
|
/// #
|
||||||
|
/// # #[derive(Component)]
|
||||||
|
/// struct Dummy;
|
||||||
/// # #[derive(Bundle)]
|
/// # #[derive(Bundle)]
|
||||||
/// # struct CombatBundle { a: u32 }; // dummy field, unit bundles are not permitted.
|
/// # struct CombatBundle { a: Dummy }; // dummy field, unit bundles are not permitted.
|
||||||
/// #
|
/// #
|
||||||
/// fn remove_combat_stats_system(mut commands: Commands, player: Res<PlayerEntity>) {
|
/// fn remove_combat_stats_system(mut commands: Commands, player: Res<PlayerEntity>) {
|
||||||
/// commands.entity(player.entity).remove_bundle::<CombatBundle>();
|
/// commands.entity(player.entity).remove_bundle::<CombatBundle>();
|
||||||
|
@ -465,6 +501,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// #
|
/// #
|
||||||
/// # struct TargetEnemy { entity: Entity }
|
/// # struct TargetEnemy { entity: Entity }
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Enemy;
|
/// # struct Enemy;
|
||||||
/// #
|
/// #
|
||||||
/// fn convert_enemy_system(mut commands: Commands, enemy: Res<TargetEnemy>) {
|
/// fn convert_enemy_system(mut commands: Commands, enemy: Res<TargetEnemy>) {
|
||||||
|
@ -675,21 +712,21 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InsertResource<T: Component> {
|
pub struct InsertResource<T: Resource> {
|
||||||
pub resource: T,
|
pub resource: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Component> Command for InsertResource<T> {
|
impl<T: Resource> Command for InsertResource<T> {
|
||||||
fn write(self, world: &mut World) {
|
fn write(self, world: &mut World) {
|
||||||
world.insert_resource(self.resource);
|
world.insert_resource(self.resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RemoveResource<T: Component> {
|
pub struct RemoveResource<T: Resource> {
|
||||||
pub phantom: PhantomData<T>,
|
pub phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Component> Command for RemoveResource<T> {
|
impl<T: Resource> Command for RemoveResource<T> {
|
||||||
fn write(self, world: &mut World) {
|
fn write(self, world: &mut World) {
|
||||||
world.remove_resource::<T>();
|
world.remove_resource::<T>();
|
||||||
}
|
}
|
||||||
|
@ -699,7 +736,8 @@ impl<T: Component> Command for RemoveResource<T> {
|
||||||
#[allow(clippy::float_cmp, clippy::approx_constant)]
|
#[allow(clippy::float_cmp, clippy::approx_constant)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
component::{ComponentDescriptor, StorageType},
|
self as bevy_ecs,
|
||||||
|
component::Component,
|
||||||
system::{CommandQueue, Commands},
|
system::{CommandQueue, Commands},
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
|
@ -708,7 +746,11 @@ mod tests {
|
||||||
Arc,
|
Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Component)]
|
||||||
|
#[component(storage = "SparseSet")]
|
||||||
|
struct SparseDropCk(DropCk);
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct DropCk(Arc<AtomicUsize>);
|
struct DropCk(Arc<AtomicUsize>);
|
||||||
impl DropCk {
|
impl DropCk {
|
||||||
fn new_pair() -> (Self, Arc<AtomicUsize>) {
|
fn new_pair() -> (Self, Arc<AtomicUsize>) {
|
||||||
|
@ -723,19 +765,22 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct W<T>(T);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn commands() {
|
fn commands() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
let mut command_queue = CommandQueue::default();
|
let mut command_queue = CommandQueue::default();
|
||||||
let entity = Commands::new(&mut command_queue, &world)
|
let entity = Commands::new(&mut command_queue, &world)
|
||||||
.spawn_bundle((1u32, 2u64))
|
.spawn_bundle((W(1u32), W(2u64)))
|
||||||
.id();
|
.id();
|
||||||
command_queue.apply(&mut world);
|
command_queue.apply(&mut world);
|
||||||
assert!(world.entities().len() == 1);
|
assert!(world.entities().len() == 1);
|
||||||
let results = world
|
let results = world
|
||||||
.query::<(&u32, &u64)>()
|
.query::<(&W<u32>, &W<u64>)>()
|
||||||
.iter(&world)
|
.iter(&world)
|
||||||
.map(|(a, b)| (*a, *b))
|
.map(|(a, b)| (a.0, b.0))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
assert_eq!(results, vec![(1u32, 2u64)]);
|
assert_eq!(results, vec![(1u32, 2u64)]);
|
||||||
// test entity despawn
|
// test entity despawn
|
||||||
|
@ -746,9 +791,9 @@ mod tests {
|
||||||
}
|
}
|
||||||
command_queue.apply(&mut world);
|
command_queue.apply(&mut world);
|
||||||
let results2 = world
|
let results2 = world
|
||||||
.query::<(&u32, &u64)>()
|
.query::<(&W<u32>, &W<u64>)>()
|
||||||
.iter(&world)
|
.iter(&world)
|
||||||
.map(|(a, b)| (*a, *b))
|
.map(|(a, b)| (a.0, b.0))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
assert_eq!(results2, vec![]);
|
assert_eq!(results2, vec![]);
|
||||||
}
|
}
|
||||||
|
@ -757,33 +802,28 @@ mod tests {
|
||||||
fn remove_components() {
|
fn remove_components() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
|
||||||
struct DenseDropCk(DropCk);
|
|
||||||
world
|
|
||||||
.register_component(ComponentDescriptor::new::<DropCk>(StorageType::SparseSet))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut command_queue = CommandQueue::default();
|
let mut command_queue = CommandQueue::default();
|
||||||
let (dense_dropck, dense_is_dropped) = DropCk::new_pair();
|
let (dense_dropck, dense_is_dropped) = DropCk::new_pair();
|
||||||
let dense_dropck = DenseDropCk(dense_dropck);
|
|
||||||
let (sparse_dropck, sparse_is_dropped) = DropCk::new_pair();
|
let (sparse_dropck, sparse_is_dropped) = DropCk::new_pair();
|
||||||
|
let sparse_dropck = SparseDropCk(sparse_dropck);
|
||||||
|
|
||||||
let entity = Commands::new(&mut command_queue, &world)
|
let entity = Commands::new(&mut command_queue, &world)
|
||||||
.spawn()
|
.spawn()
|
||||||
.insert_bundle((1u32, 2u64, dense_dropck, sparse_dropck))
|
.insert_bundle((W(1u32), W(2u64), dense_dropck, sparse_dropck))
|
||||||
.id();
|
.id();
|
||||||
command_queue.apply(&mut world);
|
command_queue.apply(&mut world);
|
||||||
let results_before = world
|
let results_before = world
|
||||||
.query::<(&u32, &u64)>()
|
.query::<(&W<u32>, &W<u64>)>()
|
||||||
.iter(&world)
|
.iter(&world)
|
||||||
.map(|(a, b)| (*a, *b))
|
.map(|(a, b)| (a.0, b.0))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
assert_eq!(results_before, vec![(1u32, 2u64)]);
|
assert_eq!(results_before, vec![(1u32, 2u64)]);
|
||||||
|
|
||||||
// test component removal
|
// test component removal
|
||||||
Commands::new(&mut command_queue, &world)
|
Commands::new(&mut command_queue, &world)
|
||||||
.entity(entity)
|
.entity(entity)
|
||||||
.remove::<u32>()
|
.remove::<W<u32>>()
|
||||||
.remove_bundle::<(u32, u64, DenseDropCk, DropCk)>();
|
.remove_bundle::<(W<u32>, W<u64>, SparseDropCk, DropCk)>();
|
||||||
|
|
||||||
assert_eq!(dense_is_dropped.load(Ordering::Relaxed), 0);
|
assert_eq!(dense_is_dropped.load(Ordering::Relaxed), 0);
|
||||||
assert_eq!(sparse_is_dropped.load(Ordering::Relaxed), 0);
|
assert_eq!(sparse_is_dropped.load(Ordering::Relaxed), 0);
|
||||||
|
@ -792,15 +832,15 @@ mod tests {
|
||||||
assert_eq!(sparse_is_dropped.load(Ordering::Relaxed), 1);
|
assert_eq!(sparse_is_dropped.load(Ordering::Relaxed), 1);
|
||||||
|
|
||||||
let results_after = world
|
let results_after = world
|
||||||
.query::<(&u32, &u64)>()
|
.query::<(&W<u32>, &W<u64>)>()
|
||||||
.iter(&world)
|
.iter(&world)
|
||||||
.map(|(a, b)| (*a, *b))
|
.map(|(a, b)| (a.0, b.0))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
assert_eq!(results_after, vec![]);
|
assert_eq!(results_after, vec![]);
|
||||||
let results_after_u64 = world
|
let results_after_u64 = world
|
||||||
.query::<&u64>()
|
.query::<&W<u64>>()
|
||||||
.iter(&world)
|
.iter(&world)
|
||||||
.copied()
|
.map(|v| v.0)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
assert_eq!(results_after_u64, vec![]);
|
assert_eq!(results_after_u64, vec![]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,36 +113,42 @@ where
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
|
self as bevy_ecs,
|
||||||
|
component::Component,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
query::With,
|
query::With,
|
||||||
schedule::{Stage, SystemStage},
|
schedule::{Stage, SystemStage},
|
||||||
system::{Commands, IntoExclusiveSystem, Query, ResMut},
|
system::{Commands, IntoExclusiveSystem, Query, ResMut},
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct Foo(f32);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parallel_with_commands_as_exclusive() {
|
fn parallel_with_commands_as_exclusive() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
|
||||||
fn removal(
|
fn removal(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
query: Query<Entity, With<f32>>,
|
query: Query<Entity, With<Foo>>,
|
||||||
mut counter: ResMut<usize>,
|
mut counter: ResMut<usize>,
|
||||||
) {
|
) {
|
||||||
for entity in query.iter() {
|
for entity in query.iter() {
|
||||||
*counter += 1;
|
*counter += 1;
|
||||||
commands.entity(entity).remove::<f32>();
|
commands.entity(entity).remove::<Foo>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut stage = SystemStage::parallel().with_system(removal);
|
let mut stage = SystemStage::parallel().with_system(removal);
|
||||||
world.spawn().insert(0.0f32);
|
world.spawn().insert(Foo(0.0f32));
|
||||||
world.insert_resource(0usize);
|
world.insert_resource(0usize);
|
||||||
stage.run(&mut world);
|
stage.run(&mut world);
|
||||||
stage.run(&mut world);
|
stage.run(&mut world);
|
||||||
assert_eq!(*world.get_resource::<usize>().unwrap(), 1);
|
assert_eq!(*world.get_resource::<usize>().unwrap(), 1);
|
||||||
|
|
||||||
let mut stage = SystemStage::parallel().with_system(removal.exclusive_system());
|
let mut stage = SystemStage::parallel().with_system(removal.exclusive_system());
|
||||||
world.spawn().insert(0.0f32);
|
world.spawn().insert(Foo(0.0f32));
|
||||||
world.insert_resource(0usize);
|
world.insert_resource(0usize);
|
||||||
stage.run(&mut world);
|
stage.run(&mut world);
|
||||||
stage.run(&mut world);
|
stage.run(&mut world);
|
||||||
|
@ -151,10 +157,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn update_archetype_for_exclusive_system_coerced() {
|
fn update_archetype_for_exclusive_system_coerced() {
|
||||||
struct Foo;
|
|
||||||
|
|
||||||
fn spawn_entity(mut commands: crate::prelude::Commands) {
|
fn spawn_entity(mut commands: crate::prelude::Commands) {
|
||||||
commands.spawn().insert(Foo);
|
commands.spawn().insert(Foo(0.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_entities(query: Query<&Foo>, mut res: ResMut<Vec<usize>>) {
|
fn count_entities(query: Query<&Foo>, mut res: ResMut<Vec<usize>>) {
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
//! ```
|
//! ```
|
||||||
//! # use bevy_ecs::prelude::*;
|
//! # use bevy_ecs::prelude::*;
|
||||||
//! #
|
//! #
|
||||||
|
//! # #[derive(Component)]
|
||||||
//! # struct Player { alive: bool }
|
//! # struct Player { alive: bool }
|
||||||
|
//! # #[derive(Component)]
|
||||||
//! # struct Score(u32);
|
//! # struct Score(u32);
|
||||||
//! # struct Round(u32);
|
//! # struct Round(u32);
|
||||||
//! #
|
//! #
|
||||||
|
@ -85,9 +87,10 @@ mod tests {
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
self as bevy_ecs,
|
||||||
archetype::Archetypes,
|
archetype::Archetypes,
|
||||||
bundle::Bundles,
|
bundle::Bundles,
|
||||||
component::Components,
|
component::{Component, Components},
|
||||||
entity::{Entities, Entity},
|
entity::{Entities, Entity},
|
||||||
query::{Added, Changed, Or, QueryState, With, Without},
|
query::{Added, Changed, Or, QueryState, With, Without},
|
||||||
schedule::{Schedule, Stage, SystemStage},
|
schedule::{Schedule, Stage, SystemStage},
|
||||||
|
@ -98,14 +101,22 @@ mod tests {
|
||||||
world::{FromWorld, World},
|
world::{FromWorld, World},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Default)]
|
#[derive(Component, Debug, Eq, PartialEq, Default)]
|
||||||
struct A;
|
struct A;
|
||||||
|
#[derive(Component)]
|
||||||
struct B;
|
struct B;
|
||||||
|
#[derive(Component)]
|
||||||
struct C;
|
struct C;
|
||||||
|
#[derive(Component)]
|
||||||
struct D;
|
struct D;
|
||||||
|
#[derive(Component)]
|
||||||
struct E;
|
struct E;
|
||||||
|
#[derive(Component)]
|
||||||
struct F;
|
struct F;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct W<T>(T);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple_system() {
|
fn simple_system() {
|
||||||
fn sys(query: Query<&A>) {
|
fn sys(query: Query<&A>) {
|
||||||
|
@ -442,15 +453,15 @@ mod tests {
|
||||||
fn remove_tracking() {
|
fn remove_tracking() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
struct Despawned(Entity);
|
struct Despawned(Entity);
|
||||||
let a = world.spawn().insert_bundle(("abc", 123)).id();
|
let a = world.spawn().insert_bundle((W("abc"), W(123))).id();
|
||||||
world.spawn().insert_bundle(("abc", 123));
|
world.spawn().insert_bundle((W("abc"), W(123)));
|
||||||
world.insert_resource(false);
|
world.insert_resource(false);
|
||||||
world.insert_resource(Despawned(a));
|
world.insert_resource(Despawned(a));
|
||||||
|
|
||||||
world.entity_mut(a).despawn();
|
world.entity_mut(a).despawn();
|
||||||
|
|
||||||
fn validate_removed(
|
fn validate_removed(
|
||||||
removed_i32: RemovedComponents<i32>,
|
removed_i32: RemovedComponents<W<i32>>,
|
||||||
despawned: Res<Despawned>,
|
despawned: Res<Despawned>,
|
||||||
mut ran: ResMut<bool>,
|
mut ran: ResMut<bool>,
|
||||||
) {
|
) {
|
||||||
|
@ -486,13 +497,13 @@ mod tests {
|
||||||
fn world_collections_system() {
|
fn world_collections_system() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
world.insert_resource(false);
|
world.insert_resource(false);
|
||||||
world.spawn().insert_bundle((42, true));
|
world.spawn().insert_bundle((W(42), W(true)));
|
||||||
fn sys(
|
fn sys(
|
||||||
archetypes: &Archetypes,
|
archetypes: &Archetypes,
|
||||||
components: &Components,
|
components: &Components,
|
||||||
entities: &Entities,
|
entities: &Entities,
|
||||||
bundles: &Bundles,
|
bundles: &Bundles,
|
||||||
query: Query<Entity, With<i32>>,
|
query: Query<Entity, With<W<i32>>>,
|
||||||
mut modified: ResMut<bool>,
|
mut modified: ResMut<bool>,
|
||||||
) {
|
) {
|
||||||
assert_eq!(query.iter().count(), 1, "entity exists");
|
assert_eq!(query.iter().count(), 1, "entity exists");
|
||||||
|
@ -501,7 +512,7 @@ mod tests {
|
||||||
let archetype = archetypes.get(location.archetype_id).unwrap();
|
let archetype = archetypes.get(location.archetype_id).unwrap();
|
||||||
let archetype_components = archetype.components().collect::<Vec<_>>();
|
let archetype_components = archetype.components().collect::<Vec<_>>();
|
||||||
let bundle_id = bundles
|
let bundle_id = bundles
|
||||||
.get_id(std::any::TypeId::of::<(i32, bool)>())
|
.get_id(std::any::TypeId::of::<(W<i32>, W<bool>)>())
|
||||||
.expect("Bundle used to spawn entity should exist");
|
.expect("Bundle used to spawn entity should exist");
|
||||||
let bundle_info = bundles.get(bundle_id).unwrap();
|
let bundle_info = bundles.get(bundle_id).unwrap();
|
||||||
let mut bundle_components = bundle_info.components().to_vec();
|
let mut bundle_components = bundle_info.components().to_vec();
|
||||||
|
@ -624,7 +635,7 @@ mod tests {
|
||||||
#[derive(Eq, PartialEq, Debug)]
|
#[derive(Eq, PartialEq, Debug)]
|
||||||
struct A(usize);
|
struct A(usize);
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Debug)]
|
#[derive(Component, Eq, PartialEq, Debug)]
|
||||||
struct B(usize);
|
struct B(usize);
|
||||||
|
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
@ -650,7 +661,7 @@ mod tests {
|
||||||
#[derive(Eq, PartialEq, Debug)]
|
#[derive(Eq, PartialEq, Debug)]
|
||||||
struct A(usize);
|
struct A(usize);
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Debug)]
|
#[derive(Component, Eq, PartialEq, Debug)]
|
||||||
struct B(usize);
|
struct B(usize);
|
||||||
|
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
@ -674,7 +685,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn system_state_change_detection() {
|
fn system_state_change_detection() {
|
||||||
#[derive(Eq, PartialEq, Debug)]
|
#[derive(Component, Eq, PartialEq, Debug)]
|
||||||
struct A(usize);
|
struct A(usize);
|
||||||
|
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
@ -709,10 +720,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn system_state_archetype_update() {
|
fn system_state_archetype_update() {
|
||||||
#[derive(Eq, PartialEq, Debug)]
|
#[derive(Component, Eq, PartialEq, Debug)]
|
||||||
struct A(usize);
|
struct A(usize);
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Debug)]
|
#[derive(Component, Eq, PartialEq, Debug)]
|
||||||
struct B(usize);
|
struct B(usize);
|
||||||
|
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
|
|
@ -43,9 +43,12 @@ use thiserror::Error;
|
||||||
/// immutably helps system parallelization.
|
/// immutably helps system parallelization.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
/// # use bevy_ecs::component::Component;
|
||||||
/// # use bevy_ecs::system::IntoSystem;
|
/// # use bevy_ecs::system::IntoSystem;
|
||||||
/// # use bevy_ecs::system::Query;
|
/// # use bevy_ecs::system::Query;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct ComponentA;
|
/// # struct ComponentA;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct ComponentB;
|
/// # struct ComponentB;
|
||||||
/// # fn system(
|
/// # fn system(
|
||||||
/// query: Query<(&ComponentA, &ComponentB)>
|
/// query: Query<(&ComponentA, &ComponentB)>
|
||||||
|
@ -60,9 +63,12 @@ use thiserror::Error;
|
||||||
/// in the same query.
|
/// in the same query.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
/// # use bevy_ecs::component::Component;
|
||||||
/// # use bevy_ecs::system::IntoSystem;
|
/// # use bevy_ecs::system::IntoSystem;
|
||||||
/// # use bevy_ecs::system::Query;
|
/// # use bevy_ecs::system::Query;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct ComponentA;
|
/// # struct ComponentA;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct ComponentB;
|
/// # struct ComponentB;
|
||||||
/// # fn system(
|
/// # fn system(
|
||||||
/// // `ComponentA` is accessed mutably, while `ComponentB` is accessed immutably.
|
/// // `ComponentA` is accessed mutably, while `ComponentB` is accessed immutably.
|
||||||
|
@ -85,10 +91,10 @@ use thiserror::Error;
|
||||||
/// will give access to the entity ID.
|
/// will give access to the entity ID.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::entity::Entity;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// # use bevy_ecs::system::IntoSystem;
|
/// # #[derive(Component)]
|
||||||
/// # use bevy_ecs::system::Query;
|
|
||||||
/// # struct ComponentA;
|
/// # struct ComponentA;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct ComponentB;
|
/// # struct ComponentB;
|
||||||
/// # fn system(
|
/// # fn system(
|
||||||
/// query: Query<(Entity, &ComponentA, &ComponentB)>
|
/// query: Query<(Entity, &ComponentA, &ComponentB)>
|
||||||
|
@ -102,11 +108,12 @@ use thiserror::Error;
|
||||||
/// out the query results that don't satisfy the given condition.
|
/// out the query results that don't satisfy the given condition.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::query::With;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// # use bevy_ecs::system::IntoSystem;
|
/// # #[derive(Component)]
|
||||||
/// # use bevy_ecs::system::Query;
|
|
||||||
/// # struct ComponentA;
|
/// # struct ComponentA;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct ComponentB;
|
/// # struct ComponentB;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct ComponentC;
|
/// # struct ComponentC;
|
||||||
/// # fn system(
|
/// # fn system(
|
||||||
/// // `ComponentC` data won't be accessed, but only entities that contain it will be queried.
|
/// // `ComponentC` data won't be accessed, but only entities that contain it will be queried.
|
||||||
|
@ -118,11 +125,12 @@ use thiserror::Error;
|
||||||
/// If you need to apply more filters in a single query, group them into a tuple:
|
/// If you need to apply more filters in a single query, group them into a tuple:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::query::{Changed, With};
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// # use bevy_ecs::system::IntoSystem;
|
/// # #[derive(Component)]
|
||||||
/// # use bevy_ecs::system::Query;
|
|
||||||
/// # struct ComponentA;
|
/// # struct ComponentA;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct ComponentB;
|
/// # struct ComponentB;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct ComponentC;
|
/// # struct ComponentC;
|
||||||
/// # fn system(
|
/// # fn system(
|
||||||
/// // Similar to the previous query, but with the addition of a `Changed` filter.
|
/// // Similar to the previous query, but with the addition of a `Changed` filter.
|
||||||
|
@ -146,9 +154,10 @@ use thiserror::Error;
|
||||||
/// `ComponentA` and `ComponentB`, and entities that contain `ComponentA` but not `ComponentB`.
|
/// `ComponentA` and `ComponentB`, and entities that contain `ComponentA` but not `ComponentB`.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::system::IntoSystem;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// # use bevy_ecs::system::Query;
|
/// # #[derive(Component)]
|
||||||
/// # struct ComponentA;
|
/// # struct ComponentA;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct ComponentB;
|
/// # struct ComponentB;
|
||||||
/// # fn system(
|
/// # fn system(
|
||||||
/// query: Query<(&ComponentA, Option<&ComponentB>)>
|
/// query: Query<(&ComponentA, Option<&ComponentB>)>
|
||||||
|
@ -168,8 +177,8 @@ use thiserror::Error;
|
||||||
/// of `Query` can be omitted.
|
/// of `Query` can be omitted.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::system::IntoSystem;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// # use bevy_ecs::system::Query;
|
/// # #[derive(Component)]
|
||||||
/// # struct MyComponent;
|
/// # struct MyComponent;
|
||||||
/// # fn tuple_system(
|
/// # fn tuple_system(
|
||||||
/// // This is correct, but can be avoided.
|
/// // This is correct, but can be avoided.
|
||||||
|
@ -197,9 +206,10 @@ use thiserror::Error;
|
||||||
/// for advanced iterator usage.
|
/// for advanced iterator usage.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::system::IntoSystem;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// # use bevy_ecs::system::Query;
|
/// # #[derive(Component)]
|
||||||
/// # struct ComponentA;
|
/// # struct ComponentA;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct ComponentB;
|
/// # struct ComponentB;
|
||||||
/// fn immutable_query_system(mut query: Query<(&ComponentA, &ComponentB)>) {
|
/// fn immutable_query_system(mut query: Query<(&ComponentA, &ComponentB)>) {
|
||||||
/// for (a, b) in query.iter() {
|
/// for (a, b) in query.iter() {
|
||||||
|
@ -278,6 +288,7 @@ where
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// #
|
/// #
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Player { name: String }
|
/// # struct Player { name: String }
|
||||||
/// #
|
/// #
|
||||||
/// fn report_names_system(query: Query<&Player>) {
|
/// fn report_names_system(query: Query<&Player>) {
|
||||||
|
@ -310,6 +321,7 @@ where
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// #
|
/// #
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Velocity { x: f32, y: f32, z: f32 }
|
/// # struct Velocity { x: f32, y: f32, z: f32 }
|
||||||
/// fn gravity_system(mut query: Query<&mut Velocity>) {
|
/// fn gravity_system(mut query: Query<&mut Velocity>) {
|
||||||
/// const DELTA: f32 = 1.0 / 60.0;
|
/// const DELTA: f32 = 1.0 / 60.0;
|
||||||
|
@ -358,8 +370,9 @@ where
|
||||||
/// In order to iterate it, use `fetch_next` method with `while let Some(..)` loop pattern.
|
/// In order to iterate it, use `fetch_next` method with `while let Some(..)` loop pattern.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # struct A;
|
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// # struct A;
|
||||||
/// # fn some_system(mut query: Query<&mut A>) {
|
/// # fn some_system(mut query: Query<&mut A>) {
|
||||||
/// // iterate using `fetch_next` in while loop
|
/// // iterate using `fetch_next` in while loop
|
||||||
/// let mut combinations = query.iter_combinations_mut();
|
/// let mut combinations = query.iter_combinations_mut();
|
||||||
|
@ -434,6 +447,7 @@ where
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// #
|
/// #
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Player { name: String }
|
/// # struct Player { name: String }
|
||||||
/// #
|
/// #
|
||||||
/// fn report_names_system(query: Query<&Player>) {
|
/// fn report_names_system(query: Query<&Player>) {
|
||||||
|
@ -471,6 +485,7 @@ where
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// #
|
/// #
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Velocity { x: f32, y: f32, z: f32 }
|
/// # struct Velocity { x: f32, y: f32, z: f32 }
|
||||||
/// fn gravity_system(mut query: Query<&mut Velocity>) {
|
/// fn gravity_system(mut query: Query<&mut Velocity>) {
|
||||||
/// const DELTA: f32 = 1.0 / 60.0;
|
/// const DELTA: f32 = 1.0 / 60.0;
|
||||||
|
@ -560,6 +575,7 @@ where
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// #
|
/// #
|
||||||
/// # struct SelectedCharacter { entity: Entity }
|
/// # struct SelectedCharacter { entity: Entity }
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Character { name: String }
|
/// # struct Character { name: String }
|
||||||
/// #
|
/// #
|
||||||
/// fn print_selected_character_name_system(
|
/// fn print_selected_character_name_system(
|
||||||
|
@ -607,6 +623,7 @@ where
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// #
|
/// #
|
||||||
/// # struct PoisonedCharacter { character_id: Entity }
|
/// # struct PoisonedCharacter { character_id: Entity }
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Health(u32);
|
/// # struct Health(u32);
|
||||||
/// #
|
/// #
|
||||||
/// fn poison_system(mut query: Query<&mut Health>, poisoned: Res<PoisonedCharacter>) {
|
/// fn poison_system(mut query: Query<&mut Health>, poisoned: Res<PoisonedCharacter>) {
|
||||||
|
@ -667,6 +684,7 @@ where
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// #
|
/// #
|
||||||
/// # struct SelectedCharacter { entity: Entity }
|
/// # struct SelectedCharacter { entity: Entity }
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Character { name: String }
|
/// # struct Character { name: String }
|
||||||
/// #
|
/// #
|
||||||
/// fn print_selected_character_name_system(
|
/// fn print_selected_character_name_system(
|
||||||
|
@ -724,6 +742,7 @@ where
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// #
|
/// #
|
||||||
/// # struct PoisonedCharacter { character_id: Entity }
|
/// # struct PoisonedCharacter { character_id: Entity }
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Health(u32);
|
/// # struct Health(u32);
|
||||||
/// #
|
/// #
|
||||||
/// fn poison_system(mut query: Query<&mut Health>, poisoned: Res<PoisonedCharacter>) {
|
/// fn poison_system(mut query: Query<&mut Health>, poisoned: Res<PoisonedCharacter>) {
|
||||||
|
@ -791,8 +810,10 @@ where
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::prelude::{IntoSystem, Query, With};
|
/// # use bevy_ecs::prelude::*;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Player;
|
/// # struct Player;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Position(f32, f32);
|
/// # struct Position(f32, f32);
|
||||||
/// fn player_system(query: Query<&Position, With<Player>>) {
|
/// fn player_system(query: Query<&Position, With<Player>>) {
|
||||||
/// let player_position = query.single();
|
/// let player_position = query.single();
|
||||||
|
@ -826,9 +847,10 @@ where
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::system::{Query, QuerySingleError};
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// # use bevy_ecs::prelude::IntoSystem;
|
/// # use bevy_ecs::system::QuerySingleError;
|
||||||
/// # struct PlayerScore(i32);
|
/// # #[derive(Component)]
|
||||||
|
/// # struct PlayerScore(i32);
|
||||||
/// fn player_scoring_system(query: Query<&PlayerScore>) {
|
/// fn player_scoring_system(query: Query<&PlayerScore>) {
|
||||||
/// match query.get_single() {
|
/// match query.get_single() {
|
||||||
/// Ok(PlayerScore(score)) => {
|
/// Ok(PlayerScore(score)) => {
|
||||||
|
@ -869,7 +891,9 @@ where
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// #
|
/// #
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Player;
|
/// # struct Player;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Health(u32);
|
/// # struct Health(u32);
|
||||||
/// #
|
/// #
|
||||||
/// fn regenerate_player_health_system(mut query: Query<&mut Health, With<Player>>) {
|
/// fn regenerate_player_health_system(mut query: Query<&mut Health, With<Player>>) {
|
||||||
|
@ -899,7 +923,9 @@ where
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// #
|
/// #
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Player;
|
/// # struct Player;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Health(u32);
|
/// # struct Health(u32);
|
||||||
/// #
|
/// #
|
||||||
/// fn regenerate_player_health_system(mut query: Query<&mut Health, With<Player>>) {
|
/// fn regenerate_player_health_system(mut query: Query<&mut Health, With<Player>>) {
|
||||||
|
@ -934,7 +960,9 @@ where
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::prelude::*;
|
/// # use bevy_ecs::prelude::*;
|
||||||
/// #
|
/// #
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Player;
|
/// # struct Player;
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct Score(u32);
|
/// # struct Score(u32);
|
||||||
/// fn update_score_system(query: Query<(), With<Player>>, mut score: ResMut<Score>) {
|
/// fn update_score_system(query: Query<(), With<Player>>, mut score: ResMut<Score>) {
|
||||||
/// if !query.is_empty() {
|
/// if !query.is_empty() {
|
||||||
|
|
|
@ -200,6 +200,9 @@ pub struct QuerySetState<T>(T);
|
||||||
|
|
||||||
impl_query_set!();
|
impl_query_set!();
|
||||||
|
|
||||||
|
pub trait Resource: Send + Sync + 'static {}
|
||||||
|
impl<T> Resource for T where T: Send + Sync + 'static {}
|
||||||
|
|
||||||
/// Shared borrow of a resource.
|
/// Shared borrow of a resource.
|
||||||
///
|
///
|
||||||
/// See the [`World`] documentation to see the usage of a resource.
|
/// See the [`World`] documentation to see the usage of a resource.
|
||||||
|
@ -211,7 +214,7 @@ impl_query_set!();
|
||||||
/// Panics when used as a [`SystemParameter`](SystemParam) if the resource does not exist.
|
/// Panics when used as a [`SystemParameter`](SystemParam) if the resource does not exist.
|
||||||
///
|
///
|
||||||
/// Use `Option<Res<T>>` instead if the resource might not always exist.
|
/// Use `Option<Res<T>>` instead if the resource might not always exist.
|
||||||
pub struct Res<'w, T: Component> {
|
pub struct Res<'w, T: Resource> {
|
||||||
value: &'w T,
|
value: &'w T,
|
||||||
ticks: &'w ComponentTicks,
|
ticks: &'w ComponentTicks,
|
||||||
last_change_tick: u32,
|
last_change_tick: u32,
|
||||||
|
@ -219,9 +222,9 @@ pub struct Res<'w, T: Component> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Res only reads a single World resource
|
// SAFE: Res only reads a single World resource
|
||||||
unsafe impl<T: Component> ReadOnlySystemParamFetch for ResState<T> {}
|
unsafe impl<T: Resource> ReadOnlySystemParamFetch for ResState<T> {}
|
||||||
|
|
||||||
impl<'w, T: Component> Debug for Res<'w, T>
|
impl<'w, T: Resource> Debug for Res<'w, T>
|
||||||
where
|
where
|
||||||
T: Debug,
|
T: Debug,
|
||||||
{
|
{
|
||||||
|
@ -230,7 +233,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w, T: Component> Res<'w, T> {
|
impl<'w, T: Resource> Res<'w, T> {
|
||||||
/// Returns true if (and only if) this resource been added since the last execution of this
|
/// Returns true if (and only if) this resource been added since the last execution of this
|
||||||
/// system.
|
/// system.
|
||||||
pub fn is_added(&self) -> bool {
|
pub fn is_added(&self) -> bool {
|
||||||
|
@ -249,7 +252,7 @@ impl<'w, T: Component> Res<'w, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w, T: Component> Deref for Res<'w, T> {
|
impl<'w, T: Resource> Deref for Res<'w, T> {
|
||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
|
@ -257,7 +260,7 @@ impl<'w, T: Component> Deref for Res<'w, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w, T: Component> AsRef<T> for Res<'w, T> {
|
impl<'w, T: Resource> AsRef<T> for Res<'w, T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn as_ref(&self) -> &T {
|
fn as_ref(&self) -> &T {
|
||||||
self.deref()
|
self.deref()
|
||||||
|
@ -270,13 +273,13 @@ pub struct ResState<T> {
|
||||||
marker: PhantomData<T>,
|
marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Component> SystemParam for Res<'a, T> {
|
impl<'a, T: Resource> SystemParam for Res<'a, T> {
|
||||||
type Fetch = ResState<T>;
|
type Fetch = ResState<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res
|
// SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res
|
||||||
// conflicts with any prior access, a panic will occur.
|
// conflicts with any prior access, a panic will occur.
|
||||||
unsafe impl<T: Component> SystemParamState for ResState<T> {
|
unsafe impl<T: Resource> SystemParamState for ResState<T> {
|
||||||
type Config = ();
|
type Config = ();
|
||||||
|
|
||||||
fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
|
fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
|
||||||
|
@ -305,7 +308,7 @@ unsafe impl<T: Component> SystemParamState for ResState<T> {
|
||||||
fn default_config() {}
|
fn default_config() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResState<T> {
|
impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for ResState<T> {
|
||||||
type Item = Res<'w, T>;
|
type Item = Res<'w, T>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -337,14 +340,14 @@ impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResState<T> {
|
||||||
/// See: [`Res<T>`]
|
/// See: [`Res<T>`]
|
||||||
pub struct OptionResState<T>(ResState<T>);
|
pub struct OptionResState<T>(ResState<T>);
|
||||||
|
|
||||||
impl<'a, T: Component> SystemParam for Option<Res<'a, T>> {
|
impl<'a, T: Resource> SystemParam for Option<Res<'a, T>> {
|
||||||
type Fetch = OptionResState<T>;
|
type Fetch = OptionResState<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Only reads a single World resource
|
// SAFE: Only reads a single World resource
|
||||||
unsafe impl<T: Component> ReadOnlySystemParamFetch for OptionResState<T> {}
|
unsafe impl<T: Resource> ReadOnlySystemParamFetch for OptionResState<T> {}
|
||||||
|
|
||||||
unsafe impl<T: Component> SystemParamState for OptionResState<T> {
|
unsafe impl<T: Resource> SystemParamState for OptionResState<T> {
|
||||||
type Config = ();
|
type Config = ();
|
||||||
|
|
||||||
fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
|
fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
|
||||||
|
@ -354,7 +357,7 @@ unsafe impl<T: Component> SystemParamState for OptionResState<T> {
|
||||||
fn default_config() {}
|
fn default_config() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for OptionResState<T> {
|
impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for OptionResState<T> {
|
||||||
type Item = Option<Res<'w, T>>;
|
type Item = Option<Res<'w, T>>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -381,13 +384,13 @@ pub struct ResMutState<T> {
|
||||||
marker: PhantomData<T>,
|
marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Component> SystemParam for ResMut<'a, T> {
|
impl<'a, T: Resource> SystemParam for ResMut<'a, T> {
|
||||||
type Fetch = ResMutState<T>;
|
type Fetch = ResMutState<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res
|
// SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res
|
||||||
// conflicts with any prior access, a panic will occur.
|
// conflicts with any prior access, a panic will occur.
|
||||||
unsafe impl<T: Component> SystemParamState for ResMutState<T> {
|
unsafe impl<T: Resource> SystemParamState for ResMutState<T> {
|
||||||
type Config = ();
|
type Config = ();
|
||||||
|
|
||||||
fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
|
fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
|
||||||
|
@ -420,7 +423,7 @@ unsafe impl<T: Component> SystemParamState for ResMutState<T> {
|
||||||
fn default_config() {}
|
fn default_config() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResMutState<T> {
|
impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for ResMutState<T> {
|
||||||
type Item = ResMut<'w, T>;
|
type Item = ResMut<'w, T>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -454,11 +457,11 @@ impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResMutState<T> {
|
||||||
/// See: [`ResMut<T>`]
|
/// See: [`ResMut<T>`]
|
||||||
pub struct OptionResMutState<T>(ResMutState<T>);
|
pub struct OptionResMutState<T>(ResMutState<T>);
|
||||||
|
|
||||||
impl<'a, T: Component> SystemParam for Option<ResMut<'a, T>> {
|
impl<'a, T: Resource> SystemParam for Option<ResMut<'a, T>> {
|
||||||
type Fetch = OptionResMutState<T>;
|
type Fetch = OptionResMutState<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: Component> SystemParamState for OptionResMutState<T> {
|
unsafe impl<T: Resource> SystemParamState for OptionResMutState<T> {
|
||||||
type Config = ();
|
type Config = ();
|
||||||
|
|
||||||
fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
|
fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
|
||||||
|
@ -468,7 +471,7 @@ unsafe impl<T: Component> SystemParamState for OptionResMutState<T> {
|
||||||
fn default_config() {}
|
fn default_config() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for OptionResMutState<T> {
|
impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for OptionResMutState<T> {
|
||||||
type Item = Option<ResMut<'w, T>>;
|
type Item = Option<ResMut<'w, T>>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -553,12 +556,12 @@ impl<'w, 's> SystemParamFetch<'w, 's> for CommandQueue {
|
||||||
/// // Note how the read local is still 0 due to the locals not being shared.
|
/// // Note how the read local is still 0 due to the locals not being shared.
|
||||||
/// assert_eq!(read_system.run((), world), 0);
|
/// assert_eq!(read_system.run((), world), 0);
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Local<'a, T: Component>(&'a mut T);
|
pub struct Local<'a, T: Resource>(&'a mut T);
|
||||||
|
|
||||||
// SAFE: Local only accesses internal state
|
// SAFE: Local only accesses internal state
|
||||||
unsafe impl<T: Component> ReadOnlySystemParamFetch for LocalState<T> {}
|
unsafe impl<T: Resource> ReadOnlySystemParamFetch for LocalState<T> {}
|
||||||
|
|
||||||
impl<'a, T: Component> Debug for Local<'a, T>
|
impl<'a, T: Resource> Debug for Local<'a, T>
|
||||||
where
|
where
|
||||||
T: Debug,
|
T: Debug,
|
||||||
{
|
{
|
||||||
|
@ -567,7 +570,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Component> Deref for Local<'a, T> {
|
impl<'a, T: Resource> Deref for Local<'a, T> {
|
||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -576,7 +579,7 @@ impl<'a, T: Component> Deref for Local<'a, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Component> DerefMut for Local<'a, T> {
|
impl<'a, T: Resource> DerefMut for Local<'a, T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
self.0
|
self.0
|
||||||
|
@ -584,14 +587,14 @@ impl<'a, T: Component> DerefMut for Local<'a, T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`SystemParamState`] of [`Local<T>`].
|
/// The [`SystemParamState`] of [`Local<T>`].
|
||||||
pub struct LocalState<T: Component>(T);
|
pub struct LocalState<T: Resource>(T);
|
||||||
|
|
||||||
impl<'a, T: Component + FromWorld> SystemParam for Local<'a, T> {
|
impl<'a, T: Resource + FromWorld> SystemParam for Local<'a, T> {
|
||||||
type Fetch = LocalState<T>;
|
type Fetch = LocalState<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: only local state is accessed
|
// SAFE: only local state is accessed
|
||||||
unsafe impl<T: Component + FromWorld> SystemParamState for LocalState<T> {
|
unsafe impl<T: Resource + FromWorld> SystemParamState for LocalState<T> {
|
||||||
type Config = Option<T>;
|
type Config = Option<T>;
|
||||||
|
|
||||||
fn init(world: &mut World, _system_meta: &mut SystemMeta, config: Self::Config) -> Self {
|
fn init(world: &mut World, _system_meta: &mut SystemMeta, config: Self::Config) -> Self {
|
||||||
|
@ -603,7 +606,7 @@ unsafe impl<T: Component + FromWorld> SystemParamState for LocalState<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w, 's, T: Component + FromWorld> SystemParamFetch<'w, 's> for LocalState<T> {
|
impl<'w, 's, T: Resource + FromWorld> SystemParamFetch<'w, 's> for LocalState<T> {
|
||||||
type Item = Local<'s, T>;
|
type Item = Local<'s, T>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -624,9 +627,11 @@ impl<'w, 's, T: Component + FromWorld> SystemParamFetch<'w, 's> for LocalState<T
|
||||||
/// Basic usage:
|
/// Basic usage:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
/// # use bevy_ecs::component::Component;
|
||||||
/// # use bevy_ecs::system::IntoSystem;
|
/// # use bevy_ecs::system::IntoSystem;
|
||||||
/// # use bevy_ecs::system::RemovedComponents;
|
/// # use bevy_ecs::system::RemovedComponents;
|
||||||
/// #
|
/// #
|
||||||
|
/// # #[derive(Component)]
|
||||||
/// # struct MyComponent;
|
/// # struct MyComponent;
|
||||||
///
|
///
|
||||||
/// fn react_on_removal(removed: RemovedComponents<MyComponent>) {
|
/// fn react_on_removal(removed: RemovedComponents<MyComponent>) {
|
||||||
|
@ -635,13 +640,13 @@ impl<'w, 's, T: Component + FromWorld> SystemParamFetch<'w, 's> for LocalState<T
|
||||||
///
|
///
|
||||||
/// # react_on_removal.system();
|
/// # react_on_removal.system();
|
||||||
/// ```
|
/// ```
|
||||||
pub struct RemovedComponents<'a, T> {
|
pub struct RemovedComponents<'a, T: Component> {
|
||||||
world: &'a World,
|
world: &'a World,
|
||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
marker: PhantomData<T>,
|
marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> RemovedComponents<'a, T> {
|
impl<'a, T: Component> RemovedComponents<'a, T> {
|
||||||
/// Returns an iterator over the entities that had their `T` [`Component`] removed.
|
/// Returns an iterator over the entities that had their `T` [`Component`] removed.
|
||||||
pub fn iter(&self) -> std::iter::Cloned<std::slice::Iter<'_, Entity>> {
|
pub fn iter(&self) -> std::iter::Cloned<std::slice::Iter<'_, Entity>> {
|
||||||
self.world.removed_with_id(self.component_id)
|
self.world.removed_with_id(self.component_id)
|
||||||
|
@ -668,7 +673,7 @@ unsafe impl<T: Component> SystemParamState for RemovedComponentsState<T> {
|
||||||
|
|
||||||
fn init(world: &mut World, _system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
|
fn init(world: &mut World, _system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
|
||||||
Self {
|
Self {
|
||||||
component_id: world.components.get_or_insert_id::<T>(),
|
component_id: world.init_component::<T>(),
|
||||||
marker: PhantomData,
|
marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -740,7 +745,7 @@ impl<'w, T: 'static> NonSend<'w, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w, T: 'static> Deref for NonSend<'w, T> {
|
impl<'w, T> Deref for NonSend<'w, T> {
|
||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
|
|
|
@ -194,7 +194,7 @@ impl<'w> EntityMut<'w> {
|
||||||
let bundle_info = self
|
let bundle_info = self
|
||||||
.world
|
.world
|
||||||
.bundles
|
.bundles
|
||||||
.init_info::<T>(&mut self.world.components);
|
.init_info::<T>(&mut self.world.components, &mut self.world.storages);
|
||||||
let mut bundle_inserter = bundle_info.get_bundle_inserter(
|
let mut bundle_inserter = bundle_info.get_bundle_inserter(
|
||||||
&mut self.world.entities,
|
&mut self.world.entities,
|
||||||
&mut self.world.archetypes,
|
&mut self.world.archetypes,
|
||||||
|
@ -219,7 +219,7 @@ impl<'w> EntityMut<'w> {
|
||||||
let entities = &mut self.world.entities;
|
let entities = &mut self.world.entities;
|
||||||
let removed_components = &mut self.world.removed_components;
|
let removed_components = &mut self.world.removed_components;
|
||||||
|
|
||||||
let bundle_info = self.world.bundles.init_info::<T>(components);
|
let bundle_info = self.world.bundles.init_info::<T>(components, storages);
|
||||||
let old_location = self.location;
|
let old_location = self.location;
|
||||||
let new_archetype_id = unsafe {
|
let new_archetype_id = unsafe {
|
||||||
remove_bundle_from_archetype(
|
remove_bundle_from_archetype(
|
||||||
|
@ -341,7 +341,7 @@ impl<'w> EntityMut<'w> {
|
||||||
let entities = &mut self.world.entities;
|
let entities = &mut self.world.entities;
|
||||||
let removed_components = &mut self.world.removed_components;
|
let removed_components = &mut self.world.removed_components;
|
||||||
|
|
||||||
let bundle_info = self.world.bundles.init_info::<T>(components);
|
let bundle_info = self.world.bundles.init_info::<T>(components, storages);
|
||||||
let old_location = self.location;
|
let old_location = self.location;
|
||||||
let new_archetype_id = unsafe {
|
let new_archetype_id = unsafe {
|
||||||
remove_bundle_from_archetype(
|
remove_bundle_from_archetype(
|
||||||
|
|
|
@ -11,13 +11,11 @@ use crate::{
|
||||||
archetype::{ArchetypeComponentId, ArchetypeComponentInfo, ArchetypeId, Archetypes},
|
archetype::{ArchetypeComponentId, ArchetypeComponentInfo, ArchetypeId, Archetypes},
|
||||||
bundle::{Bundle, BundleInserter, BundleSpawner, Bundles},
|
bundle::{Bundle, BundleInserter, BundleSpawner, Bundles},
|
||||||
change_detection::Ticks,
|
change_detection::Ticks,
|
||||||
component::{
|
component::{Component, ComponentId, ComponentTicks, Components, StorageType},
|
||||||
Component, ComponentDescriptor, ComponentId, ComponentTicks, Components, ComponentsError,
|
|
||||||
StorageType,
|
|
||||||
},
|
|
||||||
entity::{AllocAtWithoutReplacement, Entities, Entity},
|
entity::{AllocAtWithoutReplacement, Entities, Entity},
|
||||||
query::{FilterFetch, QueryState, WorldQuery},
|
query::{FilterFetch, QueryState, WorldQuery},
|
||||||
storage::{Column, SparseSet, Storages},
|
storage::{Column, SparseSet, Storages},
|
||||||
|
system::Resource,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
|
@ -172,38 +170,8 @@ impl World {
|
||||||
WorldCell::new(self)
|
WorldCell::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers a new component using the given [ComponentDescriptor]. Components do not need to
|
pub fn init_component<T: Component>(&mut self) -> ComponentId {
|
||||||
/// be manually registered. This just provides a way to override default configuration.
|
self.components.init_component::<T>(&mut self.storages)
|
||||||
/// Attempting to register a component with a type that has already been used by [World]
|
|
||||||
/// will result in an error.
|
|
||||||
///
|
|
||||||
/// The default component storage type can be overridden like this:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use bevy_ecs::{component::{ComponentDescriptor, StorageType}, world::World};
|
|
||||||
///
|
|
||||||
/// struct Position {
|
|
||||||
/// x: f32,
|
|
||||||
/// y: f32,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// let mut world = World::new();
|
|
||||||
/// world.register_component(ComponentDescriptor::new::<Position>(StorageType::SparseSet)).unwrap();
|
|
||||||
/// ```
|
|
||||||
pub fn register_component(
|
|
||||||
&mut self,
|
|
||||||
descriptor: ComponentDescriptor,
|
|
||||||
) -> Result<ComponentId, ComponentsError> {
|
|
||||||
let storage_type = descriptor.storage_type();
|
|
||||||
let component_id = self.components.add(descriptor)?;
|
|
||||||
// ensure sparse set is created for SparseSet components
|
|
||||||
if storage_type == StorageType::SparseSet {
|
|
||||||
// SAFE: just created
|
|
||||||
let info = unsafe { self.components.get_info_unchecked(component_id) };
|
|
||||||
self.storages.sparse_sets.get_or_insert(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(component_id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves an [EntityRef] that exposes read-only operations for the given `entity`.
|
/// Retrieves an [EntityRef] that exposes read-only operations for the given `entity`.
|
||||||
|
@ -211,8 +179,9 @@ impl World {
|
||||||
/// to check for entity existence instead of implicitly panic-ing.
|
/// to check for entity existence instead of implicitly panic-ing.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::world::World;
|
/// use bevy_ecs::{component::Component, world::World};
|
||||||
///
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
/// struct Position {
|
/// struct Position {
|
||||||
/// x: f32,
|
/// x: f32,
|
||||||
/// y: f32,
|
/// y: f32,
|
||||||
|
@ -236,8 +205,9 @@ impl World {
|
||||||
/// to check for entity existence instead of implicitly panic-ing.
|
/// to check for entity existence instead of implicitly panic-ing.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::world::World;
|
/// use bevy_ecs::{component::Component, world::World};
|
||||||
///
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
/// struct Position {
|
/// struct Position {
|
||||||
/// x: f32,
|
/// x: f32,
|
||||||
/// y: f32,
|
/// y: f32,
|
||||||
|
@ -284,8 +254,9 @@ impl World {
|
||||||
/// to unwrap the [EntityRef] yourself.
|
/// to unwrap the [EntityRef] yourself.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::world::World;
|
/// use bevy_ecs::{component::Component, world::World};
|
||||||
///
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
/// struct Position {
|
/// struct Position {
|
||||||
/// x: f32,
|
/// x: f32,
|
||||||
/// y: f32,
|
/// y: f32,
|
||||||
|
@ -311,8 +282,9 @@ impl World {
|
||||||
/// to unwrap the [EntityMut] yourself.
|
/// to unwrap the [EntityMut] yourself.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::world::World;
|
/// use bevy_ecs::{component::Component, world::World};
|
||||||
///
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
/// struct Position {
|
/// struct Position {
|
||||||
/// x: f32,
|
/// x: f32,
|
||||||
/// y: f32,
|
/// y: f32,
|
||||||
|
@ -338,17 +310,22 @@ impl World {
|
||||||
/// to add components to the entity or retrieve its id.
|
/// to add components to the entity or retrieve its id.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::world::World;
|
/// use bevy_ecs::{component::Component, world::World};
|
||||||
///
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
/// struct Position {
|
/// struct Position {
|
||||||
/// x: f32,
|
/// x: f32,
|
||||||
/// y: f32,
|
/// y: f32,
|
||||||
/// }
|
/// }
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct Label(&'static str);
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct Num(u32);
|
||||||
///
|
///
|
||||||
/// let mut world = World::new();
|
/// let mut world = World::new();
|
||||||
/// let entity = world.spawn()
|
/// let entity = world.spawn()
|
||||||
/// .insert(Position { x: 0.0, y: 0.0 }) // add a single component
|
/// .insert(Position { x: 0.0, y: 0.0 }) // add a single component
|
||||||
/// .insert_bundle((1, 2.0, "hello")) // add a bundle of components
|
/// .insert_bundle((Num(1), Label("hello"))) // add a bundle of components
|
||||||
/// .id();
|
/// .id();
|
||||||
///
|
///
|
||||||
/// let position = world.entity(entity).get::<Position>().unwrap();
|
/// let position = world.entity(entity).get::<Position>().unwrap();
|
||||||
|
@ -385,12 +362,17 @@ impl World {
|
||||||
/// individually is more flexible.
|
/// individually is more flexible.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::{entity::Entity, world::World};
|
/// use bevy_ecs::{component::Component, entity::Entity, world::World};
|
||||||
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct Str(&'static str);
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct Num(u32);
|
||||||
///
|
///
|
||||||
/// let mut world = World::new();
|
/// let mut world = World::new();
|
||||||
/// let entities = world.spawn_batch(vec![
|
/// let entities = world.spawn_batch(vec![
|
||||||
/// ("a", 0.0), // the first entity
|
/// (Str("a"), Num(0)), // the first entity
|
||||||
/// ("b", 1.0), // the second entity
|
/// (Str("b"), Num(1)), // the second entity
|
||||||
/// ]).collect::<Vec<Entity>>();
|
/// ]).collect::<Vec<Entity>>();
|
||||||
///
|
///
|
||||||
/// assert_eq!(entities.len(), 2);
|
/// assert_eq!(entities.len(), 2);
|
||||||
|
@ -406,8 +388,9 @@ impl World {
|
||||||
/// Retrieves a reference to the given `entity`'s [Component] of the given type.
|
/// Retrieves a reference to the given `entity`'s [Component] of the given type.
|
||||||
/// Returns [None] if the `entity` does not have a [Component] of the given type.
|
/// Returns [None] if the `entity` does not have a [Component] of the given type.
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::world::World;
|
/// use bevy_ecs::{component::Component, world::World};
|
||||||
///
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
/// struct Position {
|
/// struct Position {
|
||||||
/// x: f32,
|
/// x: f32,
|
||||||
/// y: f32,
|
/// y: f32,
|
||||||
|
@ -428,8 +411,9 @@ impl World {
|
||||||
/// Retrieves a mutable reference to the given `entity`'s [Component] of the given type.
|
/// Retrieves a mutable reference to the given `entity`'s [Component] of the given type.
|
||||||
/// Returns [None] if the `entity` does not have a [Component] of the given type.
|
/// Returns [None] if the `entity` does not have a [Component] of the given type.
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::world::World;
|
/// use bevy_ecs::{component::Component, world::World};
|
||||||
///
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
/// struct Position {
|
/// struct Position {
|
||||||
/// x: f32,
|
/// x: f32,
|
||||||
/// y: f32,
|
/// y: f32,
|
||||||
|
@ -451,8 +435,9 @@ impl World {
|
||||||
/// [Component]s. Returns `true` if the `entity` is successfully despawned and `false` if
|
/// [Component]s. Returns `true` if the `entity` is successfully despawned and `false` if
|
||||||
/// the `entity` does not exist.
|
/// the `entity` does not exist.
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::world::World;
|
/// use bevy_ecs::{component::Component, world::World};
|
||||||
///
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
/// struct Position {
|
/// struct Position {
|
||||||
/// x: f32,
|
/// x: f32,
|
||||||
/// y: f32,
|
/// y: f32,
|
||||||
|
@ -488,14 +473,15 @@ impl World {
|
||||||
/// Returns [QueryState] for the given [WorldQuery], which is used to efficiently
|
/// Returns [QueryState] for the given [WorldQuery], which is used to efficiently
|
||||||
/// run queries on the [World] by storing and reusing the [QueryState].
|
/// run queries on the [World] by storing and reusing the [QueryState].
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::{entity::Entity, world::World};
|
/// use bevy_ecs::{component::Component, entity::Entity, world::World};
|
||||||
///
|
///
|
||||||
/// #[derive(Debug, PartialEq)]
|
/// #[derive(Component, Debug, PartialEq)]
|
||||||
/// struct Position {
|
/// struct Position {
|
||||||
/// x: f32,
|
/// x: f32,
|
||||||
/// y: f32,
|
/// y: f32,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
/// struct Velocity {
|
/// struct Velocity {
|
||||||
/// x: f32,
|
/// x: f32,
|
||||||
/// y: f32,
|
/// y: f32,
|
||||||
|
@ -523,18 +509,28 @@ impl World {
|
||||||
/// and allocation of a [Vec] to store it.
|
/// and allocation of a [Vec] to store it.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::{entity::Entity, world::World};
|
/// use bevy_ecs::{component::Component, entity::Entity, world::World};
|
||||||
|
///
|
||||||
|
/// #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||||
|
/// struct Order(i32);
|
||||||
|
/// #[derive(Component, PartialEq, Debug)]
|
||||||
|
/// struct Label(&'static str);
|
||||||
|
///
|
||||||
/// let mut world = World::new();
|
/// let mut world = World::new();
|
||||||
/// let a = world.spawn().insert_bundle((2, 4.0)).id();
|
/// let a = world.spawn().insert_bundle((Order(2), Label("second"))).id();
|
||||||
/// let b = world.spawn().insert_bundle((3, 5.0)).id();
|
/// let b = world.spawn().insert_bundle((Order(3), Label("third"))).id();
|
||||||
/// let c = world.spawn().insert_bundle((1, 6.0)).id();
|
/// let c = world.spawn().insert_bundle((Order(1), Label("first"))).id();
|
||||||
/// let mut entities = world.query::<(Entity, &i32, &f64)>()
|
/// let mut entities = world.query::<(Entity, &Order, &Label)>()
|
||||||
/// .iter(&world)
|
/// .iter(&world)
|
||||||
/// .collect::<Vec<_>>();
|
/// .collect::<Vec<_>>();
|
||||||
/// // Sort the query results by their `i32` component before comparing
|
/// // Sort the query results by their `Order` component before comparing
|
||||||
/// // to expected results. Query iteration order should not be relied on.
|
/// // to expected results. Query iteration order should not be relied on.
|
||||||
/// entities.sort_by_key(|e| e.1);
|
/// entities.sort_by_key(|e| e.1);
|
||||||
/// assert_eq!(entities, vec![(c, &1, &6.0), (a, &2, &4.0), (b, &3, &5.0)]);
|
/// assert_eq!(entities, vec![
|
||||||
|
/// (c, &Order(1), &Label("first")),
|
||||||
|
/// (a, &Order(2), &Label("second")),
|
||||||
|
/// (b, &Order(3), &Label("third")),
|
||||||
|
/// ]);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn query<Q: WorldQuery>(&mut self) -> QueryState<Q, ()> {
|
pub fn query<Q: WorldQuery>(&mut self) -> QueryState<Q, ()> {
|
||||||
|
@ -544,9 +540,11 @@ impl World {
|
||||||
/// Returns [QueryState] for the given filtered [WorldQuery], which is used to efficiently
|
/// Returns [QueryState] for the given filtered [WorldQuery], which is used to efficiently
|
||||||
/// run queries on the [World] by storing and reusing the [QueryState].
|
/// run queries on the [World] by storing and reusing the [QueryState].
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::{entity::Entity, world::World, query::With};
|
/// use bevy_ecs::{component::Component, entity::Entity, world::World, query::With};
|
||||||
///
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
/// struct A;
|
/// struct A;
|
||||||
|
/// #[derive(Component)]
|
||||||
/// struct B;
|
/// struct B;
|
||||||
///
|
///
|
||||||
/// let mut world = World::new();
|
/// let mut world = World::new();
|
||||||
|
@ -592,8 +590,8 @@ impl World {
|
||||||
/// Inserts a new resource with the given `value`.
|
/// Inserts a new resource with the given `value`.
|
||||||
/// Resources are "unique" data of a given type.
|
/// Resources are "unique" data of a given type.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert_resource<T: Component>(&mut self, value: T) {
|
pub fn insert_resource<T: Resource>(&mut self, value: T) {
|
||||||
let component_id = self.components.get_or_insert_resource_id::<T>();
|
let component_id = self.components.init_resource::<T>();
|
||||||
// SAFE: component_id just initialized and corresponds to resource of type T
|
// SAFE: component_id just initialized and corresponds to resource of type T
|
||||||
unsafe { self.insert_resource_with_id(component_id, value) };
|
unsafe { self.insert_resource_with_id(component_id, value) };
|
||||||
}
|
}
|
||||||
|
@ -603,7 +601,7 @@ impl World {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert_non_send<T: 'static>(&mut self, value: T) {
|
pub fn insert_non_send<T: 'static>(&mut self, value: T) {
|
||||||
self.validate_non_send_access::<T>();
|
self.validate_non_send_access::<T>();
|
||||||
let component_id = self.components.get_or_insert_non_send_resource_id::<T>();
|
let component_id = self.components.init_non_send::<T>();
|
||||||
// SAFE: component_id just initialized and corresponds to resource of type T
|
// SAFE: component_id just initialized and corresponds to resource of type T
|
||||||
unsafe { self.insert_resource_with_id(component_id, value) };
|
unsafe { self.insert_resource_with_id(component_id, value) };
|
||||||
}
|
}
|
||||||
|
@ -611,7 +609,7 @@ impl World {
|
||||||
/// Removes the resource of a given type and returns it, if it exists. Otherwise returns [None].
|
/// Removes the resource of a given type and returns it, if it exists. Otherwise returns [None].
|
||||||
/// Resources are "unique" data of a given type.
|
/// Resources are "unique" data of a given type.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn remove_resource<T: Component>(&mut self) -> Option<T> {
|
pub fn remove_resource<T: Resource>(&mut self) -> Option<T> {
|
||||||
// SAFE: T is Send + Sync
|
// SAFE: T is Send + Sync
|
||||||
unsafe { self.remove_resource_unchecked() }
|
unsafe { self.remove_resource_unchecked() }
|
||||||
}
|
}
|
||||||
|
@ -644,7 +642,7 @@ impl World {
|
||||||
|
|
||||||
/// Returns `true` if a resource of type `T` exists. Otherwise returns `false`.
|
/// Returns `true` if a resource of type `T` exists. Otherwise returns `false`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn contains_resource<T: Component>(&self) -> bool {
|
pub fn contains_resource<T: Resource>(&self) -> bool {
|
||||||
let component_id =
|
let component_id =
|
||||||
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) {
|
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) {
|
||||||
component_id
|
component_id
|
||||||
|
@ -657,12 +655,12 @@ impl World {
|
||||||
/// Gets a reference to the resource of the given type, if it exists. Otherwise returns [None]
|
/// Gets a reference to the resource of the given type, if it exists. Otherwise returns [None]
|
||||||
/// Resources are "unique" data of a given type.
|
/// Resources are "unique" data of a given type.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_resource<T: Component>(&self) -> Option<&T> {
|
pub fn get_resource<T: Resource>(&self) -> Option<&T> {
|
||||||
let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
|
let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
|
||||||
unsafe { self.get_resource_with_id(component_id) }
|
unsafe { self.get_resource_with_id(component_id) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_resource_added<T: Component>(&self) -> bool {
|
pub fn is_resource_added<T: Resource>(&self) -> bool {
|
||||||
let component_id =
|
let component_id =
|
||||||
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) {
|
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) {
|
||||||
component_id
|
component_id
|
||||||
|
@ -679,7 +677,7 @@ impl World {
|
||||||
ticks.is_added(self.last_change_tick(), self.read_change_tick())
|
ticks.is_added(self.last_change_tick(), self.read_change_tick())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_resource_changed<T: Component>(&self) -> bool {
|
pub fn is_resource_changed<T: Resource>(&self) -> bool {
|
||||||
let component_id =
|
let component_id =
|
||||||
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) {
|
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) {
|
||||||
component_id
|
component_id
|
||||||
|
@ -699,7 +697,7 @@ impl World {
|
||||||
/// Gets a mutable reference to the resource of the given type, if it exists. Otherwise returns
|
/// Gets a mutable reference to the resource of the given type, if it exists. Otherwise returns
|
||||||
/// [None] Resources are "unique" data of a given type.
|
/// [None] Resources are "unique" data of a given type.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_resource_mut<T: Component>(&mut self) -> Option<Mut<'_, T>> {
|
pub fn get_resource_mut<T: Resource>(&mut self) -> Option<Mut<'_, T>> {
|
||||||
// SAFE: unique world access
|
// SAFE: unique world access
|
||||||
unsafe { self.get_resource_unchecked_mut() }
|
unsafe { self.get_resource_unchecked_mut() }
|
||||||
}
|
}
|
||||||
|
@ -708,7 +706,7 @@ impl World {
|
||||||
/// Gets a resource of type `T` if it exists, otherwise inserts the resource using the result of
|
/// Gets a resource of type `T` if it exists, otherwise inserts the resource using the result of
|
||||||
/// calling `func`.
|
/// calling `func`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_resource_or_insert_with<T: Component>(
|
pub fn get_resource_or_insert_with<T: Resource>(
|
||||||
&mut self,
|
&mut self,
|
||||||
func: impl FnOnce() -> T,
|
func: impl FnOnce() -> T,
|
||||||
) -> Mut<'_, T> {
|
) -> Mut<'_, T> {
|
||||||
|
@ -725,7 +723,7 @@ impl World {
|
||||||
/// This will allow aliased mutable access to the given resource type. The caller must ensure
|
/// This will allow aliased mutable access to the given resource type. The caller must ensure
|
||||||
/// that only one mutable access exists at a time.
|
/// that only one mutable access exists at a time.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn get_resource_unchecked_mut<T: Component>(&self) -> Option<Mut<'_, T>> {
|
pub unsafe fn get_resource_unchecked_mut<T: Resource>(&self) -> Option<Mut<'_, T>> {
|
||||||
let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
|
let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
|
||||||
self.get_resource_unchecked_mut_with_id(component_id)
|
self.get_resource_unchecked_mut_with_id(component_id)
|
||||||
}
|
}
|
||||||
|
@ -772,17 +770,21 @@ impl World {
|
||||||
/// worked out to share an ID space (which doesn't happen by default).
|
/// worked out to share an ID space (which doesn't happen by default).
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::{entity::Entity, world::World};
|
/// use bevy_ecs::{entity::Entity, world::World, component::Component};
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct A(&'static str);
|
||||||
|
/// #[derive(Component, PartialEq, Debug)]
|
||||||
|
/// struct B(f32);
|
||||||
///
|
///
|
||||||
/// let mut world = World::new();
|
/// let mut world = World::new();
|
||||||
/// let e0 = world.spawn().id();
|
/// let e0 = world.spawn().id();
|
||||||
/// let e1 = world.spawn().id();
|
/// let e1 = world.spawn().id();
|
||||||
/// world.insert_or_spawn_batch(vec![
|
/// world.insert_or_spawn_batch(vec![
|
||||||
/// (e0, ("a", 0.0)), // the first entity
|
/// (e0, (A("a"), B(0.0))), // the first entity
|
||||||
/// (e1, ("b", 1.0)), // the second entity
|
/// (e1, (A("b"), B(1.0))), // the second entity
|
||||||
/// ]);
|
/// ]);
|
||||||
///
|
///
|
||||||
/// assert_eq!(world.get::<f64>(e0), Some(&0.0));
|
/// assert_eq!(world.get::<B>(e0), Some(&B(0.0)));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn insert_or_spawn_batch<I, B>(&mut self, iter: I) -> Result<(), Vec<Entity>>
|
pub fn insert_or_spawn_batch<I, B>(&mut self, iter: I) -> Result<(), Vec<Entity>>
|
||||||
where
|
where
|
||||||
|
@ -795,7 +797,9 @@ impl World {
|
||||||
let iter = iter.into_iter();
|
let iter = iter.into_iter();
|
||||||
let change_tick = *self.change_tick.get_mut();
|
let change_tick = *self.change_tick.get_mut();
|
||||||
|
|
||||||
let bundle_info = self.bundles.init_info::<B>(&mut self.components);
|
let bundle_info = self
|
||||||
|
.bundles
|
||||||
|
.init_info::<B>(&mut self.components, &mut self.storages);
|
||||||
enum SpawnOrInsert<'a, 'b> {
|
enum SpawnOrInsert<'a, 'b> {
|
||||||
Spawn(BundleSpawner<'a, 'b>),
|
Spawn(BundleSpawner<'a, 'b>),
|
||||||
Insert(BundleInserter<'a, 'b>, ArchetypeId),
|
Insert(BundleInserter<'a, 'b>, ArchetypeId),
|
||||||
|
@ -884,8 +888,10 @@ impl World {
|
||||||
/// returning. This enables safe mutable access to a resource while still providing mutable
|
/// returning. This enables safe mutable access to a resource while still providing mutable
|
||||||
/// world access
|
/// world access
|
||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::world::{World, Mut};
|
/// use bevy_ecs::{component::Component, world::{World, Mut}};
|
||||||
|
/// #[derive(Component)]
|
||||||
/// struct A(u32);
|
/// struct A(u32);
|
||||||
|
/// #[derive(Component)]
|
||||||
/// struct B(u32);
|
/// struct B(u32);
|
||||||
/// let mut world = World::new();
|
/// let mut world = World::new();
|
||||||
/// world.insert_resource(A(1));
|
/// world.insert_resource(A(1));
|
||||||
|
@ -897,10 +903,7 @@ impl World {
|
||||||
/// });
|
/// });
|
||||||
/// assert_eq!(world.get_resource::<A>().unwrap().0, 2);
|
/// assert_eq!(world.get_resource::<A>().unwrap().0, 2);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn resource_scope<T: Component, U>(
|
pub fn resource_scope<T: Resource, U>(&mut self, f: impl FnOnce(&mut World, Mut<T>) -> U) -> U {
|
||||||
&mut self,
|
|
||||||
f: impl FnOnce(&mut World, Mut<T>) -> U,
|
|
||||||
) -> U {
|
|
||||||
let component_id = self
|
let component_id = self
|
||||||
.components
|
.components
|
||||||
.get_resource_id(TypeId::of::<T>())
|
.get_resource_id(TypeId::of::<T>())
|
||||||
|
@ -1041,15 +1044,15 @@ impl World {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn initialize_resource<T: Component>(&mut self) -> ComponentId {
|
pub(crate) fn initialize_resource<T: Resource>(&mut self) -> ComponentId {
|
||||||
let component_id = self.components.get_or_insert_resource_id::<T>();
|
let component_id = self.components.init_resource::<T>();
|
||||||
// SAFE: resource initialized above
|
// SAFE: resource initialized above
|
||||||
unsafe { self.initialize_resource_internal(component_id) };
|
unsafe { self.initialize_resource_internal(component_id) };
|
||||||
component_id
|
component_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn initialize_non_send_resource<T: 'static>(&mut self) -> ComponentId {
|
pub(crate) fn initialize_non_send_resource<T: 'static>(&mut self) -> ComponentId {
|
||||||
let component_id = self.components.get_or_insert_non_send_resource_id::<T>();
|
let component_id = self.components.init_non_send::<T>();
|
||||||
// SAFE: resource initialized above
|
// SAFE: resource initialized above
|
||||||
unsafe { self.initialize_resource_internal(component_id) };
|
unsafe { self.initialize_resource_internal(component_id) };
|
||||||
component_id
|
component_id
|
||||||
|
|
|
@ -27,7 +27,9 @@ where
|
||||||
let (lower, upper) = iter.size_hint();
|
let (lower, upper) = iter.size_hint();
|
||||||
let length = upper.unwrap_or(lower);
|
let length = upper.unwrap_or(lower);
|
||||||
|
|
||||||
let bundle_info = world.bundles.init_info::<I::Item>(&mut world.components);
|
let bundle_info = world
|
||||||
|
.bundles
|
||||||
|
.init_info::<I::Item>(&mut world.components, &mut world.storages);
|
||||||
world.entities.reserve(length as u32);
|
world.entities.reserve(length as u32);
|
||||||
let mut spawner = bundle_info.get_bundle_spawner(
|
let mut spawner = bundle_info.get_bundle_spawner(
|
||||||
&mut world.entities,
|
&mut world.entities,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
archetype::ArchetypeComponentId,
|
archetype::ArchetypeComponentId,
|
||||||
component::Component,
|
|
||||||
storage::SparseSet,
|
storage::SparseSet,
|
||||||
|
system::Resource,
|
||||||
world::{Mut, World},
|
world::{Mut, World},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -182,7 +182,7 @@ impl<'w> WorldCell<'w> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_resource<T: Component>(&self) -> Option<WorldBorrow<'_, T>> {
|
pub fn get_resource<T: Resource>(&self) -> Option<WorldBorrow<'_, T>> {
|
||||||
let component_id = self.world.components.get_resource_id(TypeId::of::<T>())?;
|
let component_id = self.world.components.get_resource_id(TypeId::of::<T>())?;
|
||||||
let resource_archetype = self.world.archetypes.resource();
|
let resource_archetype = self.world.archetypes.resource();
|
||||||
let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
|
let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
|
||||||
|
@ -194,7 +194,7 @@ impl<'w> WorldCell<'w> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_resource_mut<T: Component>(&self) -> Option<WorldBorrowMut<'_, T>> {
|
pub fn get_resource_mut<T: Resource>(&self) -> Option<WorldBorrowMut<'_, T>> {
|
||||||
let component_id = self.world.components.get_resource_id(TypeId::of::<T>())?;
|
let component_id = self.world.components.get_resource_id(TypeId::of::<T>())?;
|
||||||
let resource_archetype = self.world.archetypes.resource();
|
let resource_archetype = self.world.archetypes.resource();
|
||||||
let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
|
let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
|
||||||
|
|
50
crates/bevy_macro_utils/src/attrs.rs
Normal file
50
crates/bevy_macro_utils/src/attrs.rs
Normal 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
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,11 @@
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
mod attrs;
|
||||||
|
mod symbol;
|
||||||
|
|
||||||
|
pub use attrs::*;
|
||||||
|
pub use symbol::*;
|
||||||
|
|
||||||
use cargo_manifest::{DepsSet, Manifest};
|
use cargo_manifest::{DepsSet, Manifest};
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use std::{env, path::PathBuf};
|
use std::{env, path::PathBuf};
|
||||||
|
|
35
crates/bevy_macro_utils/src/symbol.rs
Normal file
35
crates/bevy_macro_utils/src/symbol.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
use bevy_core::{Pod, Zeroable};
|
use bevy_core::{Pod, Zeroable};
|
||||||
use bevy_ecs::reflect::ReflectComponent;
|
use bevy_ecs::{component::Component, reflect::ReflectComponent};
|
||||||
use bevy_math::Vec3;
|
use bevy_math::Vec3;
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use bevy_render::color::Color;
|
use bevy_render::color::Color;
|
||||||
use bevy_transform::components::GlobalTransform;
|
use bevy_transform::components::GlobalTransform;
|
||||||
|
|
||||||
/// A point light
|
/// A point light
|
||||||
#[derive(Debug, Clone, Copy, Reflect)]
|
#[derive(Component, Debug, Clone, Copy, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct PointLight {
|
pub struct PointLight {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
|
@ -77,7 +77,7 @@ impl PointLightUniform {
|
||||||
/// | 32,000–100,000 | Direct sunlight |
|
/// | 32,000–100,000 | Direct sunlight |
|
||||||
///
|
///
|
||||||
/// Source: [Wikipedia](https://en.wikipedia.org/wiki/Lux)
|
/// Source: [Wikipedia](https://en.wikipedia.org/wiki/Lux)
|
||||||
#[derive(Debug, Clone, Copy, Reflect)]
|
#[derive(Component, Debug, Clone, Copy, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct DirectionalLight {
|
pub struct DirectionalLight {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
|
|
|
@ -2,12 +2,13 @@ use crate::renderer::RenderResourceBindings;
|
||||||
|
|
||||||
use super::Camera;
|
use super::Camera;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
system::{Query, ResMut},
|
system::{Query, ResMut},
|
||||||
};
|
};
|
||||||
use bevy_utils::HashMap;
|
use bevy_utils::HashMap;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Component, Debug, Default)]
|
||||||
pub struct ActiveCamera {
|
pub struct ActiveCamera {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub entity: Option<Entity>,
|
pub entity: Option<Entity>,
|
||||||
|
|
|
@ -15,7 +15,7 @@ use bevy_transform::components::GlobalTransform;
|
||||||
use bevy_window::{WindowCreated, WindowId, WindowResized, Windows};
|
use bevy_window::{WindowCreated, WindowId, WindowResized, Windows};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Default, Debug, Reflect)]
|
#[derive(Component, Default, Debug, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct Camera {
|
pub struct Camera {
|
||||||
pub projection_matrix: Mat4,
|
pub projection_matrix: Mat4,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::DepthCalculation;
|
use super::DepthCalculation;
|
||||||
use bevy_ecs::reflect::ReflectComponent;
|
use bevy_ecs::{component::Component, reflect::ReflectComponent};
|
||||||
use bevy_math::Mat4;
|
use bevy_math::Mat4;
|
||||||
use bevy_reflect::{Reflect, ReflectDeserialize};
|
use bevy_reflect::{Reflect, ReflectDeserialize};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -10,7 +10,7 @@ pub trait CameraProjection {
|
||||||
fn depth_calculation(&self) -> DepthCalculation;
|
fn depth_calculation(&self) -> DepthCalculation;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Reflect)]
|
#[derive(Component, Debug, Clone, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct PerspectiveProjection {
|
pub struct PerspectiveProjection {
|
||||||
pub fov: f32,
|
pub fov: f32,
|
||||||
|
@ -66,7 +66,7 @@ pub enum ScalingMode {
|
||||||
FixedHorizontal,
|
FixedHorizontal,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Reflect)]
|
#[derive(Component, Debug, Clone, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct OrthographicProjection {
|
pub struct OrthographicProjection {
|
||||||
pub left: f32,
|
pub left: f32,
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use super::{Camera, DepthCalculation};
|
use super::{Camera, DepthCalculation};
|
||||||
use crate::{draw::OutsideFrustum, prelude::Visible};
|
use crate::{draw::OutsideFrustum, prelude::Visible};
|
||||||
use bevy_core::FloatOrd;
|
use bevy_core::FloatOrd;
|
||||||
use bevy_ecs::{entity::Entity, query::Without, reflect::ReflectComponent, system::Query};
|
use bevy_ecs::{
|
||||||
|
component::Component, entity::Entity, query::Without, reflect::ReflectComponent, system::Query,
|
||||||
|
};
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use bevy_transform::prelude::GlobalTransform;
|
use bevy_transform::prelude::GlobalTransform;
|
||||||
|
|
||||||
|
@ -11,7 +13,7 @@ pub struct VisibleEntity {
|
||||||
pub order: FloatOrd,
|
pub order: FloatOrd,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Reflect)]
|
#[derive(Component, Default, Debug, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct VisibleEntities {
|
pub struct VisibleEntities {
|
||||||
#[reflect(ignore)]
|
#[reflect(ignore)]
|
||||||
|
@ -42,7 +44,7 @@ pub type Layer = u8;
|
||||||
/// An entity with this component without any layers is invisible.
|
/// An entity with this component without any layers is invisible.
|
||||||
///
|
///
|
||||||
/// Entities without this component belong to layer `0`.
|
/// Entities without this component belong to layer `0`.
|
||||||
#[derive(Copy, Clone, Reflect, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Component, Copy, Clone, Reflect, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
#[reflect(Component, PartialEq)]
|
#[reflect(Component, PartialEq)]
|
||||||
pub struct RenderLayers(LayerMask);
|
pub struct RenderLayers(LayerMask);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use bevy_asset::{Asset, Assets, Handle};
|
use bevy_asset::{Asset, Assets, Handle};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
reflect::ReflectComponent,
|
reflect::ReflectComponent,
|
||||||
system::{Query, Res, ResMut, SystemParam},
|
system::{Query, Res, ResMut, SystemParam},
|
||||||
};
|
};
|
||||||
|
@ -49,7 +50,7 @@ pub enum RenderCommand {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Reflect)]
|
#[derive(Component, Debug, Clone, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct Visible {
|
pub struct Visible {
|
||||||
pub is_visible: bool,
|
pub is_visible: bool,
|
||||||
|
@ -73,12 +74,13 @@ impl Default for Visible {
|
||||||
/// This does not handle multiple "views" properly as it is a "global" filter.
|
/// This does not handle multiple "views" properly as it is a "global" filter.
|
||||||
/// This will be resolved in the future. For now, disable frustum culling if you
|
/// This will be resolved in the future. For now, disable frustum culling if you
|
||||||
/// need to support multiple views (ex: set the `SpriteSettings::frustum_culling_enabled` resource).
|
/// need to support multiple views (ex: set the `SpriteSettings::frustum_culling_enabled` resource).
|
||||||
#[derive(Debug, Default, Clone, Reflect)]
|
#[derive(Component, Debug, Default, Clone, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
|
#[component(storage = "SparseSet")]
|
||||||
pub struct OutsideFrustum;
|
pub struct OutsideFrustum;
|
||||||
|
|
||||||
/// A component that indicates how to draw an entity.
|
/// A component that indicates how to draw an entity.
|
||||||
#[derive(Debug, Clone, Reflect)]
|
#[derive(Component, Debug, Clone, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct Draw {
|
pub struct Draw {
|
||||||
#[reflect(ignore)]
|
#[reflect(ignore)]
|
||||||
|
|
|
@ -7,6 +7,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use bevy_asset::{Assets, Handle};
|
use bevy_asset::{Assets, Handle};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
query::Without,
|
query::Without,
|
||||||
reflect::ReflectComponent,
|
reflect::ReflectComponent,
|
||||||
system::{Query, Res, ResMut},
|
system::{Query, Res, ResMut},
|
||||||
|
@ -44,7 +45,7 @@ impl RenderPipeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Reflect)]
|
#[derive(Component, Debug, Clone, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct RenderPipelines {
|
pub struct RenderPipelines {
|
||||||
pub pipelines: Vec<RenderPipeline>,
|
pub pipelines: Vec<RenderPipeline>,
|
||||||
|
|
|
@ -10,12 +10,12 @@ use crate::{
|
||||||
texture::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage},
|
texture::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage},
|
||||||
Color,
|
Color,
|
||||||
};
|
};
|
||||||
use bevy_ecs::{reflect::ReflectComponent, world::World};
|
use bevy_ecs::{component::Component, reflect::ReflectComponent, world::World};
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use bevy_window::WindowId;
|
use bevy_window::WindowId;
|
||||||
|
|
||||||
/// A component that indicates that an entity should be drawn in the "main pass"
|
/// A component that indicates that an entity should be drawn in the "main pass"
|
||||||
#[derive(Clone, Debug, Default, Reflect)]
|
#[derive(Component, Clone, Debug, Default, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct MainPass;
|
pub struct MainPass;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ use crate::{
|
||||||
use bevy_app::EventReader;
|
use bevy_app::EventReader;
|
||||||
use bevy_asset::{Asset, AssetEvent, Assets, Handle, HandleId};
|
use bevy_asset::{Asset, AssetEvent, Assets, Handle, HandleId};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
prelude::QueryState,
|
prelude::QueryState,
|
||||||
query::{Changed, Or, With},
|
query::{Changed, Or, With},
|
||||||
|
@ -398,7 +399,7 @@ where
|
||||||
|
|
||||||
impl<T> SystemNode for RenderResourcesNode<T>
|
impl<T> SystemNode for RenderResourcesNode<T>
|
||||||
where
|
where
|
||||||
T: renderer::RenderResources,
|
T: renderer::RenderResources + Component,
|
||||||
{
|
{
|
||||||
fn get_system(&self) -> BoxedSystem {
|
fn get_system(&self) -> BoxedSystem {
|
||||||
let system = render_resources_node_system::<T>.config(|config| {
|
let system = render_resources_node_system::<T>.config(|config| {
|
||||||
|
@ -430,7 +431,7 @@ impl<I, T: RenderResources> Default for RenderResourcesNodeState<I, T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
fn render_resources_node_system<T: RenderResources>(
|
fn render_resources_node_system<T: RenderResources + Component>(
|
||||||
mut state: Local<RenderResourcesNodeState<Entity, T>>,
|
mut state: Local<RenderResourcesNodeState<Entity, T>>,
|
||||||
mut entities_waiting_for_textures: Local<Vec<Entity>>,
|
mut entities_waiting_for_textures: Local<Vec<Entity>>,
|
||||||
render_resource_context: Res<Box<dyn RenderResourceContext>>,
|
render_resource_context: Res<Box<dyn RenderResourceContext>>,
|
||||||
|
|
|
@ -3,6 +3,7 @@ use bevy_asset::{Asset, Assets, Handle};
|
||||||
use crate::{draw::OutsideFrustum, pipeline::RenderPipelines, Texture};
|
use crate::{draw::OutsideFrustum, pipeline::RenderPipelines, Texture};
|
||||||
pub use bevy_derive::ShaderDefs;
|
pub use bevy_derive::ShaderDefs;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
prelude::Component,
|
||||||
query::Without,
|
query::Without,
|
||||||
system::{Query, Res},
|
system::{Query, Res},
|
||||||
};
|
};
|
||||||
|
@ -70,7 +71,7 @@ impl ShaderDef for Option<Handle<Texture>> {
|
||||||
/// Updates [RenderPipelines] with the latest [ShaderDefs]
|
/// Updates [RenderPipelines] with the latest [ShaderDefs]
|
||||||
pub fn shader_defs_system<T>(mut query: Query<(&T, &mut RenderPipelines), Without<OutsideFrustum>>)
|
pub fn shader_defs_system<T>(mut query: Query<(&T, &mut RenderPipelines), Without<OutsideFrustum>>)
|
||||||
where
|
where
|
||||||
T: ShaderDefs + Send + Sync + 'static,
|
T: ShaderDefs + Component,
|
||||||
{
|
{
|
||||||
for (shader_defs, mut render_pipelines) in query.iter_mut() {
|
for (shader_defs, mut render_pipelines) in query.iter_mut() {
|
||||||
for shader_def in shader_defs.iter_shader_defs() {
|
for shader_def in shader_defs.iter_shader_defs() {
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::{
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
use bevy_asset::{Assets, Handle, HandleUntyped};
|
use bevy_asset::{Assets, Handle, HandleUntyped};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
query::{QueryState, With},
|
query::{QueryState, With},
|
||||||
reflect::ReflectComponent,
|
reflect::ReflectComponent,
|
||||||
system::{QuerySet, Res},
|
system::{QuerySet, Res},
|
||||||
|
@ -40,7 +41,7 @@ impl Plugin for WireframePlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Reflect, Default)]
|
#[derive(Component, Debug, Clone, Reflect, Default)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct Wireframe;
|
pub struct Wireframe;
|
||||||
|
|
||||||
|
|
|
@ -28,11 +28,9 @@ pub use texture_atlas_builder::*;
|
||||||
|
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
use bevy_asset::{AddAsset, Assets, Handle, HandleUntyped};
|
use bevy_asset::{AddAsset, Assets, Handle, HandleUntyped};
|
||||||
use bevy_ecs::component::{ComponentDescriptor, StorageType};
|
|
||||||
use bevy_math::Vec2;
|
use bevy_math::Vec2;
|
||||||
use bevy_reflect::TypeUuid;
|
use bevy_reflect::TypeUuid;
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
draw::OutsideFrustum,
|
|
||||||
mesh::{shape, Mesh},
|
mesh::{shape, Mesh},
|
||||||
pipeline::PipelineDescriptor,
|
pipeline::PipelineDescriptor,
|
||||||
render_graph::RenderGraph,
|
render_graph::RenderGraph,
|
||||||
|
@ -90,12 +88,6 @@ impl Plugin for SpritePlugin {
|
||||||
frustum_culling::atlas_frustum_culling_system,
|
frustum_culling::atlas_frustum_culling_system,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
app.world
|
|
||||||
.register_component(ComponentDescriptor::new::<OutsideFrustum>(
|
|
||||||
StorageType::SparseSet,
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let world_cell = app.world.cell();
|
let world_cell = app.world.cell();
|
||||||
let mut render_graph = world_cell.get_resource_mut::<RenderGraph>().unwrap();
|
let mut render_graph = world_cell.get_resource_mut::<RenderGraph>().unwrap();
|
||||||
let mut pipelines = world_cell
|
let mut pipelines = world_cell
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::ColorMaterial;
|
||||||
use bevy_asset::{Assets, Handle};
|
use bevy_asset::{Assets, Handle};
|
||||||
use bevy_core::Bytes;
|
use bevy_core::Bytes;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
query::Without,
|
query::Without,
|
||||||
system::{Query, Res},
|
system::{Query, Res},
|
||||||
};
|
};
|
||||||
|
@ -15,7 +16,7 @@ use bevy_render::{
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// General Sprite Examples: [Link](https://github.com/bevyengine/bevy/tree/latest/examples/2d)
|
/// General Sprite Examples: [Link](https://github.com/bevyengine/bevy/tree/latest/examples/2d)
|
||||||
#[derive(Debug, Default, Clone, TypeUuid, Reflect, RenderResources)]
|
#[derive(Component, Debug, Default, Clone, TypeUuid, Reflect, RenderResources)]
|
||||||
#[render_resources(from_self)]
|
#[render_resources(from_self)]
|
||||||
#[uuid = "7233c597-ccfa-411f-bd59-9af349432ada"]
|
#[uuid = "7233c597-ccfa-411f-bd59-9af349432ada"]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::Rect;
|
use crate::Rect;
|
||||||
use bevy_asset::Handle;
|
use bevy_asset::Handle;
|
||||||
use bevy_core::Bytes;
|
use bevy_core::Bytes;
|
||||||
|
use bevy_ecs::component::Component;
|
||||||
use bevy_math::Vec2;
|
use bevy_math::Vec2;
|
||||||
use bevy_reflect::TypeUuid;
|
use bevy_reflect::TypeUuid;
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
|
@ -27,7 +28,7 @@ pub struct TextureAtlas {
|
||||||
pub texture_handles: Option<HashMap<Handle<Texture>, usize>>,
|
pub texture_handles: Option<HashMap<Handle<Texture>, usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, RenderResources)]
|
#[derive(Component, Debug, Clone, RenderResources)]
|
||||||
#[render_resources(from_self)]
|
#[render_resources(from_self)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct TextureAtlasSprite {
|
pub struct TextureAtlasSprite {
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use bevy_asset::Handle;
|
use bevy_asset::Handle;
|
||||||
|
use bevy_ecs::component::Component;
|
||||||
use bevy_math::Size;
|
use bevy_math::Size;
|
||||||
use bevy_render::color::Color;
|
use bevy_render::color::Color;
|
||||||
use glyph_brush_layout::{HorizontalAlign, VerticalAlign};
|
use glyph_brush_layout::{HorizontalAlign, VerticalAlign};
|
||||||
|
|
||||||
use crate::Font;
|
use crate::Font;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Component, Debug, Default, Clone)]
|
||||||
pub struct Text {
|
pub struct Text {
|
||||||
pub sections: Vec<TextSection>,
|
pub sections: Vec<TextSection>,
|
||||||
pub alignment: TextAlignment,
|
pub alignment: TextAlignment,
|
||||||
|
@ -101,7 +102,7 @@ impl Default for TextStyle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone, Debug)]
|
#[derive(Component, Default, Copy, Clone, Debug)]
|
||||||
pub struct Text2dSize {
|
pub struct Text2dSize {
|
||||||
pub size: Size,
|
pub size: Size,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
entity::{Entity, EntityMap, MapEntities, MapEntitiesError},
|
entity::{Entity, EntityMap, MapEntities, MapEntitiesError},
|
||||||
reflect::{ReflectComponent, ReflectMapEntities},
|
reflect::{ReflectComponent, ReflectMapEntities},
|
||||||
};
|
};
|
||||||
|
@ -6,7 +7,7 @@ use bevy_reflect::Reflect;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
#[derive(Default, Clone, Debug, Reflect)]
|
#[derive(Component, Default, Clone, Debug, Reflect)]
|
||||||
#[reflect(Component, MapEntities)]
|
#[reflect(Component, MapEntities)]
|
||||||
pub struct Children(pub(crate) SmallVec<[Entity; 8]>);
|
pub struct Children(pub(crate) SmallVec<[Entity; 8]>);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::Transform;
|
use super::Transform;
|
||||||
use bevy_ecs::reflect::ReflectComponent;
|
use bevy_ecs::{component::Component, reflect::ReflectComponent};
|
||||||
use bevy_math::{Mat3, Mat4, Quat, Vec3};
|
use bevy_math::{Mat3, Mat4, Quat, Vec3};
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use std::ops::Mul;
|
use std::ops::Mul;
|
||||||
|
@ -33,7 +33,7 @@ use std::ops::Mul;
|
||||||
/// This system runs in stage [`CoreStage::PostUpdate`](crate::CoreStage::PostUpdate). If you
|
/// This system runs in stage [`CoreStage::PostUpdate`](crate::CoreStage::PostUpdate). If you
|
||||||
/// update the[`Transform`] of an entity in this stage or after, you will notice a 1 frame lag
|
/// update the[`Transform`] of an entity in this stage or after, you will notice a 1 frame lag
|
||||||
/// before the [`GlobalTransform`] is updated.
|
/// before the [`GlobalTransform`] is updated.
|
||||||
#[derive(Debug, PartialEq, Clone, Copy, Reflect)]
|
#[derive(Component, Debug, PartialEq, Clone, Copy, Reflect)]
|
||||||
#[reflect(Component, PartialEq)]
|
#[reflect(Component, PartialEq)]
|
||||||
pub struct GlobalTransform {
|
pub struct GlobalTransform {
|
||||||
pub translation: Vec3,
|
pub translation: Vec3,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
entity::{Entity, EntityMap, MapEntities, MapEntitiesError},
|
entity::{Entity, EntityMap, MapEntities, MapEntitiesError},
|
||||||
reflect::{ReflectComponent, ReflectMapEntities},
|
reflect::{ReflectComponent, ReflectMapEntities},
|
||||||
world::{FromWorld, World},
|
world::{FromWorld, World},
|
||||||
|
@ -6,7 +7,7 @@ use bevy_ecs::{
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Reflect)]
|
#[derive(Component, Debug, Copy, Clone, Eq, PartialEq, Reflect)]
|
||||||
#[reflect(Component, MapEntities, PartialEq)]
|
#[reflect(Component, MapEntities, PartialEq)]
|
||||||
pub struct Parent(pub Entity);
|
pub struct Parent(pub Entity);
|
||||||
|
|
||||||
|
@ -41,7 +42,7 @@ impl DerefMut for Parent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Reflect)]
|
#[derive(Component, Debug, Copy, Clone, Eq, PartialEq, Reflect)]
|
||||||
#[reflect(Component, MapEntities, PartialEq)]
|
#[reflect(Component, MapEntities, PartialEq)]
|
||||||
pub struct PreviousParent(pub(crate) Entity);
|
pub struct PreviousParent(pub(crate) Entity);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::GlobalTransform;
|
use super::GlobalTransform;
|
||||||
use bevy_ecs::reflect::ReflectComponent;
|
use bevy_ecs::{component::Component, reflect::ReflectComponent};
|
||||||
use bevy_math::{Mat3, Mat4, Quat, Vec3};
|
use bevy_math::{Mat3, Mat4, Quat, Vec3};
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use std::ops::Mul;
|
use std::ops::Mul;
|
||||||
|
@ -34,7 +34,7 @@ use std::ops::Mul;
|
||||||
/// This system runs in stage [`CoreStage::PostUpdate`](crate::CoreStage::PostUpdate). If you
|
/// This system runs in stage [`CoreStage::PostUpdate`](crate::CoreStage::PostUpdate). If you
|
||||||
/// update the[`Transform`] of an entity in this stage or after, you will notice a 1 frame lag
|
/// update the[`Transform`] of an entity in this stage or after, you will notice a 1 frame lag
|
||||||
/// before the [`GlobalTransform`] is updated.
|
/// before the [`GlobalTransform`] is updated.
|
||||||
#[derive(Debug, PartialEq, Clone, Copy, Reflect)]
|
#[derive(Component, Debug, PartialEq, Clone, Copy, Reflect)]
|
||||||
#[reflect(Component, PartialEq)]
|
#[reflect(Component, PartialEq)]
|
||||||
pub struct Transform {
|
pub struct Transform {
|
||||||
/// Position of the entity. In 2d, the last value of the `Vec3` is used for z-ordering.
|
/// Position of the entity. In 2d, the last value of the `Vec3` is used for z-ordering.
|
||||||
|
|
|
@ -326,12 +326,16 @@ mod tests {
|
||||||
use super::{BuildChildren, BuildWorldChildren};
|
use super::{BuildChildren, BuildWorldChildren};
|
||||||
use crate::prelude::{Children, Parent, PreviousParent};
|
use crate::prelude::{Children, Parent, PreviousParent};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
system::{CommandQueue, Commands},
|
system::{CommandQueue, Commands},
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct C(u32);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn build_children() {
|
fn build_children() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
@ -339,11 +343,11 @@ mod tests {
|
||||||
let mut commands = Commands::new(&mut queue, &world);
|
let mut commands = Commands::new(&mut queue, &world);
|
||||||
|
|
||||||
let mut children = Vec::new();
|
let mut children = Vec::new();
|
||||||
let parent = commands.spawn().insert(1).id();
|
let parent = commands.spawn().insert(C(1)).id();
|
||||||
commands.entity(parent).with_children(|parent| {
|
commands.entity(parent).with_children(|parent| {
|
||||||
children.push(parent.spawn().insert(2).id());
|
children.push(parent.spawn().insert(C(2)).id());
|
||||||
children.push(parent.spawn().insert(3).id());
|
children.push(parent.spawn().insert(C(3)).id());
|
||||||
children.push(parent.spawn().insert(4).id());
|
children.push(parent.spawn().insert(C(4)).id());
|
||||||
});
|
});
|
||||||
|
|
||||||
queue.apply(&mut world);
|
queue.apply(&mut world);
|
||||||
|
@ -369,7 +373,7 @@ mod tests {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
|
||||||
let entities = world
|
let entities = world
|
||||||
.spawn_batch(vec![(1,), (2,), (3,), (4,), (5,)])
|
.spawn_batch(vec![(C(1),), (C(2),), (C(3),), (C(4),), (C(5),)])
|
||||||
.collect::<Vec<Entity>>();
|
.collect::<Vec<Entity>>();
|
||||||
|
|
||||||
let mut queue = CommandQueue::default();
|
let mut queue = CommandQueue::default();
|
||||||
|
@ -430,7 +434,7 @@ mod tests {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
|
||||||
let entities = world
|
let entities = world
|
||||||
.spawn_batch(vec![(1,), (2,), (3,), (4,), (5,)])
|
.spawn_batch(vec![(C(1),), (C(2),), (C(3),), (C(4),), (C(5),)])
|
||||||
.collect::<Vec<Entity>>();
|
.collect::<Vec<Entity>>();
|
||||||
|
|
||||||
world.entity_mut(entities[0]).push_children(&entities[1..3]);
|
world.entity_mut(entities[0]).push_children(&entities[1..3]);
|
||||||
|
|
|
@ -70,6 +70,7 @@ impl<'w> DespawnRecursiveExt for EntityMut<'w> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
system::{CommandQueue, Commands},
|
system::{CommandQueue, Commands},
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
|
@ -77,6 +78,12 @@ mod tests {
|
||||||
use super::DespawnRecursiveExt;
|
use super::DespawnRecursiveExt;
|
||||||
use crate::{components::Children, hierarchy::BuildChildren};
|
use crate::{components::Children, hierarchy::BuildChildren};
|
||||||
|
|
||||||
|
#[derive(Component, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Debug)]
|
||||||
|
struct Idx(u32);
|
||||||
|
|
||||||
|
#[derive(Component, Clone, PartialEq, Eq, Ord, PartialOrd, Debug)]
|
||||||
|
struct N(String);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn despawn_recursive() {
|
fn despawn_recursive() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
@ -86,33 +93,35 @@ mod tests {
|
||||||
let mut commands = Commands::new(&mut queue, &world);
|
let mut commands = Commands::new(&mut queue, &world);
|
||||||
|
|
||||||
commands
|
commands
|
||||||
.spawn_bundle(("Another parent".to_owned(), 0u32))
|
.spawn_bundle((N("Another parent".to_owned()), Idx(0)))
|
||||||
.with_children(|parent| {
|
.with_children(|parent| {
|
||||||
parent.spawn_bundle(("Another child".to_owned(), 1u32));
|
parent.spawn_bundle((N("Another child".to_owned()), Idx(1)));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a grandparent entity which will _not_ be deleted
|
// Create a grandparent entity which will _not_ be deleted
|
||||||
grandparent_entity = commands.spawn_bundle(("Grandparent".to_owned(), 2u32)).id();
|
grandparent_entity = commands
|
||||||
|
.spawn_bundle((N("Grandparent".to_owned()), Idx(2)))
|
||||||
|
.id();
|
||||||
commands.entity(grandparent_entity).with_children(|parent| {
|
commands.entity(grandparent_entity).with_children(|parent| {
|
||||||
// Add a child to the grandparent (the "parent"), which will get deleted
|
// Add a child to the grandparent (the "parent"), which will get deleted
|
||||||
parent
|
parent
|
||||||
.spawn_bundle(("Parent, to be deleted".to_owned(), 3u32))
|
.spawn_bundle((N("Parent, to be deleted".to_owned()), Idx(3)))
|
||||||
// All descendents of the "parent" should also be deleted.
|
// All descendents of the "parent" should also be deleted.
|
||||||
.with_children(|parent| {
|
.with_children(|parent| {
|
||||||
parent
|
parent
|
||||||
.spawn_bundle(("First Child, to be deleted".to_owned(), 4u32))
|
.spawn_bundle((N("First Child, to be deleted".to_owned()), Idx(4)))
|
||||||
.with_children(|parent| {
|
.with_children(|parent| {
|
||||||
// child
|
// child
|
||||||
parent.spawn_bundle((
|
parent.spawn_bundle((
|
||||||
"First grand child, to be deleted".to_owned(),
|
N("First grand child, to be deleted".to_owned()),
|
||||||
5u32,
|
Idx(5),
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
parent.spawn_bundle(("Second child, to be deleted".to_owned(), 6u32));
|
parent.spawn_bundle((N("Second child, to be deleted".to_owned()), Idx(6)));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
commands.spawn_bundle(("An innocent bystander".to_owned(), 7u32));
|
commands.spawn_bundle((N("An innocent bystander".to_owned()), Idx(7)));
|
||||||
}
|
}
|
||||||
queue.apply(&mut world);
|
queue.apply(&mut world);
|
||||||
|
|
||||||
|
@ -127,7 +136,7 @@ mod tests {
|
||||||
queue.apply(&mut world);
|
queue.apply(&mut world);
|
||||||
|
|
||||||
let mut results = world
|
let mut results = world
|
||||||
.query::<(&String, &u32)>()
|
.query::<(&N, &Idx)>()
|
||||||
.iter(&world)
|
.iter(&world)
|
||||||
.map(|(a, b)| (a.clone(), *b))
|
.map(|(a, b)| (a.clone(), *b))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -144,10 +153,10 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
results,
|
results,
|
||||||
vec![
|
vec![
|
||||||
("Another parent".to_owned(), 0u32),
|
(N("Another parent".to_owned()), Idx(0)),
|
||||||
("Another child".to_owned(), 1u32),
|
(N("Another child".to_owned()), Idx(1)),
|
||||||
("Grandparent".to_owned(), 2u32),
|
(N("Grandparent".to_owned()), Idx(2)),
|
||||||
("An innocent bystander".to_owned(), 7u32)
|
(N("An innocent bystander".to_owned()), Idx(7))
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::Node;
|
use crate::Node;
|
||||||
use bevy_core::FloatOrd;
|
use bevy_core::FloatOrd;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
system::{Local, Query, Res},
|
system::{Local, Query, Res},
|
||||||
};
|
};
|
||||||
|
@ -9,7 +10,7 @@ use bevy_transform::components::GlobalTransform;
|
||||||
use bevy_window::Windows;
|
use bevy_window::Windows;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
#[derive(Component, Copy, Clone, Eq, PartialEq, Debug)]
|
||||||
pub enum Interaction {
|
pub enum Interaction {
|
||||||
Clicked,
|
Clicked,
|
||||||
Hovered,
|
Hovered,
|
||||||
|
@ -22,7 +23,7 @@ impl Default for Interaction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
#[derive(Component, Copy, Clone, Eq, PartialEq, Debug)]
|
||||||
pub enum FocusPolicy {
|
pub enum FocusPolicy {
|
||||||
Block,
|
Block,
|
||||||
Pass,
|
Pass,
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use bevy_ecs::reflect::ReflectComponent;
|
use bevy_ecs::{component::Component, reflect::ReflectComponent};
|
||||||
use bevy_math::{Rect, Size, Vec2};
|
use bevy_math::{Rect, Size, Vec2};
|
||||||
use bevy_reflect::{Reflect, ReflectDeserialize};
|
use bevy_reflect::{Reflect, ReflectDeserialize};
|
||||||
use bevy_render::renderer::RenderResources;
|
use bevy_render::renderer::RenderResources;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::ops::{Add, AddAssign};
|
use std::ops::{Add, AddAssign};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, RenderResources, Reflect)]
|
#[derive(Component, Debug, Clone, Default, RenderResources, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
pub size: Vec2,
|
pub size: Vec2,
|
||||||
|
@ -49,7 +49,7 @@ impl AddAssign<f32> for Val {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug, Reflect)]
|
#[derive(Component, Clone, PartialEq, Debug, Reflect)]
|
||||||
#[reflect(Component, PartialEq)]
|
#[reflect(Component, PartialEq)]
|
||||||
pub struct Style {
|
pub struct Style {
|
||||||
pub display: Display,
|
pub display: Display,
|
||||||
|
@ -251,7 +251,7 @@ impl Default for FlexWrap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone, Debug)]
|
#[derive(Component, Default, Copy, Clone, Debug)]
|
||||||
pub struct CalculatedSize {
|
pub struct CalculatedSize {
|
||||||
pub size: Size,
|
pub size: Size,
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ fn update_hierarchy(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
schedule::{Schedule, Stage, SystemStage},
|
schedule::{Schedule, Stage, SystemStage},
|
||||||
system::{CommandQueue, Commands},
|
system::{CommandQueue, Commands},
|
||||||
world::World,
|
world::World,
|
||||||
|
@ -63,12 +64,15 @@ mod tests {
|
||||||
|
|
||||||
use super::{ui_z_system, UI_Z_STEP};
|
use super::{ui_z_system, UI_Z_STEP};
|
||||||
|
|
||||||
fn node_with_transform(name: &str) -> (String, Node, Transform) {
|
#[derive(Component, PartialEq, Debug, Clone)]
|
||||||
(name.to_owned(), Node::default(), Transform::identity())
|
struct Label(&'static str);
|
||||||
|
|
||||||
|
fn node_with_transform(name: &'static str) -> (Label, Node, Transform) {
|
||||||
|
(Label(name), Node::default(), Transform::identity())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_without_transform(name: &str) -> (String, Node) {
|
fn node_without_transform(name: &'static str) -> (Label, Node) {
|
||||||
(name.to_owned(), Node::default())
|
(Label(name), Node::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_steps(transform: &Transform) -> u32 {
|
fn get_steps(transform: &Transform) -> u32 {
|
||||||
|
@ -127,29 +131,29 @@ mod tests {
|
||||||
schedule.run(&mut world);
|
schedule.run(&mut world);
|
||||||
|
|
||||||
let mut actual_result = world
|
let mut actual_result = world
|
||||||
.query::<(&String, &Transform)>()
|
.query::<(&Label, &Transform)>()
|
||||||
.iter(&world)
|
.iter(&world)
|
||||||
.map(|(name, transform)| (name.clone(), get_steps(transform)))
|
.map(|(name, transform)| (name.clone(), get_steps(transform)))
|
||||||
.collect::<Vec<(String, u32)>>();
|
.collect::<Vec<(Label, u32)>>();
|
||||||
actual_result.sort_unstable_by_key(|(name, _)| name.clone());
|
actual_result.sort_unstable_by_key(|(name, _)| name.0);
|
||||||
let expected_result = vec![
|
let expected_result = vec![
|
||||||
("0".to_owned(), 1),
|
(Label("0"), 1),
|
||||||
("1".to_owned(), 1),
|
(Label("1"), 1),
|
||||||
("1-0".to_owned(), 1),
|
(Label("1-0"), 1),
|
||||||
("1-0-0".to_owned(), 1),
|
(Label("1-0-0"), 1),
|
||||||
// 1-0-1 has no transform
|
// 1-0-1 has no transform
|
||||||
("1-0-2".to_owned(), 3),
|
(Label("1-0-2"), 3),
|
||||||
("1-1".to_owned(), 5),
|
(Label("1-1"), 5),
|
||||||
// 1-2 has no transform
|
// 1-2 has no transform
|
||||||
("1-2-0".to_owned(), 1),
|
(Label("1-2-0"), 1),
|
||||||
("1-2-1".to_owned(), 2),
|
(Label("1-2-1"), 2),
|
||||||
("1-2-2".to_owned(), 3),
|
(Label("1-2-2"), 3),
|
||||||
("1-2-3".to_owned(), 4),
|
(Label("1-2-3"), 4),
|
||||||
("1-3".to_owned(), 11),
|
(Label("1-3"), 11),
|
||||||
// 2 has no transform
|
// 2 has no transform
|
||||||
("2-0".to_owned(), 1),
|
(Label("2-0"), 1),
|
||||||
("2-1".to_owned(), 2),
|
(Label("2-1"), 2),
|
||||||
("2-1-0".to_owned(), 1),
|
(Label("2-1-0"), 1),
|
||||||
];
|
];
|
||||||
assert_eq!(actual_result, expected_result);
|
assert_eq!(actual_result, expected_result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
#[derive(Debug, Clone)]
|
use bevy_ecs::component::Component;
|
||||||
|
|
||||||
|
#[derive(Component, Debug, Clone)]
|
||||||
pub struct Button;
|
pub struct Button;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::CalculatedSize;
|
use crate::CalculatedSize;
|
||||||
use bevy_asset::{Assets, Handle};
|
use bevy_asset::{Assets, Handle};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
query::With,
|
query::With,
|
||||||
system::{Query, Res},
|
system::{Query, Res},
|
||||||
};
|
};
|
||||||
|
@ -8,7 +9,7 @@ use bevy_math::Size;
|
||||||
use bevy_render::texture::Texture;
|
use bevy_render::texture::Texture;
|
||||||
use bevy_sprite::ColorMaterial;
|
use bevy_sprite::ColorMaterial;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Component, Debug, Clone)]
|
||||||
pub enum Image {
|
pub enum Image {
|
||||||
KeepAspect,
|
KeepAspect,
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,14 +24,18 @@ struct ContributorSelection {
|
||||||
idx: usize,
|
idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct SelectTimer;
|
struct SelectTimer;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct ContributorDisplay;
|
struct ContributorDisplay;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct Contributor {
|
struct Contributor {
|
||||||
hue: f32,
|
hue: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct Velocity {
|
struct Velocity {
|
||||||
translation: Vec3,
|
translation: Vec3,
|
||||||
rotation: f32,
|
rotation: f32,
|
||||||
|
|
|
@ -10,8 +10,11 @@ fn main() {
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct AnimateTranslation;
|
struct AnimateTranslation;
|
||||||
|
#[derive(Component)]
|
||||||
struct AnimateRotation;
|
struct AnimateRotation;
|
||||||
|
#[derive(Component)]
|
||||||
struct AnimateScale;
|
struct AnimateScale;
|
||||||
|
|
||||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
|
|
|
@ -28,6 +28,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// this component indicates what entities should rotate
|
/// this component indicates what entities should rotate
|
||||||
|
#[derive(Component)]
|
||||||
struct Rotates;
|
struct Rotates;
|
||||||
|
|
||||||
fn rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<Rotates>>) {
|
fn rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<Rotates>>) {
|
||||||
|
|
|
@ -12,6 +12,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// this component indicates what entities should rotate
|
/// this component indicates what entities should rotate
|
||||||
|
#[derive(Component)]
|
||||||
struct Rotator;
|
struct Rotator;
|
||||||
|
|
||||||
/// rotates the parent, which will result in the child also rotating
|
/// rotates the parent, which will result in the child also rotating
|
||||||
|
|
|
@ -19,6 +19,7 @@ use bevy::{
|
||||||
window::WindowId,
|
window::WindowId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
pub struct FirstPass;
|
pub struct FirstPass;
|
||||||
|
|
||||||
pub const RENDER_TEXTURE_HANDLE: HandleUntyped =
|
pub const RENDER_TEXTURE_HANDLE: HandleUntyped =
|
||||||
|
@ -109,7 +110,9 @@ fn add_render_to_texture_graph(graph: &mut RenderGraph, size: Extent3d) {
|
||||||
graph.add_node_edge("transform", FIRST_PASS).unwrap();
|
graph.add_node_edge("transform", FIRST_PASS).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct FirstPassCube;
|
struct FirstPassCube;
|
||||||
|
#[derive(Component)]
|
||||||
struct MainPassCube;
|
struct MainPassCube;
|
||||||
|
|
||||||
/// rotates the inner cube (first pass)
|
/// rotates the inner cube (first pass)
|
||||||
|
|
|
@ -16,6 +16,7 @@ fn main() {
|
||||||
struct SceneInstance(Option<InstanceId>);
|
struct SceneInstance(Option<InstanceId>);
|
||||||
|
|
||||||
// Component that will be used to tag entities in the scene
|
// Component that will be used to tag entities in the scene
|
||||||
|
#[derive(Component)]
|
||||||
struct EntityInMyScene;
|
struct EntityInMyScene;
|
||||||
|
|
||||||
fn setup(
|
fn setup(
|
||||||
|
|
|
@ -17,6 +17,7 @@ fn main() {
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct Rotator;
|
struct Rotator;
|
||||||
|
|
||||||
/// rotates the parent, which will result in the child also rotating
|
/// rotates the parent, which will result in the child also rotating
|
||||||
|
|
|
@ -12,7 +12,7 @@ fn main() {
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Component, Debug)]
|
||||||
struct MyComponent(f64);
|
struct MyComponent(f64);
|
||||||
|
|
||||||
fn setup(mut commands: Commands) {
|
fn setup(mut commands: Commands) {
|
||||||
|
|
|
@ -37,11 +37,13 @@ use rand::random;
|
||||||
//
|
//
|
||||||
|
|
||||||
// Our game will have a number of "players". Each player has a name that identifies them
|
// Our game will have a number of "players". Each player has a name that identifies them
|
||||||
|
#[derive(Component)]
|
||||||
struct Player {
|
struct Player {
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each player also has a score. This component holds on to that score
|
// Each player also has a score. This component holds on to that score
|
||||||
|
#[derive(Component)]
|
||||||
struct Score {
|
struct Score {
|
||||||
value: usize,
|
value: usize,
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,11 +26,11 @@ const GRAVITY_CONSTANT: f32 = 0.001;
|
||||||
const SOFTENING: f32 = 0.01;
|
const SOFTENING: f32 = 0.01;
|
||||||
const NUM_BODIES: usize = 100;
|
const NUM_BODIES: usize = 100;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Component, Default)]
|
||||||
struct Mass(f32);
|
struct Mass(f32);
|
||||||
#[derive(Default)]
|
#[derive(Component, Default)]
|
||||||
struct Acceleration(Vec3);
|
struct Acceleration(Vec3);
|
||||||
#[derive(Default)]
|
#[derive(Component, Default)]
|
||||||
struct LastPos(Vec3);
|
struct LastPos(Vec3);
|
||||||
|
|
||||||
#[derive(Bundle, Default)]
|
#[derive(Bundle, Default)]
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use bevy::{prelude::*, tasks::prelude::*};
|
use bevy::{prelude::*, tasks::prelude::*};
|
||||||
use rand::random;
|
use rand::random;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct Velocity(Vec2);
|
struct Velocity(Vec2);
|
||||||
|
|
||||||
fn spawn_system(
|
fn spawn_system(
|
||||||
|
|
|
@ -23,6 +23,7 @@ fn main() {
|
||||||
|
|
||||||
// This `Struct` is just used for convenience in this example. This is the `Component` we'll be
|
// This `Struct` is just used for convenience in this example. This is the `Component` we'll be
|
||||||
// giving to the `Entity` so we have a `Component` to remove in `remove_component()`.
|
// giving to the `Entity` so we have a `Component` to remove in `remove_component()`.
|
||||||
|
#[derive(Component)]
|
||||||
struct MyComponent;
|
struct MyComponent;
|
||||||
|
|
||||||
fn setup(
|
fn setup(
|
||||||
|
|
|
@ -9,7 +9,9 @@ fn main() {
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
pub struct Player;
|
pub struct Player;
|
||||||
|
#[derive(Component)]
|
||||||
pub struct PlayerCount(usize);
|
pub struct PlayerCount(usize);
|
||||||
|
|
||||||
/// The SystemParam struct can contain any types that can also be included in a
|
/// The SystemParam struct can contain any types that can also be included in a
|
||||||
|
|
|
@ -25,24 +25,27 @@ fn main() {
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct Paddle {
|
struct Paddle {
|
||||||
speed: f32,
|
speed: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct Ball {
|
struct Ball {
|
||||||
velocity: Vec3,
|
velocity: Vec3,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Scoreboard {
|
#[derive(Component)]
|
||||||
score: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Collider {
|
enum Collider {
|
||||||
Solid,
|
Solid,
|
||||||
Scorable,
|
Scorable,
|
||||||
Paddle,
|
Paddle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Scoreboard {
|
||||||
|
score: usize,
|
||||||
|
}
|
||||||
|
|
||||||
fn setup(
|
fn setup(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||||
|
|
|
@ -18,8 +18,8 @@ fn main() {
|
||||||
// `Reflect` enable a bunch of cool behaviors, so its worth checking out the dedicated `reflect.rs`
|
// `Reflect` enable a bunch of cool behaviors, so its worth checking out the dedicated `reflect.rs`
|
||||||
// example. The `FromWorld` trait determines how your component is constructed when it loads.
|
// example. The `FromWorld` trait determines how your component is constructed when it loads.
|
||||||
// For simple use cases you can just implement the `Default` trait (which automatically implements
|
// For simple use cases you can just implement the `Default` trait (which automatically implements
|
||||||
// FromWorld). The simplest registered component just needs these two derives:
|
// FromResources). The simplest registered component just needs these two derives:
|
||||||
#[derive(Reflect, Default)]
|
#[derive(Component, Reflect, Default)]
|
||||||
#[reflect(Component)] // this tells the reflect derive to also reflect component behaviors
|
#[reflect(Component)] // this tells the reflect derive to also reflect component behaviors
|
||||||
struct ComponentA {
|
struct ComponentA {
|
||||||
pub x: f32,
|
pub x: f32,
|
||||||
|
@ -30,7 +30,7 @@ struct ComponentA {
|
||||||
// ignored with the #[reflect(ignore)] attribute. This is also generally where the `FromWorld`
|
// ignored with the #[reflect(ignore)] attribute. This is also generally where the `FromWorld`
|
||||||
// trait comes into play. `FromWorld` gives you access to your App's current ECS `Resources`
|
// trait comes into play. `FromWorld` gives you access to your App's current ECS `Resources`
|
||||||
// when you construct your component.
|
// when you construct your component.
|
||||||
#[derive(Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
struct ComponentB {
|
struct ComponentB {
|
||||||
pub value: String,
|
pub value: String,
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub fn main() {
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(RenderResources, Default, TypeUuid)]
|
#[derive(Component, RenderResources, Default, TypeUuid)]
|
||||||
#[uuid = "463e4b8a-d555-4fc2-ba9f-4c880063ba92"]
|
#[uuid = "463e4b8a-d555-4fc2-ba9f-4c880063ba92"]
|
||||||
struct TimeUniform {
|
struct TimeUniform {
|
||||||
value: f32,
|
value: f32,
|
||||||
|
|
|
@ -21,7 +21,7 @@ fn main() {
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(RenderResources, Default, TypeUuid)]
|
#[derive(Component, RenderResources, Default, TypeUuid)]
|
||||||
#[uuid = "93fb26fc-6c05-489b-9029-601edf703b6b"]
|
#[uuid = "93fb26fc-6c05-489b-9029-601edf703b6b"]
|
||||||
struct MyArrayTexture {
|
struct MyArrayTexture {
|
||||||
pub texture: Handle<Texture>,
|
pub texture: Handle<Texture>,
|
||||||
|
|
|
@ -15,6 +15,7 @@ struct BevyCounter {
|
||||||
pub count: u128,
|
pub count: u128,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct Bird {
|
struct Bird {
|
||||||
velocity: Vec3,
|
velocity: Vec3,
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,11 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A unit struct to help identify the FPS UI component, since there may be many Text components
|
// A unit struct to help identify the FPS UI component, since there may be many Text components
|
||||||
|
#[derive(Component)]
|
||||||
struct FpsText;
|
struct FpsText;
|
||||||
|
|
||||||
// A unit struct to help identify the color-changing Text component
|
// A unit struct to help identify the color-changing Text component
|
||||||
|
#[derive(Component)]
|
||||||
struct ColorText;
|
struct ColorText;
|
||||||
|
|
||||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ fn main() {
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
struct TextChanges;
|
struct TextChanges;
|
||||||
|
|
||||||
fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
|
fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Component, Default)]
|
||||||
struct Enemy {
|
struct Enemy {
|
||||||
hit_points: u32,
|
hit_points: u32,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue