use ponko::*;
struct Agent {
heading: f32,
speed: f32,
}
const WORLD: f32 = 4096.0;
fn wander(ctx: &mut Ctx) {
let dt = ctx.dt();
ctx.world
.query::<(&mut Transform2D, &mut Agent)>()
.par_for_each(|(t, a)| {
let p = &mut t.position;
a.heading += (p.x * 0.013 + p.y * 0.007 + a.heading).sin() * 0.8 * dt;
p.x = (p.x + a.heading.cos() * a.speed * dt).rem_euclid(WORLD);
p.y = (p.y + a.heading.sin() * a.speed * dt).rem_euclid(WORLD);
t.rotation = a.heading;
});
}
fn env_number(name: &str, default: f64) -> f64 {
std::env::var(name).ok().and_then(|v| v.parse().ok()).unwrap_or(default)
}
fn main() {
let agents = env_number("AGENTS", 1_000_000.0) as usize;
let ticks = env_number("TICKS", 600.0) as u64;
let spawn_start = Instant::now();
let mut setup_done = Instant::now();
let world = Game::new("headless", 1280, 720)
.sim_system(wander)
.run_headless(ticks, |ctx| {
let tex = ctx.load_texture("crab.png");
ctx.world.spawn_batch((0..agents).map(|i| {
let pos = vec2((i % 4096) as f32, (i / 4096 % 4096) as f32);
(
Transform2D::at(pos),
Sprite::new(tex),
Agent {
heading: i as f32,
speed: 30.0,
},
)
}));
println!(
"spawned {agents} agents in {:.0} ms",
spawn_start.elapsed().as_secs_f64() * 1e3
);
setup_done = Instant::now();
});
let secs = setup_done.elapsed().as_secs_f64();
println!(
"{ticks} ticks in {:.2} s: {:.0} ticks/s, {:.2} ms/tick ({} entities, {:.0} bytes/entity allocated)",
secs,
ticks as f64 / secs,
secs * 1e3 / ticks as f64,
world.len(),
world.allocated_bytes() as f64 / world.len().max(1) as f64
);
}