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 shadow_engine_2d::prelude::*;
use winit::keyboard::KeyCode;

fn main() {
    env_logger::init();

    let mut engine = Engine::builder()
        .title("Shadow Engine 2D - UI Demo")
        .size(1280, 720)
        .build();

    let _ui_system = UISystem::new(1280.0, 720.0);

    // Create title text
    let title = engine.world_mut().spawn();
    if let Some(entity) = engine.world_mut().entity_mut(title) {
        entity.add(UITransform::new(Anchor::TopCenter).with_offset(Vec2::new(-150.0, 20.0)));
        entity.add(UIText::new("Shadow Engine 2D UI").with_size(32.0).with_color(Color::CYAN));
    }

    // Create FPS counter
    let fps_text = engine.world_mut().spawn();
    if let Some(entity) = engine.world_mut().entity_mut(fps_text) {
        entity.add(UITransform::new(Anchor::TopLeft).with_offset(Vec2::new(10.0, 10.0)));
        entity.add(UIText::new("FPS: 0").with_size(16.0).with_color(Color::GREEN));
    }

    // Create health bar panel
    let health_panel = engine.world_mut().spawn();
    if let Some(entity) = engine.world_mut().entity_mut(health_panel) {
        entity.add(
            UITransform::new(Anchor::TopLeft)
                .with_offset(Vec2::new(20.0, 50.0))
                .with_size(Vec2::new(250.0, 80.0))
        );
        entity.add(UIPanel::new().with_background(Color::rgb(0.1, 0.1, 0.15)).with_border(Color::CYAN, 2.0));
    }

    // Health bar label
    let health_label = engine.world_mut().spawn();
    if let Some(entity) = engine.world_mut().entity_mut(health_label) {
        entity.add(UITransform::new(Anchor::TopLeft).with_offset(Vec2::new(30.0, 60.0)));
        entity.add(UIText::new("Health:").with_size(18.0).with_color(Color::WHITE));
    }

    // Health progress bar
    let health_bar = engine.world_mut().spawn();
    if let Some(entity) = engine.world_mut().entity_mut(health_bar) {
        entity.add(
            UITransform::new(Anchor::TopLeft)
                .with_offset(Vec2::new(30.0, 85.0))
                .with_size(Vec2::new(230.0, 30.0))
        );
        entity.add(UIProgressBar::new(100.0).with_value(75.0).with_colors(Color::RED, Color::rgb(0.2, 0.0, 0.0)));
    }

    // Energy bar panel
    let energy_panel = engine.world_mut().spawn();
    if let Some(entity) = engine.world_mut().entity_mut(energy_panel) {
        entity.add(
            UITransform::new(Anchor::TopLeft)
                .with_offset(Vec2::new(20.0, 150.0))
                .with_size(Vec2::new(250.0, 80.0))
        );
        entity.add(UIPanel::new().with_background(Color::rgb(0.1, 0.1, 0.15)).with_border(Color::YELLOW, 2.0));
    }

    // Energy bar label
    let energy_label = engine.world_mut().spawn();
    if let Some(entity) = engine.world_mut().entity_mut(energy_label) {
        entity.add(UITransform::new(Anchor::TopLeft).with_offset(Vec2::new(30.0, 160.0)));
        entity.add(UIText::new("Energy:").with_size(18.0).with_color(Color::WHITE));
    }

    // Energy progress bar
    let energy_bar = engine.world_mut().spawn();
    if let Some(entity) = engine.world_mut().entity_mut(energy_bar) {
        entity.add(
            UITransform::new(Anchor::TopLeft)
                .with_offset(Vec2::new(30.0, 185.0))
                .with_size(Vec2::new(230.0, 30.0))
        );
        entity.add(UIProgressBar::new(100.0).with_value(50.0).with_colors(Color::BLUE, Color::rgb(0.0, 0.0, 0.2)));
    }

    // Create buttons
    let button_y_start = 300.0;
    let button_spacing = 70.0;

    for i in 0..3 {
        let button = engine.world_mut().spawn();
        if let Some(entity) = engine.world_mut().entity_mut(button) {
            entity.add(
                UITransform::new(Anchor::TopLeft)
                    .with_offset(Vec2::new(20.0, button_y_start + i as f32 * button_spacing))
                    .with_size(Vec2::new(200.0, 50.0))
            );
            entity.add(UIButton::new().with_colors(
                Color::rgb(0.2, 0.5, 0.8),
                Color::rgb(0.3, 0.6, 0.9),
                Color::rgb(0.1, 0.4, 0.7),
            ));
            entity.add(UIPanel::new());
            
            let label = match i {
                0 => "Start Game",
                1 => "Settings",
                2 => "Quit",
                _ => "Button",
            };
            entity.add(UIText::new(label).with_size(20.0).with_alignment(TextAlignment::Center));
        }
    }

    // Instructions
    let instructions = engine.world_mut().spawn();
    if let Some(entity) = engine.world_mut().entity_mut(instructions) {
        entity.add(UITransform::new(Anchor::BottomLeft).with_offset(Vec2::new(20.0, -100.0)));
        entity.add(UIText::new("Press Q/E to change health\nPress A/D to change energy").with_size(14.0).with_color(Color::rgb(0.7, 0.7, 0.7)));
    }

    // Score display
    let score_text = engine.world_mut().spawn();
    if let Some(entity) = engine.world_mut().entity_mut(score_text) {
        entity.add(UITransform::new(Anchor::TopRight).with_offset(Vec2::new(-200.0, 20.0)));
        entity.add(UIText::new("Score: 0").with_size(24.0).with_color(Color::YELLOW));
    }

    let mut score = 0;
    let mut frame_count = 0;
    let mut fps_timer = 0.0;

    engine.run(move |world, input, time| {
        let dt = time.delta();
        frame_count += 1;
        fps_timer += dt;

        // Update FPS counter every 0.5 seconds
        if fps_timer >= 0.5 {
            let fps = (frame_count as f32 / fps_timer) as i32;
            if let Some(entity) = world.entity_mut(fps_text) {
                if let Some(text) = entity.get_mut::<UIText>() {
                    text.text = format!("FPS: {}", fps);
                }
            }
            frame_count = 0;
            fps_timer = 0.0;
        }

        // Update health bar
        if let Some(entity) = world.entity_mut(health_bar) {
            if let Some(bar) = entity.get_mut::<UIProgressBar>() {
                if input.key_pressed(KeyCode::KeyQ) {
                    bar.set_value((bar.value - 20.0 * dt).max(0.0));
                }
                if input.key_pressed(KeyCode::KeyE) {
                    bar.set_value((bar.value + 20.0 * dt).min(100.0));
                }
            }
        }

        // Update energy bar
        if let Some(entity) = world.entity_mut(energy_bar) {
            if let Some(bar) = entity.get_mut::<UIProgressBar>() {
                if input.key_pressed(KeyCode::KeyA) {
                    bar.set_value((bar.value - 30.0 * dt).max(0.0));
                }
                if input.key_pressed(KeyCode::KeyD) {
                    bar.set_value((bar.value + 30.0 * dt).min(100.0));
                }
            }
        }

        // Update score
        if input.key_just_pressed(KeyCode::Space) {
            score += 100;
            if let Some(entity) = world.entity_mut(score_text) {
                if let Some(text) = entity.get_mut::<UIText>() {
                    text.text = format!("Score: {}", score);
                }
            }
        }

        // Simple button hover detection (mouse position from input)
        let mouse_pos = Vec2::new(input.mouse_position().0, input.mouse_position().1);
        
        for entity in world.entities_mut() {
            if entity.has::<UIButton>() {
                // Check if mouse is over button
                let is_hovered = if let Some(transform) = entity.get::<UITransform>() {
                    transform.contains_point(mouse_pos, 1280.0, 720.0)
                } else {
                    false
                };

                // Update button state
                if let Some(button) = entity.get_mut::<UIButton>() {
                    button.state = if is_hovered {
                        ButtonState::Hovered
                    } else {
                        ButtonState::Normal
                    };
                }

                // Update panel color based on button state
                let button_color = entity.get::<UIButton>().map(|b| b.get_current_color());
                if let (Some(panel), Some(color)) = (entity.get_mut::<UIPanel>(), button_color) {
                    panel.background_color = color;
                }
            }
        }
    });
}