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::Component;
use crate::math::{Mat4, Vec2, Vec3};

#[derive(Clone, Debug)]
pub struct Camera {
    pub position: Vec2,
    pub zoom: f32,
    pub rotation: f32,
    pub viewport_width: f32,
    pub viewport_height: f32,
}

impl Camera {
    pub fn new(viewport_width: f32, viewport_height: f32) -> Self {
        Self {
            position: Vec2::ZERO,
            zoom: 1.0,
            rotation: 0.0,
            viewport_width,
            viewport_height,
        }
    }

    pub fn with_position(mut self, position: Vec2) -> Self {
        self.position = position;
        self
    }

    pub fn with_zoom(mut self, zoom: f32) -> Self {
        self.zoom = zoom;
        self
    }

    pub fn resize(&mut self, width: f32, height: f32) {
        self.viewport_width = width;
        self.viewport_height = height;
    }

    pub fn view_matrix(&self) -> Mat4 {
        // Create orthographic projection
        let left = -self.viewport_width / 2.0 / self.zoom;
        let right = self.viewport_width / 2.0 / self.zoom;
        let bottom = self.viewport_height / 2.0 / self.zoom;
        let top = -self.viewport_height / 2.0 / self.zoom;

        let ortho = Mat4::orthographic_rh(left, right, bottom, top, -1.0, 1.0);

        // Apply camera transform
        let translation = Mat4::from_translation(Vec3::new(-self.position.x, -self.position.y, 0.0));
        let rotation = Mat4::from_rotation_z(self.rotation);

        ortho * rotation * translation
    }

    pub fn screen_to_world(&self, screen_pos: Vec2) -> Vec2 {
        let normalized_x = (screen_pos.x / self.viewport_width - 0.5) * 2.0;
        let normalized_y = (screen_pos.y / self.viewport_height - 0.5) * 2.0;

        let world_x = normalized_x * (self.viewport_width / 2.0 / self.zoom) + self.position.x;
        let world_y = normalized_y * (self.viewport_height / 2.0 / self.zoom) + self.position.y;

        Vec2::new(world_x, world_y)
    }

    pub fn world_to_screen(&self, world_pos: Vec2) -> Vec2 {
        let relative_x = (world_pos.x - self.position.x) * self.zoom / (self.viewport_width / 2.0);
        let relative_y = (world_pos.y - self.position.y) * self.zoom / (self.viewport_height / 2.0);

        let screen_x = (relative_x / 2.0 + 0.5) * self.viewport_width;
        let screen_y = (relative_y / 2.0 + 0.5) * self.viewport_height;

        Vec2::new(screen_x, screen_y)
    }

    pub fn follow(&mut self, target: Vec2, smoothness: f32, dt: f32) {
        let diff = target - self.position;
        self.position += diff * smoothness * dt;
    }

    pub fn follow_bounded(&mut self, target: Vec2, bounds: CameraBounds, smoothness: f32, dt: f32) {
        let diff = target - self.position;
        let mut new_pos = self.position + diff * smoothness * dt;

        // Apply bounds
        let half_width = self.viewport_width / 2.0 / self.zoom;
        let half_height = self.viewport_height / 2.0 / self.zoom;

        new_pos.x = new_pos.x.clamp(bounds.min.x + half_width, bounds.max.x - half_width);
        new_pos.y = new_pos.y.clamp(bounds.min.y + half_height, bounds.max.y - half_height);

        self.position = new_pos;
    }

    pub fn shake(&mut self, intensity: f32) {
        use rand::Rng;
        let mut rng = rand::thread_rng();
        
        let offset_x = rng.gen_range(-intensity..intensity);
        let offset_y = rng.gen_range(-intensity..intensity);
        
        self.position += Vec2::new(offset_x, offset_y);
    }
}

impl Component for Camera {}

#[derive(Clone, Copy, Debug)]
pub struct CameraBounds {
    pub min: Vec2,
    pub max: Vec2,
}

impl CameraBounds {
    pub fn new(min: Vec2, max: Vec2) -> Self {
        Self { min, max }
    }

    pub fn from_size(width: f32, height: f32) -> Self {
        Self {
            min: Vec2::new(-width / 2.0, -height / 2.0),
            max: Vec2::new(width / 2.0, height / 2.0),
        }
    }
}

pub struct CameraController {
    pub target_entity: Option<u64>,
    pub follow_smoothness: f32,
    pub bounds: Option<CameraBounds>,
    pub zoom_speed: f32,
    pub min_zoom: f32,
    pub max_zoom: f32,
}

impl CameraController {
    pub fn new() -> Self {
        Self {
            target_entity: None,
            follow_smoothness: 5.0,
            bounds: None,
            zoom_speed: 1.0,
            min_zoom: 0.1,
            max_zoom: 10.0,
        }
    }

    pub fn follow_entity(mut self, entity_id: u64) -> Self {
        self.target_entity = Some(entity_id);
        self
    }

    pub fn with_bounds(mut self, bounds: CameraBounds) -> Self {
        self.bounds = Some(bounds);
        self
    }

    pub fn with_smoothness(mut self, smoothness: f32) -> Self {
        self.follow_smoothness = smoothness;
        self
    }

    pub fn zoom_in(&self, camera: &mut Camera, amount: f32) {
        camera.zoom = (camera.zoom + amount * self.zoom_speed).clamp(self.min_zoom, self.max_zoom);
    }

    pub fn zoom_out(&self, camera: &mut Camera, amount: f32) {
        camera.zoom = (camera.zoom - amount * self.zoom_speed).clamp(self.min_zoom, self.max_zoom);
    }
}

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