SNAKE - GRA | ARDUINO UNO

Typ_projektu
Arduino
Zdjecie główne
gra snake na konsolce uno
Krótki opis projektu

Projekt implementuje klasyczną grę zręcznościową "Snake", znaną również jako "Wąż".

Gracz steruje wężem poruszającym się po dwuwymiarowej planszy. Celem gry jest zbieranie jedzenia, co wydłuża ciało węża i zwiększa jego prędkość.

Sterowanie odbywa się za pomocą przycisków podłączonych do Arduino, a całość wyświetlana jest na ekranie OLED. Projekt wykorzystuje bibliotekę Adafruit SSD1306 do graficznej obsługi ekranu.

Gra kończy się, gdy wąż zderzy się ze ścianą lub samym sobą.

 

Niezbędne elementy

1. Płytka Arduino UNO

2. Shield SIC Game Console

3. Komputer

4. Oprogramowanie do kodu arduino (VisualStudioCode lub Arduino Uno)

Opis projektu

Ekran początkowy

Początek działania programu obejmuje inicjalizację przycisków sterujących w trybie INPUT_PULLUP, uruchomienie wyświetlacza OLED oraz przygotowanie zmiennych gry, w tym ustawienie początkowej długości węża i jego pozycji startowej na środku planszy. Wąż domyślnie składa się z trzech segmentów ustawionych poziomo.

Na ekranie wyświetlany jest jego początkowy stan wraz z obramowaniem pola gry i aktualnym wynikiem. Gra automatycznie rozpoczyna się bez osobnego ekranu powitalnego, a żywność dla węża zostaje wylosowana w losowej, dostępnej pozycji.

 

Reguły gry

Rozgrywka polega na sterowaniu wężem poruszającym się po dwuwymiarowym polu gry. Gracz zmienia kierunek ruchu węża za pomocą przycisków kierunkowych. Wąż nie może zawrócić bezpośrednio w przeciwnym kierunku.

Wąż porusza się w stałych odstępach czasu, które skracają się w miarę zdobywania punktów — im dłuższy wąż, tym szybciej się porusza. Celem gry jest zbieranie jedzenia, które pojawia się w losowych miejscach. Każde zjedzenie jedzenia wydłuża ciało węża i zwiększa wynik gracza. Gra kończy się, gdy wąż uderzy w krawędź planszy lub sam siebie. Wtedy wyświetlany jest ekran „Game Over”.

 

Działanie ważnych funkcji

drawGame() – główna funkcja odpowiedzialna za odświeżanie ekranu. Wyświetla ramkę gry, wynik gracza, jedzenie oraz aktualną pozycję węża.

spawnFood() – generuje nowe położenie jedzenia w losowej lokalizacji, z uwzględnieniem tego, by nie nachodziło ono na ciało węża, drugie jedzenie lub napis z wynikiem.

resetGame() – resetuje stan gry do początkowego: ustawia startową długość i pozycję węża, kierunek ruchu, punktację, prędkość oraz generuje nowe jedzenie. Może wyświetlić odliczanie 3...2...1 w zależności od wywołania.

drawGameOver() – wyświetla ekran „Game Over” w przypadku kolizji węża ze ścianą lub samym sobą.

setupButtons() – ustawia piny przycisków jako wejścia z podciąganiem do plusa (INPUT_PULLUP), umożliwiając ich prawidłowe odczytanie w dalszej części programu.

 

Obsługa wyświetlacza

Wyświetlanie stanu gry realizowane jest przy pomocy biblioteki Adafruit SSD1306, która umożliwia komunikację z wyświetlaczem OLED poprzez interfejs I2C. Do wyświetlania elementów gry wykorzystywane są funkcje do czyszczenia ekranu, rysowania prostokątów (ciało węża, jedzenie), ramek (granice planszy) oraz drukowania tekstów (wynik, ekran „Game Over”, odliczanie przy restarcie).

 

Aktualizacja stanu gry

Stan gry aktualizowany jest w regularnych odstępach czasu zależnych od aktualnej prędkości węża, która maleje (gra przyspiesza) wraz ze wzrostem długości węża. Aktualizacja polega na przesunięciu węża o jedną komórkę w wybranym kierunku, sprawdzeniu kolizji ze ścianami, samym sobą oraz z jedzeniem. W przypadku zjedzenia jedzenia, wąż wydłuża się, zwiększa się wynik oraz przyspiesza rozgrywka. 6. Kontrola użytkownika Gracz steruje ruchem węża za pomocą czterech przycisków kierunkowych (góra, dół, lewo, prawo). Przycisk BTN2 pozwala na zatrzymanie i wznowienie gry (pauza), natomiast przycisk BTN1 resetuje grę i uruchamia ponownie z trzysekundowym odliczaniem. Dzięki temu gracz ma możliwość szybkiego restartu w przypadku porażki lub chęci zagrania ponownie.

Zdjęcia
kod programu
#include "snake_functions.h"

void gamePlay();

// ==================================================================
// ==================================================================
// MAIN -> including game mechanics
// ==================================================================

void setup() 
{
  Serial.begin(9600);
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) 
  {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
      delay(100);
  }

  display.setRotation(2);  // <- rotate screen 180 degrees here

  setupButtons();
  randomSeed(analogRead(0));
  resetGame(false);
}

void loop() 
{
  gamePlay();
}

// ==================================================================

// whole gameplay
void gamePlay()
{
  if (digitalRead(BTN_RESTART) == LOW) 
  {
    resetGame(true);
    delay(300);
    return;
  }

  if (digitalRead(BTN_PAUSE) == LOW && millis() - lastPauseTime > 500) // debounce the button press
  { 
    paused = !paused;  // Toggle the pause state
    lastPauseTime = millis();  // Record time of last button press
  }

  if (gameOver) 
  {
    drawGameOver();
    return;
  }

  if (paused) 
  {
    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor((SCREEN_WIDTH / 2) - 30, (SCREEN_HEIGHT / 2) - 8);
    display.print("PAUSED");
    display.display();
    return;  // Skip the rest of the game logic if paused
  }

  // sterowanie
  if (digitalRead(BTN_UP) == LOW && dirY != 1) 
  {
    dirX = 0; dirY = -1;
  }
  if (digitalRead(BTN_DOWN) == LOW && dirY != -1) 
  {
    dirX = 0; dirY = 1;
  }
  if (digitalRead(BTN_LEFT) == LOW && dirX != 1) 
  {
    dirX = -1; dirY = 0;
  }
  if (digitalRead(BTN_RIGHT) == LOW && dirX != -1) 
  {
    dirX = 1; dirY = 0;
  }

  unsigned long now = millis();
  if (now - lastMoveTime >= moveInterval) 
  {
    lastMoveTime = now;
    Point newHead = {snake[0].x + dirX, snake[0].y + dirY}; // moving vector

    // ------------------------------------------
    // COLLISIONS

    // wall collision
    if (newHead.x <= 0 || newHead.y <= 0 || newHead.x >= GRID_WIDTH - 1 || newHead.y >= GRID_HEIGHT - 1) 
    {
      gameOver = true;
      return;
    }

    // snake self collision
    for (int i = 0; i < snakeLength; i++) {
      if (snake[i].x == newHead.x && snake[i].y == newHead.y)
       {
        gameOver = true;
        return;
      }
    }

    // ------------------------------------------
    // MOVEMENT

    // snake move
    for (int i = snakeLength; i > 0; i--) 
    {
      snake[i] = snake[i - 1];
    }
    snake[0] = newHead;

    // food eating
    if ((newHead.x == food1.x && newHead.y == food1.y) || (newHead.x == food2.x && newHead.y == food2.y)) 
    {
      score++; // Increment score when food is eaten
      if (snakeLength < SNAKE_MAX_LENGTH) 
      {
        snakeLength += 3;
        moveInterval = max(40, moveInterval - 20);  // Speed up
      }
      spawnFood(); // Spawn new food
    } else if (snakeLength > SNAKE_MAX_LENGTH) // dont go over array bounds
    {
      snakeLength = SNAKE_MAX_LENGTH;
    }

    // draw all
    drawGame();
  }
}
Pliki_projektu
Youtube
Tagi
#arudinouno #snake #gra #kochammel #chatgpt