Skip to content

Commit aed05ee

Browse files
committed
feat(demos): add showcase and template demos with shape interactions and audio support
- Created a new `demos` module with `showcase` and `template` demos. - Implemented the `showcase` demo featuring animated shapes, collision detection, and audio playback. - Added a `template` demo as a scaffold for future development. - Updated the main game module to allow switching between demos. - Introduced a `ShapeId` enum for identifying shapes in the showcase demo. - Enhanced shape rendering and interaction logic with new traits and methods for collision detection.
1 parent 05ec37d commit aed05ee

27 files changed

Lines changed: 2245 additions & 310 deletions

‎src/audio/audio.rs‎

Lines changed: 142 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
use std::fmt;
2+
use std::path::Path;
3+
use std::time::Duration;
4+
5+
use thiserror::Error;
6+
17
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
28
pub struct SoundId(u32);
39

@@ -7,9 +13,76 @@ impl SoundId {
713
}
814
}
915

16+
impl fmt::Display for SoundId {
17+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18+
write!(f, "{}", self.0)
19+
}
20+
}
21+
22+
#[derive(Debug, Error)]
23+
pub enum AudioError {
24+
#[error("audio output device unavailable: {0}")]
25+
OutputUnavailable(#[from] rodio::StreamError),
26+
#[error("failed to resolve current directory: {0}")]
27+
CurrentDirectory(#[source] std::io::Error),
28+
#[error("failed to normalize audio file path {path:?}: {source}")]
29+
PathNormalization {
30+
path: std::path::PathBuf,
31+
#[source]
32+
source: std::io::Error,
33+
},
34+
#[error("failed to open audio file {path:?}: {source}")]
35+
FileOpen {
36+
path: std::path::PathBuf,
37+
#[source]
38+
source: std::io::Error,
39+
},
40+
#[error("failed to decode audio file {path:?}: {source}")]
41+
Decode {
42+
path: std::path::PathBuf,
43+
#[source]
44+
source: rodio::decoder::DecoderError,
45+
},
46+
#[error("failed to clone audio file handle {path:?}: {source}")]
47+
FileClone {
48+
path: std::path::PathBuf,
49+
#[source]
50+
source: std::io::Error,
51+
},
52+
#[error("sound {0} has not been loaded")]
53+
SoundNotLoaded(SoundId),
54+
#[error("failed to create audio sink: {0}")]
55+
SinkCreation(#[source] rodio::PlayError),
56+
#[error("failed to read audio metadata {path:?}: {source}")]
57+
Metadata {
58+
path: std::path::PathBuf,
59+
#[source]
60+
source: std::io::Error,
61+
},
62+
}
63+
64+
pub type AudioResult<T> = Result<T, AudioError>;
65+
66+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
67+
pub enum LoadStrategy {
68+
Auto,
69+
Buffered,
70+
Streaming,
71+
}
72+
1073
pub trait AudioBackend {
11-
fn load(&mut self, path: &str) -> SoundId;
12-
fn play(&mut self, sound: SoundId);
74+
fn load(&mut self, path: &Path, strategy: LoadStrategy) -> AudioResult<SoundId>;
75+
fn play(&mut self, sound: SoundId) -> AudioResult<()>;
76+
fn stop(&mut self, sound: SoundId) -> AudioResult<()>;
77+
fn pause(&mut self, sound: SoundId) -> AudioResult<()>;
78+
fn resume(&mut self, sound: SoundId) -> AudioResult<()>;
79+
fn set_volume(&mut self, sound: SoundId, volume: f32) -> AudioResult<()>;
80+
fn stop_all(&mut self);
81+
fn set_master_volume(&mut self, volume: f32);
82+
fn is_playing(&self, sound: SoundId) -> bool;
83+
fn duration(&self, sound: SoundId) -> Option<Duration>;
84+
fn unload(&mut self, sound: SoundId) -> AudioResult<()>;
85+
fn unload_all(&mut self);
1386
}
1487

1588
pub struct AudioSystem {
@@ -21,11 +94,75 @@ impl AudioSystem {
2194
Self { backend }
2295
}
2396

24-
pub fn load(&mut self, path: &str) -> SoundId {
25-
self.backend.load(path)
97+
pub fn load<P>(&mut self, path: P) -> AudioResult<SoundId>
98+
where
99+
P: AsRef<Path>,
100+
{
101+
self.load_with_strategy(path, LoadStrategy::Auto)
102+
}
103+
104+
pub fn load_with_strategy<P>(&mut self, path: P, strategy: LoadStrategy) -> AudioResult<SoundId>
105+
where
106+
P: AsRef<Path>,
107+
{
108+
self.backend.load(path.as_ref(), strategy)
109+
}
110+
111+
pub fn load_buffered<P>(&mut self, path: P) -> AudioResult<SoundId>
112+
where
113+
P: AsRef<Path>,
114+
{
115+
self.load_with_strategy(path, LoadStrategy::Buffered)
26116
}
27117

28-
pub fn play(&mut self, sound: SoundId) {
118+
pub fn load_streaming<P>(&mut self, path: P) -> AudioResult<SoundId>
119+
where
120+
P: AsRef<Path>,
121+
{
122+
self.load_with_strategy(path, LoadStrategy::Streaming)
123+
}
124+
125+
pub fn play(&mut self, sound: SoundId) -> AudioResult<()> {
29126
self.backend.play(sound)
30127
}
128+
129+
pub fn stop(&mut self, sound: SoundId) -> AudioResult<()> {
130+
self.backend.stop(sound)
131+
}
132+
133+
pub fn pause(&mut self, sound: SoundId) -> AudioResult<()> {
134+
self.backend.pause(sound)
135+
}
136+
137+
pub fn resume(&mut self, sound: SoundId) -> AudioResult<()> {
138+
self.backend.resume(sound)
139+
}
140+
141+
pub fn set_volume(&mut self, sound: SoundId, volume: f32) -> AudioResult<()> {
142+
self.backend.set_volume(sound, volume)
143+
}
144+
145+
pub fn stop_all(&mut self) {
146+
self.backend.stop_all()
147+
}
148+
149+
pub fn set_master_volume(&mut self, volume: f32) {
150+
self.backend.set_master_volume(volume)
151+
}
152+
153+
pub fn is_playing(&self, sound: SoundId) -> bool {
154+
self.backend.is_playing(sound)
155+
}
156+
157+
pub fn duration(&self, sound: SoundId) -> Option<Duration> {
158+
self.backend.duration(sound)
159+
}
160+
161+
pub fn unload(&mut self, sound: SoundId) -> AudioResult<()> {
162+
self.backend.unload(sound)
163+
}
164+
165+
pub fn unload_all(&mut self) {
166+
self.backend.unload_all()
167+
}
31168
}

‎src/audio/mod.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
mod audio;
22
mod rodio_backend;
33

4-
pub use audio::{AudioBackend, AudioSystem, SoundId};
4+
pub use audio::{AudioBackend, AudioError, AudioResult, AudioSystem, LoadStrategy, SoundId};
55
pub use rodio_backend::RodioBackend;

0 commit comments

Comments
 (0)