use ponko::*;
struct Player {
slot: Option<usize>,
spin: f32,
}
const SPEED: f32 = 400.0;
const PLAYER_COLORS: [Color; MAX_GAMEPADS] = [
Color::rgb(1.0, 0.6, 0.6),
Color::rgb(0.6, 0.8, 1.0),
Color::rgb(0.6, 1.0, 0.6),
Color::rgb(1.0, 1.0, 0.5),
Color::rgb(1.0, 0.6, 1.0),
Color::rgb(0.6, 1.0, 1.0),
Color::rgb(1.0, 0.8, 0.5),
Color::rgb(0.8, 0.8, 0.8),
];
fn move_players(ctx: &mut Ctx) {
let dt = ctx.dt();
let mut sticks = [Vec2::ZERO; MAX_GAMEPADS];
let mut spins = [false; MAX_GAMEPADS];
let mut grow = [0.0; MAX_GAMEPADS];
for &pad in ctx.gamepads() {
sticks[pad.slot()] = ctx.gamepad_stick(pad, GamepadStick::Left);
spins[pad.slot()] = ctx.gamepad_pressed(pad, GamepadButton::South);
grow[pad.slot()] = ctx.gamepad_axis(pad, GamepadAxis::RightTrigger);
}
for (t, player) in ctx.world.query::<(&mut Transform2D, &mut Player)>().iter() {
let Some(slot) = player.slot else { continue };
t.position += sticks[slot] * SPEED * dt;
if spins[slot] {
player.spin = std::f32::consts::TAU;
}
t.scale = Vec2::splat(1.0 + 2.0 * grow[slot]);
}
}
fn move_keyboard_player(ctx: &mut Ctx) {
let dt = ctx.dt();
let mut dir = Vec2::ZERO;
for (key, d) in [
(Key::Left, -Vec2::X),
(Key::Right, Vec2::X),
(Key::Up, -Vec2::Y),
(Key::Down, Vec2::Y),
] {
if ctx.key_down(key) {
dir += d;
}
}
let spin = ctx.key_pressed(Key::Space);
for (t, player) in ctx.world.query::<(&mut Transform2D, &mut Player)>().iter() {
if player.slot.is_none() {
t.position += dir.normalize_or_zero() * SPEED * dt;
if spin {
player.spin = std::f32::consts::TAU;
}
}
}
}
fn join_and_leave(ctx: &mut Ctx) {
let mut connected = [false; MAX_GAMEPADS];
for pad in ctx.gamepads() {
connected[pad.slot()] = true;
}
let mut has_crab = [false; MAX_GAMEPADS];
for (e, player) in ctx.world.query::<(Entity, &Player)>().iter() {
if let Some(slot) = player.slot {
if connected[slot] {
has_crab[slot] = true;
} else {
ctx.commands.despawn(e);
}
}
}
for slot in (0..MAX_GAMEPADS).filter(|&s| connected[s] && !has_crab[s]) {
let mut sprite = Sprite::new(ctx.load_texture("crab.png"));
sprite.color = PLAYER_COLORS[slot];
let at = vec2(200.0 + slot as f32 * 120.0, 360.0);
ctx.world.spawn((
Transform2D::at(at),
sprite,
Player {
slot: Some(slot),
spin: 0.0,
},
));
}
}
fn spin(ctx: &mut Ctx) {
let dt = ctx.dt();
for (t, player) in ctx.world.query::<(&mut Transform2D, &mut Player)>().iter() {
let step = player.spin.min(4.0 * std::f32::consts::TAU * dt);
t.rotation += step;
player.spin -= step;
}
}
fn hud(ctx: &mut Ctx) {
let pads = ctx.gamepads().len();
let text = match pads {
0 => "No gamepad: plug one in, or use the arrow keys and Space".to_string(),
n => format!("{n} gamepad(s): left stick moves, A/Cross spins, right trigger grows"),
};
ctx.draw_text(&text, vec2(20.0, 20.0), 20.0, Color::WHITE);
}
fn main() {
Game::new("Crabby Engine: gamepads", 1280, 720)
.system(join_and_leave)
.system(move_players)
.system(move_keyboard_player)
.system(spin)
.system(hud)
.run(|ctx| {
let crab = ctx.load_texture("crab.png");
ctx.world.spawn((
Transform2D::at(vec2(640.0, 560.0)),
Sprite::new(crab),
Player { slot: None, spin: 0.0 },
));
});
}