You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

348 lines
12 KiB

  1. /*******************************************************************************************
  2. *
  3. * raylib - sample game: arkanoid
  4. *
  5. * Sample game Marc Palau and Ramon Santamaria
  6. *
  7. * This game has been created using raylib v1.3 (www.raylib.com)
  8. * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
  9. *
  10. * Copyright (c) 2015 Ramon Santamaria (@raysan5)
  11. *
  12. ********************************************************************************************/
  13. #include "raylib.h"
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <time.h>
  17. #include <math.h>
  18. #if defined(PLATFORM_WEB)
  19. #include <emscripten/emscripten.h>
  20. #endif
  21. //----------------------------------------------------------------------------------
  22. // Some Defines
  23. //----------------------------------------------------------------------------------
  24. #define PLAYER_MAX_LIFE 5
  25. #define LINES_OF_BRICKS 5
  26. #define BRICKS_PER_LINE 20
  27. //----------------------------------------------------------------------------------
  28. // Types and Structures Definition
  29. //----------------------------------------------------------------------------------
  30. typedef enum GameScreen { LOGO, TITLE, GAMEPLAY, ENDING } GameScreen;
  31. typedef struct Player {
  32. Vector2 position;
  33. Vector2 size;
  34. int life;
  35. } Player;
  36. typedef struct Ball {
  37. Vector2 position;
  38. Vector2 speed;
  39. int radius;
  40. bool active;
  41. } Ball;
  42. typedef struct Brick {
  43. Vector2 position;
  44. bool active;
  45. } Brick;
  46. //------------------------------------------------------------------------------------
  47. // Global Variables Declaration
  48. //------------------------------------------------------------------------------------
  49. static int screenWidth = 800;
  50. static int screenHeight = 450;
  51. static int framesCounter;
  52. static bool gameOver;
  53. static bool pause;
  54. static Player player;
  55. static Ball ball;
  56. static Brick brick[LINES_OF_BRICKS][BRICKS_PER_LINE];
  57. static Vector2 brickSize;
  58. //------------------------------------------------------------------------------------
  59. // Module Functions Declaration (local)
  60. //------------------------------------------------------------------------------------
  61. static void InitGame(void); // Initialize game
  62. static void UpdateGame(void); // Update game (one frame)
  63. static void DrawGame(void); // Draw game (one frame)
  64. static void UnloadGame(void); // Unload game
  65. static void UpdateDrawFrame(void); // Update and Draw (one frame)
  66. // Additional module functions
  67. static void UpdateBall(void);
  68. //------------------------------------------------------------------------------------
  69. // Program main entry point
  70. //------------------------------------------------------------------------------------
  71. int main()
  72. {
  73. // Initialization
  74. //--------------------------------------------------------------------------------------
  75. InitWindow(screenWidth, screenHeight, "sample game: arkanoid");
  76. InitGame();
  77. #if defined(PLATFORM_WEB)
  78. emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
  79. #else
  80. SetTargetFPS(60);
  81. //--------------------------------------------------------------------------------------
  82. // Main game loop
  83. while (!WindowShouldClose()) // Detect window close button or ESC key
  84. {
  85. // Update
  86. //----------------------------------------------------------------------------------
  87. UpdateGame();
  88. //----------------------------------------------------------------------------------
  89. // Draw
  90. //----------------------------------------------------------------------------------
  91. DrawGame();
  92. //----------------------------------------------------------------------------------
  93. }
  94. #endif
  95. // De-Initialization
  96. //--------------------------------------------------------------------------------------
  97. UnloadGame(); // Unload loaded data (textures, sounds, models...)
  98. CloseWindow(); // Close window and OpenGL context
  99. //--------------------------------------------------------------------------------------
  100. return 0;
  101. }
  102. //------------------------------------------------------------------------------------
  103. // Module Functions Definitions (local)
  104. //------------------------------------------------------------------------------------
  105. // Initialize game variables
  106. void InitGame(void)
  107. {
  108. brickSize = (Vector2){ GetScreenWidth()/BRICKS_PER_LINE, 40 };
  109. // Initialize player
  110. player.position = (Vector2){ screenWidth/2, screenHeight*7/8 };
  111. player.size = (Vector2){ screenWidth/10, 20 };
  112. player.life = PLAYER_MAX_LIFE;
  113. // Initialize ball
  114. ball.position = (Vector2){ screenWidth/2, screenHeight*7/8 - 30 };
  115. ball.speed = (Vector2){ 0, 0 };
  116. ball.radius = 7;
  117. ball.active = false;
  118. // Initialize bricks
  119. int initialDownPosition = 50;
  120. for (int i = 0; i < LINES_OF_BRICKS; i++)
  121. {
  122. for (int j = 0; j < BRICKS_PER_LINE; j++)
  123. {
  124. brick[i][j].position = (Vector2){ j*brickSize.x + brickSize.x/2, i*brickSize.y + initialDownPosition };
  125. brick[i][j].active = true;
  126. }
  127. }
  128. }
  129. // Update game (one frame)
  130. void UpdateGame(void)
  131. {
  132. if (!gameOver)
  133. {
  134. if (IsKeyPressed('P')) pause = !pause;
  135. if (!pause)
  136. {
  137. // Player movement
  138. if (IsKeyDown(KEY_LEFT)) player.position.x -= 5;
  139. if ((player.position.x - player.size.x/2) <= 0) player.position.x = player.size.x/2;
  140. if (IsKeyDown(KEY_RIGHT)) player.position.x += 5;
  141. if ((player.position.x + player.size.x/2) >= screenWidth) player.position.x = screenWidth - player.size.x/2;
  142. // Launch ball
  143. if (!ball.active)
  144. {
  145. if (IsKeyPressed(KEY_SPACE))
  146. {
  147. ball.active = true;
  148. ball.speed = (Vector2){ 0, -5 };
  149. }
  150. }
  151. UpdateBall();
  152. // Game over logic
  153. if (player.life <= 0) gameOver = true;
  154. else
  155. {
  156. gameOver = true;
  157. for (int i = 0; i < LINES_OF_BRICKS; i++)
  158. {
  159. for (int j = 0; j < BRICKS_PER_LINE; j++)
  160. {
  161. if (brick[i][j].active) gameOver = false;
  162. }
  163. }
  164. }
  165. }
  166. }
  167. else
  168. {
  169. if (IsKeyPressed(KEY_ENTER))
  170. {
  171. InitGame();
  172. gameOver = false;
  173. }
  174. }
  175. }
  176. // Draw game (one frame)
  177. void DrawGame(void)
  178. {
  179. BeginDrawing();
  180. ClearBackground(RAYWHITE);
  181. if (!gameOver)
  182. {
  183. // Draw player bar
  184. DrawRectangle(player.position.x - player.size.x/2, player.position.y - player.size.y/2, player.size.x, player.size.y, BLACK);
  185. // Draw player lives
  186. for (int i = 0; i < player.life; i++) DrawRectangle(20 + 40*i, screenHeight - 30, 35, 10, LIGHTGRAY);
  187. // Draw ball
  188. DrawCircleV(ball.position, ball.radius, MAROON);
  189. // Draw bricks
  190. for (int i = 0; i < LINES_OF_BRICKS; i++)
  191. {
  192. for (int j = 0; j < BRICKS_PER_LINE; j++)
  193. {
  194. if (brick[i][j].active)
  195. {
  196. 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);
  197. else DrawRectangle(brick[i][j].position.x - brickSize.x/2, brick[i][j].position.y - brickSize.y/2, brickSize.x, brickSize.y, DARKGRAY);
  198. }
  199. }
  200. }
  201. if (pause) DrawText("GAME PAUSED", screenWidth/2 - MeasureText("GAME PAUSED", 40)/2, screenHeight/2 - 40, 40, GRAY);
  202. }
  203. else DrawText("PRESS [ENTER] TO PLAY AGAIN", GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, GetScreenHeight()/2 - 50, 20, GRAY);
  204. EndDrawing();
  205. }
  206. // Unload game variables
  207. void UnloadGame(void)
  208. {
  209. // TODO: Unload all dynamic loaded data (textures, sounds, models...)
  210. }
  211. // Update and Draw (one frame)
  212. void UpdateDrawFrame(void)
  213. {
  214. UpdateGame();
  215. DrawGame();
  216. }
  217. //--------------------------------------------------------------------------------------
  218. // Additional module functions
  219. //--------------------------------------------------------------------------------------
  220. static void UpdateBall()
  221. {
  222. // Update position
  223. if (ball.active)
  224. {
  225. ball.position.x += ball.speed.x;
  226. ball.position.y += ball.speed.y;
  227. }
  228. else
  229. {
  230. ball.position = (Vector2){ player.position.x, screenHeight*7/8 - 30 };
  231. }
  232. // Bounce in x
  233. if (((ball.position.x + ball.radius) >= screenWidth) || ((ball.position.x - ball.radius) <= 0)) ball.speed.x *= -1;
  234. // Bounce in y
  235. if ((ball.position.y - ball.radius) <= 0) ball.speed.y *= -1;
  236. // Ball reaches bottom of the screen
  237. if ((ball.position.y + ball.radius) >= screenHeight)
  238. {
  239. ball.speed = (Vector2){ 0, 0 };
  240. ball.active = false;
  241. player.life--;
  242. }
  243. // Collision logic: ball vs player
  244. if (CheckCollisionCircleRec(ball.position, ball.radius,
  245. (Rectangle){ player.position.x - player.size.x/2, player.position.y - player.size.y/2, player.size.x, player.size.y}))
  246. {
  247. if (ball.speed.y > 0)
  248. {
  249. ball.speed.y *= -1;
  250. ball.speed.x = (ball.position.x - player.position.x)/(player.size.x/2)*5;
  251. }
  252. }
  253. // Collision logic: ball vs bricks
  254. for (int i = 0; i < LINES_OF_BRICKS; i++)
  255. {
  256. for (int j = 0; j < BRICKS_PER_LINE; j++)
  257. {
  258. if (brick[i][j].active)
  259. {
  260. // Hit below
  261. if (((ball.position.y - ball.radius) <= (brick[i][j].position.y + brickSize.y/2)) &&
  262. ((ball.position.y - ball.radius) > (brick[i][j].position.y + brickSize.y/2 + ball.speed.y)) &&
  263. ((fabs(ball.position.x - brick[i][j].position.x)) < (brickSize.x/2 + ball.radius*2/3)) && (ball.speed.y < 0))
  264. {
  265. brick[i][j].active = false;
  266. ball.speed.y *= -1;
  267. }
  268. // Hit above
  269. else if (((ball.position.y + ball.radius) >= (brick[i][j].position.y - brickSize.y/2)) &&
  270. ((ball.position.y + ball.radius) < (brick[i][j].position.y - brickSize.y/2 + ball.speed.y)) &&
  271. ((fabs(ball.position.x - brick[i][j].position.x)) < (brickSize.x/2 + ball.radius*2/3)) && (ball.speed.y > 0))
  272. {
  273. brick[i][j].active = false;
  274. ball.speed.y *= -1;
  275. }
  276. // Hit left
  277. else if (((ball.position.x + ball.radius) >= (brick[i][j].position.x - brickSize.x/2)) &&
  278. ((ball.position.x + ball.radius) < (brick[i][j].position.x - brickSize.x/2 + ball.speed.x)) &&
  279. ((fabs(ball.position.y - brick[i][j].position.y)) < (brickSize.y/2 + ball.radius*2/3)) && (ball.speed.x > 0))
  280. {
  281. brick[i][j].active = false;
  282. ball.speed.x *= -1;
  283. }
  284. // Hit right
  285. else if (((ball.position.x - ball.radius) <= (brick[i][j].position.x + brickSize.x/2)) &&
  286. ((ball.position.x - ball.radius) > (brick[i][j].position.x + brickSize.x/2 + ball.speed.x)) &&
  287. ((fabs(ball.position.y - brick[i][j].position.y)) < (brickSize.y/2 + ball.radius*2/3)) && (ball.speed.x < 0))
  288. {
  289. brick[i][j].active = false;
  290. ball.speed.x *= -1;
  291. }
  292. }
  293. }
  294. }
  295. }