This shows you the differences between two versions of the page.
iothings:proiecte:2023:snake_game [2024/01/17 22:27] eugen.georgescu2202 created |
iothings:proiecte:2023:snake_game [2024/01/17 23:27] (current) eugen.georgescu2202 |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Snake ====== | + | ====== Snake ====== |
* Author: Georgescu Eugen-Robert | * Author: Georgescu Eugen-Robert | ||
Line 5: | Line 5: | ||
* Master: AAC | * Master: AAC | ||
* Academic year: 2023-2024 | * Academic year: 2023-2024 | ||
- | * Source files: | + | * Source files: {{:iothings:proiecte:2023:snake.rar }} |
- | + | * {{:iothings:proiecte:2023:snake.pptx }} | |
- | + | ||
- | ====== Introduction ====== | + | |
===== Project Description ===== | ===== Project Description ===== | ||
Line 20: | Line 18: | ||
* OLED 0.96” Screen: an OLED screen which has a reduced energy consumption, only 0.08W. The interface type is IIC; | * OLED 0.96” Screen: an OLED screen which has a reduced energy consumption, only 0.08W. The interface type is IIC; | ||
* Black Biaxial Joystick module with 5 pins: a normal joystick | * Black Biaxial Joystick module with 5 pins: a normal joystick | ||
- | Pinout scheme of the ESP32 board | + | ==Pinout scheme of the ESP32 board:== |
- | + | {{:iothings:proiecte:2023:pinout_scheme.jpeg?700 }} | |
- | {{ :iothings:proiecte:2023:pinout_scheme.jpeg?200| }} | + | |
- | Diagram: | + | ==Diagram:== |
- | {{:iothings:proiecte:2023:diagram.png?200|}} | + | {{:iothings:proiecte:2023:diagram.png?400 }} |
- | Real life: | + | ==Real life:== |
- | {{:iothings:proiecte:2023:real_life_view.jpeg?200|}} | + | {{:iothings:proiecte:2023:real_life_view.jpeg?500 }} |
- | Function used on this project | + | ===== Function used on this project ===== |
- | Setup function | + | === Setup function === |
This function was used to start the display and clear it, and after that to initialize the snake and set its color to white. | This function was used to start the display and clear it, and after that to initialize the snake and set its color to white. | ||
- | {{:iothings:proiecte:2023:setup_function.png?200|}} | + | <code> |
+ | void setup() { | ||
+ | Serial.begin(115200); | ||
- | Loop function | + | if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) { |
+ | Serial.println(F("SSD1306 allocation failed")); | ||
+ | for (;;) | ||
+ | ; | ||
+ | } | ||
+ | |||
+ | display.clearDisplay(); // Clear the display buffer | ||
+ | |||
+ | // Initialize the snake | ||
+ | for (int i = snakeSize - 1; i >= 0; i--) { | ||
+ | snakeX[i] = snakeSize - 1 - i; | ||
+ | snakeY[i] = 0; | ||
+ | } | ||
+ | |||
+ | // Set text color and size | ||
+ | display.setTextColor(SSD1306_WHITE); | ||
+ | display.setTextSize(1); | ||
+ | |||
+ | // Start the game loop | ||
+ | gameLoop(); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | === Loop function === | ||
Here I update the snake position then I draw it. After the snake is on the screen, I spawn a pixel random on the screen that can be eaten by the snake. | Here I update the snake position then I draw it. After the snake is on the screen, I spawn a pixel random on the screen that can be eaten by the snake. | ||
- | {{:iothings:proiecte:2023:loop_function.png?200|}} | + | <code> |
+ | void gameLoop() { | ||
+ | while (true) { | ||
+ | // Update the snake position | ||
+ | updateSnake(); | ||
+ | |||
+ | // Draw the snake | ||
+ | drawSnake(); | ||
+ | |||
+ | // Check for joystick input | ||
+ | checkJoystick(); | ||
+ | |||
+ | // Check if the snake eats the pixel | ||
+ | if (snakeX[0] == pixelX && snakeY[0] == pixelY) { | ||
+ | snakeSize++; | ||
+ | spawnPixel(); // Spawn a new pixel after eating the current one | ||
+ | } | ||
+ | |||
+ | delay(200); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
- | Update function | + | === Update function === |
On this function I update the entire snake positions (each of the pixels that make the body of the snake). Then I move the head on the direction of the pixel, I get this direction from a function called: checkJoystick. After that, I check if the snake hit the boundaries of the screen to teleport on the other side of the screen. | On this function I update the entire snake positions (each of the pixels that make the body of the snake). Then I move the head on the direction of the pixel, I get this direction from a function called: checkJoystick. After that, I check if the snake hit the boundaries of the screen to teleport on the other side of the screen. | ||
- | + | <code> | |
- | {{:iothings:proiecte:2023:update_function.png?200|}} | + | void updateSnake() { |
+ | // Update the entire snake's position | ||
+ | for (int i = snakeSize - 1; i > 0; i--) { | ||
+ | snakeX[i] = snakeX[i - 1]; | ||
+ | snakeY[i] = snakeY[i - 1]; | ||
+ | } | ||
+ | |||
+ | // Move the head of the snake based on the direction | ||
+ | switch (direction) { | ||
+ | case 0: // Right | ||
+ | snakeX[0]++; | ||
+ | break; | ||
+ | case 1: // Down | ||
+ | snakeY[0]++; | ||
+ | break; | ||
+ | case 2: // Left | ||
+ | snakeX[0]--; | ||
+ | break; | ||
+ | case 3: // Up | ||
+ | snakeY[0]--; | ||
+ | break; | ||
+ | } | ||
+ | |||
+ | // Check for collisions with the screen boundaries | ||
+ | if (snakeX[0] >= SCREEN_WIDTH / 8) { | ||
+ | snakeX[0] = 0; | ||
+ | } else if (snakeX[0] < 0) { | ||
+ | snakeX[0] = SCREEN_WIDTH / 8 - 1; | ||
+ | } | ||
- | DrawSnake function | + | if (snakeY[0] >= SCREEN_HEIGHT / 8) { |
+ | snakeY[0] = 0; | ||
+ | } else if (snakeY[0] < 0) { | ||
+ | snakeY[0] = SCREEN_HEIGHT / 8 - 1; | ||
+ | } | ||
+ | for (int i = 0; i < snakeSize; i++) { | ||
+ | Serial.println("Before collision (" + String(snakeX[i]) + "," + String(snakeY[i]) + ")"); | ||
+ | } | ||
+ | // Check for collisions with the snake's body, excluding the head | ||
+ | for (int i = 1; i < snakeSize; i++) { | ||
+ | if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) { | ||
+ | // Snake head collided with its body, end the game | ||
+ | endGame(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | |||
+ | === DrawSnake function === | ||
Used to draw the snake on the screen. | Used to draw the snake on the screen. | ||
- | {{:iothings:proiecte:2023:drawSnake_function.png?200|}} | + | <code> |
+ | void drawSnake() { | ||
+ | // Clear the display | ||
+ | display.clearDisplay(); | ||
+ | |||
+ | // Draw the snake | ||
+ | for (int i = 0; i < snakeSize; i++) { | ||
+ | display.fillRect(snakeX[i] * 8, snakeY[i] * 8, 8, 8, SSD1306_WHITE); | ||
+ | } | ||
+ | |||
+ | // Draw the pixel | ||
+ | display.fillRect(pixelX * 8, pixelY * 8, 8, 8, SSD1306_WHITE); | ||
+ | |||
+ | // Display the buffer | ||
+ | display.display(); | ||
+ | } | ||
+ | </code> | ||
- | checkJoystick function | + | === checkJoystick function === |
I used this function to check the position of the joystick so I can give the snake the direction that I want it to move to. In this code I also made impossible for the snake to go in the opposite direction of the current one | I used this function to check the position of the joystick so I can give the snake the direction that I want it to move to. In this code I also made impossible for the snake to go in the opposite direction of the current one | ||
- | {{:iothings:proiecte:2023:checkJoystick_function.png?200|}} | + | <code> |
+ | void checkJoystick() { | ||
+ | int xValue = analogRead(JOYSTICK_X); | ||
+ | int yValue = analogRead(JOYSTICK_Y); | ||
+ | int OK = 0; | ||
+ | |||
+ | if (xValue < JOYSTICK_THRESHOLD && lastXValue <= JOYSTICK_THRESHOLD) { | ||
+ | // Move right | ||
+ | if (direction != 0 && direction != 2) { | ||
+ | direction = 0; | ||
+ | OK = 1; | ||
+ | } | ||
+ | } else if (xValue > JOYSTICK_THRESHOLD2 && lastXValue >= -JOYSTICK_THRESHOLD) { | ||
+ | // Move left | ||
+ | if (direction != 2 && direction != 0) { | ||
+ | direction = 2; | ||
+ | OK = 1; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | if (yValue < JOYSTICK_THRESHOLD && lastYValue <= JOYSTICK_THRESHOLD && OK == 0) { | ||
+ | // Move up | ||
+ | if (direction != 3 && direction != 1) { | ||
+ | direction = 3; | ||
+ | } | ||
+ | } else if (yValue > JOYSTICK_THRESHOLD2 && lastYValue >= -JOYSTICK_THRESHOLD && OK == 0) { | ||
+ | // Move down | ||
+ | if (direction != 1 && direction != 3) { | ||
+ | direction = 1; | ||
+ | |||
+ | } | ||
+ | } | ||
+ | |||
+ | lastXValue = xValue; | ||
+ | lastYValue = yValue; | ||
+ | } | ||
+ | </code> | ||
- | Spawnpixel function | + | === Spawnpixel function === |
I spawn the food of the snake as a pixel random on the screen. | I spawn the food of the snake as a pixel random on the screen. | ||
- | {{:iothings:proiecte:2023:Spawnpixel_function.png?200|}} | + | <code> |
+ | void spawnPixel() { | ||
+ | // Generate random coordinates for the pixel | ||
+ | pixelX = random(SCREEN_WIDTH / 8); | ||
+ | pixelY = random(SCREEN_HEIGHT / 8); | ||
+ | |||
+ | // Ensure the pixel does not spawn on the snake | ||
+ | for (int i = 0; i < snakeSize; i++) { | ||
+ | while (pixelX == snakeX[i] && pixelY == snakeY[i]) { | ||
+ | pixelX = random(SCREEN_WIDTH / 8); | ||
+ | pixelY = random(SCREEN_HEIGHT / 8); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Draw the pixel on the display | ||
+ | display.fillRect(pixelX * 8, pixelY * 8, 8, 8, SSD1306_WHITE); | ||
+ | display.display(); | ||
+ | } | ||
+ | </code> | ||
- | Endgame function | + | === Endgame function === |
Print on the screen the GAME OVER message if the snake hit himself. | Print on the screen the GAME OVER message if the snake hit himself. | ||
- | {{:iothings:proiecte:2023:Endgame_function.png?200|}} | + | <code> |
+ | void endGame() { | ||
+ | // Add your game-over logic here | ||
+ | display.clearDisplay(); | ||
+ | display.setCursor(8,8); | ||
+ | display.print("GAME OVER!"); | ||
+ | display.display(); | ||
+ | delay(5000); // Display "You Lose" for 5 seconds | ||
+ | |||
+ | // Print snake coordinates for debugging | ||
+ | Serial.println("Snake Coordinates:"); | ||
+ | for (int i = 0; i < snakeSize; i++) { | ||
+ | Serial.println("(" + String(snakeX[i]) + "," + String(snakeY[i]) + ")"); | ||
+ | } | ||
+ | |||
+ | while (true) { | ||
+ | // Do nothing | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
- | Rest of the code: | + | === Rest of the code === |
Here I declared the screen and the pins that I used. I also declared 2 THRESHOLDS because when the joystick was on the middle, I constantly get values between 1850 and 1900 and I don’t want that those values to influent the movement. On the end I declared snake characteristics. | Here I declared the screen and the pins that I used. I also declared 2 THRESHOLDS because when the joystick was on the middle, I constantly get values between 1850 and 1900 and I don’t want that those values to influent the movement. On the end I declared snake characteristics. | ||
- | {{:iothings:proiecte:2023:rest_of_code.png?200|}} | + | <code> |
- | The library that I used are: | + | #define SCREEN_WIDTH 128 |
- | {{:iothings:proiecte:2023:library.png?200|}} | + | #define SCREEN_HEIGHT 32 |
- | + | ||
- | And mostly I used the for the LCD. | + | |
- | Conclusion | + | // Define the I2C address for the display |
- | As a conclusion for the project, I realized a snake game that can be controlled using a joystick. The purpose of the game is to eat as many food as possible and to not touch the rest of the body. | + | #define OLED_ADDR 0x3C |
+ | Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_ADDR); | ||
+ | #define JOYSTICK_X A5 | ||
+ | #define JOYSTICK_Y A6 | ||
+ | #define JOYSTICK_THRESHOLD 1800 | ||
+ | #define JOYSTICK_THRESHOLD2 2000 | ||
+ | int snakeSize = 3; | ||
+ | int snakeX[100], snakeY[100]; | ||
+ | int direction = 0; // 0: right, 1: down, 2: left, 3: up | ||
+ | </code> | ||
+ | The library that I used are: | ||
+ | <code> | ||
+ | #include <Wire.h> | ||
+ | #include <Adafruit_GFX.h> | ||
+ | #include <Adafruit_SSD1306.h> | ||
+ | </code> | ||
+ | |||
+ | And mostly I used the for the LCD. | ||
+ | |||
+ | ===== Conclusion ===== | ||
+ | As a conclusion for the project, I realized a snake game that can be controlled using a joystick. The purpose of the game is to eat as many food as possible and to not touch the rest of the body. | ||
- | Bibliography: | + | ===== Bibliography ===== |
- | https://www.optimusdigital.ro/ro/senzori-senzori-de-atingere/742-modul-joystick-ps2-biaxial-negru-cu-5-pini.html?search_query=joystick&results=42 | + | https://www.optimusdigital.ro/ro/senzori-senzori-de-atingere/742-modul-joystick-ps2-biaxial-negru-cu-5-pini.html?search_query=joystick&results=42 \\ |
- | https://cleste.ro/ecra-oled-0-96-inch.html | + | https://cleste.ro/ecra-oled-0-96-inch.html \\ |
- | Placă dezvoltare NodeMCU WIFI + Bluetooth ESP32 | Cleste.ro | + | https://cleste.ro/placa-dezvoltare-nodemcu-wifi-bluetooth-esp32.html \\ |
- | ESP32 Pinout Reference: Which GPIO pins should you use? | Random Nerd Tutorials | + | https://randomnerdtutorials.com/esp32-pinout-reference-gpios/ \\ |
https://esp32io.com/tutorials/esp32-joystick | https://esp32io.com/tutorials/esp32-joystick | ||