Better framework for asset-driven entities

attempt/001
Elijah Voigt 1 year ago
parent 1a6bafa65e
commit d8f67c2cef

@ -1,4 +0,0 @@
entities/camera.entity
entities/ui_container.entity
entities/ui_title.entity
entities/window.entity

@ -1,5 +0,0 @@
name "Editor Camera"
editorTag
camera target window "window.entity"
flyCamera
transform translation 10.0 10.0 10.0 rotation 0.0 0.0 0.0 1.0 scale 1.0 1.0 1.0

@ -1,3 +0,0 @@
editorTag
uiNode
targetCamera "camera.entity"

@ -1,3 +0,0 @@
editorTag
uiText "Welcome to the editor" color #ffffff size 12.0
parent "ui_container.entity"

@ -1,3 +0,0 @@
editorTag
name "Editor Window"
window "Editor" visible false

@ -1,2 +0,0 @@
entities/camera.entity
entities/van.entity

@ -1,3 +0,0 @@
name camera
transform translation 2.0 2.0 0.0 rotation 0.0 0.0 0.0 1.0 scale 1.0 1.0 1.0
camera

@ -1,3 +0,0 @@
name van
transform translation 0.0 0.0 0.0 rotation 0.0 0.0 0.0 1.0 scale 1.0 1.0 1.0
model "models/van.glb" "Scene"

@ -0,0 +1 @@
00/a.entity

@ -0,0 +1 @@
name "Hello world"

@ -1,3 +1,5 @@
#![feature(let_chains)]
/// Camera controller /// Camera controller
pub(crate) mod camera; pub(crate) mod camera;
/// ECS scheduling `run_if` conditions /// ECS scheduling `run_if` conditions
@ -12,7 +14,6 @@ pub(crate) mod prelude;
pub(crate) mod save; pub(crate) mod save;
/// Window handling /// Window handling
pub(crate) mod window; pub(crate) mod window;
/// Save file parsing /// Save file parsing
pub(crate) mod parser; pub(crate) mod parser;
@ -26,10 +27,10 @@ fn main() {
exit_condition: bevy::window::ExitCondition::OnPrimaryClosed, exit_condition: bevy::window::ExitCondition::OnPrimaryClosed,
..default() ..default()
})) }))
.add_plugins(camera::CameraPlugin) // .add_plugins(camera::CameraPlugin)
.add_plugins(editor::EditorPlugin) // .add_plugins(editor::EditorPlugin)
.add_plugins(menu::MenuPlugin) // .add_plugins(menu::MenuPlugin)
// .add_plugins(window::WindowPlugin)
.add_plugins(save::SavePlugin) .add_plugins(save::SavePlugin)
.add_plugins(window::WindowPlugin)
.run(); .run();
} }

@ -2,14 +2,10 @@ use crate::prelude::*;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub(crate) enum SaveEntityParseError { pub(crate) enum SaveEntityParseError {
#[error("Failed to parse name")] #[error("Failed to parse `{0}`")]
Name, Component(String),
#[error("Failed to parse Transform")]
Transform,
#[error("Failed to parse Entity")] #[error("Failed to parse Entity")]
Nom(nom::Err<nom::error::Error<Box<str>>>), Nom(nom::Err<nom::error::Error<Box<str>>>),
#[error("Failed to parse camera")]
Camera,
} }
// Convert Nom error to parse error // Convert Nom error to parse error
@ -22,7 +18,7 @@ impl From<nom::Err<nom::error::Error<&str>>> for SaveEntityParseError {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
enum Token { pub(crate) enum Token {
Tag(String), Tag(String),
Str(String), Str(String),
Num(f32), Num(f32),
@ -32,27 +28,35 @@ enum Token {
/// Simple function to tokenize a string into an array of Token values /// Simple function to tokenize a string into an array of Token values
/// ///
pub(crate) fn tokenize(line: &str) -> Vec<Token> { pub(crate) fn tokenize(line: &str) -> Vec<Token> {
line.split(" ") let mut l = line;
.map(|piece| { let mut tokens = Vec::new();
if let Ok(n) = piece.parse::<f32>() {
Token::Num(n) while l.len() > 0 {
} else if let Ok((_, (_, s, _))) = tuple(( debug!("Line: {:?}", l);
tag::<&str, &str, ()>("\""), if let Ok((rem, (_, s, _))) = tuple((char::<&str, ()>('"'), take_until("\""), char::<&str, ()>('"')))(l) {
take_until1("\""), debug!("Parsed string {:?}", s);
tag::<&str, &str, ()>("\""), tokens.push(Token::Str(s.into()));
))(piece) l = rem;
{ } else if let Ok((rem, num)) = float::<&str, ()>(l) {
Token::Str(s.into()) debug!("Parsed float {:?}", num);
} else { tokens.push(Token::Num(num.into()));
Token::Tag(piece.into()) l = rem;
} } else if let Ok((rem, (_, tag, _))) = tuple((space0, alphanumeric1::<&str, ()>, space0))(l) {
}) debug!("Parsed tag {:?}", tag);
.collect() tokens.push(Token::Tag(tag.into()));
l = rem;
} else {
debug!("Breaking loop");
break
}
}
tokens
} }
#[test] #[test]
fn test_tokenize() { fn test_tokenize() {
let line = "foo \"bar\" 1.23 baz \"asdf\" etc"; let line = "foo \"bar\" 1.23 baz -3.45 \"asdf\" \"multi word string\" etc";
assert_eq!( assert_eq!(
tokenize(line), tokenize(line),
vec![ vec![
@ -60,7 +64,9 @@ fn test_tokenize() {
Token::Str("bar".into()), Token::Str("bar".into()),
Token::Num(1.23), Token::Num(1.23),
Token::Tag("baz".into()), Token::Tag("baz".into()),
Token::Num(-3.45),
Token::Str("asdf".into()), Token::Str("asdf".into()),
Token::Str("multi word string".into()),
Token::Tag("etc".into()) Token::Tag("etc".into())
] ]
); );
@ -71,8 +77,6 @@ fn test_tokenize() {
/// Returns reflected `Transform` /// Returns reflected `Transform`
/// ///
pub(crate) fn parse_save_transform(tokens: &Vec<Token>) -> Result<Box<dyn Reflect>, SaveEntityParseError> { pub(crate) fn parse_save_transform(tokens: &Vec<Token>) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
let mut transform = Transform::default();
// Tag(Transform), // Tag(Transform),
// Tag(Translation), Number, Number, Number // Tag(Translation), Number, Number, Number
// Tag(Rotation), Number, Number, Number, Number // Tag(Rotation), Number, Number, Number, Number
@ -104,9 +108,11 @@ fn test_parse_transform() {
/// Returns a reflected `Name` /// Returns a reflected `Name`
/// ///
pub(crate) fn parse_save_name(tokens: &Vec<Token>) -> Result<Box<dyn Reflect>, SaveEntityParseError> { pub(crate) fn parse_save_name(tokens: &Vec<Token>) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
todo!("parse_save_name"); if let Some((Token::Tag(t), &[Token::Str(ref s)])) = tokens.split_first() && *t == String::from("name") {
Ok(Name::new(s.clone()).clone_value())
// Err(SaveEntityParseError::Name) } else {
Err(SaveEntityParseError::Component("Name".into()))
}
} }
#[test] #[test]
@ -374,7 +380,7 @@ fn test_save_ui_text() {
/// ///
pub(crate) fn parse_save_tag<T: Component + Reflect + Default>( pub(crate) fn parse_save_tag<T: Component + Reflect + Default>(
t: &str, t: &str,
) -> impl FnOnce(&str) -> Result<Box<dyn Reflect>, SaveEntityParseError> + '_ { ) -> impl FnOnce(&Vec<Token>) -> Result<Box<dyn Reflect>, SaveEntityParseError> + '_ {
move |tokens: &Vec<Token>| -> Result<Box<dyn Reflect>, SaveEntityParseError> { move |tokens: &Vec<Token>| -> Result<Box<dyn Reflect>, SaveEntityParseError> {
todo!("parse_save_tag") todo!("parse_save_tag")
} }

@ -9,14 +9,11 @@ pub(crate) use bevy::{
render::camera::RenderTarget, render::camera::RenderTarget,
window::{PrimaryWindow, WindowCloseRequested, WindowRef}, window::{PrimaryWindow, WindowCloseRequested, WindowRef},
}; };
pub(crate) use nom::character::complete::hex_digit1;
pub(crate) use nom::{ pub(crate) use nom::{
branch::alt, bytes::complete::take_until,
bytes::complete::{tag, take_until1}, character::complete::{char, space0, alphanumeric1},
character::complete::{char, space1},
number::complete::float, number::complete::float,
sequence::tuple, sequence::tuple,
IResult,
}; };
pub(crate) use std::path::PathBuf; pub(crate) use std::path::PathBuf;
pub(crate) use thiserror::Error; pub(crate) use thiserror::Error;

@ -10,24 +10,27 @@ impl Plugin for SavePlugin {
.register_type::<SaveModel>() .register_type::<SaveModel>()
.init_asset::<SaveEntity>() .init_asset::<SaveEntity>()
.init_asset_loader::<SaveEntityLoader>() .init_asset_loader::<SaveEntityLoader>()
.init_asset::<SaveScene>()
.init_asset_loader::<SaveSceneLoader>() .init_asset_loader::<SaveSceneLoader>()
.add_systems(Startup, test_load_entity) .add_systems(Startup, load_scenes)
.add_systems( .add_systems(Update, spawn_scenes.run_if(on_event::<AssetEvent<SaveEntity>>()))
Update, .add_systems(Update, spawn_entities.run_if(any_component_added::<Handle<SaveEntity>>));
spawn_loaded_entities.run_if(on_event::<AssetEvent<SaveEntity>>()),
);
} }
} }
#[derive(Asset, TypePath, Default)] #[derive(Asset, TypePath, Default)]
pub(crate) struct SaveEntity { pub(crate) struct SaveScene {
components: Vec<Box<dyn Reflect>>, entities: Vec<Handle<SaveEntity>>,
} }
// TODO: SCALABILITY: SaveEntity should maybe be a HashSet or Vec of Box<into Bundle> // TODO: SCALABILITY: SaveEntity should maybe be a HashSet or Vec of Box<into Bundle>
// Then we tell the parse "For each line, run through each of these concrete parsers" // Then we tell the parse "For each line, run through each of these concrete parsers"
// If it matches, add it to the set of Box<Bundle> // If it matches, add it to the set of Box<Bundle>
// Ironically we basically want DynamicEntity: https://docs.rs/bevy/latest/bevy/scene/struct.DynamicEntity.html // Ironically we basically want DynamicEntity: https://docs.rs/bevy/latest/bevy/scene/struct.DynamicEntity.html
#[derive(Asset, TypePath, Default)]
pub(crate) struct SaveEntity {
components: Vec<Box<dyn Reflect>>,
}
impl SaveEntity { impl SaveEntity {
fn parse( fn parse(
@ -38,13 +41,13 @@ impl SaveEntity {
let mut entity = SaveEntity { ..default() }; let mut entity = SaveEntity { ..default() };
let fns = [ let fns = [
parse_save_name, parse_save_name,
parse_save_transform, // parse_save_transform,
parse_save_model, // parse_save_model,
parse_save_camera, // parse_save_camera,
parse_save_parent, // parse_save_parent,
parse_save_window, // parse_save_window,
parse_save_target_camera, // parse_save_target_camera,
parse_save_ui_text, // parse_save_ui_text,
// parse_save_tag::<EditorTag>("editor_tag"), // parse_save_tag::<EditorTag>("editor_tag"),
]; ];
lines lines
@ -55,7 +58,7 @@ impl SaveEntity {
let mut good = false; let mut good = false;
// Tokenize the line // Tokenize the line
let tokens = parser::tokenize(line); let tokens = tokenize(line);
// Run line against all parsers // Run line against all parsers
for f in fns { for f in fns {
@ -123,7 +126,7 @@ impl AssetLoader for SaveEntityLoader {
struct SaveSceneLoader; struct SaveSceneLoader;
impl AssetLoader for SaveSceneLoader { impl AssetLoader for SaveSceneLoader {
type Asset = DynamicScene; type Asset = SaveScene;
type Settings = (); type Settings = ();
type Error = SaveEntityLoaderError; type Error = SaveEntityLoaderError;
@ -140,13 +143,14 @@ impl AssetLoader for SaveSceneLoader {
let asset_path = load_context.path().to_path_buf(); let asset_path = load_context.path().to_path_buf();
let parent_dir = asset_path.parent().unwrap(); let parent_dir = asset_path.parent().unwrap();
let _sub_assets: Vec<Handle<SaveEntity>> = s let entities: Vec<Handle<SaveEntity>> = s
.lines() .lines()
.map(|line| parent_dir.join(line)) .map(|line| parent_dir.join(line))
.map(|path| load_context.load(path)) .map(|path| load_context.load(path))
.collect(); .collect();
info!("Entities: {:?}", entities);
Ok(DynamicScene::default()) Ok(SaveScene { entities })
} }
fn extensions(&self) -> &[&str] { fn extensions(&self) -> &[&str] {
@ -154,16 +158,18 @@ impl AssetLoader for SaveSceneLoader {
} }
} }
fn test_load_entity(loader: Res<AssetServer>, mut commands: Commands) { /// Testing system for loading a specific scene
let _: Handle<DynamicScene> = loader.load("editor/_.scene"); fn load_scenes(loader: Res<AssetServer>, mut commands: Commands) {
let handle: Handle<SaveEntity> = loader.load("levels/00/entities/van.entity"); let handle: Handle<SaveScene> = loader.load("scenes/00.scene");
commands.spawn((SpatialBundle { ..default() }, handle)); info!("Loading scene {:?}", handle);
commands.spawn(handle);
} }
fn spawn_loaded_entities( /// Spawns scenes with the Handle<Scene> marker
query: Query<(Entity, &Handle<SaveEntity>)>, fn spawn_scenes(
mut events: EventReader<AssetEvent<SaveEntity>>, query: Query<(Entity, &Handle<SaveScene>)>,
save_entities: Res<Assets<SaveEntity>>, mut events: EventReader<AssetEvent<SaveScene>>,
save_scenes: Res<Assets<SaveScene>>,
mut commands: Commands, mut commands: Commands,
) { ) {
events.read().for_each(|event| { events.read().for_each(|event| {
@ -171,18 +177,44 @@ fn spawn_loaded_entities(
query query
.iter() .iter()
.filter(|(_, handle)| handle.id() == *id) .filter(|(_, handle)| handle.id() == *id)
.for_each(|(entity, _handle)| { .for_each(|(entity, handle)| {
let saved = save_entities.get(*id).unwrap(); debug!("Spawning scene {:?} on {:?}", handle, entity);
let scene = save_scenes.get(handle).unwrap();
// Get entity with SaveEntity handle // Get entity with SaveEntity handle
let mut e = commands.entity(entity); let mut e = commands.entity(entity);
// Clear the entity // Clear the entity of descendants
e.despawn_descendants(); e.despawn_descendants();
// Populate with reflected components // Populate with entities
saved.components.iter().for_each(|dc| { e.with_children(|parent| {
e.insert_reflect(dc.clone_value()); scene.entities.iter().for_each(|this| {
parent.spawn(this.clone());
});
}); });
}); });
} }
}) })
} }
/// Spawns entities with the Handle<SaveEntity> component
fn spawn_entities(
events: Query<(Entity, &Handle<SaveEntity>), Added<Handle<SaveEntity>>>,
save_entities: Res<Assets<SaveEntity>>,
mut commands: Commands,
) {
events.iter().for_each(|(entity, handle)| {
debug!("Spawning entity {:?} {:?}", entity, handle);
// Get a handle on the
let mut e = commands.entity(entity);
// Get the entity asset containing reflected component
let save_entity = save_entities.get(handle).unwrap();
// Add each component to the entity
save_entity.components.iter().for_each(|component| {
e.insert_reflect(component.clone_value());
});
});
}

Loading…
Cancel
Save