This is an old revision of the document!


Snake Game

Autor: Grosu Gheorghe

Grupa: 334CD

Introducere

Joc de tip Snake interactiv și captivant. Jocul este afișat pe un ecran LCD și oferă mai multe niveluri și viteze, crescând astfel complexitatea și atractivitatea jocului. Jucătorul își controlează șarpele cu ajutorul unui joystick. Scopul jocului este de a obține cel mai mare scor posibil fără ca șarpele să se ciocnească de el însuși sau de obstacolele de pe ecran.

Scorul poate fi vizualizat fie în timpul jocului, fie la final, oferind astfel jucătorului un feedback asupra performanței sale. Proiectul include și un buzzer care emite diferite sunete în timpul jocului.

Am fost un mare fan al acestui joc pe telefoanele vechi (Nokia :)) si mi s-a parut interesant cum as putea implementez eu acest lucru pe un microcontroler.

Utilitatea proiectului vine in primul rand din faptul ca ma va ajuta sa inteleg tot procesul de planificare, arhitectura si legare a partii de software cu cea de hardware.

Descriere generală

Proiectul este un joc implementat pe un dispozitiv care folosește un display pentru afișarea jocului, un buzzer pentru redarea sunetelor și un joystick pentru controlul personajului. Scopul jocului este să eviți coliziunile și să obții cât mai multe puncte. Atunci când se produce o coliziune, scorul este afișat. Jucătorul poate ajusta nivelul de dificultate, inițial setat pe modul “Easy”(nivelul 1), folosind un buton(posibil tot cel din joystick). La apăsarea butonului, viteza de deplasare a personajului crește, harta se schimbă și culoarea LED-ului se modifică pentru a indica nivelul de dificultate actual sau pentru diferite efecte din timpul jocului.

O schemă bloc cu toate modulele proiectului vostru, atât software cât şi hardware însoţită de o descriere a acestora precum şi a modului în care interacţionează.

Exemplu de schemă bloc: http://www.robs-projects.com/mp3proj/newplayer.html

Hardware Design

Lista de piese:

  • Placă de Dezvoltare Compatibilă cu Arduino Nano (ATmega328p)
  • Modul LCD SPI de 1.8'' (128×160)
  • Modul Joystick Biaxial
  • Breadboard HQ
  • Fire (Mama-Tata, Tata-Tata)
  • Buzzer Pasiv
  • LED
  • Rezistente

Schema hardware

Schema electrica

Software Design

Descrierea codului aplicaţiei (firmware)

1. Mediu de dezvoltare: - Platformă de dezvoltare: Arduino IDE

  1. Arduino IDE este utilizat pentru scrierea, compilarea și încărcarea firmware-ului pe un microcontroller Arduino.

2. Librării şi surse 3rd-party:

  • TFT Library: `Adafruit_ST7735`

- Această librărie este folosită pentru a controla ecranul TFT.

  • SPI Library: `SPI`

- SPI (Serial Peripheral Interface) este utilizat pentru comunicația cu ecranul TFT.

  • EEPROM Library: `EEPROM`

- Această librărie permite stocarea persistentă a datelor, cum ar fi scorurile maxime.

  • Tone Library: `tone()`

- Functia `tone()` este utilizată pentru a genera sunete pe pinul difuzorului.

#### 3. Algoritmi şi structuri implementate: - Structuri de date:

  1. `Joystick`: Monitorizează direcția joystick-ului pentru controlul șarpelui.
  2. `GameState`: Gestionează stările jocului și timpul de cadru pentru fiecare stare.
  3. `Snake`: Păstrează lungimea și pozițiile șarpelui.

- Algoritmi:

  1. Controlul jocului: Monitorizează intrările de la joystick pentru a controla direcția șarpelui și pentru a trece între diferitele stări ale jocului.
  2. Detectarea coliziunilor: Verifică coliziunile șarpelui cu obstacolele, pereții și corpul propriu.
  3. Generarea aleatorie a poziției merelor: Utilizează funcția `random()` pentru a genera poziții aleatorii pentru mere.

#### 4. Surse şi funcţii implementate: - Setup-ul inițial:

```cpp
void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(5));
  pinMode(SW, INPUT_PULLUP);
  pinMode(PIN_RED, OUTPUT);
  pinMode(PIN_GREEN, OUTPUT);
  tft.initR(INITR_BLACKTAB);
  level_select_init();
  last_frame_time = millis();
  curr_note_start_time = 0;
}
```
- Initializează serialul pentru debugging.
- Configurează pinurile pentru joystick, LED-uri și difuzor.
- Inițializează ecranul TFT și structurile de date.

- Starea de selecție a nivelului:

```cpp
void state_level_select() {
  if (joystick.dir_y == 1) {
    selected_level = (selected_level + 1) % 3;
  } else if (joystick.dir_y == -1) {
    selected_level = (selected_level - 1);
    if (selected_level < 0) selected_level = 2;
  }
  tft.fillRect(64, 40, 20, 60, ST7735_BLACK);
  tft.fillTriangle(80, 44 + 18 * selected_level, 80, 60 + 18 * selected_level, 64, 52 + 18 * selected_level, ST7735_WHITE);
  if (digitalRead(SW) == LOW) {
    gameState.transition_to(LEVEL_INIT, LEVEL_INIT_FRAME_TIME);
  }
}
```
- Permite utilizatorului să selecteze nivelul dorit utilizând joystick-ul și butonul.

- Inițializarea nivelului:

```cpp
void state_level_init() {
  tft.fillScreen(ST7735_BLACK);
  score = 0;
  max_score = 100;
  snake.reset();
  empty_spaces = N_TILES * M_TILES - snake.length;
  memset(tiles, 0, sizeof(tiles));
  if (selected_level >= 1) {
    for (int i = 0; i < N_TILES; i++) {
      renderIfDifferent(i, OBSTACLE);
      renderIfDifferent(N_TILES * M_TILES - 1 - i, OBSTACLE);
      renderIfDifferent(i * M_TILES, OBSTACLE);
      renderIfDifferent(i * M_TILES + M_TILES - 1, OBSTACLE);
    }
    empty_spaces -= N_TILES * 4;
  }
  if (selected_level == 2) {
    for (int i = 0; i < sizeof(level3_obstacles); i++) {
      int col = level3_obstacles[i] % M_TILES; 
      int row = level3_obstacles[i] / N_TILES;
      for (int j = -1; j <= 1; j++) {
        renderIfDifferent((row + j) * M_TILES + col, OBSTACLE);
      }
      for (int k = -1; k <= 1; k++) {
        renderIfDifferent(row * M_TILES + col + k, OBSTACLE);
      }
      empty_spaces -= 3;
    }
  }
  if (random(0, 10) == 0) {
    renderIfDifferent(get_random_pos(), RED_APPLE);
  } else {
    renderIfDifferent(get_random_pos(), APPLE);
  }
  renderIfDifferent(snake.positions[snake.length - 1], SNAKE_HEAD);
  for (int i = 0; i < snake.length - 1; i++) {
    renderIfDifferent(snake.positions[i], SNAKE_BODY);
  }
  eeprom_position = selected_level * 4;
  EEPROM.get(eeprom_position, high_score);
  gameState.transition_to(LEVEL_RUNNING, LEVEL_RUNNING_FRAME_TIME);
}
```
- Configurează elementele grafică și obstacolele pentru nivelul selectat.
- Inițializează pozițiile șarpelui și merele pe ecran.

- Rularea nivelului:

```cpp
void state_level_running() {
  int new_head_x = joystick.next_x(get_x(snake.positions[snake.length - 1]));
  int new_head_y = joystick.next_y(get_y(snake.positions[snake.length - 1]));
  byte next_pos = new_head_y * M_TILES + new_head_x;
  if (snake.has_collided_with_self(next_pos)) {
    gameState.transition_to(GAME_OVER, GAME_OVER_FRAME_TIME);
    return;
  }
  if (score != max_score) {
    score += 10;
    if (score < 500) {
      analogWrite(PIN_GREEN, score / 4);
    }
  }
  if (tiles[next_pos] == APPLE || tiles[next_pos] == RED_APPLE) {
    renderIfDifferent(snake.positions[snake.length - 1], SNAKE_BODY);
    snake.grow(next_pos);
    renderIfDifferent(snake.positions[snake.length - 1], SNAKE_HEAD);
    empty_spaces--;
    renderIfDifferent(get_random_pos(), APPLE);
    if (tiles[next_pos] == RED_APPLE) {
      max_score += 100;
    } else {
      max_score += 50;
    }
    tone(SPEAKER_PIN, APPLE_EAT_TONE, APPLE_EAT_DURATION);
    return;
  }
  if (tiles[next_pos] == OBSTACLE) {
    gameState.transition_to(GAME_OVER, GAME_OVER_FRAME_TIME);
  }
  renderIfDifferent(snake.positions[0], GRASS);
  renderIfDifferent(snake.positions[snake.length - 1], SNAKE_BODY);
  snake.move(next_pos);
  renderIfDifferent(snake.positions[snake.length - 1], SNAKE_HEAD);
}
```
- Monitorizează și actualizează pozițiile șarpelui în funcție de intrările de la joystick.
- Verifică coliziunile și actualizează scorul în funcție de interacțiunea cu merele.

- Finalul jocului:

```cpp
void state_game_over() {
  if (score > high_score) {
    high_score = score;
    EEPROM.put(eeprom_position, high_score);
  }
  digitalWrite(PIN_RED, HIGH);
  tft.fillScreen(ST7735_RED);
  tft.setCursor(15, 60);
  tft.setTextSize(2);
  tft.println("YOU LOST");
  tft.setTextSize(1);
  tft.setCursor(10, 90);
  tft.println("Your points " + String(score));
  tft.setCursor(10, 110);
  tft.println("Best score " + String(high_score));
}
```
- Afișează mesajul de finalizare a jocului și scorurile.
- Salvează scorurile maxime în EEPROM pentru a le menține între sesiunile de joc.

- Ciclu principal:

```cpp
void loop() {
  if (gameState.should_update()) {
    gameState.update_time();
    joystick.set_dirs();
    switch (gameState.state) {
      case LEVEL_SELECT: state_level_select(); break;
      case LEVEL_INIT: state_level_init(); break;
      case LEVEL_RUNNING: state_level_running(); break;
      case GAME_OVER: state_game_over(); break;
    }
  }
}
```
- Actualizează starea jocului și procesează intrările utilizatorului și logica jocului în funcție de timpul de cadru.

### Concluzie Acest design modular

Rezultate Obţinute

Care au fost rezultatele obţinute în urma realizării proiectului vostru.

Concluzii

Download

O arhivă (sau mai multe dacă este cazul) cu fişierele obţinute în urma realizării proiectului: surse, scheme, etc. Un fişier README, un ChangeLog, un script de compilare şi copiere automată pe uC crează întotdeauna o impresie bună ;-).

Fişierele se încarcă pe wiki folosind facilitatea Add Images or other files. Namespace-ul în care se încarcă fişierele este de tipul :pm:prj20??:c? sau :pm:prj20??:c?:nume_student (dacă este cazul). Exemplu: Dumitru Alin, 331CC → :pm:prj2009:cc:dumitru_alin.

Jurnal

  • 28/04/2024 - Alegerea temei proiectului si descrierea sumara
  • 29/04/2024 - Comandarea componentelor hardware
  • 02/05/2024 - Crearea paginii proiectului si completarea partiala a acesteia
  • 12/05/2024 - Adaugarea schemei electrice si a schemei hardware

Bibliografie/Resurse

Listă cu documente, datasheet-uri, resurse Internet folosite, eventual grupate pe Resurse Software şi Resurse Hardware.

https://randomnerdtutorials.com/guide-to-1-8-tft-display-with-arduino/

Export to PDF

pm/prj2024/vstoica/gheorghe.grosu.1716323698.txt.gz · Last modified: 2024/05/21 23:34 by gheorghe.grosu
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