Skip to content

Instantly share code, notes, and snippets.

@jrfondren
Created Sep 14, 2021
Embed
What would you like to do?
#! /usr/bin/env dub
/++ dub.sdl:
dependency "raylib-d" version="~>3.1.0"
libs "raylib"
+/
import raylib;
import std.math;
enum PLAYER_BASE_SIZE = 20.0f;
enum PLAYER_SPEED = 6.0f;
enum PLAYER_MAX_SHOOTS = 10;
enum METEORS_SPEED = 2;
enum MAX_BIG_METEORS = 4;
enum MAX_MEDIUM_METEORS = 8;
enum MAX_SMALL_METEORS = 16;
enum screenWidth = 800, screenHeight = 450;
alias Keys = KeyboardKey;
struct Player {
Vector2 position, speed;
float acceleration, rotation;
Vector3 collider;
Color color;
}
struct Shoot {
Vector2 position, speed;
float radius, rotation;
int lifeSpawn;
bool active;
Color color;
}
struct Meteor {
Vector2 position, speed;
float radius;
bool active;
Color color;
}
bool gameOver, pause, victory;
float shipHeight = 0.0f;
Player player;
Shoot[PLAYER_MAX_SHOOTS] shoot;
Meteor[MAX_BIG_METEORS] bigMeteor;
Meteor[MAX_MEDIUM_METEORS] mediumMeteor;
Meteor[MAX_SMALL_METEORS] smallMeteor;
int midMeteorsCount, smallMeteorsCount, destroyedMeteorsCount;
void main() {
InitWindow(screenWidth, screenHeight, "classic game: asteroids");
InitGame;
SetTargetFPS(60);
while (!WindowShouldClose) {
UpdateDrawFrame;
}
UnloadGame;
CloseWindow;
}
void InitGame() {
int posx, posy;
int velx, vely;
bool correctRange = false;
victory = false;
pause = false;
shipHeight = (PLAYER_BASE_SIZE / 2) / tan(20 * DEG2RAD);
// Initialization player
player.position = Vector2(screenWidth / 2, screenHeight / 2 - shipHeight / 2);
player.speed = Vector2(0, 0);
player.acceleration = 0;
player.rotation = 0;
player.collider = Vector3(player.position.x + sin(player.rotation * DEG2RAD) * (
shipHeight / 2.5f),
player.position.y - cos(player.rotation * DEG2RAD) * (shipHeight / 2.5f), 12);
player.color = Colors.LIGHTGRAY;
destroyedMeteorsCount = 0;
// Initialization shoot
for (int i = 0; i < PLAYER_MAX_SHOOTS; i++) {
shoot[i].position = Vector2(0, 0);
shoot[i].speed = Vector2(0, 0);
shoot[i].radius = 2;
shoot[i].active = false;
shoot[i].lifeSpawn = 0;
shoot[i].color = Colors.WHITE;
}
for (int i = 0; i < MAX_BIG_METEORS; i++) {
posx = GetRandomValue(0, screenWidth);
while (!correctRange) {
if (posx > screenWidth / 2 - 150 && posx < screenWidth / 2 + 150)
posx = GetRandomValue(0, screenWidth);
else
correctRange = true;
}
correctRange = false;
posy = GetRandomValue(0, screenHeight);
while (!correctRange) {
if (posy > screenHeight / 2 - 150 && posy < screenHeight / 2 + 150)
posy = GetRandomValue(0, screenHeight);
else
correctRange = true;
}
bigMeteor[i].position = Vector2(posx, posy);
correctRange = false;
velx = GetRandomValue(-METEORS_SPEED, METEORS_SPEED);
vely = GetRandomValue(-METEORS_SPEED, METEORS_SPEED);
while (!correctRange) {
if (velx == 0 && vely == 0) {
velx = GetRandomValue(-METEORS_SPEED, METEORS_SPEED);
vely = GetRandomValue(-METEORS_SPEED, METEORS_SPEED);
} else
correctRange = true;
}
bigMeteor[i].speed = Vector2(velx, vely);
bigMeteor[i].radius = 40;
bigMeteor[i].active = true;
bigMeteor[i].color = Colors.BLUE;
}
for (int i = 0; i < MAX_MEDIUM_METEORS; i++) {
mediumMeteor[i].position = Vector2(-100, -100);
mediumMeteor[i].speed = Vector2(0, 0);
mediumMeteor[i].radius = 20;
mediumMeteor[i].active = false;
mediumMeteor[i].color = Colors.BLUE;
}
for (int i = 0; i < MAX_SMALL_METEORS; i++) {
smallMeteor[i].position = Vector2(-100, -100);
smallMeteor[i].speed = Vector2(0, 0);
smallMeteor[i].radius = 10;
smallMeteor[i].active = false;
smallMeteor[i].color = Colors.BLUE;
}
midMeteorsCount = 0;
smallMeteorsCount = 0;
}
void UpdateGame() {
if (!gameOver) {
if (IsKeyPressed('P'))
pause = !pause;
if (!pause) {
// Player logic: rotation
if (IsKeyDown(Keys.KEY_LEFT))
player.rotation -= 5;
if (IsKeyDown(Keys.KEY_RIGHT))
player.rotation += 5;
// Player logic: speed
player.speed.x = sin(player.rotation * DEG2RAD) * PLAYER_SPEED;
player.speed.y = cos(player.rotation * DEG2RAD) * PLAYER_SPEED;
// Player logic: acceleration
if (IsKeyDown(Keys.KEY_UP)) {
if (player.acceleration < 1)
player.acceleration += 0.04f;
} else {
if (player.acceleration > 0)
player.acceleration -= 0.02f;
else if (player.acceleration < 0)
player.acceleration = 0;
}
if (IsKeyDown(Keys.KEY_DOWN)) {
if (player.acceleration > 0)
player.acceleration -= 0.04f;
else if (player.acceleration < 0)
player.acceleration = 0;
}
// Player logic: movement
player.position.x += (player.speed.x * player.acceleration);
player.position.y -= (player.speed.y * player.acceleration);
// Collision logic: player vs walls
if (player.position.x > screenWidth + shipHeight)
player.position.x = -(shipHeight);
else if (player.position.x < -(shipHeight))
player.position.x = screenWidth + shipHeight;
if (player.position.y > (screenHeight + shipHeight))
player.position.y = -(shipHeight);
else if (player.position.y < -(shipHeight))
player.position.y = screenHeight + shipHeight;
// Player shoot logic
if (IsKeyPressed(Keys.KEY_SPACE)) {
for (int i = 0; i < PLAYER_MAX_SHOOTS; i++) {
if (!shoot[i].active) {
shoot[i].position = Vector2(player.position.x + sin(player.rotation * DEG2RAD) * (shipHeight),
player.position.y - cos(player.rotation * DEG2RAD) * (shipHeight));
shoot[i].active = true;
shoot[i].speed.x = 1.5 * sin(player.rotation * DEG2RAD) * PLAYER_SPEED;
shoot[i].speed.y = 1.5 * cos(player.rotation * DEG2RAD) * PLAYER_SPEED;
shoot[i].rotation = player.rotation;
break;
}
}
}
// Shoot life timer
for (int i = 0; i < PLAYER_MAX_SHOOTS; i++) {
if (shoot[i].active)
shoot[i].lifeSpawn++;
}
// Shot logic
for (int i = 0; i < PLAYER_MAX_SHOOTS; i++) {
if (shoot[i].active) {
// Movement
shoot[i].position.x += shoot[i].speed.x;
shoot[i].position.y -= shoot[i].speed.y;
// Collision logic: shoot vs walls
if (shoot[i].position.x > screenWidth + shoot[i].radius) {
shoot[i].active = false;
shoot[i].lifeSpawn = 0;
} else if (shoot[i].position.x < 0 - shoot[i].radius) {
shoot[i].active = false;
shoot[i].lifeSpawn = 0;
}
if (shoot[i].position.y > screenHeight + shoot[i].radius) {
shoot[i].active = false;
shoot[i].lifeSpawn = 0;
} else if (shoot[i].position.y < 0 - shoot[i].radius) {
shoot[i].active = false;
shoot[i].lifeSpawn = 0;
}
// Life of shoot
if (shoot[i].lifeSpawn >= 60) {
shoot[i].position = Vector2(0, 0);
shoot[i].speed = Vector2(0, 0);
shoot[i].lifeSpawn = 0;
shoot[i].active = false;
}
}
}
// Collision logic: player vs meteors
player.collider = Vector3(player.position.x + sin(
player.rotation * DEG2RAD) * (shipHeight / 2.5f),
player.position.y - cos(player.rotation * DEG2RAD) * (shipHeight / 2.5f), 12);
for (int a = 0; a < MAX_BIG_METEORS; a++) {
if (CheckCollisionCircles(Vector2(player.collider.x, player.collider.y),
player.collider.z, bigMeteor[a].position, bigMeteor[a].radius)
&& bigMeteor[a].active)
gameOver = true;
}
for (int a = 0; a < MAX_MEDIUM_METEORS; a++) {
if (CheckCollisionCircles(Vector2(player.collider.x, player.collider.y),
player.collider.z, mediumMeteor[a].position, mediumMeteor[a].radius)
&& mediumMeteor[a].active)
gameOver = true;
}
for (int a = 0; a < MAX_SMALL_METEORS; a++) {
if (CheckCollisionCircles(Vector2(player.collider.x, player.collider.y),
player.collider.z, smallMeteor[a].position, smallMeteor[a].radius)
&& smallMeteor[a].active)
gameOver = true;
}
// Meteors logic: big meteors
for (int i = 0; i < MAX_BIG_METEORS; i++) {
if (bigMeteor[i].active) {
// Movement
bigMeteor[i].position.x += bigMeteor[i].speed.x;
bigMeteor[i].position.y += bigMeteor[i].speed.y;
// Collision logic: meteor vs wall
if (bigMeteor[i].position.x > screenWidth + bigMeteor[i].radius)
bigMeteor[i].position.x = -(bigMeteor[i].radius);
else if (bigMeteor[i].position.x < 0 - bigMeteor[i].radius)
bigMeteor[i].position.x = screenWidth + bigMeteor[i].radius;
if (bigMeteor[i].position.y > screenHeight + bigMeteor[i].radius)
bigMeteor[i].position.y = -(bigMeteor[i].radius);
else if (bigMeteor[i].position.y < 0 - bigMeteor[i].radius)
bigMeteor[i].position.y = screenHeight + bigMeteor[i].radius;
}
}
// Meteors logic: medium meteors
for (int i = 0; i < MAX_MEDIUM_METEORS; i++) {
if (mediumMeteor[i].active) {
// Movement
mediumMeteor[i].position.x += mediumMeteor[i].speed.x;
mediumMeteor[i].position.y += mediumMeteor[i].speed.y;
// Collision logic: meteor vs wall
if (mediumMeteor[i].position.x > screenWidth + mediumMeteor[i].radius)
mediumMeteor[i].position.x = -(mediumMeteor[i].radius);
else if (mediumMeteor[i].position.x < 0 - mediumMeteor[i].radius)
mediumMeteor[i].position.x = screenWidth + mediumMeteor[i].radius;
if (mediumMeteor[i].position.y > screenHeight + mediumMeteor[i].radius)
mediumMeteor[i].position.y = -(mediumMeteor[i].radius);
else if (mediumMeteor[i].position.y < 0 - mediumMeteor[i].radius)
mediumMeteor[i].position.y = screenHeight + mediumMeteor[i].radius;
}
}
// Meteors logic: small meteors
for (int i = 0; i < MAX_SMALL_METEORS; i++) {
if (smallMeteor[i].active) {
// Movement
smallMeteor[i].position.x += smallMeteor[i].speed.x;
smallMeteor[i].position.y += smallMeteor[i].speed.y;
// Collision logic: meteor vs wall
if (smallMeteor[i].position.x > screenWidth + smallMeteor[i].radius)
smallMeteor[i].position.x = -(smallMeteor[i].radius);
else if (smallMeteor[i].position.x < 0 - smallMeteor[i].radius)
smallMeteor[i].position.x = screenWidth + smallMeteor[i].radius;
if (smallMeteor[i].position.y > screenHeight + smallMeteor[i].radius)
smallMeteor[i].position.y = -(smallMeteor[i].radius);
else if (smallMeteor[i].position.y < 0 - smallMeteor[i].radius)
smallMeteor[i].position.y = screenHeight + smallMeteor[i].radius;
}
}
// Collision logic: player-shoots vs meteors
for (int i = 0; i < PLAYER_MAX_SHOOTS; i++) {
if ((shoot[i].active)) {
for (int a = 0; a < MAX_BIG_METEORS; a++) {
if (bigMeteor[a].active && CheckCollisionCircles(shoot[i].position,
shoot[i].radius, bigMeteor[a].position, bigMeteor[a].radius)) {
shoot[i].active = false;
shoot[i].lifeSpawn = 0;
bigMeteor[a].active = false;
destroyedMeteorsCount++;
for (int j = 0; j < 2; j++) {
if (midMeteorsCount % 2 == 0) {
mediumMeteor[midMeteorsCount].position = Vector2(bigMeteor[a].position.x,
bigMeteor[a].position.y);
mediumMeteor[midMeteorsCount].speed = Vector2(
cos(shoot[i].rotation * DEG2RAD) * METEORS_SPEED * -1,
sin(shoot[i].rotation * DEG2RAD) * METEORS_SPEED * -1);
} else {
mediumMeteor[midMeteorsCount].position = Vector2(bigMeteor[a].position.x,
bigMeteor[a].position.y);
mediumMeteor[midMeteorsCount].speed = Vector2(
cos(shoot[i].rotation * DEG2RAD) * METEORS_SPEED,
sin(shoot[i].rotation * DEG2RAD) * METEORS_SPEED);
}
mediumMeteor[midMeteorsCount].active = true;
midMeteorsCount++;
}
//bigMeteor[a].position = (Vector2){-100, -100};
bigMeteor[a].color = Colors.RED;
a = MAX_BIG_METEORS;
}
}
for (int b = 0; b < MAX_MEDIUM_METEORS; b++) {
if (mediumMeteor[b].active && CheckCollisionCircles(shoot[i].position,
shoot[i].radius, mediumMeteor[b].position, mediumMeteor[b].radius)) {
shoot[i].active = false;
shoot[i].lifeSpawn = 0;
mediumMeteor[b].active = false;
destroyedMeteorsCount++;
for (int j = 0; j < 2; j++) {
if (smallMeteorsCount % 2 == 0) {
smallMeteor[smallMeteorsCount].position = Vector2(mediumMeteor[b].position.x,
mediumMeteor[b].position.y);
smallMeteor[smallMeteorsCount].speed = Vector2(
cos(shoot[i].rotation * DEG2RAD) * METEORS_SPEED * -1,
sin(shoot[i].rotation * DEG2RAD) * METEORS_SPEED * -1);
} else {
smallMeteor[smallMeteorsCount].position = Vector2(mediumMeteor[b].position.x,
mediumMeteor[b].position.y);
smallMeteor[smallMeteorsCount].speed = Vector2(
cos(shoot[i].rotation * DEG2RAD) * METEORS_SPEED,
sin(shoot[i].rotation * DEG2RAD) * METEORS_SPEED);
}
smallMeteor[smallMeteorsCount].active = true;
smallMeteorsCount++;
}
//mediumMeteor[b].position = (Vector2){-100, -100};
mediumMeteor[b].color = Colors.GREEN;
b = MAX_MEDIUM_METEORS;
}
}
for (int c = 0; c < MAX_SMALL_METEORS; c++) {
if (smallMeteor[c].active && CheckCollisionCircles(shoot[i].position,
shoot[i].radius, smallMeteor[c].position, smallMeteor[c].radius)) {
shoot[i].active = false;
shoot[i].lifeSpawn = 0;
smallMeteor[c].active = false;
destroyedMeteorsCount++;
smallMeteor[c].color = Colors.YELLOW;
// smallMeteor[c].position = (Vector2){-100, -100};
c = MAX_SMALL_METEORS;
}
}
}
}
}
if (destroyedMeteorsCount == MAX_BIG_METEORS + MAX_MEDIUM_METEORS + MAX_SMALL_METEORS)
victory = true;
} else {
if (IsKeyPressed(Keys.KEY_ENTER)) {
InitGame();
gameOver = false;
}
}
}
// Draw game (one frame)
void DrawGame() {
BeginDrawing();
ClearBackground(Colors.RAYWHITE);
if (!gameOver) {
// Draw spaceship
Vector2 v1 = {
player.position.x + sin(player.rotation * DEG2RAD) * (shipHeight),
player.position.y - cos(player.rotation * DEG2RAD) * (shipHeight)
};
Vector2 v2 = {
player.position.x - cos(player.rotation * DEG2RAD) * (PLAYER_BASE_SIZE / 2),
player.position.y - sin(player.rotation * DEG2RAD) * (PLAYER_BASE_SIZE / 2)
};
Vector2 v3 = {
player.position.x + cos(player.rotation * DEG2RAD) * (PLAYER_BASE_SIZE / 2),
player.position.y + sin(player.rotation * DEG2RAD) * (PLAYER_BASE_SIZE / 2)
};
DrawTriangle(v1, v2, v3, Colors.MAROON);
// Draw meteors
for (int i = 0; i < MAX_BIG_METEORS; i++) {
if (bigMeteor[i].active)
DrawCircleV(bigMeteor[i].position, bigMeteor[i].radius, Colors.DARKGRAY);
else
DrawCircleV(bigMeteor[i].position, bigMeteor[i].radius, Fade(Colors.LIGHTGRAY, 0.3f));
}
for (int i = 0; i < MAX_MEDIUM_METEORS; i++) {
if (mediumMeteor[i].active)
DrawCircleV(mediumMeteor[i].position, mediumMeteor[i].radius, Colors.GRAY);
else
DrawCircleV(mediumMeteor[i].position, mediumMeteor[i].radius,
Fade(Colors.LIGHTGRAY, 0.3f));
}
for (int i = 0; i < MAX_SMALL_METEORS; i++) {
if (smallMeteor[i].active)
DrawCircleV(smallMeteor[i].position, smallMeteor[i].radius, Colors.GRAY);
else
DrawCircleV(smallMeteor[i].position, smallMeteor[i].radius, Fade(Colors.LIGHTGRAY, 0.3f));
}
// Draw shoot
for (int i = 0; i < PLAYER_MAX_SHOOTS; i++) {
if (shoot[i].active)
DrawCircleV(shoot[i].position, shoot[i].radius, Colors.BLACK);
}
if (victory)
DrawText("VICTORY", screenWidth / 2 - MeasureText("VICTORY", 20) / 2,
screenHeight / 2, 20, Colors.LIGHTGRAY);
if (pause)
DrawText("GAME PAUSED", screenWidth / 2 - MeasureText("GAME PAUSED",
40) / 2, screenHeight / 2 - 40, 40, Colors.GRAY);
} else
DrawText("PRESS [ENTER] TO PLAY AGAIN", GetScreenWidth() / 2 - MeasureText(
"PRESS [ENTER] TO PLAY AGAIN", 20) / 2, GetScreenHeight() / 2 - 50, 20, Colors.GRAY);
EndDrawing();
}
// Unload game variables
void UnloadGame() {
// TODO: Unload all dynamic loaded data (textures, sounds, models...)
}
// Update and Draw (one frame)
void UpdateDrawFrame() {
UpdateGame();
DrawGame();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment