shadowengine2d 2.0.0

A comprehensive 2D game engine built in Rust with ECS, rendering, audio, assets, animations, and scene management
Documentation
/*
 * MIT License
 * 
 * Copyright (c) 2025 ShadowEngine2D
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

use crate::{
    engine::{Engine, Game, run_game},
    window::WindowConfig,
    entity::{EntityManager, Transform, Sprite, Velocity},
    math::{Position, Size, Color},
    resources::Resources,
    systems::{System, SystemSet},
    EngineResult,
};
use std::any::Any;
use winit::event::WindowEvent;

pub struct App {
    resources: Resources,
    startup_systems: Vec<Box<dyn System>>,
    update_systems: Vec<Box<dyn System>>,
    render_systems: Vec<Box<dyn System>>,
    window_config: WindowConfig,
}

impl App {
    pub fn new() -> AppBuilder {
        AppBuilder::new()
    }

    pub fn run(self) -> EngineResult<()> {
        let game = ShadowGame {
            app: self,
            initialized: false,
        };
        run_game(game.app.window_config.clone(), Box::new(game))
    }
}

pub struct AppBuilder {
    resources: Resources,
    startup_systems: Vec<Box<dyn System>>,
    update_systems: Vec<Box<dyn System>>,
    render_systems: Vec<Box<dyn System>>,
    window_config: WindowConfig,
}

impl AppBuilder {
    pub fn new() -> Self {
        Self {
            resources: Resources::new(),
            startup_systems: Vec::new(),
            update_systems: Vec::new(),
            render_systems: Vec::new(),
            window_config: WindowConfig::default(),
        }
    }

    pub fn set_window(mut self, config: WindowConfig) -> Self {
        self.window_config = config;
        self
    }

    pub fn insert_resource<T: 'static>(mut self, resource: T) -> Self {
        self.resources.insert(resource);
        self
    }

    pub fn add_startup_system<T: System + 'static>(mut self, system: T) -> Self {
        self.startup_systems.push(Box::new(system));
        self
    }

    pub fn add_system<T: System + 'static>(mut self, system: T) -> Self {
        self.update_systems.push(Box::new(system));
        self
    }

    pub fn add_system_set(mut self, system_set: SystemSet) -> Self {
        match system_set {
            SystemSet::Startup(systems) => {
                self.startup_systems.extend(systems);
            }
            SystemSet::Update(systems) => {
                self.update_systems.extend(systems);
            }
            SystemSet::Render(systems) => {
                self.render_systems.extend(systems);
            }
        }
        self
    }

    pub fn build(self) -> App {
        App {
            resources: self.resources,
            startup_systems: self.startup_systems,
            update_systems: self.update_systems,
            render_systems: self.render_systems,
            window_config: self.window_config,
        }
    }
}

struct ShadowGame {
    app: App,
    initialized: bool,
}

impl Game for ShadowGame {
    fn init(&mut self, engine: &mut Engine) -> EngineResult<()> {

        for system in &mut self.app.startup_systems {
            system.run(engine, &mut self.app.resources)?;
        }
        self.initialized = true;
        Ok(())
    }

    fn update(&mut self, engine: &mut Engine, delta_time: f32) -> EngineResult<()> {
        if !self.initialized {
            return Ok(());
        }

        if let Some(time) = self.app.resources.get_mut::<TimeResource>() {
            time.update(delta_time);
        }

        for system in &mut self.app.update_systems {
            system.run(engine, &mut self.app.resources)?;
        }

        Ok(())
    }

    fn render(&mut self, engine: &mut Engine) -> EngineResult<()> {
        if !self.initialized {
            return Ok(());
        }

        for system in &mut self.app.render_systems {
            system.run(engine, &mut self.app.resources)?;
        }

        engine.render()?;
        Ok(())
    }

    fn on_event(&mut self, engine: &mut Engine, event: &WindowEvent) -> EngineResult<()> {

        if let Some(input) = self.app.resources.get_mut::<InputResource>() {
            input.handle_event(event);
        }
        Ok(())
    }
}

#[derive(Debug)]
pub struct TimeResource {
    pub delta_time: f32,
    pub total_time: f32,
}

impl TimeResource {
    pub fn new() -> Self {
        Self {
            delta_time: 0.0,
            total_time: 0.0,
        }
    }

    pub fn update(&mut self, delta_time: f32) {
        self.delta_time = delta_time;
        self.total_time += delta_time;
    }
}

#[derive(Debug)]
pub struct InputResource {

}

impl InputResource {
    pub fn new() -> Self {
        Self {}
    }

    pub fn handle_event(&mut self, _event: &WindowEvent) {

    }
}

impl Default for AppBuilder {
    fn default() -> Self {
        Self::new()
    }
}