bevy_lights
Practical lights for Bevy 0.20: the fires a scene’s props actually carry. A world can hold thousands of torches and afford a dozen real lights, so this crate ranks the candidates by the light they put on you, hands the budget out without a pop, modulates each light the way a fire’s light moves, and draws the flame that motivates it — with sparks, smoke, tongues for a wide fire and a bed of coals that goes on glowing after the fire is put out.
Add it
cargo add bevy_lights
use bevy::prelude::*;
use bevy_lights::*;
#[derive(Component)]
struct MyFocus;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins((FlickerPlugin, LightPoolPlugin::<MyFocus>::default(), FlamePlugin, FirePlugin))
.add_systems(Startup, |mut commands: Commands| {
let fire = Fire::lit(FireKind::Campfire, 250.0, 0.35);
commands.spawn((fire, Transform::from_xyz(4.0, 0.0, 2.0)));
commands.spawn((MyFocus, Transform::default()));
})
.run();
}
Fire::lit(kind, lux, over) is a whole fire in one component: the kind’s flame, the light the pool hands
it, its sparks, smoke, tongues and bed of coals. Or run it first: cargo run --release --example courtyard.
What it gives you
- Fires that flicker, flare and go out (fires):
Flickerdips a light below its base at the rate its fuel bed sets,Kindleflares it up over 0.6 s,Snuffputs it out over 1.2 s with the linked flame. - A budget of real lights for a town of thousands (the light pool): ranked by the light each candidate puts on you, held with hysteresis, cross-faded on a hand-off, with a shadow budget of its own and one template that reaches every light live.
- The flame drawn where the fire is (shader flames): one additive billboard per fire, breathing on its light’s clock, in a hue of its own, shedding sparks, smoking, leaning in a wind and streaming behind a torch you carry — and going out when a wall hides its source.
- Coals that outlive the flame (the fuel bed): a temperature field that breathes with the flicker, warms as the fire catches and cools over a minute once it is put out.
- A cinematographer’s numbers (the lighting look):
fire_lumens,fill_for,blackbody_colourandwhite_balancedturn “1000 lx at 4 m” and “1900 K” into a sized, coloured light. - Every curve outside the engine (engine-free maths): the same maths as plain
f32functions with nobevyin them, for a host that drives its own lights. - What a host touches, and what it saves (host API): the components and resources a game spawns, marks and reads, and the
serdefeature that makes a placed fire one line of a scene file.
Use it in the Cauldron
The Cauldron, the Coven’s level editor, takes bevy_lights as an optional cargo feature (bevy_lights) and a
project lists it in its family.ron. Two things it does with the crate today:
- A placement’s fire is the crate’s own settings type.
family_setwrites a field of the fire’sFlicker—base,phase,depth,diameter— in<project>/family/bevy_lights.ron, and the placed prop reads back as the crate’sFlicker, so the editor has no schema of its own for it.placement_set_familyputs that value on one placement, or takes it off. - Two read-only operations answer “which of my torches are real, and how lit is this tile?” —
light_poolruns the crate’s ownLightPoolPluginover the map’s fixtures and reads itsPoolBudgetfor where the viewer stands, andlight_mapbakes abevy_light_gridilluminance field from the same fixtures.
There is no panel for the fire kinds yet. One would edit Fire { kind, lumens, phase, color, embers, smoke, tongues, bed } — the whole fire as one serde value — and read Fire::problems() beside the fields, which
is what every settings type in the crate returns: a list of Problem { field, kind } naming what cannot
work, before the value reaches a light.