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
pub(crate) mod camera;
/// ECS scheduling `run_if` conditions
@ -12,7 +14,6 @@ pub(crate) mod prelude;
pub(crate) mod save;
/// Window handling
pub(crate) mod window;
/// Save file parsing
pub(crate) mod parser;
@ -26,10 +27,10 @@ fn main() {
exit_condition: bevy::window::ExitCondition::OnPrimaryClosed,
..default()
}))
.add_plugins(camera::CameraPlugin)
.add_plugins(editor::EditorPlugin)
.add_plugins(menu::MenuPlugin)
// .add_plugins(camera::CameraPlugin)
// .add_plugins(editor::EditorPlugin)
// .add_plugins(menu::MenuPlugin)
// .add_plugins(window::WindowPlugin)
.add_plugins(save::SavePlugin)
.add_plugins(window::WindowPlugin)
.run();
}

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

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

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