/******************************************************************************************* * * raylib - sample game: arkanoid * * Sample game Marc Palau 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" #include #include #include #include #if defined(PLATFORM_ANDROID) #include "android_native_app_glue.h" #endif #if defined(PLATFORM_WEB) #include #endif //---------------------------------------------------------------------------------- // Some Defines //---------------------------------------------------------------------------------- #define PLAYER_MAX_LIFE 5 #define LINES_OF_BRICKS 5 #define BRICKS_PER_LINE 20 //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- typedef enum GameScreen { LOGO, TITLE, GAMEPLAY, ENDING } GameScreen; typedef struct Player { Vector2 position; Vector2 size; int life; } Player; typedef struct Ball { Vector2 position; Vector2 speed; int radius; bool active; } Ball; typedef struct Brick { Vector2 position; bool active; } Brick; //------------------------------------------------------------------------------------ // Global Variables Declaration //------------------------------------------------------------------------------------ static int screenWidth = 800; static int screenHeight = 450; static bool gameOver; static bool pause; static Player player; static Ball ball; static Brick brick[LINES_OF_BRICKS][BRICKS_PER_LINE]; static Vector2 brickSize; //------------------------------------------------------------------------------------ // 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 //------------------------------------------------------------------------------------ #if defined(PLATFORM_ANDROID) void android_main(struct android_app *app) #else int main(void) #endif { // Initialization //--------------------------------------------------------- #if defined(PLATFORM_ANDROID) InitWindow(screenWidth, screenHeight, app); #else InitWindow(screenWidth, screenHeight, "sample game: arkanoid"); #endif 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 //-------------------------------------------------------------------------------------- #if !defined(PLATFORM_ANDROID) return 0; #endif } //------------------------------------------------------------------------------------ // Module Functions Definitions (local) //------------------------------------------------------------------------------------ // Initialize game variables void InitGame(void) { brickSize = (Vector2){ GetScreenWidth()/BRICKS_PER_LINE, 40 }; // Initialize player player.position = (Vector2){ screenWidth/2, screenHeight*7/8 }; player.size = (Vector2){ screenWidth/10, 20 }; player.life = PLAYER_MAX_LIFE; // Initialize ball ball.position = (Vector2){ screenWidth/2, screenHeight*7/8 - 30 }; ball.speed = (Vector2){ 0, 0 }; ball.radius = 7; ball.active = false; // Initialize bricks int initialDownPosition = 50; for (int i = 0; i < LINES_OF_BRICKS; i++) { for (int j = 0; j < BRICKS_PER_LINE; j++) { brick[i][j].position = (Vector2){ j*brickSize.x + brickSize.x/2, i*brickSize.y + initialDownPosition }; brick[i][j].active = true; } } } // Update game (one frame) void UpdateGame(void) { if (!gameOver) { if (IsKeyPressed('P')) pause = !pause; if (!pause) { // Player movement logic if (IsKeyDown(KEY_LEFT)) player.position.x -= 5; if ((player.position.x - player.size.x/2) <= 0) player.position.x = player.size.x/2; if (IsKeyDown(KEY_RIGHT)) player.position.x += 5; if ((player.position.x + player.size.x/2) >= screenWidth) player.position.x = screenWidth - player.size.x/2; // Ball launching logic if (!ball.active) { if (IsKeyPressed(KEY_SPACE)) { ball.active = true; ball.speed = (Vector2){ 0, -5 }; } } // Ball movement logic if (ball.active) { ball.position.x += ball.speed.x; ball.position.y += ball.speed.y; } else { ball.position = (Vector2){ player.position.x, screenHeight*7/8 - 30 }; } // Collision logic: ball vs walls if (((ball.position.x + ball.radius) >= screenWidth) || ((ball.position.x - ball.radius) <= 0)) ball.speed.x *= -1; if ((ball.position.y - ball.radius) <= 0) ball.speed.y *= -1; if ((ball.position.y + ball.radius) >= screenHeight) { ball.speed = (Vector2){ 0, 0 }; ball.active = false; player.life--; } // Collision logic: ball vs player if (CheckCollisionCircleRec(ball.position, ball.radius, (Rectangle){ player.position.x - player.size.x/2, player.position.y - player.size.y/2, player.size.x, player.size.y})) { if (ball.speed.y > 0) { ball.speed.y *= -1; ball.speed.x = (ball.position.x - player.position.x)/(player.size.x/2)*5; } } // Collision logic: ball vs bricks for (int i = 0; i < LINES_OF_BRICKS; i++) { for (int j = 0; j < BRICKS_PER_LINE; j++) { if (brick[i][j].active) { // Hit below if (((ball.position.y - ball.radius) <= (brick[i][j].position.y + brickSize.y/2)) && ((ball.position.y - ball.radius) > (brick[i][j].position.y + brickSize.y/2 + ball.speed.y)) && ((fabs(ball.position.x - brick[i][j].position.x)) < (brickSize.x/2 + ball.radius*2/3)) && (ball.speed.y < 0)) { brick[i][j].active = false; ball.speed.y *= -1; } // Hit above else if (((ball.position.y + ball.radius) >= (brick[i][j].position.y - brickSize.y/2)) && ((ball.position.y + ball.radius) < (brick[i][j].position.y - brickSize.y/2 + ball.speed.y)) && ((fabs(ball.position.x - brick[i][j].position.x)) < (brickSize.x/2 + ball.radius*2/3)) && (ball.speed.y > 0)) { brick[i][j].active = false; ball.speed.y *= -1; } // Hit left else if (((ball.position.x + ball.radius) >= (brick[i][j].position.x - brickSize.x/2)) && ((ball.position.x + ball.radius) < (brick[i][j].position.x - brickSize.x/2 + ball.speed.x)) && ((fabs(ball.position.y - brick[i][j].position.y)) < (brickSize.y/2 + ball.radius*2/3)) && (ball.speed.x > 0)) { brick[i][j].active = false; ball.speed.x *= -1; } // Hit right else if (((ball.position.x - ball.radius) <= (brick[i][j].position.x + brickSize.x/2)) && ((ball.position.x - ball.radius) > (brick[i][j].position.x + brickSize.x/2 + ball.speed.x)) && ((fabs(ball.position.y - brick[i][j].position.y)) < (brickSize.y/2 + ball.radius*2/3)) && (ball.speed.x < 0)) { brick[i][j].active = false; ball.speed.x *= -1; } } } } // Game over logic if (player.life <= 0) gameOver = true; else { gameOver = true; for (int i = 0; i < LINES_OF_BRICKS; i++) { for (int j = 0; j < BRICKS_PER_LINE; j++) { if (brick[i][j].active) gameOver = false; } } } } } else { if (IsKeyPressed(KEY_ENTER)) { InitGame(); gameOver = false; } } } // Draw game (one frame) void DrawGame(void) { BeginDrawing(); ClearBackground(RAYWHITE); if (!gameOver) { // Draw player bar DrawRectangle(player.position.x - player.size.x/2, player.position.y - player.size.y/2, player.size.x, player.size.y, BLACK); // Draw player lives for (int i = 0; i < player.life; i++) DrawRectangle(20 + 40*i, screenHeight - 30, 35, 10, LIGHTGRAY); // Draw ball DrawCircleV(ball.position, ball.radius, MAROON); // Draw bricks for (int i = 0; i < LINES_OF_BRICKS; i++) { for (int j = 0; j < BRICKS_PER_LINE; j++) { if (brick[i][j].active) { if ((i + j) % 2 == 0) DrawRectangle(brick[i][j].position.x - brickSize.x/2, brick[i][j].position.y - brickSize.y/2, brickSize.x, brickSize.y, GRAY); else DrawRectangle(brick[i][j].position.x - brickSize.x/2, brick[i][j].position.y - brickSize.y/2, brickSize.x, brickSize.y, DARKGRAY); } } } 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(); }