codecraft 0.1.1

A minimalist 3D game engine built on parts of Bevy (ECS, color) with wgpu and winit: OpenPBR materials, clustered lighting, an immediate-mode UI, audio and gamepad haptics
Documentation
//! Two scenes and the UI to move between them: a menu of buttons, and a
//! room you can walk back out of.
//!
//! ```text
//! cargo run --example menu
//! ```
//!
//! A scene owns what it spawns -- meshes, lights, panels, the camera rig --
//! and a scene change takes all of it down before the next one is set up,
//! so neither scene has to know what the other left behind.
use codecraft::glam::Vec3;
use codecraft::prelude::*;
use codecraft::sceneobjects::lights::default_lights;
use codecraft::{KeyCode, Light, OrbitCamera, gizmos, primitives};

/// The menu: a panel of buttons in the middle of an empty frame.
struct Menu;

impl Scene for Menu {
    fn setup(&mut self, app: &mut AppState) {
        app.spawn(Heading::text("CODECRAFT").y_ratio(0.25));

        // A button's click handler runs inside the UI schedule and cannot
        // borrow the app, so a scene change goes through `SceneCommands`,
        // which is cheap to clone into as many handlers as need one.
        let scenes = app.scenes();
        app.spawn(
            Panel::new("Menu")
                .add(Button::new("ENTER THE ROOM", move || scenes.change(Room)))
                .add(Button::new("QUIT", || std::process::exit(0))),
        );
    }
}

/// Somewhere to be: a floor, a few blocks, and a way back.
struct Room;

impl Scene for Room {
    fn setup(&mut self, app: &mut AppState) {
        app.spawn(gizmos::grid().fade(8.0, 20.0));

        let [key, rim] = default_lights();
        app.spawn_entity((key, Light::item("Key Light")));
        app.spawn_entity((rim, Light::item("Rim Light")));

        for (i, x) in [-2.0, 0.0, 2.0].into_iter().enumerate() {
            let height = 0.5 + i as f32 * 0.5;
            app.spawn_primitive(
                primitives::Box::new(1.0, height, 1.0)
                    .at(x, height / 2.0, 0.0)
                    .color(Color::srgb(0.55, 0.65, 0.75)),
            );
        }

        app.spawn_entity(OrbitCamera::new(Vec3::new(0.0, 0.5, 0.0), 7.0).touchpad());

        let scenes = app.scenes();
        app.spawn(
            Panel::new("Room")
                .anchor(Anchor::TopLeft)
                .add(Button::new("BACK", move || scenes.change(Menu))),
        );
    }

    fn update(&mut self, app: &mut AppState) {
        // The keyboard is read straight off the app; a key is a state, so
        // `just_pressed` is the edge and `pressed` the hold.
        if app.keys().just_pressed(KeyCode::Escape) {
            app.change_scene(Menu);
        }
    }
}

fn main() {
    App::new("menu").scene(Menu).run();
}