use ponko::*;
struct Vel(Vec2);
struct Life(f32);
struct Settings {
rng: u64,
crab: Texture,
}
impl Settings {
fn random(&mut self) -> f32 {
self.rng ^= self.rng << 13;
self.rng ^= self.rng >> 7;
self.rng ^= self.rng << 17;
(self.rng >> 40) as f32 / (1u64 << 24) as f32
}
}
const LIFETIME: f32 = 8.0;
fn spawn_crab(ctx: &mut Ctx, at: Vec2) {
let s = ctx.world.resource_mut::<Settings>();
let angle = s.random() * std::f32::consts::TAU;
let speed = 40.0 + s.random() * 120.0;
let tint = Color::rgb(0.5 + s.random() * 0.5, 0.5 + s.random() * 0.5, 0.5 + s.random() * 0.5);
let crab = s.crab;
ctx.world.spawn((
Transform2D::at(at),
Sprite::new(crab).with_color(tint),
Vel(Vec2::from_angle(angle) * speed),
Life(LIFETIME),
));
}
fn movement(ctx: &mut Ctx) {
let dt = ctx.dt();
let bounds = ctx.window_size();
ctx.world.query::<(&mut Transform2D, &mut Vel)>().for_each(|(t, v)| {
t.position += v.0 * dt;
t.rotation = v.0.to_angle();
if t.position.x < 0.0 || t.position.x > bounds.x {
v.0.x = -v.0.x;
}
if t.position.y < 0.0 || t.position.y > bounds.y {
v.0.y = -v.0.y;
}
t.position = t.position.clamp(Vec2::ZERO, bounds);
});
}
fn aging(ctx: &mut Ctx) {
let dt = ctx.dt();
let commands = &mut ctx.commands;
ctx.world
.query::<(Entity, &mut Life, &mut Sprite)>()
.for_each(|(e, life, sprite)| {
life.0 -= dt;
sprite.color = sprite.color.with_alpha((life.0 / LIFETIME).clamp(0.0, 1.0));
if life.0 <= 0.0 {
commands.despawn(e);
}
});
}
fn controls(ctx: &mut Ctx) {
if ctx.key_pressed(Key::Space) {
if ctx.is_paused() { ctx.resume() } else { ctx.pause() }
}
if ctx.key_pressed(Key::Period) {
ctx.step();
}
for (key, speed) in [(Key::Num1, 1.0), (Key::Num2, 4.0), (Key::Num3, 16.0), (Key::Num4, 64.0)] {
if ctx.key_pressed(key) {
ctx.set_sim_speed(speed);
}
}
if ctx.mouse_down(MouseButton::Left) {
let at = ctx.mouse_pos();
for _ in 0..5 {
spawn_crab(ctx, at);
}
}
if ctx.key_pressed(Key::Escape) {
ctx.quit();
}
}
fn main() {
Game::new("Crabby Engine: ECS", 1280, 720)
.sim_system(movement)
.sim_system(aging)
.system(controls)
.run(|ctx| {
let crab = ctx.load_texture("crab.png");
ctx.world.insert_resource(Settings {
rng: 0x9E37_79B9_7F4A_7C15,
crab,
});
let center = ctx.window_size() / 2.0;
for _ in 0..500 {
spawn_crab(ctx, center);
}
let mut next_report = 1.0;
move |ctx: &mut Ctx| {
if ctx.time() >= next_report {
next_report += 1.0;
let sim = if ctx.is_paused() {
"paused".to_string()
} else {
format!("{}x", ctx.sim_speed())
};
println!(
"crabs: {:>6} fps: {:>5.1} sim: {sim:>6} tick: {}",
ctx.world.len(),
ctx.fps(),
ctx.sim_tick()
);
}
}
});
}