use macroquad::prelude::*;
const SHIP_HEIGHT: f32 = 25.;
const SHIP_BASE: f32 = 22.;
struct Ship {
pos: Vec2,
rot: f32,
vel: Vec2,
}
struct Bullet {
pos: Vec2,
vel: Vec2,
shot_at: f64,
collided: bool,
}
struct Asteroid {
pos: Vec2,
vel: Vec2,
rot: f32,
rot_speed: f32,
size: f32,
sides: u8,
collided: bool,
}
fn wrap_around(v: &Vec2) -> Vec2 {
let mut vr = Vec2::new(v.x, v.y);
if vr.x > screen_width() {
vr.x = 0.;
}
if vr.x < 0. {
vr.x = screen_width()
}
if vr.y > screen_height() {
vr.y = 0.;
}
if vr.y < 0. {
vr.y = screen_height()
}
vr
}
#[macroquad::main("Asteroids")]
async fn main() {
let mut ship = Ship {
pos: Vec2::new(screen_width() / 2., screen_height() / 2.),
rot: 0.,
vel: Vec2::new(0., 0.),
};
let mut bullets = Vec::new();
let mut last_shot = get_time();
let mut asteroids = Vec::new();
let mut gameover = false;
let mut screen_center;
loop {
if gameover {
clear_background(LIGHTGRAY);
let mut text = "You Win!. Press [enter] to play again.";
let font_size = 30.;
if asteroids.len() > 0 {
text = "Game Over. Press [enter] to play again.";
}
let text_size = measure_text(text, None, font_size as _, 1.0);
draw_text(
text,
screen_width() / 2. - text_size.width / 2.,
screen_height() / 2. - text_size.height / 2.,
font_size,
DARKGRAY,
);
if is_key_down(KeyCode::Enter) {
ship = Ship {
pos: Vec2::new(screen_width() / 2., screen_height() / 2.),
rot: 0.,
vel: Vec2::new(0., 0.),
};
bullets = Vec::new();
asteroids = Vec::new();
gameover = false;
screen_center = Vec2::new(screen_width() / 2., screen_height() / 2.);
for _ in 0..10 {
asteroids.push(Asteroid {
pos: screen_center
+ Vec2::new(rand::gen_range(-1., 1.), rand::gen_range(-1., 1.))
.normalize()
* screen_width().min(screen_height())
/ 2.,
vel: Vec2::new(rand::gen_range(-1., 1.), rand::gen_range(-1., 1.)),
rot: 0.,
rot_speed: rand::gen_range(-2., 2.),
size: screen_width().min(screen_height()) / 10.,
sides: rand::gen_range(3, 8),
collided: false,
})
}
}
next_frame().await;
continue;
}
let frame_t = get_time();
let rotation = ship.rot.to_radians();
let mut acc = -ship.vel / 100.; if is_key_down(KeyCode::Up) {
acc = Vec2::new(rotation.sin(), -rotation.cos()) / 3.;
}
if is_key_down(KeyCode::Space) && frame_t - last_shot > 0.5 {
let rot_vec = Vec2::new(rotation.sin(), -rotation.cos());
bullets.push(Bullet {
pos: ship.pos + rot_vec * SHIP_HEIGHT / 2.,
vel: rot_vec * 7.,
shot_at: frame_t,
collided: false,
});
last_shot = frame_t;
}
if is_key_down(KeyCode::Right) {
ship.rot += 5.;
} else if is_key_down(KeyCode::Left) {
ship.rot -= 5.;
}
ship.vel += acc;
if ship.vel.length() > 5. {
ship.vel = ship.vel.normalize() * 5.;
}
ship.pos += ship.vel;
ship.pos = wrap_around(&ship.pos);
for bullet in bullets.iter_mut() {
bullet.pos += bullet.vel;
}
for asteroid in asteroids.iter_mut() {
asteroid.pos += asteroid.vel;
asteroid.pos = wrap_around(&asteroid.pos);
asteroid.rot += asteroid.rot_speed;
}
bullets.retain(|bullet| bullet.shot_at + 1.5 > frame_t);
let mut new_asteroids = Vec::new();
for asteroid in asteroids.iter_mut() {
if (asteroid.pos - ship.pos).length() < asteroid.size + SHIP_HEIGHT / 3. {
gameover = true;
break;
}
for bullet in bullets.iter_mut() {
if (asteroid.pos - bullet.pos).length() < asteroid.size {
asteroid.collided = true;
bullet.collided = true;
if asteroid.sides > 3 {
new_asteroids.push(Asteroid {
pos: asteroid.pos,
vel: Vec2::new(bullet.vel.y, -bullet.vel.x).normalize()
* rand::gen_range(1., 3.),
rot: rand::gen_range(0., 360.),
rot_speed: rand::gen_range(-2., 2.),
size: asteroid.size * 0.8,
sides: asteroid.sides - 1,
collided: false,
});
new_asteroids.push(Asteroid {
pos: asteroid.pos,
vel: Vec2::new(-bullet.vel.y, bullet.vel.x).normalize()
* rand::gen_range(1., 3.),
rot: rand::gen_range(0., 360.),
rot_speed: rand::gen_range(-2., 2.),
size: asteroid.size * 0.8,
sides: asteroid.sides - 1,
collided: false,
})
}
break;
}
}
}
bullets.retain(|bullet| bullet.shot_at + 1.5 > frame_t && !bullet.collided);
asteroids.retain(|asteroid| !asteroid.collided);
asteroids.append(&mut new_asteroids);
if asteroids.len() == 0 {
gameover = true;
}
if gameover {
continue;
}
clear_background(LIGHTGRAY);
for bullet in bullets.iter() {
draw_circle(bullet.pos.x, bullet.pos.y, 2., BLACK);
}
for asteroid in asteroids.iter() {
draw_poly_lines(
asteroid.pos.x,
asteroid.pos.y,
asteroid.sides,
asteroid.size,
asteroid.rot,
2.,
BLACK,
)
}
let v1 = Vec2::new(
ship.pos.x + rotation.sin() * SHIP_HEIGHT / 2.,
ship.pos.y - rotation.cos() * SHIP_HEIGHT / 2.,
);
let v2 = Vec2::new(
ship.pos.x - rotation.cos() * SHIP_BASE / 2. - rotation.sin() * SHIP_HEIGHT / 2.,
ship.pos.y - rotation.sin() * SHIP_BASE / 2. + rotation.cos() * SHIP_HEIGHT / 2.,
);
let v3 = Vec2::new(
ship.pos.x + rotation.cos() * SHIP_BASE / 2. - rotation.sin() * SHIP_HEIGHT / 2.,
ship.pos.y + rotation.sin() * SHIP_BASE / 2. + rotation.cos() * SHIP_HEIGHT / 2.,
);
draw_triangle_lines(v1, v2, v3, 2., BLACK);
next_frame().await
}
}