mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 06:00:20 +00:00
Migrate audio to required components (#15573)
# Objective What's that? Another PR for the grand migration to required components? This time, audio! ## Solution Deprecate `AudioSourceBundle`, `AudioBundle`, and `PitchBundle`, as per the [chosen proposal](https://hackmd.io/@bevy/required_components/%2Fzxgp-zMMRUCdT7LY1ZDQwQ). However, we cannot call the component `AudioSource`, because that's what the stored asset is called. I deliberated on a few names, like `AudioHandle`, or even just `Audio`, but landed on `AudioPlayer`, since it's probably the most accurate and "nice" name for this. Open to alternatives though. --- ## Migration Guide Replace all insertions of `AudioSoucreBundle`, `AudioBundle`, and `PitchBundle` with the `AudioPlayer` component. The other components required by it will now be inserted automatically. In cases where the generics cannot be inferred, you may need to specify them explicitly. For example: ```rust commands.spawn(AudioPlayer::<AudioSource>(asset_server.load("sounds/sick_beats.ogg"))); ```
This commit is contained in:
parent
eb51b4c28e
commit
ed151e756c
14 changed files with 102 additions and 70 deletions
|
@ -1,3 +1,5 @@
|
|||
#![expect(deprecated)]
|
||||
|
||||
use crate::{AudioSource, Decodable};
|
||||
use bevy_asset::{Asset, Handle};
|
||||
use bevy_derive::Deref;
|
||||
|
@ -228,8 +230,40 @@ impl Default for SpatialScale {
|
|||
pub struct DefaultSpatialScale(pub SpatialScale);
|
||||
|
||||
/// Bundle for playing a standard bevy audio asset
|
||||
#[deprecated(
|
||||
since = "0.15.0",
|
||||
note = "Use the `AudioPlayer` component instead. Inserting it will now also insert a `PlaybackSettings` component automatically."
|
||||
)]
|
||||
pub type AudioBundle = AudioSourceBundle<AudioSource>;
|
||||
|
||||
/// A component for playing a sound.
|
||||
///
|
||||
/// Insert this component onto an entity to trigger an audio source to begin playing.
|
||||
///
|
||||
/// If the handle refers to an unavailable asset (such as if it has not finished loading yet),
|
||||
/// the audio will not begin playing immediately. The audio will play when the asset is ready.
|
||||
///
|
||||
/// When Bevy begins the audio playback, an [`AudioSink`][crate::AudioSink] component will be
|
||||
/// added to the entity. You can use that component to control the audio settings during playback.
|
||||
///
|
||||
/// Playback can be configured using the [`PlaybackSettings`] component. Note that changes to the
|
||||
/// `PlaybackSettings` component will *not* affect already-playing audio.
|
||||
#[derive(Component, Reflect)]
|
||||
#[reflect(Component)]
|
||||
#[require(PlaybackSettings)]
|
||||
pub struct AudioPlayer<Source = AudioSource>(pub Handle<Source>)
|
||||
where
|
||||
Source: Asset + Decodable;
|
||||
|
||||
impl<Source> Clone for AudioPlayer<Source>
|
||||
where
|
||||
Source: Asset + Decodable,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Bundle for playing a sound.
|
||||
///
|
||||
/// Insert this bundle onto an entity to trigger a sound source to begin playing.
|
||||
|
@ -240,12 +274,16 @@ pub type AudioBundle = AudioSourceBundle<AudioSource>;
|
|||
/// When Bevy begins the audio playback, an [`AudioSink`][crate::AudioSink] component will be
|
||||
/// added to the entity. You can use that component to control the audio settings during playback.
|
||||
#[derive(Bundle)]
|
||||
#[deprecated(
|
||||
since = "0.15.0",
|
||||
note = "Use the `AudioPlayer` component instead. Inserting it will now also insert a `PlaybackSettings` component automatically."
|
||||
)]
|
||||
pub struct AudioSourceBundle<Source = AudioSource>
|
||||
where
|
||||
Source: Asset + Decodable,
|
||||
{
|
||||
/// Asset containing the audio data to play.
|
||||
pub source: Handle<Source>,
|
||||
pub source: AudioPlayer<Source>,
|
||||
/// Initial settings that the audio starts playing with.
|
||||
/// If you would like to control the audio while it is playing,
|
||||
/// query for the [`AudioSink`][crate::AudioSink] component.
|
||||
|
@ -265,7 +303,7 @@ impl<T: Asset + Decodable> Clone for AudioSourceBundle<T> {
|
|||
impl<T: Decodable + Asset> Default for AudioSourceBundle<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
source: Default::default(),
|
||||
source: AudioPlayer(Handle::default()),
|
||||
settings: Default::default(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
AudioSourceBundle, Decodable, DefaultSpatialScale, GlobalVolume, PlaybackMode,
|
||||
PlaybackSettings, SpatialAudioSink, SpatialListener,
|
||||
AudioPlayer, Decodable, DefaultSpatialScale, GlobalVolume, PlaybackMode, PlaybackSettings,
|
||||
SpatialAudioSink, SpatialListener,
|
||||
};
|
||||
use bevy_asset::{Asset, Assets, Handle};
|
||||
use bevy_ecs::{prelude::*, system::SystemParam};
|
||||
|
@ -89,8 +89,7 @@ impl<'w, 's> EarPositions<'w, 's> {
|
|||
|
||||
/// Plays "queued" audio through the [`AudioOutput`] resource.
|
||||
///
|
||||
/// "Queued" audio is any audio entity (with the components from
|
||||
/// [`AudioBundle`][crate::AudioBundle] that does not have an
|
||||
/// "Queued" audio is any audio entity (with an [`AudioPlayer`] component) that does not have an
|
||||
/// [`AudioSink`]/[`SpatialAudioSink`] component.
|
||||
///
|
||||
/// This system detects such entities, checks if their source asset
|
||||
|
@ -141,7 +140,7 @@ pub(crate) fn play_queued_audio_system<Source: Asset + Decodable>(
|
|||
let emitter_translation = if let Some(emitter_transform) = maybe_emitter_transform {
|
||||
(emitter_transform.translation() * scale).into()
|
||||
} else {
|
||||
warn!("Spatial AudioBundle with no GlobalTransform component. Using zero.");
|
||||
warn!("Spatial AudioPlayer with no GlobalTransform component. Using zero.");
|
||||
Vec3::ZERO.into()
|
||||
};
|
||||
|
||||
|
@ -264,16 +263,22 @@ pub(crate) fn cleanup_finished_audio<T: Decodable + Asset>(
|
|||
}
|
||||
for (entity, sink) in &query_nonspatial_remove {
|
||||
if sink.sink.empty() {
|
||||
commands
|
||||
.entity(entity)
|
||||
.remove::<(AudioSourceBundle<T>, AudioSink, PlaybackRemoveMarker)>();
|
||||
commands.entity(entity).remove::<(
|
||||
AudioPlayer<T>,
|
||||
AudioSink,
|
||||
PlaybackSettings,
|
||||
PlaybackRemoveMarker,
|
||||
)>();
|
||||
}
|
||||
}
|
||||
for (entity, sink) in &query_spatial_remove {
|
||||
if sink.sink.empty() {
|
||||
commands
|
||||
.entity(entity)
|
||||
.remove::<(AudioSourceBundle<T>, SpatialAudioSink, PlaybackRemoveMarker)>();
|
||||
commands.entity(entity).remove::<(
|
||||
AudioPlayer<T>,
|
||||
SpatialAudioSink,
|
||||
PlaybackSettings,
|
||||
PlaybackRemoveMarker,
|
||||
)>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
//!
|
||||
//! ```no_run
|
||||
//! # use bevy_ecs::prelude::*;
|
||||
//! # use bevy_audio::{AudioBundle, AudioPlugin, PlaybackSettings};
|
||||
//! # use bevy_audio::{AudioPlayer, AudioPlugin, AudioSource, PlaybackSettings};
|
||||
//! # use bevy_asset::{AssetPlugin, AssetServer};
|
||||
//! # use bevy_app::{App, AppExit, NoopPluginGroup as MinimalPlugins, Startup};
|
||||
//! fn main() {
|
||||
|
@ -20,10 +20,10 @@
|
|||
//! }
|
||||
//!
|
||||
//! fn play_background_audio(asset_server: Res<AssetServer>, mut commands: Commands) {
|
||||
//! commands.spawn(AudioBundle {
|
||||
//! source: asset_server.load("background_audio.ogg"),
|
||||
//! settings: PlaybackSettings::LOOP,
|
||||
//! });
|
||||
//! commands.spawn((
|
||||
//! AudioPlayer::<AudioSource>(asset_server.load("background_audio.ogg")),
|
||||
//! PlaybackSettings::LOOP,
|
||||
//! ));
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
|
@ -38,11 +38,13 @@ mod sinks;
|
|||
/// The audio prelude.
|
||||
///
|
||||
/// This includes the most common types in this crate, re-exported for your convenience.
|
||||
#[expect(deprecated)]
|
||||
pub mod prelude {
|
||||
#[doc(hidden)]
|
||||
pub use crate::{
|
||||
AudioBundle, AudioSink, AudioSinkPlayback, AudioSource, AudioSourceBundle, Decodable,
|
||||
GlobalVolume, Pitch, PitchBundle, PlaybackSettings, SpatialAudioSink, SpatialListener,
|
||||
AudioBundle, AudioPlayer, AudioSink, AudioSinkPlayback, AudioSource, AudioSourceBundle,
|
||||
Decodable, GlobalVolume, Pitch, PitchBundle, PlaybackSettings, SpatialAudioSink,
|
||||
SpatialListener,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -66,7 +68,7 @@ struct AudioPlaySet;
|
|||
|
||||
/// Adds support for audio playback to a Bevy Application
|
||||
///
|
||||
/// Insert an [`AudioBundle`] onto your entities to play audio.
|
||||
/// Insert an [`AudioPlayer`] onto your entities to play audio.
|
||||
#[derive(Default)]
|
||||
pub struct AudioPlugin {
|
||||
/// The global volume for all audio entities.
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![expect(deprecated)]
|
||||
|
||||
use crate::{AudioSourceBundle, Decodable};
|
||||
use bevy_asset::Asset;
|
||||
use bevy_reflect::TypePath;
|
||||
|
@ -35,4 +37,8 @@ impl Decodable for Pitch {
|
|||
}
|
||||
|
||||
/// Bundle for playing a bevy note sound
|
||||
#[deprecated(
|
||||
since = "0.15.0",
|
||||
note = "Use the `AudioPlayer<Pitch>` component instead. Inserting it will now also insert a `PlaybackSettings` component automatically."
|
||||
)]
|
||||
pub type PitchBundle = AudioSourceBundle<Pitch>;
|
||||
|
|
|
@ -76,7 +76,7 @@ pub trait AudioSinkPlayback {
|
|||
/// Used to control audio during playback.
|
||||
///
|
||||
/// Bevy inserts this component onto your entities when it begins playing an audio source.
|
||||
/// Use [`AudioBundle`][crate::AudioBundle] to trigger that to happen.
|
||||
/// Use [`AudioPlayer`][crate::AudioPlayer] to trigger that to happen.
|
||||
///
|
||||
/// You can use this component to modify the playback settings while the audio is playing.
|
||||
///
|
||||
|
|
|
@ -11,8 +11,7 @@ fn main() {
|
|||
}
|
||||
|
||||
fn setup(asset_server: Res<AssetServer>, mut commands: Commands) {
|
||||
commands.spawn(AudioBundle {
|
||||
source: asset_server.load("sounds/Windless Slopes.ogg"),
|
||||
..default()
|
||||
});
|
||||
commands.spawn(AudioPlayer::<AudioSource>(
|
||||
asset_server.load("sounds/Windless Slopes.ogg"),
|
||||
));
|
||||
}
|
||||
|
|
|
@ -12,10 +12,7 @@ fn main() {
|
|||
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
commands.spawn((
|
||||
AudioBundle {
|
||||
source: asset_server.load("sounds/Windless Slopes.ogg"),
|
||||
..default()
|
||||
},
|
||||
AudioPlayer::<AudioSource>(asset_server.load("sounds/Windless Slopes.ogg")),
|
||||
MyMusic,
|
||||
));
|
||||
}
|
||||
|
|
|
@ -99,8 +99,5 @@ fn setup(mut assets: ResMut<Assets<SineAudio>>, mut commands: Commands) {
|
|||
let audio_handle = assets.add(SineAudio {
|
||||
frequency: 440., // this is the frequency of A4
|
||||
});
|
||||
commands.spawn(AudioSourceBundle {
|
||||
source: audio_handle,
|
||||
..default()
|
||||
});
|
||||
commands.spawn(AudioPlayer(audio_handle));
|
||||
}
|
||||
|
|
|
@ -30,10 +30,10 @@ fn play_pitch(
|
|||
) {
|
||||
for _ in events.read() {
|
||||
info!("playing pitch with frequency: {}", frequency.0);
|
||||
commands.spawn(PitchBundle {
|
||||
source: pitch_assets.add(Pitch::new(frequency.0, Duration::new(1, 0))),
|
||||
settings: PlaybackSettings::DESPAWN,
|
||||
});
|
||||
commands.spawn((
|
||||
AudioPlayer(pitch_assets.add(Pitch::new(frequency.0, Duration::new(1, 0)))),
|
||||
PlaybackSettings::DESPAWN,
|
||||
));
|
||||
info!("number of pitch assets: {}", pitch_assets.len());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,33 +72,29 @@ fn change_track(
|
|||
commands.entity(track).insert(FadeOut);
|
||||
}
|
||||
|
||||
// Spawn a new `AudioBundle` with the appropriate soundtrack based on
|
||||
// Spawn a new `AudioPlayer` with the appropriate soundtrack based on
|
||||
// the game state.
|
||||
//
|
||||
// Volume is set to start at zero and is then increased by the fade_in system.
|
||||
match game_state.as_ref() {
|
||||
GameState::Peaceful => {
|
||||
commands.spawn((
|
||||
AudioBundle {
|
||||
source: soundtrack_player.track_list.first().unwrap().clone(),
|
||||
settings: PlaybackSettings {
|
||||
mode: bevy::audio::PlaybackMode::Loop,
|
||||
volume: bevy::audio::Volume::ZERO,
|
||||
..default()
|
||||
},
|
||||
AudioPlayer(soundtrack_player.track_list.first().unwrap().clone()),
|
||||
PlaybackSettings {
|
||||
mode: bevy::audio::PlaybackMode::Loop,
|
||||
volume: bevy::audio::Volume::ZERO,
|
||||
..default()
|
||||
},
|
||||
FadeIn,
|
||||
));
|
||||
}
|
||||
GameState::Battle => {
|
||||
commands.spawn((
|
||||
AudioBundle {
|
||||
source: soundtrack_player.track_list.get(1).unwrap().clone(),
|
||||
settings: PlaybackSettings {
|
||||
mode: bevy::audio::PlaybackMode::Loop,
|
||||
volume: bevy::audio::Volume::ZERO,
|
||||
..default()
|
||||
},
|
||||
AudioPlayer(soundtrack_player.track_list.get(1).unwrap().clone()),
|
||||
PlaybackSettings {
|
||||
mode: bevy::audio::PlaybackMode::Loop,
|
||||
volume: bevy::audio::Volume::ZERO,
|
||||
..default()
|
||||
},
|
||||
FadeIn,
|
||||
));
|
||||
|
|
|
@ -37,10 +37,8 @@ fn setup(
|
|||
MeshMaterial2d(materials.add(Color::from(BLUE))),
|
||||
Transform::from_translation(Vec3::new(0.0, 50.0, 0.0)),
|
||||
Emitter::default(),
|
||||
AudioBundle {
|
||||
source: asset_server.load("sounds/Windless Slopes.ogg"),
|
||||
settings: PlaybackSettings::LOOP.with_spatial(true),
|
||||
},
|
||||
AudioPlayer::<AudioSource>(asset_server.load("sounds/Windless Slopes.ogg")),
|
||||
PlaybackSettings::LOOP.with_spatial(true),
|
||||
));
|
||||
|
||||
let listener = SpatialListener::new(gap);
|
||||
|
|
|
@ -28,10 +28,8 @@ fn setup(
|
|||
MeshMaterial3d(materials.add(Color::from(BLUE))),
|
||||
Transform::from_xyz(0.0, 0.0, 0.0),
|
||||
Emitter::default(),
|
||||
AudioBundle {
|
||||
source: asset_server.load("sounds/Windless Slopes.ogg"),
|
||||
settings: PlaybackSettings::LOOP.with_spatial(true),
|
||||
},
|
||||
AudioPlayer::<AudioSource>(asset_server.load("sounds/Windless Slopes.ogg")),
|
||||
PlaybackSettings::LOOP.with_spatial(true),
|
||||
));
|
||||
|
||||
let listener = SpatialListener::new(gap);
|
||||
|
|
|
@ -416,11 +416,7 @@ fn play_collision_sound(
|
|||
if !collision_events.is_empty() {
|
||||
// This prevents events staying active on the next frame.
|
||||
collision_events.clear();
|
||||
commands.spawn(AudioBundle {
|
||||
source: sound.clone(),
|
||||
// auto-despawn the entity when playback finishes
|
||||
settings: PlaybackSettings::DESPAWN,
|
||||
});
|
||||
commands.spawn((AudioPlayer(sound.clone()), PlaybackSettings::DESPAWN));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -157,10 +157,10 @@ fn button_handler(
|
|||
}
|
||||
|
||||
fn setup_music(asset_server: Res<AssetServer>, mut commands: Commands) {
|
||||
commands.spawn(AudioBundle {
|
||||
source: asset_server.load("sounds/Windless Slopes.ogg"),
|
||||
settings: PlaybackSettings::LOOP,
|
||||
});
|
||||
commands.spawn((
|
||||
AudioPlayer::<AudioSource>(asset_server.load("sounds/Windless Slopes.ogg")),
|
||||
PlaybackSettings::LOOP,
|
||||
));
|
||||
}
|
||||
|
||||
// Pause audio when app goes into background and resume when it returns.
|
||||
|
|
Loading…
Reference in a new issue