saving my place

main
Elijah Voigt 2 years ago
parent 1963c290a1
commit 2433f6386d

@ -64,6 +64,7 @@ fn main() {
.init_resource::<FontInfo>() .init_resource::<FontInfo>()
.add_asset::<Monologue>() .add_asset::<Monologue>()
.init_asset_loader::<MonologueLoader>() .init_asset_loader::<MonologueLoader>()
.add_event::<ControlAudio>()
.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(Update, quit.run_if(ui::activated::<QuitAction>))
.add_systems( .add_systems(
@ -81,7 +82,7 @@ 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)) .add_systems(Update, (audio_ui, play_audio, pause_audio, control_audio))
.add_systems( .add_systems(
Update, Update,
( (
@ -126,12 +127,41 @@ fn main() {
Update, Update,
( (
sync_asset_buttons::<Font>, sync_asset_buttons::<Font>,
sync_remove_asset_buttons::<Font>,
sync_asset_buttons::<AudioSource>, sync_asset_buttons::<AudioSource>,
sync_remove_asset_buttons::<AudioSource>,
sync_asset_buttons::<Monologue>, sync_asset_buttons::<Monologue>,
sync_remove_asset_buttons::<Monologue>,
sync_asset_buttons::<Level>, sync_asset_buttons::<Level>,
sync_remove_asset_buttons::<Level>,
sync_asset_buttons::<Gltf>, sync_asset_buttons::<Gltf>,
sync_remove_asset_buttons::<Gltf>,
sync_asset_buttons::<Scene>, sync_asset_buttons::<Scene>,
sync_remove_asset_buttons::<Scene>,
sync_asset_buttons::<AnimationClip>, sync_asset_buttons::<AnimationClip>,
sync_remove_asset_buttons::<AnimationClip>,
),
)
.add_systems(
Update,
(
add_timeline_epoch.run_if(ui::activated::<AddEpoch>),
set_epoch_gltf,
load_epoch_gltf,
set_epoch_scene,
load_epoch_scene,
set_epoch_camera,
load_epoch_camera,
set_epoch_music,
load_epoch_music,
set_epoch_monologue,
load_epoch_monologue,
set_epoch_font,
load_epoch_font,
set_epoch_sfx,
load_epoch_sfx,
set_epoch_animations,
load_epoch_animations,
), ),
) )
.run(); .run();
@ -184,6 +214,7 @@ fn initialize_ui(mut commands: Commands) {
..default() ..default()
}; };
// Assets widget
commands commands
.spawn(NodeBundle { .spawn(NodeBundle {
style: Style { style: Style {
@ -328,6 +359,8 @@ fn initialize_ui(mut commands: Commands) {
ui::Sorting(0), ui::Sorting(0),
)); ));
}); });
// Actions widget
commands commands
.spawn(NodeBundle { .spawn(NodeBundle {
style: Style { style: Style {
@ -422,6 +455,88 @@ fn initialize_ui(mut commands: Commands) {
ui::Sorting(0), ui::Sorting(0),
)); ));
}); });
// Actions widget
commands
.spawn(NodeBundle {
style: Style {
bottom: Val::Px(0.0),
right: 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::Row,
overflow: Overflow::clip(),
..default()
},
background_color: Color::WHITE.into(),
border_color: Color::BLACK.into(),
..default()
},
ui::Sorting(99),
ui::Select::Single,
TimelineWidget,
))
.with_children(|parent| {
// "Add Epoch" button
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::Row,
align_items: AlignItems::Center,
justify_items: JustifyItems::Center,
overflow: Overflow::clip(),
..default()
},
background_color: Color::WHITE.into(),
border_color: Color::BLACK.into(),
..default()
},
ui::Select::Action,
))
.with_children(|parent| {
parent.spawn((
simple_button.clone(),
AddEpoch,
ui::Title {
text: "+".into(),
..default()
},
));
});
})
.id();
parent.spawn((
ui::TitleBarBase::new(Color::WHITE).bundle(),
ui::Title {
text: "Timeline".into(),
..default()
},
ui::Minimize { target: container },
ui::Sorting(0),
));
});
} }
fn welcome_message(mut writer: EventWriter<ui::Alert>) { fn welcome_message(mut writer: EventWriter<ui::Alert>) {
@ -522,12 +637,23 @@ mod audio {
}); });
} }
pub fn play_audio( #[derive(Debug, Event)]
events: Query<&ui::TargetAsset<AudioSource>, (With<Button>, Added<ui::Active>)>, 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>>, root: Query<Entity, With<AudioRoot>>,
mut commands: Commands, mut commands: Commands,
sources: Query<(Entity, &Handle<AudioSource>), With<AudioSink>>,
) { ) {
events.iter().for_each(|ui::TargetAsset { handle }| { 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() { let root = if let Ok(entity) = root.get_single() {
entity entity
} else { } else {
@ -542,29 +668,48 @@ mod audio {
..default() ..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( pub fn pause_audio(
mut events: RemovedComponents<ui::Active>, mut events: RemovedComponents<ui::Active>,
targets: Query<&ui::TargetAsset<AudioSource>>, targets: Query<&ui::TargetAsset<AudioSource>>,
sources: Query<(Entity, &Handle<AudioSource>), With<AudioSink>>, mut writer: EventWriter<ControlAudio>,
mut commands: Commands,
) { ) {
// Iterate over the audio button events // Iterate over the audio button events
events.iter().for_each(|entity| { events.iter().for_each(|entity| {
// Get the handle for the audio source we want to despawn // Get the handle for the audio source we want to despawn
if let Ok(ui::TargetAsset { handle }) = targets.get(entity) { if let Ok(ui::TargetAsset { handle }) = targets.get(entity) {
if let Some(entity) = sources.iter().find_map(|(entity, source_handle)| { writer.send(ControlAudio::Stop(handle.clone()));
if source_handle == handle {
Some(entity)
} else {
None
}
}) {
commands.entity(entity).despawn_recursive();
}
} }
}); });
} }
@ -1568,6 +1713,8 @@ mod level {
builder.allow::<LevelRoot>(); builder.allow::<LevelRoot>();
builder.allow::<AudioRoot>(); builder.allow::<AudioRoot>();
// TODO: Serialize Timeline
// Scene components // Scene components
builder.allow::<Handle<Scene>>(); builder.allow::<Handle<Scene>>();
@ -1650,8 +1797,6 @@ mod asset_sync {
use super::*; use super::*;
// This sets buttons to active when their associated handle is spawned // This sets buttons to active when their associated handle is spawned
// TODO: Remove active when handle is despawned?
// ONLY IF there are no instances of that handle [!any(*)]
pub fn sync_asset_buttons<T: Asset>( pub fn sync_asset_buttons<T: Asset>(
events: Query<&Handle<T>, Added<Handle<T>>>, events: Query<&Handle<T>, Added<Handle<T>>>,
buttons: Query<(Entity, &ui::TargetAsset<T>)>, buttons: Query<(Entity, &ui::TargetAsset<T>)>,
@ -1674,4 +1819,369 @@ mod asset_sync {
}); });
}); });
} }
// Remove active when handle is despawned?
// ONLY IF there are no instances of that handle [!any(*)]
pub fn sync_remove_asset_buttons<T: Asset>(
mut events: RemovedComponents<Handle<T>>,
asset_entities: Query<&Handle<T>>,
buttons: Query<(Entity, &ui::TargetAsset<T>)>,
mut commands: Commands,
) {
events
.iter()
.find_map(|this_asset_entity| asset_entities.get(this_asset_entity).ok())
.iter()
.for_each(|this_handle| {
info!("Syncing removal of {:?}", this_handle);
buttons
.iter()
.find_map(|(entity, ui::TargetAsset { handle })| {
if handle == *this_handle {
Some(entity)
} else {
None
}
})
.iter()
.for_each(|&entity| {
commands.entity(entity).remove::<ui::Active>();
});
});
}
}
use timeline::*;
mod timeline {
use super::*;
/// Timeline widget marker
#[derive(Debug, Component)]
pub struct TimelineWidget;
/// Add Epoch component, used on a button to trigger a new epoch addition
#[derive(Debug, Component)]
pub struct AddEpoch;
/// Epoch ID Component
#[derive(Debug, Reflect, Component, Clone)]
pub struct EpochId {
id: usize,
}
/// Epoch GLTF Component
#[derive(Debug, Reflect, Component, Clone)]
pub struct EpochGltf {
gltf: Handle<Gltf>,
}
/// Epoch Scene Component
#[derive(Debug, Reflect, Component, Clone)]
pub struct EpochScene {
scene: Handle<Scene>,
}
/// Epoch Camera Component, marking the current camera
#[derive(Debug, Reflect, Component, Clone)]
pub struct EpochCamera {
camera: Entity,
}
/// Epoch music component, marking the opening track for this epoch
#[derive(Debug, Reflect, Default, Component, Clone)]
pub struct EpochMusic {
music: Handle<AudioSource>,
}
/// Epoch monologue, marking the dialog spoken this epoch
#[derive(Debug, Reflect, Component, Clone)]
pub struct EpochMonologue {
monologue: Handle<Monologue>,
}
/// Epoch font, marking the font used for this epoch's monologue
#[derive(Debug, Reflect, Component, Clone)]
pub struct EpochFont {
font: Handle<Font>,
}
/// A vector of audios looping this epoch as background tracks
#[derive(Debug, Reflect, Component, Clone)]
pub struct EpochSfx {
sfx: Vec<Handle<AudioSource>>,
}
/// Epoch animations, looping this epoch
#[derive(Debug, Reflect, Component, Clone)]
pub struct EpochAnimations {
animations: Vec<Handle<AnimationClip>>,
}
/// System for adding an epoch to the level's timeline
/// Triggered when a button with the AddEpoch marker is Active
pub fn add_timeline_epoch(
root: Query<(Entity, &Children), With<TimelineWidget>>,
mut commands: Commands,
) {
info!("Adding timeline epoch");
root.iter().for_each(|(entity, children)| {
let id = children.iter().len();
let name = format!("{}", id);
commands.entity(entity).with_children(|parent| {
parent.spawn((
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()
},
EpochId { id },
));
});
});
}
/// Set the GLTF for the current epoch
pub fn set_epoch_gltf(
events: Query<&ui::TargetAsset<Gltf>, Added<ui::Active>>,
active_epoch: Query<Entity, (With<ui::Active>, With<EpochId>)>,
mut commands: Commands,
) {
// Each time a GLTF is selected in the editor
events.iter().for_each(|ui::TargetAsset { handle }| {
// Iterate over all (0 or 1) active epochs
active_epoch.iter().for_each(|entity| {
// Set the GLTF (overwrite existing GLTF selections)
commands.entity(entity).insert(EpochGltf {
gltf: handle.clone(),
});
// TODO: Unset Scene, Camera, Animations
});
});
}
pub fn load_epoch_gltf(events: Query<Option<&EpochGltf>, (Added<ui::Active>, With<EpochId>)>) {
events.iter().for_each(|epoch_gltf| {
warn!("TODO: Load epoch GLTF!");
})
}
pub fn set_epoch_scene(
events: Query<&ui::TargetAsset<Scene>, Added<ui::Active>>,
active_epoch: Query<Entity, (With<ui::Active>, With<EpochId>)>,
mut commands: Commands,
) {
// Each time a Scene is selected in the editor
events.iter().for_each(|ui::TargetAsset { handle }| {
// Iterate over all (0 or 1) active epochs
active_epoch.iter().for_each(|entity| {
// Set the Scene (overwrite existing Scene selections)
commands.entity(entity).insert(EpochScene {
scene: handle.clone(),
});
});
});
}
pub fn load_epoch_scene(
events: Query<Option<&EpochScene>, (Added<ui::Active>, With<EpochId>)>,
) {
events.iter().for_each(|epoch_scene| {
warn!("TODO: Load epoch Scene!");
})
}
pub fn set_epoch_camera(
events: Query<&ui::TargetEntity, Added<ui::Active>>,
active_epoch: Query<Entity, (With<ui::Active>, With<EpochId>)>,
mut commands: Commands,
) {
// Each time a Scene is selected in the editor
events.iter().for_each(|ui::TargetEntity { entity }| {
// Iterate over all (0 or 1) active epochs
active_epoch.iter().for_each(|this_entity| {
// Set the Scene (overwrite existing Scene selections)
commands
.entity(this_entity)
.insert(EpochCamera { camera: *entity });
});
});
}
pub fn load_epoch_camera(
events: Query<Option<&EpochCamera>, (Added<ui::Active>, With<EpochId>)>,
) {
events.iter().for_each(|epoch_camera| {
warn!("TODO: Load epoch Camera");
})
}
pub fn set_epoch_music(
events: Query<&ui::TargetAsset<AudioSource>, Added<ui::Active>>,
active_epoch: Query<Entity, (With<ui::Active>, With<EpochId>)>,
mut commands: Commands,
) {
// Each time a Scene is selected in the editor
events.iter().for_each(|ui::TargetAsset { handle }| {
info!("TODO: Select scene music");
// // Iterate over all (0 or 1) active epochs
// active_epoch.iter().for_each(|entity| {
// // Set the Scene (overwrite existing Scene selections)
// commands.entity(entity).insert(EpochMusic { music: handle.clone() });
// });
});
}
pub fn load_epoch_music(
events: Query<Option<&EpochMusic>, (Added<ui::Active>, With<EpochId>)>,
) {
events.iter().for_each(|epoch_music| {
warn!("TODO: Load epoch music!");
})
}
pub fn set_epoch_monologue(
events: Query<&ui::TargetAsset<Monologue>, Added<ui::Active>>,
active_epoch: Query<Entity, (With<ui::Active>, With<EpochId>)>,
mut commands: Commands,
) {
// Each time a Scene is selected in the editor
events.iter().for_each(|ui::TargetAsset { handle }| {
// Iterate over all (0 or 1) active epochs
active_epoch.iter().for_each(|entity| {
// Set the Scene (overwrite existing Scene selections)
commands.entity(entity).insert(EpochMonologue {
monologue: handle.clone(),
});
});
});
}
pub fn load_epoch_monologue(
events: Query<Option<&EpochMonologue>, (Added<ui::Active>, With<EpochId>)>,
) {
events.iter().for_each(|epoch_monologue| {
warn!("TODO: unset epoch Monologue!");
epoch_monologue
.iter()
.for_each(|EpochMonologue { monologue }| {
warn!("TODO: Set level epoch");
});
});
}
pub fn set_epoch_font(
events: Query<&ui::TargetAsset<Font>, Added<ui::Active>>,
active_epoch: Query<Entity, (With<ui::Active>, With<EpochId>)>,
mut commands: Commands,
) {
// Each time a Scene is selected in the editor
events.iter().for_each(|ui::TargetAsset { handle }| {
// Iterate over all (0 or 1) active epochs
active_epoch.iter().for_each(|entity| {
// Set the Scene (overwrite existing Scene selections)
commands.entity(entity).insert(EpochFont {
font: handle.clone(),
});
});
});
}
pub fn load_epoch_font(
events: Query<Option<&EpochFont>, (Added<ui::Active>, With<EpochId>)>,
mut font_info: ResMut<FontInfo>,
) {
events.iter().for_each(|epoch_font| {
font_info.default = epoch_font.map(|EpochFont { font }| font.clone());
});
}
pub fn set_epoch_sfx(
events: Query<&ui::TargetAsset<AudioSource>, Added<ui::Active>>,
mut active_epoch: Query<(Entity, Option<&mut EpochSfx>), (With<ui::Active>, With<EpochId>)>,
mut commands: Commands,
) {
// Each time a Scene is selected in the editor
events.iter().for_each(|ui::TargetAsset { handle }| {
// Iterate over all (0 or 1) active epochs
active_epoch.iter_mut().for_each(|(entity, maybe_sfx)| {
info!("Adding sfx {:?} to epoch {:?}", maybe_sfx, entity);
if let Some(mut epoch_sfx) = maybe_sfx {
epoch_sfx.sfx.push(handle.clone());
} else {
// Set the Scene (overwrite existing Scene selections)
commands.entity(entity).insert(EpochSfx {
sfx: vec![handle.clone()],
});
}
});
});
}
pub fn load_epoch_sfx(
added: Query<Entity, (Added<ui::Active>, With<EpochId>)>,
mut removed: RemovedComponents<ui::Active>,
epoch_sfx: Query<&EpochSfx>,
mut writer: EventWriter<ControlAudio>,
) {
removed.iter().for_each(|entity| {
epoch_sfx.get(entity).iter().for_each(|EpochSfx { sfx }| {
sfx.iter().for_each(|handle| {
writer.send(ControlAudio::Stop(handle.clone()));
});
});
});
added.iter().for_each(|entity| {
epoch_sfx.get(entity).iter().for_each(|EpochSfx { sfx }| {
sfx.iter().for_each(|handle| {
writer.send(ControlAudio::Loop(handle.clone()));
});
});
});
}
pub fn set_epoch_animations(
events: Query<&ui::TargetAsset<AnimationClip>, Added<ui::Active>>,
mut active_epoch: Query<
(Entity, Option<&mut EpochAnimations>),
(With<ui::Active>, With<EpochId>),
>,
mut commands: Commands,
) {
// Each time a Scene is selected in the editor
events.iter().for_each(|ui::TargetAsset { handle }| {
// Iterate over all (0 or 1) active epochs
active_epoch
.iter_mut()
.for_each(|(entity, maybe_animations)| {
if let Some(mut epoch_animations) = maybe_animations {
epoch_animations.animations.push(handle.clone());
} else {
// Set the Scene (overwrite existing Scene selections)
commands.entity(entity).insert(EpochAnimations {
animations: vec![handle.clone()],
});
}
});
});
}
pub fn load_epoch_animations(
events: Query<Option<&EpochAnimations>, (Added<ui::Active>, With<EpochId>)>,
) {
events.iter().for_each(|epoch_animations| {
warn!("TODO: Load epoch Animations!");
})
}
} }

Loading…
Cancel
Save