bevy_lit 0.9.0

A lighting 2d library for Bevy
Documentation
use bevy::{
    color::palettes::tailwind::{BLUE_300, BLUE_600, GRAY_200, GRAY_700, YELLOW_600},
    prelude::*,
    window::PrimaryWindow,
};
use bevy_lit::prelude::*;

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, Lighting2dPlugin))
        .insert_resource(ClearColor(Color::from(GRAY_200)))
        .add_systems(Startup, setup)
        .add_systems(Update, update_cursor_light)
        .add_systems(FixedUpdate, update_moving_lights)
        .run();
}

#[derive(Component)]
struct CursorLight;

#[derive(Component)]
struct MovingLights;

const X_EXTENT: f32 = 700.;

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    commands.spawn((
        Camera2d,
        Lighting2dSettings {
            blur: 4,
            edge_intensity: 8.0,
            raymarch: RaymarchSettings {
                max_steps: 32,
                jitter_contrib: 0.5,
                sharpness: 10.,
            },
            ..default()
        },
        AmbientLight2d {
            intensity: 0.1,
            color: Color::from(BLUE_300),
        },
    ));

    let shapes = [
        meshes.add(Circle::new(50.0)),
        meshes.add(Annulus::new(25.0, 50.0)),
        meshes.add(Capsule2d::new(25.0, 50.0)),
        meshes.add(Rhombus::new(75.0, 100.0)),
        meshes.add(Rectangle::new(50.0, 100.0)),
        meshes.add(RegularPolygon::new(50.0, 6)),
        meshes.add(Triangle2d::new(
            Vec2::Y * 50.0,
            Vec2::new(-50.0, -50.0),
            Vec2::new(50.0, -50.0),
        )),
    ];
    let color = materials.add(Color::from(GRAY_700));
    let num_shapes = shapes.len();

    for (i, shape) in shapes.into_iter().enumerate() {
        commands.spawn((
            Mesh2d(shape),
            MeshMaterial2d(color.clone()),
            LightOccluder2d::default(),
            Transform::from_xyz(
                -X_EXTENT / 2. + i as f32 / (num_shapes - 1) as f32 * X_EXTENT,
                0.0,
                0.0,
            ),
        ));
    }

    let moving_point_light = PointLight2d {
        color: Color::from(BLUE_600),
        intensity: 2.0,
        outer_radius: 1100.0,
        falloff: 3.0,
        ..default()
    };

    commands.spawn((
        MovingLights,
        Transform::default(),
        Visibility::default(),
        children![
            (
                moving_point_light.clone(),
                Transform::from_xyz(-X_EXTENT + 50. / 2., 0.0, 0.0),
            ),
            (
                moving_point_light,
                Transform::from_xyz(X_EXTENT + 50. / 2., 0.0, 0.0),
            )
        ],
    ));

    commands.spawn((
        CursorLight,
        PointLight2d {
            color: Color::from(YELLOW_600),
            intensity: 2.0,
            outer_radius: 400.0,
            falloff: 10.0,
            ..default()
        },
    ));
}

fn update_cursor_light(
    window: Single<&Window, With<PrimaryWindow>>,
    camera: Single<(&Camera, &GlobalTransform), With<Lighting2dSettings>>,
    mut point_light_transform: Single<&mut Transform, With<CursorLight>>,
) {
    let (camera, camera_transform) = camera.into_inner();

    if let Some(world_position) = window
        .cursor_position()
        .and_then(|cursor| camera.viewport_to_world(camera_transform, cursor).ok())
        .map(|ray| ray.origin.truncate().extend(0.0))
    {
        point_light_transform.translation = world_position;
    }
}

fn update_moving_lights(
    time: Res<Time>,
    mut point_light_query: Query<&mut Transform, With<MovingLights>>,
) {
    for mut transform in &mut point_light_query {
        transform.rotation *= Quat::from_rotation_z(time.delta_secs() / 12.0);
    }
}