Snake

  • Author: Georgescu Eugen-Robert
  • Email: eugen.georgescu2202@stud.mec.upb.ro
  • Master: AAC
  • Academic year: 2023-2024
  • Source files: snake.rar

Project Description

The purpose of this project is to be able to play a snake game with a joystick. The game is displayed on the JDMO.96A-1 display and can be played using a joystick to set the direction in which the snake will move. If the snake eats himself a “GAME OVER” text will be displayed, otherwise, the snake grows after each food that it ate.

Hardware description

I used the following components for this project:

  • WEMOS LOLIN32 based on the ESP32 microprocessor: a low-power system with integrated Wi-Fi and dual-mode Bluetooth;
  • 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
Pinout scheme of the ESP32 board:

pinout_scheme.jpeg

Diagram:

Real life:

real_life_view.jpeg

Function used on this project

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.

void setup() {
  Serial.begin(115200);

  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();
}

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.

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);
  }
}

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.

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

  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();
    }
  }
}

DrawSnake function

Used to draw the snake on the screen.

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();
}

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

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

Spawnpixel function

I spawn the food of the snake as a pixel random on the screen.

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();
}

Endgame function

Print on the screen the GAME OVER message if the snake hit himself.

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

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.

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32

// Define the I2C address for the display
#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

The library that I used are:

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

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

iothings/proiecte/2023/snake_game.txt · Last modified: 2024/01/17 23:27 by eugen.georgescu2202
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0