Added quit button, working on level import/export

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

@ -42,12 +42,21 @@
),
)),
"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: {
"bevy_render::view::visibility::Visibility": Inherited,
"bevy_transform::components::transform::Transform": (
@ -87,10 +96,6 @@
z: 0.0,
),
)),
"bevy_hierarchy::components::parent::Parent": (1),
"bevy_hierarchy::components::children::Children": ([
199,
]),
"bevy_asset::handle::Handle<bevy_scene::scene::Scene>": (
id: AssetPathId(((13241355290950327508), (2370051114748836591))),
),

@ -3,6 +3,21 @@
// Editor for creating Monologue Trees levels
//
// 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:
// * edit textbox with actions
@ -16,6 +31,7 @@
use bevy::{
asset::{Asset, AssetLoader, Assets, ChangeWatcher, LoadContext, LoadedAsset},
audio::PlaybackMode,
core_pipeline::tonemapping::DebandDither,
gltf::Gltf,
prelude::*,
utils::{BoxedFuture, Duration},
@ -56,11 +72,13 @@ fn main() {
},
))
.register_type::<LevelRoot>()
.register_type::<AudioRoot>()
.init_resource::<AssetRegistry>()
.init_resource::<FontInfo>()
.add_asset::<Monologue>()
.init_asset_loader::<MonologueLoader>()
.add_systems(Startup, (initialize_ui, init_texts_ui, welcome_message))
.add_systems(Update, quit.run_if(ui::activated::<QuitAction>))
.add_systems(
Update,
(
@ -111,7 +129,10 @@ fn main() {
level_ui,
load_level,
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();
@ -127,12 +148,17 @@ pub struct TabRoot;
#[reflect(Component)]
pub struct LevelRoot;
#[derive(Debug, Component, Reflect, Default)]
#[reflect(Component)]
pub struct AudioRoot;
#[derive(Debug, Component)]
pub struct EditorCamera;
fn initialize_ui(mut commands: Commands) {
// Empty entity for populating the level being edited
commands.spawn((SpatialBundle { ..default() }, LevelRoot::default()));
commands.spawn((SpatialBundle { ..default() }, LevelRoot));
commands.spawn(AudioRoot);
commands.spawn((
Camera2dBundle { ..default() },
@ -372,7 +398,16 @@ fn initialize_ui(mut commands: Commands) {
ClearLevel,
ui::Sorting(3),
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()
},
));
@ -446,7 +481,7 @@ mod audio {
events.iter().for_each(|event| match event {
AssetEvent::Created { handle } => {
info!("Asset created! {:?}", event);
let id = create_asset_button(
create_asset_button(
&widget,
&mut commands,
ui::TargetAsset {
@ -455,14 +490,6 @@ mod audio {
get_asset_name(&server, handle.clone()),
None,
);
commands.entity(id).insert(AudioSourceBundle {
source: handle.clone(),
settings: PlaybackSettings {
mode: PlaybackMode::Loop,
paused: true,
..default()
},
});
}
AssetEvent::Removed { handle } => {
info!("Asset removed! {:?}", event);
@ -483,7 +510,7 @@ mod audio {
handle: handle.clone(),
},
);
let id = create_asset_button(
create_asset_button(
&widget,
&mut commands,
ui::TargetAsset {
@ -492,30 +519,56 @@ mod audio {
get_asset_name(&server, handle.clone()),
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(),
settings: PlaybackSettings {
mode: PlaybackMode::Loop,
paused: true,
paused: false,
..default()
},
});
}
});
});
})
}
pub fn play_audio(events: Query<&AudioSink, (With<Button>, Added<ui::Active>)>) {
events.iter().for_each(|sink| {
sink.play();
pub fn pause_audio(
mut events: RemovedComponents<ui::Active>,
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
}
}) {
commands.entity(entity).despawn_recursive();
}
}
});
}
pub fn pause_audio(mut events: RemovedComponents<ui::Active>, sinks: Query<&AudioSink>) {
events
.iter()
.filter_map(|entity| sinks.get(entity).ok())
.for_each(|sink| sink.stop());
}
}
use assets::*;
@ -710,7 +763,9 @@ mod gltf {
mut commands: Commands,
) {
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| {
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::*;
mod level {
use std::fs::File;
use std::io::Write;
use bevy::tasks::IoTaskPool;
use super::*;
@ -1487,7 +1541,9 @@ mod level {
mut commands: Commands,
) {
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 {
scene: handle.clone(),
..default()
@ -1496,7 +1552,8 @@ mod 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>,
world: &World,
) {
@ -1505,31 +1562,48 @@ mod level {
builder.deny_all_resources();
// builder.allow_all();
builder.deny::<ComputedVisibility>();
// Exclude computed visibility
builder.deny_all();
// Level administrivia
builder.allow::<LevelRoot>();
builder.allow::<AudioRoot>();
// Scene components
builder.allow::<Handle<Scene>>();
builder.allow::<Visibility>();
// Spatial components
builder.allow::<Transform>();
builder.allow::<GlobalTransform>();
builder.allow::<Visibility>();
// Audio components
builder.allow::<Handle<AudioSource>>();
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
builder.extract_entity(r);
builder.extract_entity(level);
match children.get(r) {
Ok(cs) => {
builder.extract_entities(cs.iter().map(|&entity| entity));
}
Err(e) => warn!("Empty level! {:?}", e),
if let Ok(kids) = children.get(level) {
builder.extract_entities(kids.into_iter().map(|&e| e));
} else {
warn!("Level is empty!");
}
});
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()
.spawn(async move {
// Write the scene RON data to file
File::create(format!("assets/output.scn.ron"))
.and_then(|mut file| file.write(serialized.as_bytes()))
std::fs::write(format!("assets/output.scn.ron"), serialized.as_bytes())
.expect("Error while writing scene to file");
})
.detach();
}
pub fn rehydrate_level(
events: Query<Entity, (Added<Visibility>, Without<ComputedVisibility>)>,
pub fn rehydrate_level<W: Component, WO: Component + Default>(
events: Query<Entity, (Added<W>, Without<WO>)>,
mut commands: Commands,
) {
events.iter().for_each(|entity| {
commands
.entity(entity)
.insert(ComputedVisibility::default());
commands.entity(entity).insert(WO::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