use bevy_ecs::prelude::*;
pub trait Scene: Send + Sync + 'static {
fn on_enter(&mut self, _world: &mut World) {}
fn on_update(&mut self, _world: &mut World) {}
fn on_exit(&mut self, _world: &mut World) {}
}
struct BoxedScene {
scene: Box<dyn Scene>,
}
#[derive(Resource)]
pub struct SceneManager {
scenes: rustc_hash::FxHashMap<String, BoxedScene>,
scene_stack: Vec<String>,
}
impl SceneManager {
#[must_use]
pub fn new() -> Self {
Self {
scenes: rustc_hash::FxHashMap::default(),
scene_stack: Vec::new(),
}
}
pub fn register_scene<S: Scene>(&mut self, name: &str, scene: S) {
self.scenes.insert(
name.to_string(),
BoxedScene {
scene: Box::new(scene),
},
);
log::info!("SceneManager: registered scene '{name}'");
}
pub fn switch_to(&mut self, name: &str, world: &mut World) {
if !self.scenes.contains_key(name) {
log::warn!("SceneManager: scene '{name}' not registered");
return;
}
for scene_name in self.scene_stack.drain(..).rev() {
if let Some(boxed) = self.scenes.get_mut(&scene_name) {
boxed.scene.on_exit(world);
}
}
if let Some(boxed) = self.scenes.get_mut(name) {
boxed.scene.on_enter(world);
}
self.scene_stack.push(name.to_string());
log::info!("SceneManager: switched to scene '{name}'");
}
pub fn push_overlay(&mut self, name: &str, world: &mut World) {
if !self.scenes.contains_key(name) {
log::warn!("SceneManager: scene '{name}' not registered");
return;
}
if let Some(boxed) = self.scenes.get_mut(name) {
boxed.scene.on_enter(world);
}
self.scene_stack.push(name.to_string());
log::info!("SceneManager: pushed overlay '{name}'");
}
pub fn pop_overlay(&mut self, world: &mut World) {
if self.scene_stack.len() <= 1 {
return;
}
if let Some(name) = self.scene_stack.pop() {
if let Some(boxed) = self.scenes.get_mut(&name) {
boxed.scene.on_exit(world);
}
log::info!("SceneManager: popped overlay '{name}'");
}
}
#[must_use]
pub fn current_scene(&self) -> Option<&str> {
self.scene_stack.last().map(std::string::String::as_str)
}
pub fn update_all(&mut self, world: &mut World) {
for name in &self.scene_stack {
if let Some(boxed) = self.scenes.get_mut(name) {
boxed.scene.on_update(world);
}
}
}
}
impl Default for SceneManager {
fn default() -> Self {
Self::new()
}
}