Table of Contents

Student: Andrei Petrea
Grupa: 331CC

PacMan

Introducere

PacMan este un joc arcade dezvoltat de compania Namco in anul 1980, fiind considerat unul dintre cele mai influente jocuri din toate timpurile, fiind responsabil pentru popularizarea jocurilor arcade si a jocurilor video in constientul maselor, generand venituri de 14 miliarde de dolari si vanzand peste 43 de milioane de unitati.

Scopul proiectului meu este sa portez acest joc, de pe arcade pe un chip Arduino, pentru a ma putea juca acest joc intr-un mod cat mai autentic cu masina originala cu care venea odinioara. Ma voi folosi de butoane, ecran lcd, difuzor si led-uri pentru a realiza acest lucru.

Descriere generală

Voi folosi un Arduino Uno pe post de unitate de procesare, la care voi atasa un ecran de 1.8” LCD pentru a afisa jocul, un speaker pentru a canta melodii si 5 butoane pentru a controla cele 4 directii (sus, jos, stanga, dreapta) + buton de start. Ecranul vine si cu un adaptor SD prin intermediul caruia care voi afisa imagine de start. La final ma voi folosi de un LED RGB pentru a afisa finalul (RED - pierdere, BLUE - castig).

Hardware Design

Lista de piese

Schema electrica

Pinii folositi

Forma Initiala

pacman_initial_design.jpeg

Forma Finala

pacman_schema_finala.jpeg

Software Design

Mediu de dezvoltare

Arduino IDE, limbaj C/C++

Biblioteci folosite

PacMan

Descrierea implementarii

Structura unui obiect

Pentru a opera mai usor cu logica jocului, mi-am creat structura PacmanObject, care contine 4 campuri

// Symbols depending on object type
#define PACMAN "C"
#define FOOD "*"
#define ENEMY "#"
 
struct PacmanObject {
  int16_t x;
  int16_t y;
  bool draw;
  String symbol;
};
Start-up screen

La inceput, afisez o imagine sugestiva cu PacMan, acompaniata de muzica clasica si aprinderea led-ului in culoarea Verde. In cazul in care apar erori la incarcarea imaginii, voi afisa un mesaj pe ecran.

void intro() {
  if (!imageError) {
    TFTscreen.image(logo, 20, 25);
  } else {
    TFTscreen.text("Va saluta", 40, 64);
    TFTscreen.text("Andrei", 64, 90);
  }
  analogWrite(PIN_GREEN, 64);
  playPacmanIntro();
  TFTscreen.text("Press START", 18, 85);
}
Game Loop

Ca sa inceapa jocul, jucatorul trebuie sa apese butonul de START (butonul din centru), care va crea o noua arena, plasand PacMan-ul in centru ei, si plasand 4 inamici si 7 puncte aleatoriu pe harta.

void loop()
{
  int value = analogRead(A0);
  if (!gameStarted) {
    if (checkRange(value, 260, 330)) {
      gameStarted = true;
      gameOver = false;
      ateAll = false;
      score = 0;
      initBoard();
      analogWrite(PIN_RED, 0);
      analogWrite(PIN_BLUE, 0);
      analogWrite(PIN_GREEN, 64);
    }
  }
  // Rest of code...
}

Pentru realizarea randomizarii pozitiilor, am folosit functiile rand si srand din biblioteca standard C, seed-ul pe care l-am ales fiind obtinut prin citirea unui pin analogic neconectat (in cazul meu, A1).

void setup()
{
  // Rest of code...
  srand(analogRead(A1));
}

Odata intrat in joc, ne putem deplasa UP, DOWN, LEFT si RIGHT prin apasarea celor 4 butoane, dispuse conform celor 4 puncte cardinale. In functie de care buton a fost apasat, voi apela functia move, care are ca argument, deplasarea pe care i-o voi da PacMan-ului.

void loop()
{
   // Rest of code...
   if (!gameOver) {
      draw();
      if (checkRange(value, 120, 170)) {
        move(0, -1);
      } else if (checkRange(value, 190, 230)) {
        move(-1, 0);
      } else if (checkRange(value, 420, 500)) {
        move(1, 0);
      } else if (checkRange(value, 900, 1024)) {
        move(0, 1);
      }
    } 
      // Rest of code...
}

In functia move, voi muta PacMan-ul la pozitia noua, si voi calcula folosind functia checkCollision, daca ma intersectez cu obiectul de tip FOOD sau cu ENEMY. Ma voi folosi si de functia checkRange, pentru a oferi toleranta la detectia coliziunii.

bool checkCollision(PacmanObject obj1, PacmanObject obj2) {
  return (checkRange(obj1.x, obj2.x - 2, obj2.x + 2) && checkRange(obj1.y, obj2.y - 2, obj2.y + 2) && checkRange(obj2.x, obj1.x - 2, obj1.x + 2) && checkRange(obj2.y, obj1.y - 2, obj1.y + 2));
}
 
bool checkRange(int val, int low, int high) {
  return (val >= low && val <= high);
}

Inedit, pentru inamici, am folosit un algoritm de tip Hill-Climbing, care este apelat atunci cand jucatorul face o mutare, facand ca acestia sa se deplaseze catre PacMan. Fiecare inamic se va uita la starile sale vecine (UP, DOWN, LEFT, RIGHT in ordinea aceasta) si va alege prima stare mai apropiata decat pozitia sa curenta, folosind distanta Manhattan ca euristica.

void move(int16_t dx, int16_t dy)
{
   // Rest of code...
   for (i = 0; i < NR_ENEMIES; i++) {
    int8_t j, k;
    int16_t currDistance = manhattanDistance(pacMan, enemies[i]);
    PacmanObject aux;
    for (j = -1; j <= 1; j++) {
      bool ok = true;
      for (k = -1; k <= 1; k++) {
        if (abs(j) != abs(k)) {
          aux.x = enemies[i].x + j;
          aux.y = enemies[i].y + k;
          int16_t distance = manhattanDistance(pacMan, aux);
          if (distance < currDistance) {
            enemies[i].x = aux.x;
            enemies[i].y = aux.y;
            ok = false;
            break;
          }
        }
      }
      if (!ok) {
        break;
      }
    }
  }
}
}
 
int16_t manhattanDistance(PacmanObject obj1, PacmanObject obj2) {
  return (abs(obj2.x - obj1.x) + abs(obj2.y - obj1.y));
}
Final screen

La final, jucatorul ori a colectat toate punctele ori a fost ucis de catre inamic. In functie de cele 2 urmari, se va afisa la ecran un mesaj corespunzator, se va schimba culoarea led-ului intr-una care reflecta rezultatul final si se va pune la speaker o melodie aferenta.

void loop()
{
  // Rest of code...
      TFTscreen.fillScreen(TFT_BLACK);
      char scoreSir[11];
      sprintf(scoreSir, "Scor: %d\n", score);
      if (ateAll) {
        TFTscreen.text("Ati castigat! :)", 15, 50);
        TFTscreen.text(scoreSir, 40, 70);
        analogWrite(PIN_GREEN, 0);
        analogWrite(PIN_BLUE, 64);
        playPacmanIntro();
      } else {
        TFTscreen.text("Ati pierdut! :(", 15, 50);
        TFTscreen.text(scoreSir, 40, 70);
        analogWrite(PIN_GREEN, 0);
        analogWrite(PIN_RED, 64);
        playFailedSong();
      }
      gameStarted = false;
   // Rest of code...
}

Concluzii

Rezultate Obţinute

Jurnal

Bibliografie/Resurse

Resurse Hardware

Resurse Software

Download

Download archive

Export to PDF