use std::collections::HashMap;
use std::any::Any;
use std::sync::Arc;
use crate::{
entity::{EntityId, EntityManager},
resources::Resources,
assets::{AssetId, AssetManager},
EngineResult,
math::{Position, Color},
time::Time,
};
#[derive(Debug)]
pub struct SceneManager {
scenes: HashMap<SceneId, Box<dyn Scene>>,
current_scene: Option<SceneId>,
next_scene: Option<SceneId>,
transition: Option<SceneTransition>,
scene_stack: Vec<SceneId>,
next_id: SceneId,
global_resources: Resources,
scene_data: HashMap<SceneId, SceneData>,
}
impl SceneManager {
pub fn new() -> Self {
Self {
scenes: HashMap::new(),
current_scene: None,
next_scene: None,
transition: None,
scene_stack: Vec::new(),
next_id: SceneId(1),
global_resources: Resources::new(),
scene_data: HashMap::new(),
}
}
pub fn register_scene(&mut self, scene: Box<dyn Scene>) -> SceneId {
let id = self.next_id;
self.next_id.0 += 1;
self.scene_data.insert(id, SceneData::new(id, scene.get_name()));
self.scenes.insert(id, scene);
id
}
pub fn set_scene(&mut self, scene_id: SceneId) {
self.next_scene = Some(scene_id);
}
pub fn push_scene(&mut self, scene_id: SceneId) {
if let Some(current) = self.current_scene {
self.scene_stack.push(current);
}
self.set_scene(scene_id);
}
pub fn pop_scene(&mut self) {
if let Some(previous) = self.scene_stack.pop() {
self.set_scene(previous);
}
}
pub fn set_transition(&mut self, transition: SceneTransition) {
self.transition = Some(transition);
}
pub fn update(&mut self, entity_manager: &mut EntityManager, delta_time: f32) -> EngineResult<()> {
if let Some(next_scene) = self.next_scene.take() {
self.perform_scene_transition(next_scene, entity_manager, delta_time)?;
}
if let Some(transition) = &mut self.transition {
transition.update(delta_time);
if transition.is_complete() {
self.transition = None;
}
}
if let Some(current_id) = self.current_scene {
if let Some(scene) = self.scenes.get_mut(¤t_id) {
scene.update(entity_manager, &mut self.global_resources, delta_time)?;
}
}
Ok(())
}
pub fn render(&mut self, entity_manager: &mut EntityManager) -> EngineResult<()> {
if let Some(current_id) = self.current_scene {
if let Some(scene) = self.scenes.get_mut(¤t_id) {
scene.render(entity_manager, &mut self.global_resources)?;
}
}
if let Some(transition) = &self.transition {
transition.render(entity_manager)?;
}
Ok(())
}
pub fn get_current_scene(&self) -> Option<SceneId> {
self.current_scene
}
pub fn get_scene_data(&self, scene_id: SceneId) -> Option<&SceneData> {
self.scene_data.get(&scene_id)
}
pub fn get_scene_data_mut(&mut self, scene_id: SceneId) -> Option<&mut SceneData> {
self.scene_data.get_mut(&scene_id)
}
pub fn get_global_resources(&self) -> &Resources {
&self.global_resources
}
pub fn get_global_resources_mut(&mut self) -> &mut Resources {
&mut self.global_resources
}
pub fn has_scene(&self, scene_id: SceneId) -> bool {
self.scenes.contains_key(&scene_id)
}
pub fn remove_scene(&mut self, scene_id: SceneId) -> Option<Box<dyn Scene>> {
self.scene_data.remove(&scene_id);
self.scenes.remove(&scene_id)
}
pub fn get_all_scenes(&self) -> Vec<SceneId> {
self.scenes.keys().cloned().collect()
}
pub fn preload_scene_assets(&mut self, scene_id: SceneId, asset_manager: &mut AssetManager) -> EngineResult<()> {
if let Some(scene) = self.scenes.get_mut(&scene_id) {
scene.preload_assets(asset_manager)?;
}
Ok(())
}
pub fn unload_scene_assets(&mut self, scene_id: SceneId, asset_manager: &mut AssetManager) -> EngineResult<()> {
if let Some(scene) = self.scenes.get_mut(&scene_id) {
scene.unload_assets(asset_manager)?;
}
Ok(())
}
fn perform_scene_transition(&mut self, next_scene: SceneId, entity_manager: &mut EntityManager, _delta_time: f32) -> EngineResult<()> {
if let Some(current_id) = self.current_scene {
if let Some(scene) = self.scenes.get_mut(¤t_id) {
scene.on_exit(entity_manager, &mut self.global_resources)?;
}
}
if let Some(current_id) = self.current_scene {
if let Some(data) = self.scene_data.get_mut(¤t_id) {
data.last_exit_time = Some(std::time::SystemTime::now());
data.exit_count += 1;
}
}
self.current_scene = Some(next_scene);
if let Some(scene) = self.scenes.get_mut(&next_scene) {
scene.on_enter(entity_manager, &mut self.global_resources)?;
}
if let Some(data) = self.scene_data.get_mut(&next_scene) {
data.last_enter_time = Some(std::time::SystemTime::now());
data.enter_count += 1;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SceneId(pub u32);
pub trait Scene: std::fmt::Debug + Send {
fn get_name(&self) -> String;
fn on_enter(&mut self, entity_manager: &mut EntityManager, resources: &mut Resources) -> EngineResult<()> {
Ok(())
}
fn on_exit(&mut self, entity_manager: &mut EntityManager, resources: &mut Resources) -> EngineResult<()> {
Ok(())
}
fn update(&mut self, entity_manager: &mut EntityManager, resources: &mut Resources, delta_time: f32) -> EngineResult<()>;
fn render(&mut self, entity_manager: &mut EntityManager, resources: &mut Resources) -> EngineResult<()> {
Ok(())
}
fn preload_assets(&mut self, asset_manager: &mut AssetManager) -> EngineResult<()> {
Ok(())
}
fn unload_assets(&mut self, asset_manager: &mut AssetManager) -> EngineResult<()> {
Ok(())
}
fn handle_event(&mut self, event: &SceneEvent, entity_manager: &mut EntityManager, resources: &mut Resources) -> EngineResult<()> {
Ok(())
}
fn get_config(&self) -> SceneConfig {
SceneConfig::default()
}
}
#[derive(Debug)]
pub struct SceneData {
pub id: SceneId,
pub name: String,
pub enter_count: u32,
pub exit_count: u32,
pub total_time: f32,
pub last_enter_time: Option<std::time::SystemTime>,
pub last_exit_time: Option<std::time::SystemTime>,
pub custom_data: HashMap<String, Box<dyn Any + Send + Sync>>,
}
impl SceneData {
pub fn new(id: SceneId, name: String) -> Self {
Self {
id,
name,
enter_count: 0,
exit_count: 0,
total_time: 0.0,
last_enter_time: None,
last_exit_time: None,
custom_data: HashMap::new(),
}
}
pub fn set_custom_data<T: Any + Send + Sync>(&mut self, key: String, data: T) {
self.custom_data.insert(key, Box::new(data));
}
pub fn get_custom_data<T: Any + Send + Sync>(&self, key: &str) -> Option<&T> {
self.custom_data.get(key)?.downcast_ref::<T>()
}
}
#[derive(Debug, Clone)]
pub struct SceneConfig {
pub clear_entities_on_enter: bool,
pub pause_previous_scene: bool,
pub overlay_mode: bool,
pub background_color: Option<Color>,
pub required_assets: Vec<AssetId>,
}
impl Default for SceneConfig {
fn default() -> Self {
Self {
clear_entities_on_enter: true,
pause_previous_scene: true,
overlay_mode: false,
background_color: None,
required_assets: Vec::new(),
}
}
}
#[derive(Debug)]
pub enum SceneTransition {
Instant,
Fade {
duration: f32,
elapsed: f32,
color: Color,
fade_out: bool,
},
Slide {
duration: f32,
elapsed: f32,
direction: SlideDirection,
progress: f32,
},
CrossFade {
duration: f32,
elapsed: f32,
},
Custom {
duration: f32,
elapsed: f32,
effect: Box<dyn TransitionEffect>,
},
}
impl SceneTransition {
pub fn fade(duration: f32, color: Color) -> Self {
Self::Fade {
duration,
elapsed: 0.0,
color,
fade_out: false,
}
}
pub fn slide(duration: f32, direction: SlideDirection) -> Self {
Self::Slide {
duration,
elapsed: 0.0,
direction,
progress: 0.0,
}
}
pub fn cross_fade(duration: f32) -> Self {
Self::CrossFade {
duration,
elapsed: 0.0,
}
}
pub fn custom(duration: f32, effect: Box<dyn TransitionEffect>) -> Self {
Self::Custom {
duration,
elapsed: 0.0,
effect,
}
}
pub fn update(&mut self, delta_time: f32) {
match self {
Self::Instant => {},
Self::Fade { duration, elapsed, fade_out, .. } => {
*elapsed += delta_time;
if *elapsed >= *duration / 2.0 && !*fade_out {
*fade_out = true;
}
},
Self::Slide { duration, elapsed, progress, .. } => {
*elapsed += delta_time;
*progress = (*elapsed / *duration).clamp(0.0, 1.0);
},
Self::CrossFade { duration, elapsed } => {
*elapsed += delta_time;
},
Self::Custom { duration, elapsed, effect } => {
*elapsed += delta_time;
effect.update(delta_time, *elapsed / *duration);
},
}
}
pub fn is_complete(&self) -> bool {
match self {
Self::Instant => true,
Self::Fade { duration, elapsed, .. } => *elapsed >= *duration,
Self::Slide { duration, elapsed, .. } => *elapsed >= *duration,
Self::CrossFade { duration, elapsed } => *elapsed >= *duration,
Self::Custom { duration, elapsed, .. } => *elapsed >= *duration,
}
}
pub fn render(&self, entity_manager: &mut EntityManager) -> EngineResult<()> {
match self {
Self::Instant => {},
Self::Fade { duration, elapsed, color, fade_out } => {
let progress = (*elapsed / *duration).clamp(0.0, 1.0);
let alpha = if *fade_out {
1.0 - progress
} else {
progress
};
let fade_color = Color::new(color.r, color.g, color.b, alpha);
self.render_fullscreen_quad(entity_manager, fade_color)?;
},
Self::Slide { progress, direction, .. } => {
self.render_slide_effect(entity_manager, *progress, *direction)?;
},
Self::CrossFade { duration, elapsed } => {
let alpha = (*elapsed / *duration).clamp(0.0, 1.0);
self.render_crossfade(entity_manager, alpha)?;
},
Self::Custom { elapsed, duration, effect } => {
effect.render(entity_manager, *elapsed / *duration)?;
},
}
Ok(())
}
fn render_fullscreen_quad(&self, _entity_manager: &mut EntityManager, _color: Color) -> EngineResult<()> {
Ok(())
}
fn render_slide_effect(&self, _entity_manager: &mut EntityManager, _progress: f32, _direction: SlideDirection) -> EngineResult<()> {
Ok(())
}
fn render_crossfade(&self, _entity_manager: &mut EntityManager, _alpha: f32) -> EngineResult<()> {
Ok(())
}
}
#[derive(Debug, Clone, Copy)]
pub enum SlideDirection {
Left,
Right,
Up,
Down,
}
pub trait TransitionEffect: std::fmt::Debug + Send {
fn update(&mut self, delta_time: f32, progress: f32);
fn render(&self, entity_manager: &mut EntityManager, progress: f32) -> EngineResult<()>;
}
#[derive(Debug)]
pub enum SceneEvent {
EntitySpawned(EntityId),
EntityDestroyed(EntityId),
AssetLoaded(AssetId),
AssetUnloaded(AssetId),
Custom(String, Box<dyn Any + Send + Sync>),
}
#[derive(Debug)]
pub struct MenuScene {
name: String,
menu_items: Vec<MenuItem>,
selected_index: usize,
background_color: Color,
title_text: String,
}
impl MenuScene {
pub fn new(name: String, title: String) -> Self {
Self {
name,
menu_items: Vec::new(),
selected_index: 0,
background_color: Color::new(0.1, 0.1, 0.2, 1.0),
title_text: title,
}
}
pub fn add_item(&mut self, item: MenuItem) {
self.menu_items.push(item);
}
pub fn set_background_color(&mut self, color: Color) {
self.background_color = color;
}
pub fn get_selected_item(&self) -> Option<&MenuItem> {
self.menu_items.get(self.selected_index)
}
pub fn select_next(&mut self) {
if !self.menu_items.is_empty() {
self.selected_index = (self.selected_index + 1) % self.menu_items.len();
}
}
pub fn select_previous(&mut self) {
if !self.menu_items.is_empty() {
self.selected_index = if self.selected_index == 0 {
self.menu_items.len() - 1
} else {
self.selected_index - 1
};
}
}
fn render_menu(&self, entity_manager: &mut EntityManager) -> EngineResult<()> {
for (index, item) in self.menu_items.iter().enumerate() {
let is_selected = index == self.selected_index;
let y_pos = 200.0 + index as f32 * 60.0;
let color = if is_selected {
Color::new(1.0, 1.0, 0.0, 1.0)
} else {
Color::WHITE
};
self.render_menu_item(entity_manager, item, Position::new(400.0, y_pos), color)?;
}
Ok(())
}
fn render_menu_item(&self, _entity_manager: &mut EntityManager, _item: &MenuItem, _position: Position, _color: Color) -> EngineResult<()> {
Ok(())
}
}
impl Scene for MenuScene {
fn get_name(&self) -> String {
self.name.clone()
}
fn on_enter(&mut self, entity_manager: &mut EntityManager, _resources: &mut Resources) -> EngineResult<()> {
self.selected_index = 0;
Ok(())
}
fn update(&mut self, _entity_manager: &mut EntityManager, _resources: &mut Resources, _delta_time: f32) -> EngineResult<()> {
Ok(())
}
fn render(&mut self, entity_manager: &mut EntityManager, _resources: &mut Resources) -> EngineResult<()> {
self.render_menu(entity_manager)
}
}
#[derive(Debug, Clone)]
pub struct MenuItem {
pub text: String,
pub action: MenuAction,
pub enabled: bool,
}
impl MenuItem {
pub fn new(text: String, action: MenuAction) -> Self {
Self {
text,
action,
enabled: true,
}
}
pub fn disabled(text: String, action: MenuAction) -> Self {
Self {
text,
action,
enabled: false,
}
}
}
#[derive(Debug, Clone)]
pub enum MenuAction {
None,
ChangeScene(SceneId),
Quit,
Custom(String),
}
#[derive(Debug)]
pub struct GameScene {
name: String,
level_id: u32,
entities: Vec<EntityId>,
background_music: Option<AssetId>,
background_image: Option<AssetId>,
spawn_points: Vec<Position>,
objectives: Vec<GameObjective>,
score: u32,
lives: u32,
time_limit: Option<f32>,
elapsed_time: f32,
}
impl GameScene {
pub fn new(name: String, level_id: u32) -> Self {
Self {
name,
level_id,
entities: Vec::new(),
background_music: None,
background_image: None,
spawn_points: Vec::new(),
objectives: Vec::new(),
score: 0,
lives: 3,
time_limit: None,
elapsed_time: 0.0,
}
}
pub fn set_background_music(&mut self, asset_id: AssetId) {
self.background_music = Some(asset_id);
}
pub fn set_background_image(&mut self, asset_id: AssetId) {
self.background_image = Some(asset_id);
}
pub fn add_spawn_point(&mut self, position: Position) {
self.spawn_points.push(position);
}
pub fn add_objective(&mut self, objective: GameObjective) {
self.objectives.push(objective);
}
pub fn set_time_limit(&mut self, seconds: f32) {
self.time_limit = Some(seconds);
}
pub fn add_score(&mut self, points: u32) {
self.score += points;
}
pub fn lose_life(&mut self) -> bool {
if self.lives > 0 {
self.lives -= 1;
}
self.lives == 0
}
pub fn is_time_up(&self) -> bool {
if let Some(limit) = self.time_limit {
self.elapsed_time >= limit
} else {
false
}
}
pub fn are_objectives_complete(&self) -> bool {
self.objectives.iter().all(|obj| obj.is_complete())
}
fn spawn_entities(&mut self, entity_manager: &mut EntityManager) -> EngineResult<()> {
for position in &self.spawn_points {
let entity = entity_manager.create_entity();
self.entities.push(entity);
}
Ok(())
}
fn update_objectives(&mut self, entity_manager: &EntityManager) {
for objective in &mut self.objectives {
objective.update(entity_manager);
}
}
}
impl Scene for GameScene {
fn get_name(&self) -> String {
self.name.clone()
}
fn on_enter(&mut self, entity_manager: &mut EntityManager, _resources: &mut Resources) -> EngineResult<()> {
self.score = 0;
self.elapsed_time = 0.0;
self.entities.clear();
self.spawn_entities(entity_manager)?;
Ok(())
}
fn on_exit(&mut self, entity_manager: &mut EntityManager, _resources: &mut Resources) -> EngineResult<()> {
for entity in &self.entities {
entity_manager.remove_entity(*entity);
}
self.entities.clear();
Ok(())
}
fn update(&mut self, entity_manager: &mut EntityManager, _resources: &mut Resources, delta_time: f32) -> EngineResult<()> {
self.elapsed_time += delta_time;
self.update_objectives(entity_manager);
if self.are_objectives_complete() {
} else if self.is_time_up() || self.lives == 0 {
}
Ok(())
}
fn render(&mut self, _entity_manager: &mut EntityManager, _resources: &mut Resources) -> EngineResult<()> {
self.render_hud()?;
Ok(())
}
fn preload_assets(&mut self, asset_manager: &mut AssetManager) -> EngineResult<()> {
if let Some(music_id) = self.background_music {
}
if let Some(bg_id) = self.background_image {
}
Ok(())
}
fn get_config(&self) -> SceneConfig {
SceneConfig {
clear_entities_on_enter: true,
pause_previous_scene: false,
overlay_mode: false,
background_color: Some(Color::new(0.2, 0.3, 0.4, 1.0)),
required_assets: {
let mut assets = Vec::new();
if let Some(music) = self.background_music {
assets.push(music);
}
if let Some(bg) = self.background_image {
assets.push(bg);
}
assets
},
}
}
}
impl GameScene {
fn render_hud(&self) -> EngineResult<()> {
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct GameObjective {
pub description: String,
pub objective_type: ObjectiveType,
pub target_value: i32,
pub current_value: i32,
pub completed: bool,
}
impl GameObjective {
pub fn new(description: String, objective_type: ObjectiveType, target_value: i32) -> Self {
Self {
description,
objective_type,
target_value,
current_value: 0,
completed: false,
}
}
pub fn update(&mut self, _entity_manager: &EntityManager) {
match self.objective_type {
ObjectiveType::CollectItems => {
},
ObjectiveType::DefeatEnemies => {
},
ObjectiveType::ReachLocation => {
},
ObjectiveType::SurviveTime => {
},
ObjectiveType::Custom(_) => {
},
}
if self.current_value >= self.target_value {
self.completed = true;
}
}
pub fn is_complete(&self) -> bool {
self.completed
}
pub fn add_progress(&mut self, amount: i32) {
self.current_value += amount;
if self.current_value >= self.target_value {
self.completed = true;
}
}
}
#[derive(Debug, Clone)]
pub enum ObjectiveType {
CollectItems,
DefeatEnemies,
ReachLocation,
SurviveTime,
Custom(String),
}
#[derive(Debug)]
pub struct LoadingScene {
name: String,
progress: f32,
status_text: String,
assets_to_load: Vec<AssetId>,
loaded_assets: Vec<AssetId>,
target_scene: Option<SceneId>,
}
impl LoadingScene {
pub fn new(name: String, assets: Vec<AssetId>, target_scene: SceneId) -> Self {
Self {
name,
progress: 0.0,
status_text: "Loading...".to_string(),
assets_to_load: assets,
loaded_assets: Vec::new(),
target_scene: Some(target_scene),
}
}
fn update_progress(&mut self) {
if !self.assets_to_load.is_empty() {
self.progress = self.loaded_assets.len() as f32 / self.assets_to_load.len() as f32;
} else {
self.progress = 1.0;
}
self.status_text = format!("Loading... {:.0}%", self.progress * 100.0);
}
fn is_loading_complete(&self) -> bool {
self.progress >= 1.0
}
}
impl Scene for LoadingScene {
fn get_name(&self) -> String {
self.name.clone()
}
fn update(&mut self, _entity_manager: &mut EntityManager, _resources: &mut Resources, _delta_time: f32) -> EngineResult<()> {
self.update_progress();
if self.is_loading_complete() {
if let Some(target) = self.target_scene {
println!("Loading complete, switching to scene {:?}", target);
}
}
Ok(())
}
fn render(&mut self, entity_manager: &mut EntityManager, _resources: &mut Resources) -> EngineResult<()> {
self.render_loading_screen(entity_manager)
}
}
impl LoadingScene {
fn render_loading_screen(&self, _entity_manager: &mut EntityManager) -> EngineResult<()> {
Ok(())
}
}
#[derive(Debug)]
pub struct PauseScene {
name: String,
menu_items: Vec<MenuItem>,
selected_index: usize,
background_alpha: f32,
}
impl PauseScene {
pub fn new(name: String) -> Self {
let mut scene = Self {
name,
menu_items: Vec::new(),
selected_index: 0,
background_alpha: 0.5,
};
scene.add_item(MenuItem::new("Resume".to_string(), MenuAction::Custom("resume".to_string())));
scene.add_item(MenuItem::new("Settings".to_string(), MenuAction::Custom("settings".to_string())));
scene.add_item(MenuItem::new("Main Menu".to_string(), MenuAction::Custom("main_menu".to_string())));
scene.add_item(MenuItem::new("Quit".to_string(), MenuAction::Quit));
scene
}
pub fn add_item(&mut self, item: MenuItem) {
self.menu_items.push(item);
}
}
impl Scene for PauseScene {
fn get_name(&self) -> String {
self.name.clone()
}
fn update(&mut self, _entity_manager: &mut EntityManager, _resources: &mut Resources, _delta_time: f32) -> EngineResult<()> {
Ok(())
}
fn render(&mut self, entity_manager: &mut EntityManager, _resources: &mut Resources) -> EngineResult<()> {
let overlay_color = Color::new(0.0, 0.0, 0.0, self.background_alpha);
for (index, item) in self.menu_items.iter().enumerate() {
let is_selected = index == self.selected_index;
let y_pos = 250.0 + index as f32 * 50.0;
let color = if is_selected {
Color::new(1.0, 1.0, 0.0, 1.0)
} else {
Color::WHITE
};
}
Ok(())
}
fn get_config(&self) -> SceneConfig {
SceneConfig {
clear_entities_on_enter: false,
pause_previous_scene: true,
overlay_mode: true,
background_color: None,
required_assets: Vec::new(),
}
}
}
impl Default for SceneManager {
fn default() -> Self {
Self::new()
}
}