shadow_engine_2d 2.0.1

A modern, high-performance 2D game engine built in Rust with ECS, physics, particles, audio, and more
Documentation
use crate::ecs::World;
use crate::graphics::Renderer;
use crate::input::InputState;
use crate::time::Time;
use std::sync::Arc;
use winit::{
    event::*,
    event_loop::EventLoop,
    window::{Window, WindowBuilder},
};

pub struct Engine {
    event_loop: Option<EventLoop<()>>,
    window: Arc<Window>,
    renderer: Renderer,
    world: World,
    input: InputState,
    time: Time,
}

pub struct EngineBuilder {
    title: String,
    width: u32,
    height: u32,
}

impl EngineBuilder {
    pub fn new() -> Self {
        Self {
            title: "Shadow Engine 2D".to_string(),
            width: 1280,
            height: 720,
        }
    }

    pub fn title(mut self, title: impl Into<String>) -> Self {
        self.title = title.into();
        self
    }

    pub fn size(mut self, width: u32, height: u32) -> Self {
        self.width = width;
        self.height = height;
        self
    }

    pub fn build(self) -> Engine {
        let event_loop = EventLoop::new().unwrap();
        let window = Arc::new(WindowBuilder::new()
            .with_title(self.title)
            .with_inner_size(winit::dpi::PhysicalSize::new(self.width, self.height))
            .build(&event_loop)
            .unwrap());

        let renderer = pollster::block_on(Renderer::new(window.clone()));
        let world = World::new();
        let input = InputState::new();
        let time = Time::new();

        Engine {
            event_loop: Some(event_loop),
            window,
            renderer,
            world,
            input,
            time,
        }
    }
}

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

impl Engine {
    pub fn builder() -> EngineBuilder {
        EngineBuilder::new()
    }

    pub fn world(&self) -> &World {
        &self.world
    }

    pub fn world_mut(&mut self) -> &mut World {
        &mut self.world
    }

    pub fn run<F>(mut self, mut update_fn: F)
    where
        F: FnMut(&mut World, &InputState, &Time) + 'static,
    {
        let event_loop = self.event_loop.take().unwrap();

        event_loop
            .run(move |event, control_flow| {
                match event {
                    Event::WindowEvent {
                        ref event,
                        window_id,
                    } if window_id == self.window.id() => {
                        self.input.process_event(event);

                        match event {
                            WindowEvent::CloseRequested => control_flow.exit(),
                            WindowEvent::Resized(physical_size) => {
                                self.renderer.resize(*physical_size);
                            }
                            WindowEvent::RedrawRequested => {
                                self.time.update();
                                
                                // Call user update function
                                update_fn(&mut self.world, &self.input, &self.time);

                                // Render
                                match self.renderer.render(&self.world) {
                                    Ok(_) => {}
                                    Err(wgpu::SurfaceError::Lost) => {
                                        self.renderer.resize(self.renderer.size())
                                    }
                                    Err(wgpu::SurfaceError::OutOfMemory) => control_flow.exit(),
                                    Err(e) => eprintln!("Render error: {:?}", e),
                                }

                                self.input.end_frame();
                            }
                            _ => {}
                        }
                    }
                    Event::AboutToWait => {
                        self.window.request_redraw();
                    }
                    _ => {}
                }
            })
            .unwrap();
    }
}