1use ponko::*;
8
9struct Agent {
10 heading: f32,
11 speed: f32,
12}
13
14const WORLD: f32 = 4096.0;
15
16fn wander(ctx: &mut Ctx) {
18 let dt = ctx.dt();
19 ctx.world
20 .query::<(&mut Transform2D, &mut Agent)>()
21 .par_for_each(|(t, a)| {
22 let p = &mut t.position;
23 a.heading += (p.x * 0.013 + p.y * 0.007 + a.heading).sin() * 0.8 * dt;
24 p.x = (p.x + a.heading.cos() * a.speed * dt).rem_euclid(WORLD);
25 p.y = (p.y + a.heading.sin() * a.speed * dt).rem_euclid(WORLD);
26 t.rotation = a.heading;
27 });
28}
29
30fn env_number(name: &str, default: f64) -> f64 {
31 std::env::var(name).ok().and_then(|v| v.parse().ok()).unwrap_or(default)
32}
33
34fn main() {
35 let agents = env_number("AGENTS", 1_000_000.0) as usize;
36 let ticks = env_number("TICKS", 600.0) as u64;
37
38 let spawn_start = Instant::now();
39 let mut setup_done = Instant::now();
40 let world = Game::new("headless", 1280, 720)
41 .sim_system(wander)
42 .run_headless(ticks, |ctx| {
43 let tex = ctx.load_texture("crab.png");
44 ctx.world.spawn_batch((0..agents).map(|i| {
45 let pos = vec2((i % 4096) as f32, (i / 4096 % 4096) as f32);
46 (
47 Transform2D::at(pos),
48 Sprite::new(tex),
49 Agent {
50 heading: i as f32,
51 speed: 30.0,
52 },
53 )
54 }));
55 println!(
56 "spawned {agents} agents in {:.0} ms",
57 spawn_start.elapsed().as_secs_f64() * 1e3
58 );
59 setup_done = Instant::now();
60 });
61 let secs = setup_done.elapsed().as_secs_f64();
62 println!(
63 "{ticks} ticks in {:.2} s: {:.0} ticks/s, {:.2} ms/tick ({} entities, {:.0} bytes/entity allocated)",
64 secs,
65 ticks as f64 / secs,
66 secs * 1e3 / ticks as f64,
67 world.len(),
68 world.allocated_bytes() as f64 / world.len().max(1) as f64
69 );
70}