scene: spawning

This commit is contained in:
Carter Anderson 2020-05-29 15:51:36 -07:00
parent 065a94aca8
commit 651f213570
5 changed files with 129 additions and 32 deletions

View file

@ -20,13 +20,17 @@ impl GuidEntityAllocator {
let entity = if !self.next_ids.read().is_empty() { let entity = if !self.next_ids.read().is_empty() {
self.next_ids.write().pop_front().unwrap() self.next_ids.write().pop_front().unwrap()
} else { } else {
Entity::new(rand::random::<u32>(), Wrapping(1)) Entity::new(Self::new_entity_id(), Wrapping(1))
}; };
self.entities.write().insert(entity.index()); self.entities.write().insert(entity.index());
entity entity
} }
pub fn new_entity_id() -> u32 {
rand::random::<u32>()
}
/// Creates an iterator which allocates new `Entity` IDs. /// Creates an iterator which allocates new `Entity` IDs.
pub fn create_entities(&self) -> GuidCreateEntityIter { pub fn create_entities(&self) -> GuidCreateEntityIter {
GuidCreateEntityIter { allocator: self } GuidCreateEntityIter { allocator: self }

View file

@ -9,8 +9,10 @@ bevy_app = { path = "../bevy_app" }
bevy_asset = { path = "../bevy_asset" } bevy_asset = { path = "../bevy_asset" }
bevy_type_registry = { path = "../bevy_type_registry" } bevy_type_registry = { path = "../bevy_type_registry" }
bevy_property = { path = "../bevy_property" } bevy_property = { path = "../bevy_property" }
legion = { path = "../bevy_legion", features = ["serialize"] } legion = { path = "../bevy_legion", features = ["serialize"] }
serde = { version = "1.0", features = ["derive"]} serde = { version = "1.0", features = ["derive"]}
ron = { git = "https://github.com/ron-rs/ron", rev = "b43c1074d517131fd0cfc1deb96e11f95d6f42d8" } ron = { git = "https://github.com/ron-rs/ron", rev = "b43c1074d517131fd0cfc1deb96e11f95d6f42d8" }
uuid = { version = "0.8", features = ["v4", "serde"] }
anyhow = "1.0" anyhow = "1.0"
thiserror = "1.0" thiserror = "1.0"

View file

@ -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 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)] #[derive(Default)]
pub struct Scene { pub struct Scene {
@ -12,7 +11,7 @@ pub struct Scene {
} }
pub struct Entity { pub struct Entity {
pub entity: u32, pub entity: EntityIndex,
pub components: Vec<DynamicProperties>, pub components: Vec<DynamicProperties>,
} }
@ -38,10 +37,8 @@ impl Scene {
}) })
} }
let properties = component_registration.get_component_properties( let properties = component_registration
&component_resource_set, .get_component_properties(&component_resource_set, index);
index,
);
entities[index].components.push(properties.to_dynamic()); entities[index].components.push(properties.to_dynamic());
} }

View file

@ -2,14 +2,34 @@ use crate::Scene;
use bevy_app::{EventReader, Events, FromResources, GetEventReader}; use bevy_app::{EventReader, Events, FromResources, GetEventReader};
use bevy_asset::{AssetEvent, Assets, Handle}; use bevy_asset::{AssetEvent, Assets, Handle};
use bevy_type_registry::TypeRegistry; 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::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
num::Wrapping, num::Wrapping,
}; };
use thiserror::Error; 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 { 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>>, scene_asset_event_reader: EventReader<AssetEvent<Scene>>,
scenes_to_spawn: Vec<Handle<Scene>>, scenes_to_spawn: Vec<Handle<Scene>>,
scenes_to_load: Vec<Handle<Scene>>, scenes_to_load: Vec<Handle<Scene>>,
@ -19,7 +39,9 @@ impl FromResources for SceneSpawner {
fn from_resources(resources: &Resources) -> Self { fn from_resources(resources: &Resources) -> Self {
SceneSpawner { SceneSpawner {
scene_asset_event_reader: resources.get_event_reader::<AssetEvent<Scene>>(), 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_spawn: Default::default(),
scenes_to_load: Default::default(), scenes_to_load: Default::default(),
} }
@ -35,12 +57,12 @@ pub enum SceneSpawnError {
} }
impl SceneSpawner { impl SceneSpawner {
pub fn spawn(&mut self, scene: Handle<Scene>) { pub fn spawn(&mut self, scene_handle: Handle<Scene>) {
self.scenes_to_spawn.push(scene); self.scenes_to_spawn.push(scene_handle);
} }
pub fn load(&mut self, scene: Handle<Scene>) { pub fn load(&mut self, scene_handle: Handle<Scene>) {
self.scenes_to_load.push(scene); self.scenes_to_load.push(scene_handle);
} }
pub fn load_sync( pub fn load_sync(
@ -48,6 +70,34 @@ impl SceneSpawner {
world: &mut World, world: &mut World,
resources: &Resources, resources: &Resources,
scene_handle: Handle<Scene>, 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> { ) -> Result<(), SceneSpawnError> {
let type_registry = resources.get::<TypeRegistry>().unwrap(); let type_registry = resources.get::<TypeRegistry>().unwrap();
let component_registry = type_registry.component.read().unwrap(); let component_registry = type_registry.component.read().unwrap();
@ -58,16 +108,23 @@ impl SceneSpawner {
handle: scene_handle, 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() { 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 // 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() { 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 = world.insert((), vec![()])[0];
} }
entity_ids.push(entity);
for component in scene_entity.components.iter() { for component in scene_entity.components.iter() {
let component_registration = component_registry let component_registration = component_registry
.get_with_name(&component.type_name) .get_with_name(&component.type_name)
@ -77,12 +134,23 @@ impl SceneSpawner {
component_registration.add_component_to_entity(world, resources, entity, component); component_registration.add_component_to_entity(world, resources, entity, component);
} }
} }
self.loaded_scene_entities.insert(scene_handle, entity_ids);
Ok(()) 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 scenes_to_load = self.scenes_to_load.drain(..).collect::<Vec<_>>();
let mut non_existent_scenes = Vec::new(); let mut non_existent_scenes = Vec::new();
for scene_handle in scenes_to_load { for scene_handle in scenes_to_load {
@ -91,11 +159,29 @@ impl SceneSpawner {
Err(SceneSpawnError::NonExistentScene { .. }) => { Err(SceneSpawnError::NonExistentScene { .. }) => {
non_existent_scenes.push(scene_handle) non_existent_scenes.push(scene_handle)
} }
Err(err) => panic!("{:?}", err), Err(err) => return Err(err),
} }
} }
self.scenes_to_load = non_existent_scenes; 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 mut scene_spawner = resources.get_mut::<SceneSpawner>().unwrap();
let scene_asset_events = resources.get::<Events<AssetEvent<Scene>>>().unwrap(); let scene_asset_events = resources.get::<Events<AssetEvent<Scene>>>().unwrap();
let mut updated_spawned_scenes = Vec::new();
for event in scene_spawner for event in scene_spawner
.scene_asset_event_reader .scene_asset_event_reader
.iter(&scene_asset_events) .iter(&scene_asset_events)
{ {
if let AssetEvent::Modified { handle } = event { 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); 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();
} }

View file

@ -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. // 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. // 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. // 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. // 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); 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. // This tells the AssetServer to watch for changes to assets.
// It enables our scenes to automatically reload in game when we modify their files // It enables our scenes to automatically reload in game when we modify their files
asset_server.watch_for_changes().unwrap(); asset_server.watch_for_changes().unwrap();