mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 12:43:34 +00:00
scene: spawning
This commit is contained in:
parent
065a94aca8
commit
651f213570
5 changed files with 129 additions and 32 deletions
|
@ -20,13 +20,17 @@ impl GuidEntityAllocator {
|
|||
let entity = if !self.next_ids.read().is_empty() {
|
||||
self.next_ids.write().pop_front().unwrap()
|
||||
} else {
|
||||
Entity::new(rand::random::<u32>(), Wrapping(1))
|
||||
Entity::new(Self::new_entity_id(), Wrapping(1))
|
||||
};
|
||||
|
||||
self.entities.write().insert(entity.index());
|
||||
entity
|
||||
}
|
||||
|
||||
pub fn new_entity_id() -> u32 {
|
||||
rand::random::<u32>()
|
||||
}
|
||||
|
||||
/// Creates an iterator which allocates new `Entity` IDs.
|
||||
pub fn create_entities(&self) -> GuidCreateEntityIter {
|
||||
GuidCreateEntityIter { allocator: self }
|
||||
|
|
|
@ -9,8 +9,10 @@ bevy_app = { path = "../bevy_app" }
|
|||
bevy_asset = { path = "../bevy_asset" }
|
||||
bevy_type_registry = { path = "../bevy_type_registry" }
|
||||
bevy_property = { path = "../bevy_property" }
|
||||
|
||||
legion = { path = "../bevy_legion", features = ["serialize"] }
|
||||
serde = { version = "1.0", features = ["derive"]}
|
||||
ron = { git = "https://github.com/ron-rs/ron", rev = "b43c1074d517131fd0cfc1deb96e11f95d6f42d8" }
|
||||
uuid = { version = "0.8", features = ["v4", "serde"] }
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
|
@ -1,10 +1,9 @@
|
|||
use anyhow::Result;
|
||||
use bevy_type_registry::ComponentRegistry;
|
||||
use bevy_property::{PropertyTypeRegistry, DynamicProperties};
|
||||
use legion::prelude::{Resources, World};
|
||||
use serde::Serialize;
|
||||
use std::num::Wrapping;
|
||||
use crate::serde::SceneSerializer;
|
||||
use anyhow::Result;
|
||||
use bevy_property::{DynamicProperties, PropertyTypeRegistry};
|
||||
use bevy_type_registry::ComponentRegistry;
|
||||
use legion::{entity::EntityIndex, prelude::World};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Scene {
|
||||
|
@ -12,7 +11,7 @@ pub struct Scene {
|
|||
}
|
||||
|
||||
pub struct Entity {
|
||||
pub entity: u32,
|
||||
pub entity: EntityIndex,
|
||||
pub components: Vec<DynamicProperties>,
|
||||
}
|
||||
|
||||
|
@ -38,10 +37,8 @@ impl Scene {
|
|||
})
|
||||
}
|
||||
|
||||
let properties = component_registration.get_component_properties(
|
||||
&component_resource_set,
|
||||
index,
|
||||
);
|
||||
let properties = component_registration
|
||||
.get_component_properties(&component_resource_set, index);
|
||||
|
||||
entities[index].components.push(properties.to_dynamic());
|
||||
}
|
||||
|
|
|
@ -2,14 +2,34 @@ use crate::Scene;
|
|||
use bevy_app::{EventReader, Events, FromResources, GetEventReader};
|
||||
use bevy_asset::{AssetEvent, Assets, Handle};
|
||||
use bevy_type_registry::TypeRegistry;
|
||||
use legion::prelude::{Entity, Resources, World};
|
||||
use legion::{
|
||||
entity::EntityIndex,
|
||||
guid_entity_allocator::GuidEntityAllocator,
|
||||
prelude::{Entity, Resources, World},
|
||||
};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
num::Wrapping,
|
||||
};
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
struct InstanceInfo {
|
||||
entity_map: HashMap<EntityIndex, EntityIndex>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
struct InstanceId(Uuid);
|
||||
|
||||
impl InstanceId {
|
||||
pub fn new() -> Self {
|
||||
InstanceId(Uuid::new_v4())
|
||||
}
|
||||
}
|
||||
pub struct SceneSpawner {
|
||||
loaded_scene_entities: HashMap<Handle<Scene>, Vec<Entity>>,
|
||||
loaded_scenes: HashSet<Handle<Scene>>,
|
||||
spawned_scenes: HashMap<Handle<Scene>, Vec<InstanceId>>,
|
||||
spawned_instances: HashMap<InstanceId, InstanceInfo>,
|
||||
scene_asset_event_reader: EventReader<AssetEvent<Scene>>,
|
||||
scenes_to_spawn: Vec<Handle<Scene>>,
|
||||
scenes_to_load: Vec<Handle<Scene>>,
|
||||
|
@ -19,7 +39,9 @@ impl FromResources for SceneSpawner {
|
|||
fn from_resources(resources: &Resources) -> Self {
|
||||
SceneSpawner {
|
||||
scene_asset_event_reader: resources.get_event_reader::<AssetEvent<Scene>>(),
|
||||
loaded_scene_entities: Default::default(),
|
||||
spawned_scenes: Default::default(),
|
||||
spawned_instances: Default::default(),
|
||||
loaded_scenes: Default::default(),
|
||||
scenes_to_spawn: Default::default(),
|
||||
scenes_to_load: Default::default(),
|
||||
}
|
||||
|
@ -35,12 +57,12 @@ pub enum SceneSpawnError {
|
|||
}
|
||||
|
||||
impl SceneSpawner {
|
||||
pub fn spawn(&mut self, scene: Handle<Scene>) {
|
||||
self.scenes_to_spawn.push(scene);
|
||||
pub fn spawn(&mut self, scene_handle: Handle<Scene>) {
|
||||
self.scenes_to_spawn.push(scene_handle);
|
||||
}
|
||||
|
||||
pub fn load(&mut self, scene: Handle<Scene>) {
|
||||
self.scenes_to_load.push(scene);
|
||||
pub fn load(&mut self, scene_handle: Handle<Scene>) {
|
||||
self.scenes_to_load.push(scene_handle);
|
||||
}
|
||||
|
||||
pub fn load_sync(
|
||||
|
@ -48,6 +70,34 @@ impl SceneSpawner {
|
|||
world: &mut World,
|
||||
resources: &Resources,
|
||||
scene_handle: Handle<Scene>,
|
||||
) -> Result<(), SceneSpawnError> {
|
||||
Self::load_internal(world, resources, scene_handle, None)?;
|
||||
self.loaded_scenes.insert(scene_handle);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn spawn_sync(
|
||||
&mut self,
|
||||
world: &mut World,
|
||||
resources: &Resources,
|
||||
scene_handle: Handle<Scene>,
|
||||
) -> Result<(), SceneSpawnError> {
|
||||
let instance_id = InstanceId::new();
|
||||
let mut instance_info = InstanceInfo {
|
||||
entity_map: HashMap::default(),
|
||||
};
|
||||
Self::load_internal(world, resources, scene_handle, Some(&mut instance_info))?;
|
||||
self.spawned_instances.insert(instance_id, instance_info);
|
||||
let spawned = self.spawned_scenes.entry(scene_handle).or_insert_with(|| Vec::new());
|
||||
spawned.push(instance_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_internal(
|
||||
world: &mut World,
|
||||
resources: &Resources,
|
||||
scene_handle: Handle<Scene>,
|
||||
mut instance_info: Option<&mut InstanceInfo>,
|
||||
) -> Result<(), SceneSpawnError> {
|
||||
let type_registry = resources.get::<TypeRegistry>().unwrap();
|
||||
let component_registry = type_registry.component.read().unwrap();
|
||||
|
@ -58,16 +108,23 @@ impl SceneSpawner {
|
|||
handle: scene_handle,
|
||||
})?;
|
||||
|
||||
// TODO: this vec might not be needed
|
||||
let mut entity_ids = Vec::with_capacity(scene.entities.len());
|
||||
for scene_entity in scene.entities.iter() {
|
||||
let entity_id = if let Some(ref mut instance_info) = instance_info {
|
||||
*instance_info
|
||||
.entity_map
|
||||
.entry(scene_entity.entity)
|
||||
.or_insert_with(|| GuidEntityAllocator::new_entity_id())
|
||||
} else {
|
||||
scene_entity.entity
|
||||
};
|
||||
let mut entity = Entity::new(entity_id, Wrapping(1));
|
||||
// TODO: use EntityEntry when legion refactor is finished
|
||||
let mut entity = Entity::new(scene_entity.entity, Wrapping(1));
|
||||
if world.get_entity_location(entity).is_none() {
|
||||
world.entity_allocator.push_next_ids((&[entity]).iter().cloned());
|
||||
world
|
||||
.entity_allocator
|
||||
.push_next_ids((&[entity]).iter().cloned());
|
||||
entity = world.insert((), vec![()])[0];
|
||||
}
|
||||
entity_ids.push(entity);
|
||||
for component in scene_entity.components.iter() {
|
||||
let component_registration = component_registry
|
||||
.get_with_name(&component.type_name)
|
||||
|
@ -77,12 +134,23 @@ impl SceneSpawner {
|
|||
component_registration.add_component_to_entity(world, resources, entity, component);
|
||||
}
|
||||
}
|
||||
|
||||
self.loaded_scene_entities.insert(scene_handle, entity_ids);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_queued_scenes(&mut self, world: &mut World, resources: &Resources) {
|
||||
pub fn update_spawned_scenes(&mut self, world: &mut World, resources: &Resources, scene_handles: &[Handle<Scene>]) -> Result<(), SceneSpawnError> {
|
||||
for scene_handle in scene_handles {
|
||||
if let Some(spawned_instances) = self.spawned_scenes.get(scene_handle) {
|
||||
for instance_id in spawned_instances.iter() {
|
||||
if let Some(instance_info) = self.spawned_instances.get_mut(instance_id) {
|
||||
Self::load_internal(world, resources, *scene_handle, Some(instance_info))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_queued_scenes(&mut self, world: &mut World, resources: &Resources) -> Result<(), SceneSpawnError> {
|
||||
let scenes_to_load = self.scenes_to_load.drain(..).collect::<Vec<_>>();
|
||||
let mut non_existent_scenes = Vec::new();
|
||||
for scene_handle in scenes_to_load {
|
||||
|
@ -91,11 +159,29 @@ impl SceneSpawner {
|
|||
Err(SceneSpawnError::NonExistentScene { .. }) => {
|
||||
non_existent_scenes.push(scene_handle)
|
||||
}
|
||||
Err(err) => panic!("{:?}", err),
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
self.scenes_to_load = non_existent_scenes;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn spawn_queued_scenes(&mut self, world: &mut World, resources: &Resources) -> Result<(), SceneSpawnError> {
|
||||
let scenes_to_spawn = self.scenes_to_spawn.drain(..).collect::<Vec<_>>();
|
||||
let mut non_existent_scenes = Vec::new();
|
||||
for scene_handle in scenes_to_spawn {
|
||||
match self.spawn_sync(world, resources, scene_handle) {
|
||||
Ok(_) => {}
|
||||
Err(SceneSpawnError::NonExistentScene { .. }) => {
|
||||
non_existent_scenes.push(scene_handle)
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
self.scenes_to_spawn = non_existent_scenes;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,16 +189,22 @@ pub fn scene_spawner_system(world: &mut World, resources: &mut Resources) {
|
|||
let mut scene_spawner = resources.get_mut::<SceneSpawner>().unwrap();
|
||||
let scene_asset_events = resources.get::<Events<AssetEvent<Scene>>>().unwrap();
|
||||
|
||||
let mut updated_spawned_scenes = Vec::new();
|
||||
for event in scene_spawner
|
||||
.scene_asset_event_reader
|
||||
.iter(&scene_asset_events)
|
||||
{
|
||||
if let AssetEvent::Modified { handle } = event {
|
||||
if scene_spawner.loaded_scene_entities.contains_key(handle) {
|
||||
if scene_spawner.loaded_scenes.contains(handle) {
|
||||
scene_spawner.load(*handle);
|
||||
}
|
||||
if scene_spawner.spawned_scenes.contains_key(handle) {
|
||||
updated_spawned_scenes.push(*handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scene_spawner.load_queued_scenes(world, resources);
|
||||
scene_spawner.load_queued_scenes(world, resources).unwrap();
|
||||
scene_spawner.spawn_queued_scenes(world, resources).unwrap();
|
||||
scene_spawner.update_spawned_scenes(world, resources, &updated_spawned_scenes).unwrap();
|
||||
}
|
||||
|
|
|
@ -56,13 +56,15 @@ fn load_scene_system(asset_server: Res<AssetServer>, mut scene_spawner: ResMut<S
|
|||
|
||||
// SceneSpawner can "spawn" scenes. "spawning" a scene creates a new instance of the scene in the World with new entity ids.
|
||||
// This guarantees that it will not overwrite existing entities.
|
||||
// scene_spawner.spawn(scene_handle);
|
||||
scene_spawner.spawn(scene_handle);
|
||||
|
||||
// SceneSpawner can also "load" scenes. "loading" a scene preserves the entity ids in the scene.
|
||||
// In general, you should "spawn" scenes when you are dynamically composing your World and "load" scenes for things like game saves.
|
||||
|
||||
scene_spawner.load(scene_handle);
|
||||
|
||||
// we have now loaded `scene_handle` AND spawned it, which means our World now has one set of entities with the Scene's ids and
|
||||
// one set of entities with new ids
|
||||
|
||||
// This tells the AssetServer to watch for changes to assets.
|
||||
// It enables our scenes to automatically reload in game when we modify their files
|
||||
asset_server.watch_for_changes().unwrap();
|
||||
|
|
Loading…
Reference in a new issue