Open QbieShay opened 5 years ago
This should help:
Have a system (let's call it BgmSystem
) which subscribes to day / night cycle change events.
#[derive(Debug)]
pub enum DayCycleEvent {
DayBegin,
NightBegin,
}
type BgmSystemData<'s> = (
Read<'s, EventChannel<DayCycleEvent>>,
// ..
);
The BgmSystem
needs to be able to play sounds.
Because we need to stop the sound when the day/night cycle changes, we need to hold onto a Sink
:
use std::io::Cursor;
use rodio;
let device = rodio::default_output_device().expect("No default output device");
let source = music.musics.get("day/night soundtrack").expect("not found").clone();
let reader = Cursor::new(source);
let sink = rodio::play_once(device, reader);
// On change events, we can go:
sink.stop();
Note: Amethyst's amethyst::audio::Output.play_once(..)
does not return you the Sink
, so you can't use that as is -- you can if this is changed, I think it's a good idea.
We need to stop the sound on an event, so we'll insert it into the world.
type BgmSystemData<'s> = (
Read<'s, EventChannel<DayCycleEvent>>,
Read<'s, Music>,
// Inserted by `AudioSystem.setup()`, so you need that in your dispatcher.
Read<'s, Option<Device>>,
Write<'s, Option<Sink>>,
);
impl System<'s> for BgmSystem {
type SystemData = BgmSystemData<'s>;
fn run(&mut self, (channel, music, device, sink_resource): Self::SystemData) {
// On event:
// Play soundtrack.
let source = music.musics.get("day/night soundtrack").expect("not found").clone();
let reader = Cursor::new(source);
let sink = rodio::play_once(device, reader);
*sink_resource = Some(sink);
}
}
rodio
's Sink
type does not allow you to re-use a sink after invoking sink.stop()
. So to switch audio, we need a new Sink
every time.
impl System<'s> for BgmSystem {
type SystemData = BgmSystemData<'s>;
fn run(&mut self, (channel, music, device, sink_resource): Self::SystemData) {
// On event:
// Stop previous soundtrack if any.
if let Some(sink) = *sink_resource {
sink.stop();
}
// Play soundtrack.
// ..
}
}
Listen to events:
#[derive(Debug, Default)]
pub struct BgmSystem {
/// Reader ID for the `DayCycleEvent` event channel.
day_cycle_event_rid: Option<ReaderId<DayCycleEvent>>,
}
impl System<'s> for BgmSystem {
type SystemData = BgmSystemData<'s>;
fn run(&mut self, (channel, music, device, sink_resource): Self::SystemData) {
channel
.read(
self.day_cycle_event_rid
.as_mut()
.expect("Expected reader ID to exist for HitDetectionSystem."),
)
.for_each(|ev| {
// Stop previous soundtrack if any.
if let Some(sink) = *sink_resource {
sink.stop();
}
// Play soundtrack.
let source = match ev {
DayCycleEvent::DayBegin => "day.wav",
DayCycleEvent::NightBegin => "night.wav",
}
let source = music.musics.get().expect("soundtrack not loaded.").clone();
// ..
});
}
fn setup(&mut self, res: &mut Resources) {
Self::SystemData::setup(res);
self.day_cycle_event_rid = Some(
res.fetch_mut::<EventChannel<DayCycleEvent>>()
.register_reader(),
);
}
}
When the day / night cycle changes, send a new DayCycleEvent
.
// In `DayNightCycle`, when night time happens:
day_cycle_event_channel.write(DayCycleEvent::NightBegin);
When the game starts, we need to send the first event to start the first soundtrack.
// In `SomethingState.on_start()`
day_cycle_event_channel.write(DayCycleEvent::DayBegin);
Wow that's a lot of information! Thank you so much! I'll try to process and understand all :) in the meantime, if this becomes urgent, please feel free to implement it, I don't want to hold back anyone
At the moment, the system used by evoli to play audio is amethyst's AudioBundle/DjSystem.
Changing the music that is being played is currently not possible, a new, controllable system is required.
I'm not entirely sure what would be a good implementation for this.
Related to #76 ( and what's currently blocking me on #80 :< )
Sorry in advance if i mix up some concepts, I'm new!