2c219fb814
Inspired by #504. Instead of requiring the user to do PLATFORM_ANDROID #ifdefery, have the android_main entry point exported by raylib and call the user-defined main. This way many games could (in theory) run unmodified on Android and elsewhere. This is untested!
401 lines
13 KiB
C
401 lines
13 KiB
C
/*******************************************************************************************
|
|
*
|
|
* raylib - sample game: space invaders
|
|
*
|
|
* Sample game developed by Ian Eito, Albert Martos and Ramon Santamaria
|
|
*
|
|
* This game has been created using raylib v1.3 (www.raylib.com)
|
|
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
|
|
*
|
|
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
|
|
*
|
|
********************************************************************************************/
|
|
|
|
#include "raylib.h"
|
|
|
|
#if defined(PLATFORM_WEB)
|
|
#include <emscripten/emscripten.h>
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------------
|
|
// Some Defines
|
|
//----------------------------------------------------------------------------------
|
|
#define NUM_SHOOTS 50
|
|
#define NUM_MAX_ENEMIES 50
|
|
#define FIRST_WAVE 10
|
|
#define SECOND_WAVE 20
|
|
#define THIRD_WAVE 50
|
|
|
|
//----------------------------------------------------------------------------------
|
|
// Types and Structures Definition
|
|
//----------------------------------------------------------------------------------
|
|
typedef enum { FIRST = 0, SECOND, THIRD } EnemyWave;
|
|
|
|
typedef struct Player{
|
|
Rectangle rec;
|
|
Vector2 speed;
|
|
Color color;
|
|
} Player;
|
|
|
|
typedef struct Enemy{
|
|
Rectangle rec;
|
|
Vector2 speed;
|
|
bool active;
|
|
Color color;
|
|
} Enemy;
|
|
|
|
typedef struct Shoot{
|
|
Rectangle rec;
|
|
Vector2 speed;
|
|
bool active;
|
|
Color color;
|
|
} Shoot;
|
|
|
|
//------------------------------------------------------------------------------------
|
|
// Global Variables Declaration
|
|
//------------------------------------------------------------------------------------
|
|
static int screenWidth = 800;
|
|
static int screenHeight = 450;
|
|
|
|
static bool gameOver;
|
|
static bool pause;
|
|
static int score;
|
|
static bool victory;
|
|
|
|
static Player player;
|
|
static Enemy enemy[NUM_MAX_ENEMIES];
|
|
static Shoot shoot[NUM_SHOOTS];
|
|
static EnemyWave wave;
|
|
|
|
static int shootRate;
|
|
static float alpha;
|
|
|
|
static int activeEnemies;
|
|
static int enemiesKill;
|
|
static bool smooth;
|
|
|
|
//------------------------------------------------------------------------------------
|
|
// Module Functions Declaration (local)
|
|
//------------------------------------------------------------------------------------
|
|
static void InitGame(void); // Initialize game
|
|
static void UpdateGame(void); // Update game (one frame)
|
|
static void DrawGame(void); // Draw game (one frame)
|
|
static void UnloadGame(void); // Unload game
|
|
static void UpdateDrawFrame(void); // Update and Draw (one frame)
|
|
|
|
//------------------------------------------------------------------------------------
|
|
// Program main entry point
|
|
//------------------------------------------------------------------------------------
|
|
int main(void)
|
|
{
|
|
// Initialization (Note windowTitle is unused on Android)
|
|
//---------------------------------------------------------
|
|
InitWindow(screenWidth, screenHeight, "sample game: space invaders");
|
|
|
|
InitGame();
|
|
|
|
#if defined(PLATFORM_WEB)
|
|
emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
|
|
#else
|
|
|
|
SetTargetFPS(60);
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
// Main game loop
|
|
while (!WindowShouldClose()) // Detect window close button or ESC key
|
|
{
|
|
// Update and Draw
|
|
//----------------------------------------------------------------------------------
|
|
UpdateDrawFrame();
|
|
//----------------------------------------------------------------------------------
|
|
}
|
|
#endif
|
|
|
|
// De-Initialization
|
|
//--------------------------------------------------------------------------------------
|
|
UnloadGame(); // Unload loaded data (textures, sounds, models...)
|
|
|
|
CloseWindow(); // Close window and OpenGL context
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
// Module Functions Definitions (local)
|
|
//------------------------------------------------------------------------------------
|
|
|
|
// Initialize game variables
|
|
void InitGame(void)
|
|
{
|
|
// Initialize game variables
|
|
shootRate = 0;
|
|
pause = false;
|
|
gameOver = false;
|
|
victory = false;
|
|
smooth = false;
|
|
wave = FIRST;
|
|
activeEnemies = FIRST_WAVE;
|
|
enemiesKill = 0;
|
|
score = 0;
|
|
alpha = 0;
|
|
|
|
// Initialize player
|
|
player.rec.x = 20;
|
|
player.rec.y = 50;
|
|
player.rec.width = 20;
|
|
player.rec.height = 20;
|
|
player.speed.x = 5;
|
|
player.speed.y = 5;
|
|
player.color = BLACK;
|
|
|
|
// Initialize enemies
|
|
for (int i = 0; i < NUM_MAX_ENEMIES; i++)
|
|
{
|
|
enemy[i].rec.width = 10;
|
|
enemy[i].rec.height = 10;
|
|
enemy[i].rec.x = GetRandomValue(screenWidth, screenWidth + 1000);
|
|
enemy[i].rec.y = GetRandomValue(0, screenHeight - enemy[i].rec.height);
|
|
enemy[i].speed.x = 5;
|
|
enemy[i].speed.y = 5;
|
|
enemy[i].active = true;
|
|
enemy[i].color = GRAY;
|
|
}
|
|
|
|
// Initialize shoots
|
|
for (int i = 0; i < NUM_SHOOTS; i++)
|
|
{
|
|
shoot[i].rec.x = player.rec.x;
|
|
shoot[i].rec.y = player.rec.y + player.rec.height/4;
|
|
shoot[i].rec.width = 10;
|
|
shoot[i].rec.height = 5;
|
|
shoot[i].speed.x = 7;
|
|
shoot[i].speed.y = 0;
|
|
shoot[i].active = false;
|
|
shoot[i].color = MAROON;
|
|
}
|
|
}
|
|
|
|
// Update game (one frame)
|
|
void UpdateGame(void)
|
|
{
|
|
if (!gameOver)
|
|
{
|
|
if (IsKeyPressed('P')) pause = !pause;
|
|
|
|
if (!pause)
|
|
{
|
|
switch (wave)
|
|
{
|
|
case FIRST:
|
|
{
|
|
if (!smooth)
|
|
{
|
|
alpha += 0.02f;
|
|
|
|
if (alpha >= 1.0f) smooth = true;
|
|
}
|
|
|
|
if (smooth) alpha -= 0.02f;
|
|
|
|
if (enemiesKill == activeEnemies)
|
|
{
|
|
enemiesKill = 0;
|
|
|
|
for (int i = 0; i < activeEnemies; i++)
|
|
{
|
|
if (!enemy[i].active) enemy[i].active = true;
|
|
}
|
|
|
|
activeEnemies = SECOND_WAVE;
|
|
wave = SECOND;
|
|
smooth = false;
|
|
alpha = 0.0f;
|
|
}
|
|
} break;
|
|
case SECOND:
|
|
{
|
|
if (!smooth)
|
|
{
|
|
alpha += 0.02f;
|
|
|
|
if (alpha >= 1.0f) smooth = true;
|
|
}
|
|
|
|
if (smooth) alpha -= 0.02f;
|
|
|
|
if (enemiesKill == activeEnemies)
|
|
{
|
|
enemiesKill = 0;
|
|
|
|
for (int i = 0; i < activeEnemies; i++)
|
|
{
|
|
if (!enemy[i].active) enemy[i].active = true;
|
|
}
|
|
|
|
activeEnemies = THIRD_WAVE;
|
|
wave = THIRD;
|
|
smooth = false;
|
|
alpha = 0.0f;
|
|
}
|
|
} break;
|
|
case THIRD:
|
|
{
|
|
if (!smooth)
|
|
{
|
|
alpha += 0.02f;
|
|
|
|
if (alpha >= 1.0f) smooth = true;
|
|
}
|
|
|
|
if (smooth) alpha -= 0.02f;
|
|
|
|
if (enemiesKill == activeEnemies) victory = true;
|
|
|
|
} break;
|
|
default: break;
|
|
}
|
|
|
|
// Player movement
|
|
if (IsKeyDown(KEY_RIGHT)) player.rec.x += player.speed.x;
|
|
if (IsKeyDown(KEY_LEFT)) player.rec.x -= player.speed.x;
|
|
if (IsKeyDown(KEY_UP)) player.rec.y -= player.speed.y;
|
|
if (IsKeyDown(KEY_DOWN)) player.rec.y += player.speed.y;
|
|
|
|
// Player collision with enemy
|
|
for (int i = 0; i < activeEnemies; i++)
|
|
{
|
|
if (CheckCollisionRecs(player.rec, enemy[i].rec)) gameOver = true;
|
|
}
|
|
|
|
// Enemy behaviour
|
|
for (int i = 0; i < activeEnemies; i++)
|
|
{
|
|
if (enemy[i].active)
|
|
{
|
|
enemy[i].rec.x -= enemy[i].speed.x;
|
|
|
|
if (enemy[i].rec.x < 0)
|
|
{
|
|
enemy[i].rec.x = GetRandomValue(screenWidth, screenWidth + 1000);
|
|
enemy[i].rec.y = GetRandomValue(0, screenHeight - enemy[i].rec.height);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Wall behaviour
|
|
if (player.rec.x <= 0) player.rec.x = 0;
|
|
if (player.rec.x + player.rec.width >= screenWidth) player.rec.x = screenWidth - player.rec.width;
|
|
if (player.rec.y <= 0) player.rec.y = 0;
|
|
if (player.rec.y + player.rec.height >= screenHeight) player.rec.y = screenHeight - player.rec.height;
|
|
|
|
//Shoot initialization
|
|
if (IsKeyDown(KEY_SPACE))
|
|
{
|
|
shootRate += 5;
|
|
|
|
for (int i = 0; i < NUM_SHOOTS; i++)
|
|
{
|
|
if (!shoot[i].active && shootRate%20 == 0)
|
|
{
|
|
shoot[i].rec.x = player.rec.x;
|
|
shoot[i].rec.y = player.rec.y + player.rec.height/4;
|
|
shoot[i].active = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Shoot logic
|
|
for (int i = 0; i < NUM_SHOOTS; i++)
|
|
{
|
|
if (shoot[i].active)
|
|
{
|
|
// Movement
|
|
shoot[i].rec.x += shoot[i].speed.x;
|
|
|
|
// Collision with enemy
|
|
for (int j = 0; j < activeEnemies; j++)
|
|
{
|
|
if (enemy[j].active)
|
|
{
|
|
if (CheckCollisionRecs(shoot[i].rec, enemy[j].rec))
|
|
{
|
|
shoot[i].active = false;
|
|
enemy[j].rec.x = GetRandomValue(screenWidth, screenWidth + 1000);
|
|
enemy[j].rec.y = GetRandomValue(0, screenHeight - enemy[j].rec.height);
|
|
shootRate = 0;
|
|
enemiesKill++;
|
|
score += 100;
|
|
}
|
|
|
|
if (shoot[i].rec.x + shoot[i].rec.width >= screenWidth)
|
|
{
|
|
shoot[i].active = false;
|
|
shootRate = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (IsKeyPressed(KEY_ENTER))
|
|
{
|
|
InitGame();
|
|
gameOver = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw game (one frame)
|
|
void DrawGame(void)
|
|
{
|
|
BeginDrawing();
|
|
|
|
ClearBackground(RAYWHITE);
|
|
|
|
if (!gameOver)
|
|
{
|
|
DrawRectangleRec(player.rec, player.color);
|
|
|
|
if (wave == FIRST) DrawText("FIRST WAVE", screenWidth/2 - MeasureText("FIRST WAVE", 40)/2, screenHeight/2 - 40, 40, Fade(BLACK, alpha));
|
|
else if (wave == SECOND) DrawText("SECOND WAVE", screenWidth/2 - MeasureText("SECOND WAVE", 40)/2, screenHeight/2 - 40, 40, Fade(BLACK, alpha));
|
|
else if (wave == THIRD) DrawText("THIRD WAVE", screenWidth/2 - MeasureText("THIRD WAVE", 40)/2, screenHeight/2 - 40, 40, Fade(BLACK, alpha));
|
|
|
|
for (int i = 0; i < activeEnemies; i++)
|
|
{
|
|
if (enemy[i].active) DrawRectangleRec(enemy[i].rec, enemy[i].color);
|
|
}
|
|
|
|
for (int i = 0; i < NUM_SHOOTS; i++)
|
|
{
|
|
if (shoot[i].active) DrawRectangleRec(shoot[i].rec, shoot[i].color);
|
|
}
|
|
|
|
DrawText(FormatText("%04i", score), 20, 20, 40, GRAY);
|
|
|
|
if (victory) DrawText("YOU WIN", screenWidth/2 - MeasureText("YOU WIN", 40)/2, screenHeight/2 - 40, 40, BLACK);
|
|
|
|
if (pause) DrawText("GAME PAUSED", screenWidth/2 - MeasureText("GAME PAUSED", 40)/2, screenHeight/2 - 40, 40, GRAY);
|
|
}
|
|
else DrawText("PRESS [ENTER] TO PLAY AGAIN", GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, GetScreenHeight()/2 - 50, 20, GRAY);
|
|
|
|
EndDrawing();
|
|
}
|
|
|
|
// Unload game variables
|
|
void UnloadGame(void)
|
|
{
|
|
// TODO: Unload all dynamic loaded data (textures, sounds, models...)
|
|
}
|
|
|
|
// Update and Draw (one frame)
|
|
void UpdateDrawFrame(void)
|
|
{
|
|
UpdateGame();
|
|
DrawGame();
|
|
}
|