1use ponko::*;
9
10struct Player {
12 slot: Option<usize>,
13 spin: f32,
14}
15
16const SPEED: f32 = 400.0;
17
18const PLAYER_COLORS: [Color; MAX_GAMEPADS] = [
20 Color::rgb(1.0, 0.6, 0.6),
21 Color::rgb(0.6, 0.8, 1.0),
22 Color::rgb(0.6, 1.0, 0.6),
23 Color::rgb(1.0, 1.0, 0.5),
24 Color::rgb(1.0, 0.6, 1.0),
25 Color::rgb(0.6, 1.0, 1.0),
26 Color::rgb(1.0, 0.8, 0.5),
27 Color::rgb(0.8, 0.8, 0.8),
28];
29
30fn move_players(ctx: &mut Ctx) {
32 let dt = ctx.dt();
33 let mut sticks = [Vec2::ZERO; MAX_GAMEPADS];
35 let mut spins = [false; MAX_GAMEPADS];
36 let mut grow = [0.0; MAX_GAMEPADS];
37 for &pad in ctx.gamepads() {
38 sticks[pad.slot()] = ctx.gamepad_stick(pad, GamepadStick::Left);
39 spins[pad.slot()] = ctx.gamepad_pressed(pad, GamepadButton::South);
40 grow[pad.slot()] = ctx.gamepad_axis(pad, GamepadAxis::RightTrigger);
41 }
42 for (t, player) in ctx.world.query::<(&mut Transform2D, &mut Player)>().iter() {
43 let Some(slot) = player.slot else { continue };
44 t.position += sticks[slot] * SPEED * dt;
45 if spins[slot] {
46 player.spin = std::f32::consts::TAU;
47 }
48 t.scale = Vec2::splat(1.0 + 2.0 * grow[slot]);
49 }
50}
51fn move_keyboard_player(ctx: &mut Ctx) {
55 let dt = ctx.dt();
56 let mut dir = Vec2::ZERO;
57 for (key, d) in [
58 (Key::Left, -Vec2::X),
59 (Key::Right, Vec2::X),
60 (Key::Up, -Vec2::Y),
61 (Key::Down, Vec2::Y),
62 ] {
63 if ctx.key_down(key) {
64 dir += d;
65 }
66 }
67 let spin = ctx.key_pressed(Key::Space);
68 for (t, player) in ctx.world.query::<(&mut Transform2D, &mut Player)>().iter() {
69 if player.slot.is_none() {
70 t.position += dir.normalize_or_zero() * SPEED * dt;
71 if spin {
72 player.spin = std::f32::consts::TAU;
73 }
74 }
75 }
76}
77
78fn join_and_leave(ctx: &mut Ctx) {
80 let mut connected = [false; MAX_GAMEPADS];
81 for pad in ctx.gamepads() {
82 connected[pad.slot()] = true;
83 }
84 let mut has_crab = [false; MAX_GAMEPADS];
85 for (e, player) in ctx.world.query::<(Entity, &Player)>().iter() {
86 if let Some(slot) = player.slot {
87 if connected[slot] {
88 has_crab[slot] = true;
89 } else {
90 ctx.commands.despawn(e);
91 }
92 }
93 }
94 for slot in (0..MAX_GAMEPADS).filter(|&s| connected[s] && !has_crab[s]) {
95 let mut sprite = Sprite::new(ctx.load_texture("crab.png"));
96 sprite.color = PLAYER_COLORS[slot];
97 let at = vec2(200.0 + slot as f32 * 120.0, 360.0);
98 ctx.world.spawn((
99 Transform2D::at(at),
100 sprite,
101 Player {
102 slot: Some(slot),
103 spin: 0.0,
104 },
105 ));
106 }
107}
108
109fn spin(ctx: &mut Ctx) {
111 let dt = ctx.dt();
112 for (t, player) in ctx.world.query::<(&mut Transform2D, &mut Player)>().iter() {
113 let step = player.spin.min(4.0 * std::f32::consts::TAU * dt);
114 t.rotation += step;
115 player.spin -= step;
116 }
117}
118
119fn hud(ctx: &mut Ctx) {
120 let pads = ctx.gamepads().len();
121 let text = match pads {
122 0 => "No gamepad: plug one in, or use the arrow keys and Space".to_string(),
123 n => format!("{n} gamepad(s): left stick moves, A/Cross spins, right trigger grows"),
124 };
125 ctx.draw_text(&text, vec2(20.0, 20.0), 20.0, Color::WHITE);
126}
127
128fn main() {
129 Game::new("Crabby Engine: gamepads", 1280, 720)
130 .system(join_and_leave)
131 .system(move_players)
132 .system(move_keyboard_player)
133 .system(spin)
134 .system(hud)
135 .run(|ctx| {
136 let crab = ctx.load_texture("crab.png");
137 ctx.world.spawn((
138 Transform2D::at(vec2(640.0, 560.0)),
139 Sprite::new(crab),
140 Player { slot: None, spin: 0.0 },
141 ));
142 });
143}