Closed PudgeKim closed 1 year ago
How is it different from having another storage with a Disabled
component?
The iteration call site would be a bit more complicated but not that much. If you really want it, you could make a custom view that hides the second storage.
Alternatively you could (ab)use the deletion tracking. If you don't already use it, you could delete the components, they wouldn't show up in the iteration of course. When you want to unmark you just have to go through the deleted components and add them back.
Some archetype-based ecs have an issue opened to disable components/entities (bevy for example) but it's mainly about performance. With archetype if you add/remove a component, all components of the entity have to move.
If you really want it, you could make a custom view that hides the second storage.
Could you give me some more explanation about this? I know what a custom view is but I don't know about hiding the second storage.
It's quite a bit of boilerplate but you only need it once. Borrow
, AllStoragesBorrow
and BorrowInfo
can be derived but not the iterators traits because we do custom things inside.
With this version you add/remove the Disabled
component to mark/unmark an entity. Then when you want to iterate but skip marked entities you use DisabledView
/DisabledViewMut
instead of the regular ones. It will basically mimic (&view, !&disabled)
but all wrapped in a single type.
If you wanted to disable a single component and not the entity you could make Disabled
generic and the rest should be almost identical.
use shipyard::iter::{AbstractMut, IntoAbstract};
use shipyard::*;
fn main() {
let mut world = World::new();
world.add_entity(A);
world.add_entity((A, Disabled));
world.add_entity(A);
world.run(sys);
}
#[derive(Component, Debug)]
struct A;
fn sys(mut vm_a: DisabledViewMut<A>) {
for (id, a) in (&mut vm_a).iter().with_id() {
dbg!(id, a);
}
}
#[derive(Component)]
struct Disabled;
#[derive(Borrow, AllStoragesBorrow, BorrowInfo)]
struct DisabledView<'v, T: Component + Send + Sync> {
view: View<'v, T>,
disabled: View<'v, Disabled>,
}
struct DisabledAbsView<'v, T: Component + Send + Sync>(
(
<&'v View<'v, T> as IntoAbstract>::AbsView,
Not<<&'v View<'v, Disabled> as IntoAbstract>::AbsView>,
),
);
impl<'v, T: Component + Send + Sync> IntoAbstract for &'v DisabledView<'v, T> {
type AbsView = DisabledAbsView<'v, T>;
fn into_abstract(self) -> Self::AbsView {
DisabledAbsView((self.view.into_abstract(), (!&self.disabled).into_abstract()))
}
fn len(&self) -> Option<usize> {
(&self.view).len()
}
fn type_id(&self) -> info::TypeId {
(&self.view).type_id()
}
fn inner_type_id(&self) -> info::TypeId {
(&self.view).inner_type_id()
}
fn dense(&self) -> *const EntityId {
(&self.view).dense()
}
fn is_not(&self) -> bool {
true
}
}
impl<'v, T: Component + Send + Sync> AbstractMut for DisabledAbsView<'v, T> {
type Out = <<&'v View<'v, T> as IntoAbstract>::AbsView as AbstractMut>::Out;
type Index = <<&'v View<'v, T> as IntoAbstract>::AbsView as AbstractMut>::Index;
unsafe fn get_data(&self, index: usize) -> Self::Out {
self.0.get_data(index).0
}
unsafe fn get_datas(&self, index: Self::Index) -> Self::Out {
self.0.get_data(index).0
}
fn indices_of(&self, entity_id: EntityId, index: usize, mask: u16) -> Option<Self::Index> {
Some(self.0.indices_of(entity_id, index, mask)?.0)
}
unsafe fn indices_of_unchecked(
&self,
_entity_id: EntityId,
_index: usize,
_mask: u16,
) -> Self::Index {
unreachable!()
}
unsafe fn get_id(&self, _index: usize) -> EntityId {
unreachable!()
}
fn len(&self) -> usize {
self.0.len()
}
}
#[derive(Borrow, AllStoragesBorrow, BorrowInfo)]
struct DisabledViewMut<'v, T: Component + Send + Sync> {
view: ViewMut<'v, T>,
// Only needs to be mut if you want to be able to (un)mark entities with it
disabled: ViewMut<'v, Disabled>,
}
struct DisabledAbsViewMut<'tmp, 'v, T: Component + Send + Sync>(
(
<&'tmp mut ViewMut<'v, T> as IntoAbstract>::AbsView,
Not<<&'tmp ViewMut<'v, Disabled> as IntoAbstract>::AbsView>,
),
);
impl<'tmp, 'v, T: Component + Send + Sync> IntoAbstract for &'tmp mut DisabledViewMut<'v, T> {
type AbsView = DisabledAbsViewMut<'tmp, 'v, T>;
fn into_abstract(self) -> Self::AbsView {
DisabledAbsViewMut((
(&mut self.view).into_abstract(),
(!&self.disabled).into_abstract(),
))
}
fn len(&self) -> Option<usize> {
(&self.view).len()
}
fn type_id(&self) -> info::TypeId {
(&self.view).type_id()
}
fn inner_type_id(&self) -> info::TypeId {
(&self.view).inner_type_id()
}
fn dense(&self) -> *const EntityId {
(&self.view).dense()
}
fn is_not(&self) -> bool {
true
}
}
impl<'tmp, 'v, T: Component + Send + Sync> AbstractMut for DisabledAbsViewMut<'tmp, 'v, T> {
type Out = <<&'tmp mut ViewMut<'v, T> as IntoAbstract>::AbsView as AbstractMut>::Out;
type Index = <<&'tmp mut ViewMut<'v, T> as IntoAbstract>::AbsView as AbstractMut>::Index;
unsafe fn get_data(&self, index: usize) -> Self::Out {
self.0.get_data(index).0
}
unsafe fn get_datas(&self, index: Self::Index) -> Self::Out {
self.0.get_data(index).0
}
fn indices_of(&self, entity_id: EntityId, index: usize, mask: u16) -> Option<Self::Index> {
Some(self.0.indices_of(entity_id, index, mask)?.0)
}
unsafe fn indices_of_unchecked(
&self,
_entity_id: EntityId,
_index: usize,
_mask: u16,
) -> Self::Index {
unreachable!()
}
unsafe fn get_id(&self, _index: usize) -> EntityId {
unreachable!()
}
fn len(&self) -> usize {
self.0.len()
}
}
Thank you for a detailed explanation!
Suppose EntityId 1 has three components. (Component A, B, C) I hope this kind of feature is added.
What do you think about it?