123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- /******************************************************************************
- * M5Snake *
- * ------- *
- * Snake game for M5Stack-Core with Gameboy Keyboard faces *
- * Author: Olivier Staquet *
- * Last version available on https://github.com/ostaquet/M5Snake *
- ******************************************************************************
- * MIT License
- *
- * Copyright (c) 2020 Olivier Staquet
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *******************************************************************************/
- #include <M5Stack.h>
- #include "GameboyInput.h"
- #include "Power.h"
- #include "GameBoard.h"
- #define GAME_STATUS_INIT 0x00
- #define GAME_STATUS_MENU 0x10
- #define GAME_STATUS_START_GAME 0x20
- #define GAME_STATUS_GAME 0x21
- #define GAME_STATUS_GAMEOVER 0x30
- #define GAME_CYCLES 3 // Define the number of cycle before moving the snake
- /******************************************************************************
- * Global variables
- *******************************************************************************/
- // Global game status
- uint8_t game_status = GAME_STATUS_INIT;
- void gameLoop();
- void fromInitToMenu();
- void fromMenuToGame();
- void fromGameToGameOver();
- /******************************************************************************
- * Initialize components
- *******************************************************************************/
- void setup() {
- // Set game status
- game_status = GAME_STATUS_INIT;
-
- // Initialize M5Stack (LCD = true, SD = false, Serial = true, I2C = true)
- M5.begin(true, false, true, true);
- // Show that we are alive
- M5.Lcd.setBrightness(100);
- M5.Lcd.fillScreen(BLACK);
- M5.Lcd.setTextSize(2);
- M5.Lcd.println(F("M5Snake by Olivier Staquet"));
- // Disable white noise on speaker
- // (according to https://community.m5stack.com/topic/61/noise-on-speaker/15)
- dacWrite(25,0);
- // Start the Gameboy faces controller
- GameboyInput.begin();
- // Show battery management
- Power.begin();
- Power.adaptChargeMode();
- M5.Lcd.print(F("Battery level "));
- int8_t batt = Power.getBatteryLevel();
- if(batt >= 0) {
- M5.Lcd.print(batt);
- M5.Lcd.println(F("%"));
- } else {
- M5.Lcd.println(F("unavailable"));
- }
- // Wait some time
- delay(1000);
- }
- /******************************************************************************
- * Main loop
- *******************************************************************************/
- void loop() {
- // Depending on status, run the routine
- switch(game_status) {
- // Init phase (over)
- case GAME_STATUS_INIT :
- fromInitToMenu();
- break;
-
- // Start menu
- case GAME_STATUS_MENU :
- if(GameboyInput.getActivity() == GAMEBOY_KEY_START) {
- fromMenuToGame();
- }
- break;
- // Game in progress
- case GAME_STATUS_GAME :
- gameLoop();
- break;
- // Game over
- case GAME_STATUS_GAMEOVER :
- delay(3000);
- fromInitToMenu();
- break;
- }
- // By default, always adapt the charge mode for each cycle
- Power.adaptChargeMode();
- // Cycle delay
- delay(25);
- }
- /******************************************************************************
- * Routine for the game loop (Snake moving and eating cherry depending on input
- *******************************************************************************/
- void gameLoop() {
- // Check the activity from the input
- switch(GameboyInput.getActivity()) {
- case GAMEBOY_KEY_UP :
- GameBoard.setDirection(DIRECTION_UP);
- break;
- case GAMEBOY_KEY_RIGHT :
- GameBoard.setDirection(DIRECTION_RIGHT);
- break;
- case GAMEBOY_KEY_DOWN :
- GameBoard.setDirection(DIRECTION_DOWN);
- break;
- case GAMEBOY_KEY_LEFT :
- GameBoard.setDirection(DIRECTION_LEFT);
- break;
- }
- // Move the snake
- if(!GameBoard.moveSnake()) {
- // If cannot move the snake -> game over...
- fromGameToGameOver();
- } else {
- // Add a cherry some time...
- if(random(0,15 * GAME_CYCLES) == 0) {
- GameBoard.addCherry();
- }
-
- GameBoard.refresh();
- }
- }
- /******************************************************************************
- * Routine from GAME_STATUS_INIT to GAME_STATUS_MENU
- *******************************************************************************/
- void fromInitToMenu() {
- // Clear the screen
- M5.Lcd.setBrightness(100);
- M5.Lcd.fillScreen(BLACK);
- // Show the title
- M5.Lcd.setTextSize(5);
- M5.Lcd.setTextColor(RED);
- M5.Lcd.drawString(F("M5Snake"), (M5.Lcd.width() - M5.Lcd.textWidth(F("M5Snake"))) / 2, M5.Lcd.height() / 4);
-
- M5.Lcd.setTextSize(1);
- M5.Lcd.setTextColor(WHITE);
- M5.Lcd.drawString(F("by Olivier Staquet"), (M5.Lcd.width() - M5.Lcd.textWidth(F("by Olivier Staquet"))) / 2, M5.Lcd.height() / 2);
- // Show "Press start"
- M5.Lcd.setTextSize(2);
- M5.Lcd.setTextColor(WHITE);
- 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);
-
- game_status = GAME_STATUS_MENU;
- }
- /******************************************************************************
- * Routine from GAME_STATUS_MENU to GAME_STATUS_GAME
- *******************************************************************************/
- void fromMenuToGame() {
- // Init the board game
- GameBoard.begin(GAME_CYCLES);
- // Define the head of the snake
- GameBoard.startSnake();
- GameBoard.refresh();
-
- game_status = GAME_STATUS_GAME;
- }
- /******************************************************************************
- * Routine from GAME_STATUS_GAME to GAME_STATUS_GAMEOVER
- *******************************************************************************/
- void fromGameToGameOver() {
- // Clear the screen
- M5.Lcd.setBrightness(100);
- M5.Lcd.fillScreen(RED);
- M5.Lcd.setTextSize(5);
- M5.Lcd.setTextColor(BLACK);
- M5.Lcd.drawString(F("GAME OVER"), (M5.Lcd.width() - M5.Lcd.textWidth(F("GAME OVER"))) / 2, M5.Lcd.height() / 4);
- // Max score...
- M5.Lcd.setTextSize(2);
- M5.Lcd.setTextColor(WHITE);
- M5.Lcd.drawString(F("Score"), (M5.Lcd.width() - M5.Lcd.textWidth(F("Score"))) / 2, M5.Lcd.height() / 2);
-
- M5.Lcd.setTextSize(4);
- M5.Lcd.setTextColor(WHITE);
- M5.Lcd.drawString(String(GameBoard.getMaxScore()), (M5.Lcd.width() - M5.Lcd.textWidth(String(GameBoard.getMaxScore()))) / 2, (M5.Lcd.height() / 4) * 3);
- game_status = GAME_STATUS_GAMEOVER;
- }
|