-
11MAIN CODE
Here's the main code we used in this project and its a simple one.
#include <Adafruit_Protomatter.h> // Pin definitions #define R1 2 #define G1 3 #define B1 4 #define R2 5 #define G2 8 #define B2 9 #define A 10 #define B 16 #define C 18 #define D 20 #define CLK 11 #define LAT 12 #define OE 13 // Button pins #define BUTTON_UP 7 #define BUTTON_DOWN 6 #define BUTTON_LEFT 15 #define BUTTON_RIGHT 14 #define WIDTH 64 #define HEIGHT 32 #define SNAKE_LENGTH 8 uint8_t rgbPins[] = { R1, G1, B1, R2, G2, B2 }; uint8_t addrPins[] = { A, B, C, D }; Adafruit_Protomatter matrix(WIDTH, HEIGHT, 1, rgbPins, 4, addrPins, CLK, LAT, OE, false); struct SnakeSegment { int x; int y; }; SnakeSegment snake[WIDTH * HEIGHT]; // Increased size to allow snake growth int snakeLength = SNAKE_LENGTH; int dx = 1, dy = 0; uint16_t snakeColor = matrix.color565(0, 255, 0); // Green color uint16_t foodColor = matrix.color565(255, 0, 0); // Red color uint16_t gameOverColor = matrix.color565(255, 0, 0); // Red color for "Game Over" screen int foodX, foodY; int score = 0; // Score variable bool gameOver = false; // Game over flag void placeFood() { // Ensure the new food position does not overlap with the snake bool foodOnSnake; do { foodX = random(WIDTH); foodY = random(HEIGHT); foodOnSnake = false; for (int i = 0; i < snakeLength; i++) { if (snake[i].x == foodX && snake[i].y == foodY) { foodOnSnake = true; break; } } } while (foodOnSnake); } void setup() { matrix.begin(); pinMode(BUTTON_UP, INPUT_PULLUP); pinMode(BUTTON_DOWN, INPUT_PULLUP); pinMode(BUTTON_LEFT, INPUT_PULLUP); pinMode(BUTTON_RIGHT, INPUT_PULLUP); // Initialize snake in the middle of the screen for (int i = 0; i < snakeLength; i++) { snake[i] = { WIDTH / 2 - i, HEIGHT / 2 }; } placeFood(); // Place initial food // Clear screen matrix.fillScreen(matrix.color565(0, 0, 0)); matrix.show(); } void drawScore() { matrix.setCursor(WIDTH - 10, 2); // Adjust cursor to top-right corner matrix.setTextColor(matrix.color565(255, 255, 255)); // Set text color to white for visibility matrix.print(score); // Display only the score number } void checkGameOver() { // Check if snake collides with itself for (int i = 1; i < snakeLength; i++) { if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) { gameOver = true; break; } } } void resetGame() { snakeLength = SNAKE_LENGTH; dx = 1; dy = 0; score = 0; gameOver = false; // Initialize snake in the middle of the screen for (int i = 0; i < snakeLength; i++) { snake[i] = { WIDTH / 2 - i, HEIGHT / 2 }; } placeFood(); // Place initial food } void drawGameOver() { // Fill the screen with red matrix.fillScreen(gameOverColor); // Draw "Game Over!" in black uint16_t blackColor = matrix.color565(0, 0, 0); matrix.setCursor((WIDTH / 2) - 30, (HEIGHT / 2) - 4); matrix.setTextColor(blackColor); matrix.print("Game Over!"); // Display score in white matrix.setCursor(WIDTH - 10, 2); // Adjust cursor to top-right corner matrix.setTextColor(blackColor); matrix.print(score); } void loop() { if (gameOver) { drawGameOver(); matrix.show(); delay(5000); // Wait 5 seconds resetGame(); // Reset the game return; } // Check button states and update direction if (!digitalRead(BUTTON_UP) && dy == 0) { dx = 0; dy = -1; } else if (!digitalRead(BUTTON_DOWN) && dy == 0) { dx = 0; dy = 1; } else if (!digitalRead(BUTTON_LEFT) && dx == 0) { dx = -1; dy = 0; } else if (!digitalRead(BUTTON_RIGHT) && dx == 0) { dx = 1; dy = 0; } // Move snake for (int i = snakeLength - 1; i > 0; i--) { snake[i] = snake[i - 1]; } snake[0].x += dx; snake[0].y += dy; // Wrap around screen edges if (snake[0].x >= WIDTH) snake[0].x = 0; if (snake[0].x < 0) snake[0].x = WIDTH - 1; if (snake[0].y >= HEIGHT) snake[0].y = 0; if (snake[0].y < 0) snake[0].y = HEIGHT - 1; // Check if snake eats the food if (snake[0].x == foodX && snake[0].y == foodY) { snakeLength++; score++; // Increment score placeFood(); } // Clear screen matrix.fillScreen(matrix.color565(0, 0, 0)); // Draw snake for (int i = 0; i < snakeLength; i++) { matrix.drawPixel(snake[i].x, snake[i].y, snakeColor); } // Draw food matrix.drawPixel(foodX, foodY, foodColor); // Draw score drawScore(); // Update display matrix.show(); // Check for game over checkGameOver(); delay(100); // Adjust delay for speed control }
This code is divided into seven sections which are the following-
Library and Pin Definitions- which includes the Adafruit_Protomatter Library to control the RGB Matrix along with the Pins for RGB Signal, Address lines, clock, Snake length, latch and outoput enable as well as buttons are also defined here.
Matrix Initialization and Snake Structure- Here, the matrix is set up with width, height, RGB pins, address pins, clock, latch, and output enable. the snake structure Defines the structure for the snake's segments, initializing snake length, direction, colors, food position, score, and game over flag.
Place Food Function- Places food on the matrix, ensuring it doesn't overlap with the snake's position.
Setup Function- Initializes the matrix and button pins. Positions the snake in the middle of the screen and places the initial food.
Drawing Functions- drawScore displays the score on the screen. drawGameOver displays the "Game Over" message and the score.
Game Logic Functions- checkGameOver checks if the snake collides with itself, setting the game over flag. resetGame resets the game variables and initializes the snake position.
Main Loop- Controls the main game logic, including snake movement, food placement, score updates, drawing, and game-over checks.
The loop checks for button inputs to control the direction of the snake, updates the snake's position, handles screen wrapping, checks for food consumption, and updates the display. If the game is over, it displays the "Game Over" message, waits for 5 seconds, and then resets the game.
-
12RESULT
Here is the end result of this long but straightforward build: a functional handheld snake game console that functions perfectly. It has an integrated battery that enables the user to carry it around and play games while on the go.
We can control Snake's movements with the DPad. The objective is to consume as much food or red dots as possible; the score mark position in the upper right corner of the screen indicates our progress.
Snake can cross display boundaries; however, if he inadvertently cuts himself during gameplay, the game ends and you are presented with a red screen that reads "Game over" along with your score. The game will restart and continue after five seconds of waiting.
By hitting the PUSH button on the console's back, the entire device can be switched on or off.
We can use a MicroB cable and a standard 5V smartphone charger to charge the lithium cell. The status LED will continue to flicker during charging; however, it will stop blinking and remain on after the battery is fully charged.
This project was successful overall, and I won't be focusing on a V2 anytime soon.
The Snake Game Console can run virtually anything, including games. To run games, we need to build them ourselves or port already-existing games, but that is a topic for another time.
Please let me know if you require any additional assistance; all the documents, files, and code are included in the article.
Thanks for reaching this far, and I will be back with a new project pretty soon.
Peace.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.