Audio and interactive button states worksgit status

main
Elijah Voigt 2 years ago
parent e159346b89
commit eef3acdade

@ -5,15 +5,14 @@
// TODO: // TODO:
// * Tree Organization: GLTF contains Animations and Scenes // * Tree Organization: GLTF contains Animations and Scenes
// * Camera can only select one at a time. // * Camera can only select one at a time.
// * (easy) Load audios like current GLTFs
// * (easy) Loop audio enable/disable
// * (easy) Better Colorscheme // * (easy) Better Colorscheme
// * (easy) Interactive buttons (hover/click)
// * (medium) Visual errors for bad GLTFs // * (medium) Visual errors for bad GLTFs
// * (medium) Collapsable containers (Gltfs, Animations, Scenes, Audio Clips, Cameras) // * (medium) Collapsable containers (Gltfs, Animations, Scenes, Audio Clips, Cameras)
// * (medium) Spawn clicked scene // * (medium) Spawn clicked scene
// * (medium) Play clicked animation // * (medium) Play clicked animation
// * (idea) Use enum instead of markers for exclusive UI // * (idea) Use enum instead of markers for exclusive UI
// * (medium) Add fonts similar to Audios based on inspect-fonts
// * (hard) Add Dialogs (requires text box UI, saving, loading)
use bevy::{ use bevy::{
asset::{AssetPath, Assets}, asset::{AssetPath, Assets},
@ -57,6 +56,7 @@ fn main() {
// Audio Systems // Audio Systems
load_audio, load_audio,
unload_audio, unload_audio,
manage_audio_ui,
play_audio, play_audio,
// Level Import/Export systems // Level Import/Export systems
export_level, export_level,
@ -72,7 +72,8 @@ fn main() {
#[derive(Debug, Component)] #[derive(Debug, Component)]
enum UiRef<T> { enum UiRef<T> {
Handle(T), Handle(T),
Entity(T), // Entity(T),
// Event(T),
} }
/// UI: /// UI:
@ -135,7 +136,7 @@ fn initialize_ui(mut commands: Commands) {
fn load_bogus( fn load_bogus(
mut events: EventReader<KeyboardInput>, mut events: EventReader<KeyboardInput>,
root: Query<Entity, With<AnimationsUi>>, root: Query<Entity, (With<AnimationsUi>, Without<UiRef<Handle<AnimationClip>>>)>,
mut commands: Commands, mut commands: Commands,
) { ) {
events events
@ -197,7 +198,7 @@ fn get_fname(asset_path: AssetPath, suffixes: &[&str]) -> String {
/// This should be a separate async system /// This should be a separate async system
fn manage_gltf_ui( fn manage_gltf_ui(
mut events: EventReader<AssetEvent<Gltf>>, mut events: EventReader<AssetEvent<Gltf>>,
root: Query<Entity, With<GltfsUi>>, root: Query<Entity, (With<GltfsUi>, Without<UiRef<Handle<Gltf>>>)>,
mut commands: Commands, mut commands: Commands,
server: Res<AssetServer>, server: Res<AssetServer>,
) { ) {
@ -214,6 +215,9 @@ fn manage_gltf_ui(
_ => None, _ => None,
}) })
.for_each(|(handle, name)| { .for_each(|(handle, name)| {
root.iter().for_each(|entity| {
commands.entity(entity).log_components();
});
commands commands
.spawn(( .spawn((
GameUiButton, GameUiButton,
@ -236,7 +240,7 @@ struct ScenesUi;
/// Sync scene assets with UI /// Sync scene assets with UI
fn manage_scene_ui( fn manage_scene_ui(
mut events: EventReader<AssetEvent<Scene>>, mut events: EventReader<AssetEvent<Scene>>,
root: Query<Entity, With<ScenesUi>>, root: Query<Entity, (With<ScenesUi>, Without<UiRef<Handle<Scene>>>)>,
mut commands: Commands, mut commands: Commands,
gltfs: Res<Assets<Gltf>>, gltfs: Res<Assets<Gltf>>,
registry: Res<AssetRegistry>, registry: Res<AssetRegistry>,
@ -252,10 +256,6 @@ fn manage_scene_ui(
|gltf_handle| match gltfs.get(&gltf_handle.clone().typed::<Gltf>()) { |gltf_handle| match gltfs.get(&gltf_handle.clone().typed::<Gltf>()) {
Some(gltf) => { Some(gltf) => {
gltf.named_scenes.iter().find_map(|(name, scene_handle)| { gltf.named_scenes.iter().find_map(|(name, scene_handle)| {
info!(
"scene_handle({:?}) == handle({:?})",
scene_handle, handle
);
(scene_handle == handle).then_some(name) (scene_handle == handle).then_some(name)
}) })
} }
@ -292,7 +292,7 @@ struct AnimationsUi;
fn manage_animation_ui( fn manage_animation_ui(
mut events: EventReader<AssetEvent<AnimationClip>>, mut events: EventReader<AssetEvent<AnimationClip>>,
root: Query<Entity, With<AnimationsUi>>, root: Query<Entity, (With<AnimationsUi>, Without<UiRef<Handle<AnimationClip>>>)>,
mut commands: Commands, mut commands: Commands,
gltfs: Res<Assets<Gltf>>, gltfs: Res<Assets<Gltf>>,
registry: Res<AssetRegistry>, registry: Res<AssetRegistry>,
@ -309,10 +309,6 @@ fn manage_animation_ui(
match gltfs.get(&gltf_handle.clone().typed::<Gltf>()) { match gltfs.get(&gltf_handle.clone().typed::<Gltf>()) {
Some(gltf) => gltf.named_animations.iter().find_map( Some(gltf) => gltf.named_animations.iter().find_map(
|(name, animation_handle)| { |(name, animation_handle)| {
info!(
"animation_handle({:?}) == handle({:?})",
animation_handle, handle
);
(animation_handle == handle).then_some(name) (animation_handle == handle).then_some(name)
}, },
), ),
@ -342,7 +338,83 @@ fn manage_animation_ui(
struct AudioClipsUi; struct AudioClipsUi;
/// Drag+Drop import Audio to editor /// Drag+Drop import Audio to editor
fn load_audio() {} fn load_audio(
mut events: EventReader<FileDragAndDrop>,
server: Res<AssetServer>,
mut assets: ResMut<AssetRegistry>,
) {
events
.iter()
.filter_map(|event| match event {
FileDragAndDrop::DroppedFile { path_buf, .. } => Some(path_buf),
_ => None,
})
.for_each(|path_buf| {
let path = path_buf.as_path();
let handle = server.load_untyped(path);
assets.0.insert(handle);
});
}
fn manage_audio_ui(
mut events: EventReader<AssetEvent<AudioSource>>,
root: Query<Entity, (With<AudioClipsUi>, Without<UiRef<Handle<AudioSource>>>)>,
mut commands: Commands,
server: Res<AssetServer>,
) {
events
.iter()
.filter_map(|event| match event {
AssetEvent::Created { handle } => {
let asset_path = server
.get_handle_path(handle.clone())
.expect("Fetch Asset Path");
let name = get_fname(asset_path, &[".ogg"]);
Some((handle.clone(), String::from(name)))
}
_ => None,
})
.for_each(|(handle, name)| {
commands
.spawn((
GameUiButton,
Name::new(name),
NodeBundle { ..default() },
AudioClipsUi,
AudioBundle {
source: handle.clone(),
settings: PlaybackSettings {
mode: bevy::audio::PlaybackMode::Loop,
paused: true,
..default()
},
},
))
.set_parent(root.single());
});
}
/// Play/Loop Audio
fn play_audio(
mut events: Query<
(&Interaction, &AudioSink, &mut UiElementState),
(Changed<Interaction>, With<AudioClipsUi>),
>,
) {
events
.iter_mut()
.for_each(|(interaction, sink, mut state)| match interaction {
Interaction::Pressed => {
sink.toggle();
*state = match *state {
UiElementState::Enabled => UiElementState::Active,
_ => UiElementState::Enabled,
}
}
_ => (),
});
}
/// Remove audio from editor /// Remove audio from editor
fn unload_audio() {} fn unload_audio() {}
@ -350,9 +422,6 @@ fn unload_audio() {}
/// Spawn Scene /// Spawn Scene
fn spawn_scene() {} fn spawn_scene() {}
/// Play/Loop Audio
fn play_audio() {}
/// Export level /// Export level
fn export_level() {} fn export_level() {}

@ -1,3 +1,5 @@
/// TODO: Sorted list/set
///
use bevy::{prelude::*, window::PrimaryWindow}; use bevy::{prelude::*, window::PrimaryWindow};
pub struct GameUiPlugin; pub struct GameUiPlugin;
@ -11,11 +13,21 @@ impl Plugin for GameUiPlugin {
manage_ui_set, manage_ui_set,
manage_ui_button, manage_ui_button,
manage_cursor, manage_cursor,
manage_button_interaction,
), ),
); );
} }
} }
/// Describes the state of an element
#[derive(Debug, Component)]
pub enum UiElementState {
Enabled,
Disabled,
Active,
Error,
}
/// GameUiList for holding ordered collections of objects /// GameUiList for holding ordered collections of objects
#[derive(Debug, Component)] #[derive(Debug, Component)]
pub struct GameUiList; pub struct GameUiList;
@ -58,7 +70,7 @@ fn manage_ui_set(events: Query<(Entity, &Name), Added<GameUiSet>>, mut commands:
.entity(entity) .entity(entity)
.insert(NodeBundle { .insert(NodeBundle {
style: Style { style: Style {
// width: Val::Px(100.0), width: Val::Px(200.0),
margin: UiRect::all(Val::Px(2.0)), margin: UiRect::all(Val::Px(2.0)),
padding: UiRect::all(Val::Px(2.0)), padding: UiRect::all(Val::Px(2.0)),
border: UiRect::all(Val::Px(2.0)), border: UiRect::all(Val::Px(2.0)),
@ -87,18 +99,21 @@ fn manage_ui_button(events: Query<(Entity, &Name), Added<GameUiButton>>, mut com
events.iter().for_each(|(entity, name)| { events.iter().for_each(|(entity, name)| {
commands commands
.entity(entity) .entity(entity)
.insert(ButtonBundle { .insert((
style: Style { ButtonBundle {
margin: UiRect::all(Val::Px(2.0)), style: Style {
padding: UiRect::all(Val::Px(2.0)), margin: UiRect::all(Val::Px(2.0)),
border: UiRect::all(Val::Px(2.0)), padding: UiRect::all(Val::Px(2.0)),
justify_content: JustifyContent::Center, border: UiRect::all(Val::Px(2.0)),
justify_content: JustifyContent::Center,
..default()
},
background_color: BackgroundColor(Color::GREEN),
border_color: BorderColor(Color::BLACK),
..default() ..default()
}, },
background_color: BackgroundColor(Color::GREEN), UiElementState::Enabled,
border_color: BorderColor(Color::BLACK), ))
..default()
})
.with_children(|parent| { .with_children(|parent| {
parent.spawn(TextBundle::from_section(name, TextStyle::default())); parent.spawn(TextBundle::from_section(name, TextStyle::default()));
}); });
@ -108,14 +123,38 @@ fn manage_ui_button(events: Query<(Entity, &Name), Added<GameUiButton>>, mut com
/// Manage the cursor icon for better immersion /// Manage the cursor icon for better immersion
fn manage_cursor( fn manage_cursor(
mut primary_window: Query<&mut Window, With<PrimaryWindow>>, mut primary_window: Query<&mut Window, With<PrimaryWindow>>,
events: Query<&Interaction, With<Interaction>>, events: Query<&Interaction, (Changed<Interaction>, With<Button>)>,
) { ) {
events.iter().for_each(|event| { if !events.is_empty() {
let mut window = primary_window.single_mut(); let mut window = primary_window.single_mut();
window.cursor.icon = match event { window.cursor.icon = events
Interaction::Pressed => CursorIcon::Grabbing, .iter()
Interaction::Hovered => CursorIcon::Hand, .find(|&event| *event != Interaction::None)
Interaction::None => CursorIcon::Default, .map_or(CursorIcon::Default, |event| match event {
} Interaction::Hovered | Interaction::Pressed => CursorIcon::Hand,
}); Interaction::None => CursorIcon::Help, // Shouldn't be reachable
});
}
}
fn manage_button_interaction(
mut events: Query<
(&Interaction, &UiElementState, &mut BackgroundColor),
(Changed<Interaction>, With<Button>),
>,
) {
events
.iter_mut()
.for_each(|(interaction, state, mut bg_color)| {
bg_color.0 = match state {
UiElementState::Enabled => match interaction {
Interaction::Pressed => Color::BLACK,
Interaction::Hovered => Color::ORANGE,
Interaction::None => Color::GREEN,
},
UiElementState::Active => Color::PINK,
UiElementState::Error => Color::RED,
UiElementState::Disabled => Color::GRAY,
}
});
} }

Loading…
Cancel
Save