Getting Started
This page gets you from zero to a working scatter run. Choose the core library if you want a lightweight Rust API, or the Bevy plugin if you want asset-driven authoring and ECS integration.
Prerequisites
- Rust 1.89 (see
rust-toolchain.toml) - Basic familiarity with Rust and Cargo
Install
Hello, scatter
use glam::Vec2;
use rand::{rngs::StdRng, SeedableRng};
use map_scatter::prelude::*;
fn main() {
// 1) Author a field graph for a "kind".
let mut spec = FieldGraphSpec::default();
spec.add_with_semantics(
"probability",
NodeSpec::constant(1.0),
FieldSemantics::Probability,
);
let grass = Kind::new("grass", spec);
// 2) Build a layer using a sampling strategy (jittered grid here).
let layer = Layer::new_with(
"grass",
vec![grass],
JitterGridSampling::new(0.35, 5.0),
)
// Optional: produce an overlay mask to reuse in later layers.
.with_overlay((256, 256), 3);
// 3) Assemble a plan (one or more layers).
let plan = Plan::new().with_layer(layer);
// 4) Prepare runtime.
let mut cache = FieldProgramCache::new();
let textures = TextureRegistry::new();
let cfg = RunConfig::new(Vec2::new(100.0, 100.0))
.with_chunk_extent(32.0)
.with_raster_cell_size(1.0)
.with_grid_halo(2);
// 5) Run.
let mut rng = StdRng::seed_from_u64(42);
let mut runner = ScatterRunner::new(cfg, &textures, &mut cache);
let result = runner.run(&plan, &mut rng);
println!(
"Placed {} instances (evaluated: {}, rejected: {}).",
result.placements.len(),
result.positions_evaluated,
result.positions_rejected
);
}
Create a scatter plan in assets/simple.scatter:
(
layers: [
(
id: "dots",
kinds: [
(
id: "dots",
spec: (
nodes: {
"probability": Constant(
params: ConstantParams(value: 1.0),
),
},
semantics: {
"probability": Probability,
},
),
),
],
sampling: JitterGrid(
jitter: 1.0,
cell_size: 1.0,
),
selection_strategy: WeightedRandom,
),
],
)
Trigger a scatter run once the asset is loaded:
use bevy::prelude::*;
use bevy_map_scatter::prelude::*;
#[derive(Resource, Default)]
struct PlanHandle(Handle<ScatterPlanAsset>);
fn main() {
App::new()
.init_resource::<PlanHandle>()
.add_plugins(DefaultPlugins)
.add_plugins(MapScatterPlugin)
.add_systems(Startup, load_plan)
.add_systems(Update, trigger_request)
.add_observer(log_finished)
.run();
}
fn load_plan(mut handle: ResMut<PlanHandle>, assets: Res<AssetServer>) {
handle.0 = assets.load("simple.scatter");
}
fn trigger_request(
mut commands: Commands,
mut once: Local<bool>,
handle: Res<PlanHandle>,
assets: Res<Assets<ScatterPlanAsset>>,
) {
if *once || assets.get(&handle.0).is_none() {
return;
}
let domain = Vec2::new(10.0, 10.0);
let config = RunConfig::new(domain)
.with_chunk_extent(domain.x)
.with_raster_cell_size(1.0);
let entity = commands.spawn_empty().id();
commands.trigger(ScatterRequest::new(entity, handle.0.clone(), config, 42));
*once = true;
}
fn log_finished(finished: On<ScatterFinished>, mut commands: Commands) {
info!(
"Scatter run {} finished: placements={} evaluated={} rejected={}",
finished.entity,
finished.result.placements.len(),
finished.result.positions_evaluated,
finished.result.positions_rejected
);
commands.entity(finished.entity).despawn();
}
Run examples
- Core library:
cargo run -p map_scatter_examples --bin samplers-poisson-basic - Bevy:
cargo run -p bevy_map_scatter_examples --bin quick-start
See crates/map_scatter_examples/src/bin and crates/bevy_map_scatter_examples/src/bin for the full list.
Next steps
- Read the Concepts page to understand field graphs and layers.
- Follow the Bevy Integration guide for asset workflows and streaming.
- Review the Architecture overview for a deeper model of the pipeline.