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 | ||