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.

286 lines
9.6 KiB

  1. /*******************************************************************************************
  2. *
  3. * raylib - sample game: snake
  4. *
  5. * Sample game developed by Ian Eito, Albert Martos 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. #if defined(PLATFORM_WEB)
  15. #include <emscripten/emscripten.h>
  16. #endif
  17. //----------------------------------------------------------------------------------
  18. // Some Defines
  19. //----------------------------------------------------------------------------------
  20. #define SNAKE_LENGTH 256
  21. #define SQUARE_SIZE 31
  22. //----------------------------------------------------------------------------------
  23. // Types and Structures Definition
  24. //----------------------------------------------------------------------------------
  25. typedef struct Snake {
  26. Vector2 position;
  27. Vector2 size;
  28. Vector2 speed;
  29. Color color;
  30. } Snake;
  31. typedef struct Food {
  32. Vector2 position;
  33. Vector2 size;
  34. bool active;
  35. Color color;
  36. } Food;
  37. //------------------------------------------------------------------------------------
  38. // Global Variables Declaration
  39. //------------------------------------------------------------------------------------
  40. static const int screenWidth = 800;
  41. static const int screenHeight = 450;
  42. static int framesCounter = 0;
  43. static bool gameOver = false;
  44. static bool pause = false;
  45. static Food fruit = { 0 };
  46. static Snake snake[SNAKE_LENGTH] = { 0 };
  47. static Vector2 snakePosition[SNAKE_LENGTH] = { 0 };
  48. static bool allowMove = false;
  49. static Vector2 offset = { 0 };
  50. static int counterTail = 0;
  51. //------------------------------------------------------------------------------------
  52. // Module Functions Declaration (local)
  53. //------------------------------------------------------------------------------------
  54. static void InitGame(void); // Initialize game
  55. static void UpdateGame(void); // Update game (one frame)
  56. static void DrawGame(void); // Draw game (one frame)
  57. static void UnloadGame(void); // Unload game
  58. static void UpdateDrawFrame(void); // Update and Draw (one frame)
  59. //------------------------------------------------------------------------------------
  60. // Program main entry point
  61. //------------------------------------------------------------------------------------
  62. int main(void)
  63. {
  64. // Initialization (Note windowTitle is unused on Android)
  65. //---------------------------------------------------------
  66. InitWindow(screenWidth, screenHeight, "sample game: snake");
  67. InitGame();
  68. #if defined(PLATFORM_WEB)
  69. emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
  70. #else
  71. SetTargetFPS(60);
  72. //--------------------------------------------------------------------------------------
  73. // Main game loop
  74. while (!WindowShouldClose()) // Detect window close button or ESC key
  75. {
  76. // Update and Draw
  77. //----------------------------------------------------------------------------------
  78. UpdateDrawFrame();
  79. //----------------------------------------------------------------------------------
  80. }
  81. #endif
  82. // De-Initialization
  83. //--------------------------------------------------------------------------------------
  84. UnloadGame(); // Unload loaded data (textures, sounds, models...)
  85. CloseWindow(); // Close window and OpenGL context
  86. //--------------------------------------------------------------------------------------
  87. return 0;
  88. }
  89. //------------------------------------------------------------------------------------
  90. // Module Functions Definitions (local)
  91. //------------------------------------------------------------------------------------
  92. // Initialize game variables
  93. void InitGame(void)
  94. {
  95. framesCounter = 0;
  96. gameOver = false;
  97. pause = false;
  98. counterTail = 1;
  99. allowMove = false;
  100. offset.x = screenWidth%SQUARE_SIZE;
  101. offset.y = screenHeight%SQUARE_SIZE;
  102. for (int i = 0; i < SNAKE_LENGTH; i++)
  103. {
  104. snake[i].position = (Vector2){ offset.x/2, offset.y/2 };
  105. snake[i].size = (Vector2){ SQUARE_SIZE, SQUARE_SIZE };
  106. snake[i].speed = (Vector2){ SQUARE_SIZE, 0 };
  107. if (i == 0) snake[i].color = DARKBLUE;
  108. else snake[i].color = BLUE;
  109. }
  110. for (int i = 0; i < SNAKE_LENGTH; i++)
  111. {
  112. snakePosition[i] = (Vector2){ 0.0f, 0.0f };
  113. }
  114. fruit.size = (Vector2){ SQUARE_SIZE, SQUARE_SIZE };
  115. fruit.color = SKYBLUE;
  116. fruit.active = false;
  117. }
  118. // Update game (one frame)
  119. void UpdateGame(void)
  120. {
  121. if (!gameOver)
  122. {
  123. if (IsKeyPressed('P')) pause = !pause;
  124. if (!pause)
  125. {
  126. // Player control
  127. if (IsKeyPressed(KEY_RIGHT) && (snake[0].speed.x == 0) && allowMove)
  128. {
  129. snake[0].speed = (Vector2){ SQUARE_SIZE, 0 };
  130. allowMove = false;
  131. }
  132. if (IsKeyPressed(KEY_LEFT) && (snake[0].speed.x == 0) && allowMove)
  133. {
  134. snake[0].speed = (Vector2){ -SQUARE_SIZE, 0 };
  135. allowMove = false;
  136. }
  137. if (IsKeyPressed(KEY_UP) && (snake[0].speed.y == 0) && allowMove)
  138. {
  139. snake[0].speed = (Vector2){ 0, -SQUARE_SIZE };
  140. allowMove = false;
  141. }
  142. if (IsKeyPressed(KEY_DOWN) && (snake[0].speed.y == 0) && allowMove)
  143. {
  144. snake[0].speed = (Vector2){ 0, SQUARE_SIZE };
  145. allowMove = false;
  146. }
  147. // Snake movement
  148. for (int i = 0; i < counterTail; i++) snakePosition[i] = snake[i].position;
  149. if ((framesCounter%5) == 0)
  150. {
  151. for (int i = 0; i < counterTail; i++)
  152. {
  153. if (i == 0)
  154. {
  155. snake[0].position.x += snake[0].speed.x;
  156. snake[0].position.y += snake[0].speed.y;
  157. allowMove = true;
  158. }
  159. else snake[i].position = snakePosition[i-1];
  160. }
  161. }
  162. // Wall behaviour
  163. if (((snake[0].position.x) > (screenWidth - offset.x)) ||
  164. ((snake[0].position.y) > (screenHeight - offset.y)) ||
  165. (snake[0].position.x < 0) || (snake[0].position.y < 0))
  166. {
  167. gameOver = true;
  168. }
  169. // Collision with yourself
  170. for (int i = 1; i < counterTail; i++)
  171. {
  172. if ((snake[0].position.x == snake[i].position.x) && (snake[0].position.y == snake[i].position.y)) gameOver = true;
  173. }
  174. // Fruit position calculation
  175. if (!fruit.active)
  176. {
  177. fruit.active = true;
  178. fruit.position = (Vector2){ GetRandomValue(0, (screenWidth/SQUARE_SIZE) - 1)*SQUARE_SIZE + offset.x/2, GetRandomValue(0, (screenHeight/SQUARE_SIZE) - 1)*SQUARE_SIZE + offset.y/2 };
  179. for (int i = 0; i < counterTail; i++)
  180. {
  181. while ((fruit.position.x == snake[i].position.x) && (fruit.position.y == snake[i].position.y))
  182. {
  183. fruit.position = (Vector2){ GetRandomValue(0, (screenWidth/SQUARE_SIZE) - 1)*SQUARE_SIZE + offset.x/2, GetRandomValue(0, (screenHeight/SQUARE_SIZE) - 1)*SQUARE_SIZE + offset.y/2 };
  184. i = 0;
  185. }
  186. }
  187. }
  188. // Collision
  189. if ((snake[0].position.x < (fruit.position.x + fruit.size.x) && (snake[0].position.x + snake[0].size.x) > fruit.position.x) &&
  190. (snake[0].position.y < (fruit.position.y + fruit.size.y) && (snake[0].position.y + snake[0].size.y) > fruit.position.y))
  191. {
  192. snake[counterTail].position = snakePosition[counterTail - 1];
  193. counterTail += 1;
  194. fruit.active = false;
  195. }
  196. framesCounter++;
  197. }
  198. }
  199. else
  200. {
  201. if (IsKeyPressed(KEY_ENTER))
  202. {
  203. InitGame();
  204. gameOver = false;
  205. }
  206. }
  207. }
  208. // Draw game (one frame)
  209. void DrawGame(void)
  210. {
  211. BeginDrawing();
  212. ClearBackground(RAYWHITE);
  213. if (!gameOver)
  214. {
  215. // Draw grid lines
  216. for (int i = 0; i < screenWidth/SQUARE_SIZE + 1; i++)
  217. {
  218. DrawLineV((Vector2){SQUARE_SIZE*i + offset.x/2, offset.y/2}, (Vector2){SQUARE_SIZE*i + offset.x/2, screenHeight - offset.y/2}, LIGHTGRAY);
  219. }
  220. for (int i = 0; i < screenHeight/SQUARE_SIZE + 1; i++)
  221. {
  222. DrawLineV((Vector2){offset.x/2, SQUARE_SIZE*i + offset.y/2}, (Vector2){screenWidth - offset.x/2, SQUARE_SIZE*i + offset.y/2}, LIGHTGRAY);
  223. }
  224. // Draw snake
  225. for (int i = 0; i < counterTail; i++) DrawRectangleV(snake[i].position, snake[i].size, snake[i].color);
  226. // Draw fruit to pick
  227. DrawRectangleV(fruit.position, fruit.size, fruit.color);
  228. if (pause) DrawText("GAME PAUSED", screenWidth/2 - MeasureText("GAME PAUSED", 40)/2, screenHeight/2 - 40, 40, GRAY);
  229. }
  230. else DrawText("PRESS [ENTER] TO PLAY AGAIN", GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, GetScreenHeight()/2 - 50, 20, GRAY);
  231. EndDrawing();
  232. }
  233. // Unload game variables
  234. void UnloadGame(void)
  235. {
  236. // TODO: Unload all dynamic loaded data (textures, sounds, models...)
  237. }
  238. // Update and Draw (one frame)
  239. void UpdateDrawFrame(void)
  240. {
  241. UpdateGame();
  242. DrawGame();
  243. }