move actions to their own UI element

main
Elijah Voigt 2 years ago
parent e6ceffa8d1
commit 5952ee5608

@ -3,30 +3,19 @@
// Editor for creating Monologue Trees levels
//
// BUGS:
// * Not loading scenes with Active refactor
// * Not pause animations with Active refactor
// * Camera order ambiguity
// * Load new GLTF -> Despawn all level entities
//
// TODO:
// * Disable auto start/load
// * (hard) Harden Active Camera
// * (medium) Despawn entire scene when GLTF changed?
// * (medium) Select Font -> "Default Font" Resource
// * (medium) Pre-compute animation target entities
// * (medium) Animation buttons only visible when playable
// * (easy) Clear button to wipe spawned scene
// * Better handle hide/close monologue
// * (brutal) export level
// * (hard) import level
//
// Asset types:
// * Audios (done)
// * Loop individual (done)
// * Gltfs (doing)
// * Scenes
// * Animations
// * Play/Pause all
// * Fonts (done)
// * Monologues (done)
use bevy::{
asset::{Asset, AssetLoader, Assets, ChangeWatcher, LoadContext, LoadedAsset},
@ -72,8 +61,6 @@ fn main() {
.init_resource::<AssetRegistry>()
.add_asset::<Monologue>()
.init_asset_loader::<MonologueLoader>()
.add_event::<CustomAssetEvent<Scene>>()
.add_event::<CustomAssetEvent<AnimationClip>>()
.add_systems(Startup, (initialize_ui, init_texts_ui, welcome_message))
.add_systems(
Update,
@ -89,16 +76,8 @@ fn main() {
Update,
(remove_scenes_ui, add_scenes_ui, control_active_scenes),
)
.add_systems(
Update,
(
cameras_ui,
manage_active_camera,
control_active_camera,
fallback_camera,
),
)
.add_systems(Update, (audio_ui, play_audio))
.add_systems(Update, (cameras_ui, manage_active_camera, fallback_camera))
.add_systems(Update, (audio_ui, play_audio, pause_audio))
.add_systems(
Update,
(
@ -106,7 +85,7 @@ fn main() {
gltf_ui,
fonts_ui,
texts_ui,
manage_active_gltf,
control_active_gltf,
show_preview_text,
sync_monologue_font,
),
@ -126,13 +105,6 @@ fn main() {
#[derive(Resource, Default)]
pub struct AssetRegistry(Vec<HandleUntyped>);
#[derive(Event)]
pub enum CustomAssetEvent<T: Asset> {
Add { handle: Handle<T>, name: String },
Remove { handle: Handle<T> },
Clear,
}
#[derive(Debug, Component)]
pub struct TabRoot;
@ -162,6 +134,15 @@ fn initialize_ui(mut commands: Commands) {
..default()
};
let simple_button = ButtonBundle {
style: Style {
..base_style.clone()
},
background_color: Color::WHITE.into(),
border_color: Color::BLACK.into(),
..default()
};
commands
.spawn(NodeBundle {
style: Style {
@ -198,15 +179,6 @@ fn initialize_ui(mut commands: Commands) {
// HACK: This is super janky but I think we need it like this for UI layout rules
let mut content_containers: Vec<(String, Entity)> = Vec::new();
let simple_button = ButtonBundle {
style: Style {
..base_style.clone()
},
background_color: Color::WHITE.into(),
border_color: Color::BLACK.into(),
..default()
};
// Containers with asset content
parent
.spawn((
@ -297,25 +269,6 @@ fn initialize_ui(mut commands: Commands) {
));
},
);
parent.spawn((
simple_button.clone(),
ClearAssets,
ui::Sorting(90),
ui::Title {
text: "Clear Assets".into(),
..default()
},
));
parent.spawn((
simple_button.clone(),
ClearLevel,
ui::Sorting(95),
ui::Title {
text: "Reset Level".into(),
..default()
},
));
});
})
.id();
@ -329,6 +282,73 @@ fn initialize_ui(mut commands: Commands) {
ui::Sorting(0),
));
});
commands
.spawn(NodeBundle {
style: Style {
bottom: Val::Px(0.0),
left: Val::Px(0.0),
position_type: PositionType::Absolute,
border: UiRect::all(Val::Px(1.0)),
margin: UiRect::all(Val::Px(1.0)),
padding: UiRect::all(Val::Px(1.0)),
flex_direction: FlexDirection::Column,
overflow: Overflow::clip(),
..default()
},
background_color: Color::WHITE.into(),
border_color: Color::BLACK.into(),
..default()
})
.with_children(|parent| {
let container = parent
.spawn((
NodeBundle {
style: Style {
border: UiRect::all(Val::Px(1.0)),
margin: UiRect::all(Val::Px(1.0)),
padding: UiRect::all(Val::Px(1.0)),
flex_direction: FlexDirection::Column,
overflow: Overflow::clip(),
..default()
},
background_color: Color::WHITE.into(),
border_color: Color::BLACK.into(),
..default()
},
ui::Sorting(99),
ui::Select::Action,
))
.with_children(|parent| {
parent.spawn((
simple_button.clone(),
ClearAssets,
ui::Sorting(1),
ui::Title {
text: "Clear Assets".into(),
..default()
},
));
parent.spawn((
simple_button.clone(),
ClearLevel,
ui::Sorting(2),
ui::Title {
text: "Reset Level".into(),
..default()
},
));
})
.id();
parent.spawn((
ui::TitleBarBase::new(Color::WHITE).bundle(),
ui::Title {
text: "Actions".into(),
..default()
},
ui::Minimize { target: container },
ui::Sorting(0),
));
});
}
fn welcome_message(mut writer: EventWriter<ui::Alert>) {
@ -466,27 +486,22 @@ mod audio {
});
}
pub fn play_audio(
events: Query<(Entity, &Interaction, &AudioSink), (With<Button>, Changed<Interaction>)>,
mut commands: Commands,
) {
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>, sinks: Query<&AudioSink>) {
events
.iter()
.filter(|(_, &interaction, _)| interaction == Interaction::Pressed)
.for_each(|(entity, _, sink)| {
sink.toggle();
if sink.is_paused() {
commands.entity(entity).remove::<ui::Active>();
} else {
commands.entity(entity).insert(ui::Active);
}
});
.filter_map(|entity| sinks.get(entity).ok())
.for_each(|sink| sink.stop());
}
}
use assets::*;
mod assets {
use super::*;
pub fn get_asset_name<T: Asset>(server: &AssetServer, handle: Handle<T>) -> String {
@ -602,7 +617,6 @@ mod gltf {
#[derive(Debug, Component, Default)]
pub struct GltfWidget;
// TODO: Mark selected gltf as active ~single exclusive~
pub fn gltf_ui(
mut events: EventReader<AssetEvent<Gltf>>,
mut commands: Commands,
@ -655,24 +669,14 @@ mod gltf {
});
}
pub fn manage_active_gltf(
events: Query<
(Entity, &Interaction, Option<&ui::Active>),
(With<Button>, Changed<Interaction>),
>,
pub fn control_active_gltf(
events: Query<Entity, (With<ui::TargetAsset<Gltf>>, Added<ui::Active>)>,
root: Query<Entity, With<LevelRoot>>,
mut commands: Commands,
) {
events
.iter()
.filter(|(_, &interaction, _)| interaction == Interaction::Pressed)
.for_each(|(entity, _, active_ish)| match active_ish {
Some(_) => {
commands.entity(entity).remove::<ui::Active>();
}
None => {
commands.entity(entity).insert(ui::Active);
}
});
events.iter().for_each(|_| {
commands.entity(root.single()).despawn_descendants();
});
}
}
@ -1194,8 +1198,9 @@ mod cameras {
pub struct CameraWidget;
pub fn cameras_ui(
mut added: Query<(Entity, &mut Camera, &Name), (Added<Camera>, Without<EditorCamera>)>,
mut added: Query<(Entity, &mut Camera, &Name), Added<Camera>>,
mut removed: RemovedComponents<Camera>,
editor_camera: Query<Entity, With<EditorCamera>>,
widget: Query<Entity, With<CameraWidget>>,
current: Query<(Entity, &ui::TargetEntity)>,
mut commands: Commands,
@ -1212,47 +1217,25 @@ mod cameras {
ui::TargetEntity { entity },
name.as_str().into(),
);
camera.is_active = false;
camera.is_active = entity == editor_camera.single();
});
}
/// Set the camera active component based on button clicks
pub fn manage_active_camera(
events: Query<(&Interaction, &ui::TargetEntity), Changed<Interaction>>,
cameras: Query<Entity, With<Camera>>,
mut commands: Commands,
events: Query<&ui::TargetEntity, Added<ui::Active>>,
mut cameras: Query<(Entity, &mut Camera)>,
) {
events
.iter()
.filter(|(&interaction, _)| interaction == Interaction::Pressed)
.for_each(|(_, ui::TargetEntity { entity })| {
cameras.iter().for_each(|this_entity| {
if this_entity == *entity {
info!("Marking {:?} as active camera", entity);
commands.entity(this_entity).insert(ui::Active);
} else {
info!("Marking {:?} as inactive camera", entity);
commands.entity(this_entity).remove::<ui::Active>();
}
});
events.iter().for_each(|ui::TargetEntity { entity }| {
cameras.iter_mut().for_each(|(this_entity, mut camera)| {
if this_entity == *entity {
info!("Marking {:?} as active camera", entity);
camera.is_active = true;
} else {
info!("Marking {:?} as inactive camera", entity);
camera.is_active = false;
}
});
}
/// Set the active camera based on the Active marker component
pub fn control_active_camera(
added: Query<Entity, (Added<ui::Active>, With<Camera>)>,
mut removed: RemovedComponents<ui::Active>,
mut cameras: Query<&mut Camera>,
) {
removed.iter().for_each(|entity| {
if let Ok(mut camera) = cameras.get_mut(entity) {
camera.is_active = false;
}
});
added.iter().for_each(|entity| {
if let Ok(mut camera) = cameras.get_mut(entity) {
camera.is_active = true;
}
});
}
@ -1360,12 +1343,13 @@ mod reset {
mut commands: Commands,
) {
events.iter().for_each(|entity| {
// Clear buttons holding asset references
asset_holders
.iter()
.for_each(|entity| commands.entity(entity).despawn_recursive());
// Empty asset registry
registry.0.clear();
// TODO: .clear() assets?
commands.entity(entity).remove::<ui::Active>();
})
}
}

@ -423,28 +423,48 @@ mod buttons {
pub enum Select {
Multi,
Single,
Action,
}
pub fn manage_select_active(
events: Query<(Entity, &Parent), Added<Active>>,
children: Query<(&Select, &Children)>,
events: Query<
(Entity, &Parent, Option<&Active>, &Interaction),
(With<Button>, Changed<Interaction>),
>,
selects: Query<(&Select, &Children)>,
mut commands: Commands,
) {
events.iter().for_each(|(entity, parent)| {
if let Ok((select, childs)) = children.get(parent.get()) {
match select {
Select::Single => {
childs
.iter()
.filter(|&child| *child != entity)
.for_each(|&child| {
commands.entity(child).remove::<Active>();
})
}
Select::Multi => (),
}
}
});
events
.iter()
.filter(|(_, _, _, &interaction)| interaction == Interaction::Pressed)
.for_each(|(entity, parent, active, _)| {
selects
.get(parent.get())
.iter()
.for_each(|(select, children)| {
match active {
Some(_) => {
commands.entity(entity).remove::<Active>();
}
None => {
commands.entity(entity).insert(Active);
}
}
match select {
Select::Action => {
commands.entity(entity).remove::<Active>();
}
Select::Multi => (),
Select::Single => {
children.iter().filter(|&child| *child != entity).for_each(
|&child| {
commands.entity(child).remove::<Active>();
},
);
}
}
});
});
}
}

Loading…
Cancel
Save