Added quit button, working on level import/export

main
Elijah Voigt 2 years ago
parent 27522e2209
commit 36605781e8

@ -42,12 +42,21 @@
), ),
)), )),
"editor::LevelRoot": (), "editor::LevelRoot": (),
"bevy_hierarchy::components::children::Children": ([
198,
]),
}, },
), ),
198: ( 2: (
components: {
"editor::AudioRoot": (),
},
),
191: (
components: {
"bevy_asset::handle::Handle<bevy_audio::audio_source::AudioSource>": (
id: AssetPathId(((3014684909402542266), (8823233378563626002))),
),
},
),
200: (
components: { components: {
"bevy_render::view::visibility::Visibility": Inherited, "bevy_render::view::visibility::Visibility": Inherited,
"bevy_transform::components::transform::Transform": ( "bevy_transform::components::transform::Transform": (
@ -87,10 +96,6 @@
z: 0.0, z: 0.0,
), ),
)), )),
"bevy_hierarchy::components::parent::Parent": (1),
"bevy_hierarchy::components::children::Children": ([
199,
]),
"bevy_asset::handle::Handle<bevy_scene::scene::Scene>": ( "bevy_asset::handle::Handle<bevy_scene::scene::Scene>": (
id: AssetPathId(((13241355290950327508), (2370051114748836591))), id: AssetPathId(((13241355290950327508), (2370051114748836591))),
), ),

@ -3,6 +3,21 @@
// Editor for creating Monologue Trees levels // Editor for creating Monologue Trees levels
// //
// BUGS: // BUGS:
// * When Handle<T> is loaded, the button for TargetAsset<T> should load as well
// * Exported level should preserve active camera
// * Picking new GLTF resets audio without resetting buttons
// * Error with exported scene w/ animation:
// WARN bevy_asset::asset_server: encountered an error while loading an asset:
// no registration found for type
// `alloc::vec::Vec<
// alloc::vec::Vec<
// core::option::Option<
// bevy_ecs::entity::Entity
// >>>`
// at output.scn.ron:723:23
// This is the `path_cache` field on an Animation
// ...
// Also many other components out of the box just straight up don't work...
// //
// TODO: // TODO:
// * edit textbox with actions // * edit textbox with actions
@ -16,6 +31,7 @@
use bevy::{ use bevy::{
asset::{Asset, AssetLoader, Assets, ChangeWatcher, LoadContext, LoadedAsset}, asset::{Asset, AssetLoader, Assets, ChangeWatcher, LoadContext, LoadedAsset},
audio::PlaybackMode, audio::PlaybackMode,
core_pipeline::tonemapping::DebandDither,
gltf::Gltf, gltf::Gltf,
prelude::*, prelude::*,
utils::{BoxedFuture, Duration}, utils::{BoxedFuture, Duration},
@ -56,11 +72,13 @@ fn main() {
}, },
)) ))
.register_type::<LevelRoot>() .register_type::<LevelRoot>()
.register_type::<AudioRoot>()
.init_resource::<AssetRegistry>() .init_resource::<AssetRegistry>()
.init_resource::<FontInfo>() .init_resource::<FontInfo>()
.add_asset::<Monologue>() .add_asset::<Monologue>()
.init_asset_loader::<MonologueLoader>() .init_asset_loader::<MonologueLoader>()
.add_systems(Startup, (initialize_ui, init_texts_ui, welcome_message)) .add_systems(Startup, (initialize_ui, init_texts_ui, welcome_message))
.add_systems(Update, quit.run_if(ui::activated::<QuitAction>))
.add_systems( .add_systems(
Update, Update,
( (
@ -111,7 +129,10 @@ fn main() {
level_ui, level_ui,
load_level, load_level,
export_level.run_if(ui::activated::<ExportLevel>), export_level.run_if(ui::activated::<ExportLevel>),
rehydrate_level, rehydrate_level::<Visibility, ComputedVisibility>,
rehydrate_level::<Handle<AudioSource>, PlaybackSettings>,
//rehydrate_level::<Handle<AnimationClip>, AnimationPlayer>,
//rehydrate_level::<DebandDither, Camera3d>,
), ),
) )
.run(); .run();
@ -127,12 +148,17 @@ pub struct TabRoot;
#[reflect(Component)] #[reflect(Component)]
pub struct LevelRoot; pub struct LevelRoot;
#[derive(Debug, Component, Reflect, Default)]
#[reflect(Component)]
pub struct AudioRoot;
#[derive(Debug, Component)] #[derive(Debug, Component)]
pub struct EditorCamera; pub struct EditorCamera;
fn initialize_ui(mut commands: Commands) { fn initialize_ui(mut commands: Commands) {
// Empty entity for populating the level being edited // Empty entity for populating the level being edited
commands.spawn((SpatialBundle { ..default() }, LevelRoot::default())); commands.spawn((SpatialBundle { ..default() }, LevelRoot));
commands.spawn(AudioRoot);
commands.spawn(( commands.spawn((
Camera2dBundle { ..default() }, Camera2dBundle { ..default() },
@ -372,7 +398,16 @@ fn initialize_ui(mut commands: Commands) {
ClearLevel, ClearLevel,
ui::Sorting(3), ui::Sorting(3),
ui::Title { ui::Title {
text: "Reset Level".into(), text: "Clear Level".into(),
..default()
},
));
parent.spawn((
simple_button.clone(),
QuitAction,
ui::Sorting(3),
ui::Title {
text: "Quit".into(),
..default() ..default()
}, },
)); ));
@ -446,7 +481,7 @@ mod audio {
events.iter().for_each(|event| match event { events.iter().for_each(|event| match event {
AssetEvent::Created { handle } => { AssetEvent::Created { handle } => {
info!("Asset created! {:?}", event); info!("Asset created! {:?}", event);
let id = create_asset_button( create_asset_button(
&widget, &widget,
&mut commands, &mut commands,
ui::TargetAsset { ui::TargetAsset {
@ -455,14 +490,6 @@ mod audio {
get_asset_name(&server, handle.clone()), get_asset_name(&server, handle.clone()),
None, None,
); );
commands.entity(id).insert(AudioSourceBundle {
source: handle.clone(),
settings: PlaybackSettings {
mode: PlaybackMode::Loop,
paused: true,
..default()
},
});
} }
AssetEvent::Removed { handle } => { AssetEvent::Removed { handle } => {
info!("Asset removed! {:?}", event); info!("Asset removed! {:?}", event);
@ -483,7 +510,7 @@ mod audio {
handle: handle.clone(), handle: handle.clone(),
}, },
); );
let id = create_asset_button( create_asset_button(
&widget, &widget,
&mut commands, &mut commands,
ui::TargetAsset { ui::TargetAsset {
@ -492,29 +519,55 @@ mod audio {
get_asset_name(&server, handle.clone()), get_asset_name(&server, handle.clone()),
None, None,
); );
commands.entity(id).insert(AudioSourceBundle { }
});
}
pub fn play_audio(
events: Query<&ui::TargetAsset<AudioSource>, (With<Button>, Added<ui::Active>)>,
root: Query<Entity, With<AudioRoot>>,
mut commands: Commands,
) {
events.iter().for_each(|ui::TargetAsset { handle }| {
let root = if let Ok(entity) = root.get_single() {
entity
} else {
commands.spawn(AudioRoot).id()
};
commands.entity(root).with_children(|parent| {
parent.spawn(AudioSourceBundle {
source: handle.clone(), source: handle.clone(),
settings: PlaybackSettings { settings: PlaybackSettings {
mode: PlaybackMode::Loop, mode: PlaybackMode::Loop,
paused: true, paused: false,
..default() ..default()
}, },
}); });
}
}); });
})
} }
pub fn play_audio(events: Query<&AudioSink, (With<Button>, Added<ui::Active>)>) { pub fn pause_audio(
events.iter().for_each(|sink| { mut events: RemovedComponents<ui::Active>,
sink.play(); targets: Query<&ui::TargetAsset<AudioSource>>,
}); sources: Query<(Entity, &Handle<AudioSource>), With<AudioSink>>,
mut commands: Commands,
) {
// Iterate over the audio button events
events.iter().for_each(|entity| {
// Get the handle for the audio source we want to despawn
if let Ok(ui::TargetAsset { handle }) = targets.get(entity) {
if let Some(entity) = sources.iter().find_map(|(entity, source_handle)| {
if source_handle == handle {
Some(entity)
} else {
None
} }
}) {
pub fn pause_audio(mut events: RemovedComponents<ui::Active>, sinks: Query<&AudioSink>) { commands.entity(entity).despawn_recursive();
events }
.iter() }
.filter_map(|entity| sinks.get(entity).ok()) });
.for_each(|sink| sink.stop());
} }
} }
@ -710,7 +763,9 @@ mod gltf {
mut commands: Commands, mut commands: Commands,
) { ) {
events.iter().for_each(|_| { events.iter().for_each(|_| {
commands.entity(root.single()).despawn_descendants(); root.iter().for_each(|entity| {
commands.entity(entity).despawn_descendants();
});
}); });
} }
} }
@ -1369,7 +1424,9 @@ mod reset {
actives.iter().for_each(|entity| { actives.iter().for_each(|entity| {
commands.entity(entity).remove::<ui::Active>(); commands.entity(entity).remove::<ui::Active>();
}); });
commands.entity(root.single()).despawn_descendants(); root.iter().for_each(|entity| {
commands.entity(entity).despawn_descendants();
});
}) })
} }
@ -1411,9 +1468,6 @@ mod reset {
pub use level::*; pub use level::*;
mod level { mod level {
use std::fs::File;
use std::io::Write;
use bevy::tasks::IoTaskPool; use bevy::tasks::IoTaskPool;
use super::*; use super::*;
@ -1487,7 +1541,9 @@ mod level {
mut commands: Commands, mut commands: Commands,
) { ) {
events.iter().for_each(|ui::TargetAsset { handle }| { events.iter().for_each(|ui::TargetAsset { handle }| {
commands.entity(root.single()).despawn_recursive(); root.iter().for_each(|entity| {
commands.entity(entity).despawn_recursive();
});
commands.spawn(DynamicSceneBundle { commands.spawn(DynamicSceneBundle {
scene: handle.clone(), scene: handle.clone(),
..default() ..default()
@ -1496,7 +1552,8 @@ mod level {
} }
pub fn export_level( pub fn export_level(
root: Query<Entity, With<LevelRoot>>, level_root: Query<Entity, With<LevelRoot>>,
audio_root: Query<Entity, With<AudioRoot>>,
children: Query<&Children>, children: Query<&Children>,
world: &World, world: &World,
) { ) {
@ -1505,31 +1562,48 @@ mod level {
builder.deny_all_resources(); builder.deny_all_resources();
// builder.allow_all(); // Exclude computed visibility
builder.deny::<ComputedVisibility>(); builder.deny_all();
// Level administrivia // Level administrivia
builder.allow::<LevelRoot>(); builder.allow::<LevelRoot>();
builder.allow::<AudioRoot>();
// Scene components // Scene components
builder.allow::<Handle<Scene>>(); builder.allow::<Handle<Scene>>();
builder.allow::<Visibility>();
// Spatial components
builder.allow::<Transform>(); builder.allow::<Transform>();
builder.allow::<GlobalTransform>(); builder.allow::<GlobalTransform>();
builder.allow::<Visibility>();
// Audio components // Audio components
builder.allow::<Handle<AudioSource>>(); builder.allow::<Handle<AudioSource>>();
builder.allow::<PlaybackSettings>(); builder.allow::<PlaybackSettings>();
root.iter().for_each(|r| { // Text components
builder.allow::<Handle<Font>>();
builder.allow::<Handle<Monologue>>();
level_root.iter().for_each(|level| {
// Extract the level root // Extract the level root
builder.extract_entity(r); builder.extract_entity(level);
match children.get(r) { if let Ok(kids) = children.get(level) {
Ok(cs) => { builder.extract_entities(kids.into_iter().map(|&e| e));
builder.extract_entities(cs.iter().map(|&entity| entity)); } else {
warn!("Level is empty!");
} }
Err(e) => warn!("Empty level! {:?}", e), });
audio_root.iter().for_each(|audio| {
// Extract the level root
builder.extract_entity(audio);
if let Ok(kids) = children.get(audio) {
builder.extract_entities(kids.into_iter().map(|&e| e));
} else {
warn!("Audio is empty!");
} }
}); });
@ -1542,21 +1616,32 @@ mod level {
IoTaskPool::get() IoTaskPool::get()
.spawn(async move { .spawn(async move {
// Write the scene RON data to file // Write the scene RON data to file
File::create(format!("assets/output.scn.ron")) std::fs::write(format!("assets/output.scn.ron"), serialized.as_bytes())
.and_then(|mut file| file.write(serialized.as_bytes()))
.expect("Error while writing scene to file"); .expect("Error while writing scene to file");
}) })
.detach(); .detach();
} }
pub fn rehydrate_level( pub fn rehydrate_level<W: Component, WO: Component + Default>(
events: Query<Entity, (Added<Visibility>, Without<ComputedVisibility>)>, events: Query<Entity, (Added<W>, Without<WO>)>,
mut commands: Commands, mut commands: Commands,
) { ) {
events.iter().for_each(|entity| { events.iter().for_each(|entity| {
commands commands.entity(entity).insert(WO::default());
.entity(entity)
.insert(ComputedVisibility::default());
}); });
} }
} }
use quit::*;
mod quit {
use super::*;
use bevy::app::AppExit;
#[derive(Debug, Component, Default)]
pub struct QuitAction;
pub fn quit(mut exit: EventWriter<AppExit>) {
exit.send(AppExit);
}
}

Loading…
Cancel
Save