refactor some editor functions to files

Focusing on getting button -> event refactor.
As well as spawned thing -> button active refactor.

Both should allow levels to work propery, setting buttons active and
such.

Once that's working just iron out bugs like double spawn.
main
Elijah Voigt 2 years ago
parent 2433f6386d
commit 2b9f96d4b4

@ -2,6 +2,13 @@
// //
// Editor for creating Monologue Trees levels // Editor for creating Monologue Trees levels
// //
// REFACTOR:
// * Monologue -> Event -> Active -> UI
// * Scene -> Event -> Active -> UI
// * Animation -> Event -> Active -> UI
// * Font -> Event -> Active -> UI
// * Gltf -> Event -> Active -> UI
//
// BUGS: // BUGS:
// * When Handle<T> is loaded, the button for TargetAsset<T> should load as well // * When Handle<T> is loaded, the button for TargetAsset<T> should load as well
// * Exported level should preserve active camera // * Exported level should preserve active camera
@ -18,12 +25,15 @@
use bevy::{ use bevy::{
asset::{Asset, AssetLoader, Assets, ChangeWatcher, LoadContext, LoadedAsset}, asset::{Asset, AssetLoader, Assets, ChangeWatcher, LoadContext, LoadedAsset},
audio::PlaybackMode,
gltf::Gltf, gltf::Gltf,
prelude::*, prelude::*,
utils::{BoxedFuture, Duration}, utils::{BoxedFuture, Duration},
}; };
use monologue_trees::{debug::*, ui}; use monologue_trees::{
debug::*,
editor::{assets::*, audio::*, *},
ui,
};
const WELCOME_MESSAGES: &'static [&'static str] = &[ const WELCOME_MESSAGES: &'static [&'static str] = &[
"Welcome to the Monologue Trees editor!", "Welcome to the Monologue Trees editor!",
@ -82,7 +92,16 @@ fn main() {
(remove_scenes_ui, add_scenes_ui, control_active_scenes), (remove_scenes_ui, add_scenes_ui, control_active_scenes),
) )
.add_systems(Update, (cameras_ui, manage_active_camera, fallback_camera)) .add_systems(Update, (cameras_ui, manage_active_camera, fallback_camera))
.add_systems(Update, (audio_ui, play_audio, pause_audio, control_audio)) .add_systems(
Update,
(
audio_ui,
ui_control_audio,
ui_active::<AudioSource>,
ui_inactive::<AudioSource>,
control_audio,
),
)
.add_systems( .add_systems(
Update, Update,
( (
@ -167,9 +186,6 @@ fn main() {
.run(); .run();
} }
#[derive(Resource, Default)]
pub struct AssetRegistry(Vec<HandleUntyped>);
#[derive(Debug, Component)] #[derive(Debug, Component)]
pub struct TabRoot; pub struct TabRoot;
@ -177,10 +193,6 @@ 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;
@ -271,46 +283,22 @@ fn initialize_ui(mut commands: Commands) {
ui::Sorting(2), ui::Sorting(2),
)) ))
.with_children(|parent| { .with_children(|parent| {
content_containers.push(spawn_tab_container::<FontWidget>( content_containers
"Font", .push(spawn_tab_container::<FontWidget>("Font", parent));
parent, content_containers
ui::Select::Single, .push(spawn_tab_container::<MonologueWidget>("Monologue", parent));
)); content_containers
content_containers.push(spawn_tab_container::<MonologueWidget>( .push(spawn_tab_container::<AudioWidget>("Audio", parent));
"Monologue", content_containers
parent, .push(spawn_tab_container::<LevelWidget>("Level", parent));
ui::Select::Single, content_containers
)); .push(spawn_tab_container::<GltfWidget>("Gltf", parent));
content_containers.push(spawn_tab_container::<AudioWidget>( content_containers
"Audio", .push(spawn_tab_container::<SceneWidget>("Scene", parent));
parent, content_containers
ui::Select::Multi, .push(spawn_tab_container::<CameraWidget>("Camera", parent));
)); content_containers
content_containers.push(spawn_tab_container::<LevelWidget>( .push(spawn_tab_container::<AnimationWidget>("Animation", parent));
"Level",
parent,
ui::Select::Single,
));
content_containers.push(spawn_tab_container::<GltfWidget>(
"Gltf",
parent,
ui::Select::Single,
));
content_containers.push(spawn_tab_container::<SceneWidget>(
"Scene",
parent,
ui::Select::Single,
));
content_containers.push(spawn_tab_container::<CameraWidget>(
"Camera",
parent,
ui::Select::Single,
));
content_containers.push(spawn_tab_container::<AnimationWidget>(
"Animation",
parent,
ui::Select::Multi,
));
}); });
// Container for tabs that open/close containers // Container for tabs that open/close containers
@ -548,7 +536,6 @@ fn welcome_message(mut writer: EventWriter<ui::Alert>) {
fn spawn_tab_container<T: Default + Component>( fn spawn_tab_container<T: Default + Component>(
title: &'static str, title: &'static str,
parent: &mut ChildBuilder, parent: &mut ChildBuilder,
select: ui::Select,
) -> (String, Entity) { ) -> (String, Entity) {
( (
title.into(), title.into(),
@ -571,277 +558,11 @@ fn spawn_tab_container<T: Default + Component>(
T::default(), T::default(),
ui::Scroll, ui::Scroll,
Interaction::default(), Interaction::default(),
select,
)) ))
.id(), .id(),
) )
} }
use audio::*;
mod audio {
use super::*;
#[derive(Debug, Component, Default)]
pub struct AudioWidget;
pub fn audio_ui(
mut events: EventReader<AssetEvent<AudioSource>>,
mut commands: Commands,
widget: Query<Entity, With<AudioWidget>>,
current: Query<(Entity, &ui::TargetAsset<AudioSource>)>,
server: Res<AssetServer>,
) {
events.iter().for_each(|event| match event {
AssetEvent::Created { handle } => {
info!("Asset created! {:?}", event);
create_asset_button(
&widget,
&mut commands,
ui::TargetAsset {
handle: handle.clone(),
},
get_asset_name(&server, handle.clone()),
None,
);
}
AssetEvent::Removed { handle } => {
info!("Asset removed! {:?}", event);
destroy_asset_button(
&current,
&mut commands,
&ui::TargetAsset {
handle: handle.clone(),
},
);
}
AssetEvent::Modified { handle } => {
info!("Asset modified! {:?}", event);
destroy_asset_button(
&current,
&mut commands,
&ui::TargetAsset {
handle: handle.clone(),
},
);
create_asset_button(
&widget,
&mut commands,
ui::TargetAsset {
handle: handle.clone(),
},
get_asset_name(&server, handle.clone()),
None,
);
}
});
}
#[derive(Debug, Event)]
pub enum ControlAudio {
Play(Handle<AudioSource>),
Loop(Handle<AudioSource>),
Stop(Handle<AudioSource>),
}
pub fn control_audio(
mut events: EventReader<ControlAudio>,
root: Query<Entity, With<AudioRoot>>,
mut commands: Commands,
sources: Query<(Entity, &Handle<AudioSource>), With<AudioSink>>,
) {
events.iter().for_each(|event| match event {
ControlAudio::Play(handle) => (),
ControlAudio::Loop(handle) => {
info!("Looping audio {:?}", 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: false,
..default()
},
});
info!("Done spawning");
});
}
ControlAudio::Stop(handle) => {
info!("Stopping audio {:?}", handle);
sources
.iter()
.find_map(|(entity, source_handle)| {
if source_handle == handle {
Some(entity)
} else {
None
}
})
.iter()
.for_each(|&entity| {
commands.entity(entity).despawn_recursive();
info!("Done despawning");
});
}
});
}
pub fn play_audio(
events: Query<&ui::TargetAsset<AudioSource>, (With<Button>, Added<ui::Active>)>,
mut writer: EventWriter<ControlAudio>,
) {
events.iter().for_each(|ui::TargetAsset { handle }| {
writer.send(ControlAudio::Loop(handle.clone()));
})
}
pub fn pause_audio(
mut events: RemovedComponents<ui::Active>,
targets: Query<&ui::TargetAsset<AudioSource>>,
mut writer: EventWriter<ControlAudio>,
) {
// 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) {
writer.send(ControlAudio::Stop(handle.clone()));
}
});
}
}
use assets::*;
mod assets {
use super::*;
#[derive(Debug, Component)]
pub struct ReloadAssets;
pub fn reload_assets(
server: Res<AssetServer>,
mut registry: ResMut<AssetRegistry>,
mut writer: EventWriter<ui::Alert>,
) {
match server.load_folder(".") {
Ok(handles) => registry.0 = handles,
Err(e) => writer.send(ui::Alert::Warn(format!(
"Could not find `assets` folder!\n{:?}",
e
))),
}
}
pub fn get_asset_name<T: Asset>(server: &AssetServer, handle: Handle<T>) -> String {
if let Some(asset_path) = server.get_handle_path(handle.clone()) {
if let Some(stem) = asset_path.path().file_stem() {
if let Some(val) = stem.to_str() {
String::from(val)
} else {
String::from("???")
}
} else {
String::from("???")
}
} else {
String::from("???")
}
}
pub fn create_asset_button<A: Asset, C: Component>(
root: &Query<Entity, With<C>>,
commands: &mut Commands,
target: ui::TargetAsset<A>,
name: String,
font: Option<Handle<Font>>,
) -> Entity {
commands
.spawn((
target,
ButtonBundle {
style: Style {
border: UiRect::all(Val::Px(1.0)),
margin: UiRect::all(Val::Px(1.0)),
padding: UiRect::all(Val::Px(1.0)),
..default()
},
border_color: Color::BLACK.into(),
..default()
},
ui::Title {
text: name,
font: font.clone(),
},
))
.set_parent(root.single())
.id()
}
pub fn create_entity_button<C: Component>(
root: &Query<Entity, With<C>>,
commands: &mut Commands,
target: ui::TargetEntity,
name: String,
) -> Entity {
commands
.spawn((
target,
ButtonBundle {
style: Style {
border: UiRect::all(Val::Px(1.0)),
margin: UiRect::all(Val::Px(1.0)),
padding: UiRect::all(Val::Px(1.0)),
..default()
},
border_color: Color::BLACK.into(),
..default()
},
ui::Title {
text: name,
..default()
},
))
.set_parent(root.single())
.id()
}
pub fn destroy_asset_button<A: Asset>(
current: &Query<(Entity, &ui::TargetAsset<A>)>,
commands: &mut Commands,
target: &ui::TargetAsset<A>,
) {
if let Some(entity) = current.iter().find_map(|(entity, this)| {
if this.handle == target.handle {
Some(entity)
} else {
None
}
}) {
commands.entity(entity).despawn_recursive();
}
}
pub fn destroy_entity_button(
current: &Query<(Entity, &ui::TargetEntity)>,
commands: &mut Commands,
target: &ui::TargetEntity,
) {
if let Some(entity) = current.iter().find_map(|(entity, this)| {
if this.entity == target.entity {
Some(entity)
} else {
None
}
}) {
commands.entity(entity).despawn_recursive();
}
}
}
use gltf::*; use gltf::*;
mod gltf { mod gltf {
use super::*; use super::*;
@ -979,20 +700,24 @@ mod scenes {
// A scene button was marked inactive // A scene button was marked inactive
removed.iter().for_each(|entity| { removed.iter().for_each(|entity| {
// Get the handle associated with that button // Get the handle associated with that button
if let Ok(ui::TargetAsset { handle }) = scene_refs.get(entity) { scene_refs
if let Some(entity) = scenes.iter().find_map(|(entity, this_handle)| { .get(entity)
if this_handle == handle { .iter()
Some(entity) .for_each(|ui::TargetAsset { handle }| {
} else { scenes
None .iter()
} .find_map(|(entity, this_handle)| (this_handle == handle).then_some(entity))
}) { .iter()
.for_each(|&entity| {
commands.entity(entity).despawn_recursive(); commands.entity(entity).despawn_recursive();
} });
} });
}); });
added.iter().for_each(|entity| { added.iter().for_each(|entity| {
if let Ok(ui::TargetAsset { handle }) = scene_refs.get(entity) { scene_refs
.get(entity)
.iter()
.for_each(|ui::TargetAsset { handle }| {
info!("Spawning Scene {:?}", handle); info!("Spawning Scene {:?}", handle);
commands commands
.entity(level_root.single()) .entity(level_root.single())
@ -1002,7 +727,7 @@ mod scenes {
..default() ..default()
}); });
}); });
} });
}); });
} }
} }
@ -1058,11 +783,7 @@ mod animations {
.iter() .iter()
.flat_map(|(_, gltf)| gltf.named_animations.iter()) .flat_map(|(_, gltf)| gltf.named_animations.iter())
.filter_map(|(clip_name, handle)| { .filter_map(|(clip_name, handle)| {
if let Some(clip) = clips.get(&handle) { clips.get(&handle).map(|clip| (clip_name, handle, clip))
Some((clip_name, handle, clip))
} else {
None
}
}) })
.filter(|(_, _, clip)| clip.compatible_with(player_name)) .filter(|(_, _, clip)| clip.compatible_with(player_name))
.for_each(|(clip_name, handle, _)| { .for_each(|(clip_name, handle, _)| {

@ -0,0 +1,121 @@
use crate::ui;
use bevy::{asset::Asset, prelude::*};
#[derive(Resource, Default)]
pub struct AssetRegistry(pub Vec<HandleUntyped>);
#[derive(Debug, Component)]
pub struct ReloadAssets;
pub fn reload_assets(
server: Res<AssetServer>,
mut registry: ResMut<AssetRegistry>,
mut writer: EventWriter<ui::Alert>,
) {
match server.load_folder(".") {
Ok(handles) => registry.0 = handles,
Err(e) => writer.send(ui::Alert::Warn(format!(
"Could not find `assets` folder!\n{:?}",
e
))),
}
}
pub fn get_asset_name<T: Asset>(server: &AssetServer, handle: Handle<T>) -> String {
if let Some(asset_path) = server.get_handle_path(handle.clone()) {
if let Some(stem) = asset_path.path().file_stem() {
if let Some(val) = stem.to_str() {
String::from(val)
} else {
String::from("???")
}
} else {
String::from("???")
}
} else {
String::from("???")
}
}
pub fn create_asset_button<A: Asset, C: Component>(
root: &Query<Entity, With<C>>,
commands: &mut Commands,
target: ui::TargetAsset<A>,
name: String,
font: Option<Handle<Font>>,
) -> Entity {
commands
.spawn((
target,
ButtonBundle {
style: Style {
border: UiRect::all(Val::Px(1.0)),
margin: UiRect::all(Val::Px(1.0)),
padding: UiRect::all(Val::Px(1.0)),
..default()
},
border_color: Color::BLACK.into(),
..default()
},
ui::Title {
text: name,
font: font.clone(),
},
))
.set_parent(root.single())
.id()
}
pub fn create_entity_button<C: Component>(
root: &Query<Entity, With<C>>,
commands: &mut Commands,
target: ui::TargetEntity,
name: String,
) -> Entity {
commands
.spawn((
target,
ButtonBundle {
style: Style {
border: UiRect::all(Val::Px(1.0)),
margin: UiRect::all(Val::Px(1.0)),
padding: UiRect::all(Val::Px(1.0)),
..default()
},
border_color: Color::BLACK.into(),
..default()
},
ui::Title {
text: name,
..default()
},
))
.set_parent(root.single())
.id()
}
pub fn destroy_asset_button<A: Asset>(
current: &Query<(Entity, &ui::TargetAsset<A>)>,
commands: &mut Commands,
target: &ui::TargetAsset<A>,
) {
if let Some(entity) = current
.iter()
.find_map(|(entity, this)| (this.handle == target.handle).then_some(entity))
{
commands.entity(entity).despawn_recursive();
}
}
pub fn destroy_entity_button(
current: &Query<(Entity, &ui::TargetEntity)>,
commands: &mut Commands,
target: &ui::TargetEntity,
) {
if let Some(entity) = current
.iter()
.find_map(|(entity, this)| (this.entity == target.entity).then_some(entity))
{
commands.entity(entity).despawn_recursive();
}
}

@ -0,0 +1,138 @@
use crate::{editor::assets::*, ui};
use bevy::{audio::PlaybackMode, prelude::*};
#[derive(Debug, Component, Reflect, Default)]
#[reflect(Component)]
pub struct AudioRoot;
#[derive(Debug, Component, Default)]
pub struct AudioWidget;
pub fn audio_ui(
mut events: EventReader<AssetEvent<AudioSource>>,
mut commands: Commands,
widget: Query<Entity, With<AudioWidget>>,
current: Query<(Entity, &ui::TargetAsset<AudioSource>)>,
server: Res<AssetServer>,
) {
events.iter().for_each(|event| match event {
AssetEvent::Created { handle } => {
info!("Asset created! {:?}", event);
create_asset_button(
&widget,
&mut commands,
ui::TargetAsset {
handle: handle.clone(),
},
get_asset_name(&server, handle.clone()),
None,
);
}
AssetEvent::Removed { handle } => {
info!("Asset removed! {:?}", event);
destroy_asset_button(
&current,
&mut commands,
&ui::TargetAsset {
handle: handle.clone(),
},
);
}
AssetEvent::Modified { handle } => {
info!("Asset modified! {:?}", event);
destroy_asset_button(
&current,
&mut commands,
&ui::TargetAsset {
handle: handle.clone(),
},
);
create_asset_button(
&widget,
&mut commands,
ui::TargetAsset {
handle: handle.clone(),
},
get_asset_name(&server, handle.clone()),
None,
);
}
});
}
#[derive(Debug, Event)]
pub enum ControlAudio {
Loop(Handle<AudioSource>),
Stop(Handle<AudioSource>),
}
pub fn control_audio(
mut events: EventReader<ControlAudio>,
root: Query<Entity, With<AudioRoot>>,
mut commands: Commands,
sources: Query<(Entity, &Handle<AudioSource>), With<AudioSink>>,
) {
events.iter().for_each(|event| match event {
ControlAudio::Loop(handle) => {
info!("Looping audio {:?}", 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: false,
..default()
},
});
info!("Done spawning");
});
}
ControlAudio::Stop(handle) => {
info!("Stopping audio {:?}", handle);
sources
.iter()
.find_map(|(entity, source_handle)| {
if source_handle == handle {
Some(entity)
} else {
None
}
})
.iter()
.for_each(|&entity| {
commands.entity(entity).despawn_recursive();
info!("Done despawning");
});
}
});
}
pub fn ui_control_audio(
events: Query<
(
&Interaction,
&ui::TargetAsset<AudioSource>,
Option<&ui::Active>,
),
(With<Button>, Changed<Interaction>),
>,
mut writer: EventWriter<ControlAudio>,
) {
events
.iter()
.filter_map(
|(interaction, ui::TargetAsset { handle }, active)| match interaction {
Interaction::Pressed => Some((handle, active)),
_ => None,
},
)
.for_each(|(handle, active)| match active {
Some(_) => writer.send(ControlAudio::Stop(handle.clone())),
None => writer.send(ControlAudio::Loop(handle.clone())),
});
}

@ -0,0 +1,47 @@
pub mod audio;
pub mod assets;
use crate::ui;
use bevy::{asset::Asset, prelude::*};
pub fn ui_active<T: Asset>(
events: Query<&Handle<T>, Added<Handle<T>>>,
buttons: Query<(Entity, &ui::TargetAsset<T>)>,
mut commands: Commands,
) {
events.iter().for_each(|this_handle| {
buttons
.iter()
.find_map(|(entity, ui::TargetAsset { handle })| {
if handle == this_handle {
Some(entity)
} else {
None
}
})
.and_then(|entity| {
commands.entity(entity).insert(ui::Active);
Some(())
});
});
}
pub fn ui_inactive<T: Asset>(
mut events: RemovedComponents<Handle<T>>,
targets: Query<(Entity, &ui::TargetAsset<T>), With<ui::Active>>,
sources: Query<&Handle<T>>,
mut commands: Commands,
) {
events.iter().for_each(|_| {
targets
.iter()
.find_map(|(entity, ui::TargetAsset { handle })| {
(!sources.iter().any(|this_handle| this_handle == handle)).then_some(entity)
})
.and_then(|entity| {
commands.entity(entity).remove::<ui::Active>();
Some(())
});
});
}

@ -3,3 +3,5 @@ pub mod debug;
pub mod text; pub mod text;
pub mod ui; pub mod ui;
pub mod editor;

@ -452,26 +452,28 @@ mod buttons {
.get(parent.get()) .get(parent.get())
.iter() .iter()
.for_each(|(select, children)| match interaction { .for_each(|(select, children)| match interaction {
Interaction::Pressed => { Interaction::Pressed => match select {
match active { Select::None => (),
Some(_) => { Select::Multi | Select::Action => {
if active.is_some() {
commands.entity(entity).remove::<Active>(); commands.entity(entity).remove::<Active>();
} } else {
None => {
commands.entity(entity).insert(Active); commands.entity(entity).insert(Active);
} }
} }
match select {
Select::Multi | Select::Action => (),
Select::Single => { Select::Single => {
if active.is_some() {
commands.entity(entity).remove::<Active>();
} else {
commands.entity(entity).insert(Active);
}
children.iter().filter(|&child| *child != entity).for_each( children.iter().filter(|&child| *child != entity).for_each(
|&child| { |&child| {
commands.entity(child).remove::<Active>(); commands.entity(child).remove::<Active>();
}, },
); );
} }
} },
}
// A silly hack to get actions to maintain the active tag for 1+ frames // A silly hack to get actions to maintain the active tag for 1+ frames
Interaction::None => match select { Interaction::None => match select {
Select::Action => { Select::Action => {

Loading…
Cancel
Save