add entities with resources, auto disabled IsResource

This commit is contained in:
Trashtalk 2025-07-12 09:12:20 +00:00
parent 956c1a669a
commit 073df4bf9d
6 changed files with 59 additions and 12 deletions

View File

@ -207,7 +207,6 @@ mod tests {
use crate::{
prelude::World,
query::{Has, With},
resource::IsResource,
};
use alloc::{vec, vec::Vec};
@ -279,9 +278,6 @@ mod tests {
let mut world = World::new();
world.register_disabling_component::<CustomDisabled>();
// We don't want to query resources for this test.
world.register_disabling_component::<IsResource>();
world.spawn_empty();
world.spawn(Disabled);
world.spawn(CustomDisabled);

View File

@ -332,8 +332,6 @@ mod tests {
#[test]
fn builder_or() {
let mut world = World::new();
// We don't want to query resources for this test.
world.register_disabling_component::<IsResource>();
world.spawn((A(0), B(0)));
world.spawn(B(0));

View File

@ -2659,7 +2659,6 @@ mod tests {
use crate::component::Component;
use crate::entity::Entity;
use crate::prelude::World;
use crate::resource::IsResource;
#[derive(Component, Debug, PartialEq, PartialOrd, Clone, Copy)]
struct A(f32);
@ -2670,8 +2669,6 @@ mod tests {
#[test]
fn query_iter_sorts() {
let mut world = World::new();
// We don't want to query resources for this test.
world.register_disabling_component::<IsResource>();
for i in 0..100 {
world.spawn(A(i as f32));

View File

@ -1850,8 +1850,6 @@ mod tests {
#[test]
fn can_transmute_empty_tuple() {
let mut world = World::new();
// We don't want to query resources for this test.
world.register_disabling_component::<IsResource>();
world.register_component::<A>();
let entity = world.spawn(A(10)).id();
@ -2210,7 +2208,7 @@ mod tests {
#[test]
fn query_default_filters_updates_is_dense() {
let mut world = World::new();
let num_resources = world.components().num_resources();
let num_resources = world.resource_count() as usize;
world.spawn((Table, Sparse));
world.spawn(Table);
world.spawn(Sparse);

View File

@ -112,6 +112,11 @@ impl<R: Resource> Default for ResourceEntity<R> {
#[derive(Component, Default, Debug)]
pub struct IsResource;
/// Used in conjunction with [`ResourceEntity<R>`], when no type information is available.
/// This is used by [`insert_resource_by_id`].
#[derive(Resource)]
pub(crate) struct TypeErasedResource;
#[cfg(test)]
#[expect(clippy::print_stdout, reason = "Allowed in tests.")]
mod tests {

View File

@ -22,6 +22,7 @@ use crate::{
event::BufferedEvent,
lifecycle::{ComponentHooks, ADD, DESPAWN, INSERT, REMOVE, REPLACE},
prelude::{Add, Despawn, Insert, Remove, Replace},
resource::{IsResource, ResourceEntity, TypeErasedResource},
};
pub use bevy_ecs_macros::FromWorld;
use bevy_utils::prelude::DebugName;
@ -169,6 +170,7 @@ impl World {
// This sets up `Disabled` as a disabling component, via the FromWorld impl
self.init_resource::<DefaultQueryFilters>();
self.register_disabling_component::<IsResource>();
}
/// Creates a new empty [`World`].
///
@ -226,6 +228,12 @@ impl World {
.saturating_sub(self.components.resource_entities.len() as u32)
}
/// Retrieves the number of [`Resource`]s in the world.
#[inline]
pub fn resource_count(&self) -> u32 {
self.components.resource_entities.len() as u32
}
/// Retrieves this world's [`Archetypes`] collection.
#[inline]
pub fn archetypes(&self) -> &Archetypes {
@ -1702,6 +1710,18 @@ impl World {
pub fn init_resource<R: Resource + FromWorld>(&mut self) -> ComponentId {
let caller = MaybeLocation::caller();
let component_id = self.components_registrator().register_resource::<R>();
if !self
.components
.resource_entities
.contains_key(&component_id)
{
let entity = self.spawn(ResourceEntity::<R>::default()).id();
self.components
.resource_entities
.insert(component_id, entity);
}
if self
.storages
.resources
@ -1739,6 +1759,17 @@ impl World {
caller: MaybeLocation,
) {
let component_id = self.components_registrator().register_resource::<R>();
if !self
.components
.resource_entities
.contains_key(&component_id)
{
let entity = self.spawn(ResourceEntity::<R>::default()).id();
self.components
.resource_entities
.insert(component_id, entity);
}
OwningPtr::make(value, |ptr| {
// SAFETY: component_id was just initialized and corresponds to resource of type R.
unsafe {
@ -1806,6 +1837,10 @@ impl World {
#[inline]
pub fn remove_resource<R: Resource>(&mut self) -> Option<R> {
let component_id = self.components.get_valid_resource_id(TypeId::of::<R>())?;
if let Some(entity) = self.components.resource_entities.remove(&component_id) {
self.despawn(entity);
}
let (ptr, _, _) = self.storages.resources.get_mut(component_id)?.remove()?;
// SAFETY: `component_id` was gotten via looking up the `R` type
unsafe { Some(ptr.read::<R>()) }
@ -2709,6 +2744,20 @@ impl World {
) {
let change_tick = self.change_tick();
if !self
.components
.resource_entities
.contains_key(&component_id)
{
// Since we don't know the type, we use a placeholder type.
let entity = self
.spawn(ResourceEntity::<TypeErasedResource>::default())
.id();
self.components
.resource_entities
.insert(component_id, entity);
}
let resource = self.initialize_resource_internal(component_id);
// SAFETY: `value` is valid for `component_id`, ensured by caller
unsafe {
@ -3401,6 +3450,10 @@ impl World {
/// **You should prefer to use the typed API [`World::remove_resource`] where possible and only
/// use this in cases where the actual types are not known at compile time.**
pub fn remove_resource_by_id(&mut self, component_id: ComponentId) -> Option<()> {
if let Some(entity) = self.components.resource_entities.remove(&component_id) {
self.despawn(entity);
}
self.storages
.resources
.get_mut(component_id)?