Snake_Gameboy.ino 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /******************************************************************************
  2. * M5Snake *
  3. * ------- *
  4. * Snake game for M5Stack-Core with Gameboy Keyboard faces *
  5. * Author: Olivier Staquet *
  6. * Last version available on https://github.com/ostaquet/M5Snake *
  7. ******************************************************************************
  8. * MIT License
  9. *
  10. * Copyright (c) 2020 Olivier Staquet
  11. *
  12. * Permission is hereby granted, free of charge, to any person obtaining a copy
  13. * of this software and associated documentation files (the "Software"), to deal
  14. * in the Software without restriction, including without limitation the rights
  15. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  16. * copies of the Software, and to permit persons to whom the Software is
  17. * furnished to do so, subject to the following conditions:
  18. *
  19. * The above copyright notice and this permission notice shall be included in all
  20. * copies or substantial portions of the Software.
  21. *
  22. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  23. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  24. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  25. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  26. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  27. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  28. * SOFTWARE.
  29. *******************************************************************************/
  30. #include <M5Stack.h>
  31. #include "GameboyInput.h"
  32. #include "Power.h"
  33. #include "GameBoard.h"
  34. #define GAME_STATUS_INIT 0x00
  35. #define GAME_STATUS_MENU 0x10
  36. #define GAME_STATUS_START_GAME 0x20
  37. #define GAME_STATUS_GAME 0x21
  38. #define GAME_STATUS_GAMEOVER 0x30
  39. #define GAME_CYCLES 3 // Define the number of cycle before moving the snake
  40. /******************************************************************************
  41. * Global variables
  42. *******************************************************************************/
  43. // Global game status
  44. uint8_t game_status = GAME_STATUS_INIT;
  45. void gameLoop();
  46. void fromInitToMenu();
  47. void fromMenuToGame();
  48. void fromGameToGameOver();
  49. /******************************************************************************
  50. * Initialize components
  51. *******************************************************************************/
  52. void setup() {
  53. // Set game status
  54. game_status = GAME_STATUS_INIT;
  55. // Initialize M5Stack (LCD = true, SD = false, Serial = true, I2C = true)
  56. M5.begin(true, false, true, true);
  57. // Show that we are alive
  58. M5.Lcd.setBrightness(100);
  59. M5.Lcd.fillScreen(BLACK);
  60. M5.Lcd.setTextSize(2);
  61. M5.Lcd.println(F("M5Snake by Olivier Staquet"));
  62. // Disable white noise on speaker
  63. // (according to https://community.m5stack.com/topic/61/noise-on-speaker/15)
  64. dacWrite(25,0);
  65. // Start the Gameboy faces controller
  66. GameboyInput.begin();
  67. // Show battery management
  68. Power.begin();
  69. Power.adaptChargeMode();
  70. M5.Lcd.print(F("Battery level "));
  71. int8_t batt = Power.getBatteryLevel();
  72. if(batt >= 0) {
  73. M5.Lcd.print(batt);
  74. M5.Lcd.println(F("%"));
  75. } else {
  76. M5.Lcd.println(F("unavailable"));
  77. }
  78. // Wait some time
  79. delay(1000);
  80. }
  81. /******************************************************************************
  82. * Main loop
  83. *******************************************************************************/
  84. void loop() {
  85. // Depending on status, run the routine
  86. switch(game_status) {
  87. // Init phase (over)
  88. case GAME_STATUS_INIT :
  89. fromInitToMenu();
  90. break;
  91. // Start menu
  92. case GAME_STATUS_MENU :
  93. if(GameboyInput.getActivity() == GAMEBOY_KEY_START) {
  94. fromMenuToGame();
  95. }
  96. break;
  97. // Game in progress
  98. case GAME_STATUS_GAME :
  99. gameLoop();
  100. break;
  101. // Game over
  102. case GAME_STATUS_GAMEOVER :
  103. delay(3000);
  104. fromInitToMenu();
  105. break;
  106. }
  107. // By default, always adapt the charge mode for each cycle
  108. Power.adaptChargeMode();
  109. // Cycle delay
  110. delay(25);
  111. }
  112. /******************************************************************************
  113. * Routine for the game loop (Snake moving and eating cherry depending on input
  114. *******************************************************************************/
  115. void gameLoop() {
  116. // Check the activity from the input
  117. switch(GameboyInput.getActivity()) {
  118. case GAMEBOY_KEY_UP :
  119. GameBoard.setDirection(DIRECTION_UP);
  120. break;
  121. case GAMEBOY_KEY_RIGHT :
  122. GameBoard.setDirection(DIRECTION_RIGHT);
  123. break;
  124. case GAMEBOY_KEY_DOWN :
  125. GameBoard.setDirection(DIRECTION_DOWN);
  126. break;
  127. case GAMEBOY_KEY_LEFT :
  128. GameBoard.setDirection(DIRECTION_LEFT);
  129. break;
  130. }
  131. // Move the snake
  132. if(!GameBoard.moveSnake()) {
  133. // If cannot move the snake -> game over...
  134. fromGameToGameOver();
  135. } else {
  136. // Add a cherry some time...
  137. if(random(0,15 * GAME_CYCLES) == 0) {
  138. GameBoard.addCherry();
  139. }
  140. GameBoard.refresh();
  141. }
  142. }
  143. /******************************************************************************
  144. * Routine from GAME_STATUS_INIT to GAME_STATUS_MENU
  145. *******************************************************************************/
  146. void fromInitToMenu() {
  147. // Clear the screen
  148. M5.Lcd.setBrightness(100);
  149. M5.Lcd.fillScreen(BLACK);
  150. // Show the title
  151. M5.Lcd.setTextSize(5);
  152. M5.Lcd.setTextColor(RED);
  153. M5.Lcd.drawString(F("M5Snake"), (M5.Lcd.width() - M5.Lcd.textWidth(F("M5Snake"))) / 2, M5.Lcd.height() / 4);
  154. M5.Lcd.setTextSize(1);
  155. M5.Lcd.setTextColor(WHITE);
  156. M5.Lcd.drawString(F("by Olivier Staquet"), (M5.Lcd.width() - M5.Lcd.textWidth(F("by Olivier Staquet"))) / 2, M5.Lcd.height() / 2);
  157. // Show "Press start"
  158. M5.Lcd.setTextSize(2);
  159. M5.Lcd.setTextColor(WHITE);
  160. M5.Lcd.drawString(F("Press START to continue"), (M5.Lcd.width() - M5.Lcd.textWidth(F("Press START to continue"))) / 2, (M5.Lcd.height() / 4) * 3);
  161. game_status = GAME_STATUS_MENU;
  162. }
  163. /******************************************************************************
  164. * Routine from GAME_STATUS_MENU to GAME_STATUS_GAME
  165. *******************************************************************************/
  166. void fromMenuToGame() {
  167. // Init the board game
  168. GameBoard.begin(GAME_CYCLES);
  169. // Define the head of the snake
  170. GameBoard.startSnake();
  171. GameBoard.refresh();
  172. game_status = GAME_STATUS_GAME;
  173. }
  174. /******************************************************************************
  175. * Routine from GAME_STATUS_GAME to GAME_STATUS_GAMEOVER
  176. *******************************************************************************/
  177. void fromGameToGameOver() {
  178. // Clear the screen
  179. M5.Lcd.setBrightness(100);
  180. M5.Lcd.fillScreen(RED);
  181. M5.Lcd.setTextSize(5);
  182. M5.Lcd.setTextColor(BLACK);
  183. M5.Lcd.drawString(F("GAME OVER"), (M5.Lcd.width() - M5.Lcd.textWidth(F("GAME OVER"))) / 2, M5.Lcd.height() / 4);
  184. // Max score...
  185. M5.Lcd.setTextSize(2);
  186. M5.Lcd.setTextColor(WHITE);
  187. M5.Lcd.drawString(F("Score"), (M5.Lcd.width() - M5.Lcd.textWidth(F("Score"))) / 2, M5.Lcd.height() / 2);
  188. M5.Lcd.setTextSize(4);
  189. M5.Lcd.setTextColor(WHITE);
  190. M5.Lcd.drawString(String(GameBoard.getMaxScore()), (M5.Lcd.width() - M5.Lcd.textWidth(String(GameBoard.getMaxScore()))) / 2, (M5.Lcd.height() / 4) * 3);
  191. game_status = GAME_STATUS_GAMEOVER;
  192. }