Table of Contents

Joc Pac-Man

Autor: Azamfirei Eduard-Mihail
Grupa: 335 CA

Introducere

Proiectul are ca scop implementarea unui joc de Pac-Man.
Jucătorul se va putea deplasa pe hartă cu scopul de a aduna puncte pentru a crește scorul pentru a acumula toate punctele pana la terminarea timpului.
Acesta, după acumularea unui anumit punctaj va putea activa pentru o perioadă o abilitate de invincibilitate(se va opri timer-ul).
După 3000 de puncte timer-ul se va reseta cu o secundă.
Motivația din spatele alegerii proiectului este recrearea jocului atât de cunoscut într-o manieră personală,
astfel valorificând cunoștințele acumulate despre Arduino.
Consider că proiectul este util, fiind o modalitate ușoară de relaxare.

Descriere generală

Schema bloc:

Funcționalitate:

Jucătorul va putea porni jocul cu ajutorul butonului. Butonul va folosi întreruperi externe.
Întregul joc va fi afișat pe un ecran LCD(ce foloșeste SPI).
În urma pornirii jocului, jucătorul poate manevra personajul cu ajutorul joystick-ului fie pe linie
sau coloană. Acesta se va putea plimba pe o planșă în formă de labirint pentru
a colecta cât mai multe puncte pentru a crește scorul.
Jucătorul va porni cu o singură viață si la diferite limite de punctaj acesta va mai putea primii încă
una. Acestea vor fi simbolizate cu ajutorul a 2 led-uri RGB(led-ul este verde-jucătorul are viața
respectivă, roșu-nu). Dacă timer-ul ajunge la zero, jucătorul pierde o viață.
Va mai exista un ecran mai mic LCD care va afișa scorul curent și timpul rămas și un bec albastru ce va simboliza
o putere de invincibilate care va opri timer-ul pentru o perioadă de timp, care poate fi obținută după atingerea
unui punctaj și activată tot prin apăsarea butonului.
De asemenea, se va folosi un buzzer ce va produce diferite sunete la interacțiunea jucătorului cu
mediul înconjurător.

Hardware Design

Lista de piese:

Schema electrică:

Conexiuni propriu-zise:

Pentru conectarea ecranului ILI9341 prin SPI ,acesta a fost conectat cu rezistențe de 10 KOhm la pinii digitali(pentru a nu se arde).
Ecranul LCD 1602 a fost conectat prin I2C la pinii specifici SCL si SDA, iar joystick-ul are pinii pentru cele 2 axe (Ox si Oy)
conectați la pinii analogici A2 și A3.
De asemenea, LED-urile RGB cât și LED-ul albastru au fost conectate cu rezistențe de 2 KOhm la pinii digitali.
Buzzer-ul a fost conectat direct la pinul digital deoarece sunetul nu era cel dorit.
Butonul(switch-ul) folosește întreruperi externe și a fost conectat la pin-ul digital 2(INT0).

Software Design

Proiectul a fost realizat cu ajutorul aplicației Arduino, iar bibliotecile folosite sunt LiquidCrystal_I2C.h pentru afișarea scorului și a timpului rămas pe ecranul LCD 1602, SPI.h, Adafruit_GFX.h și Adafruit_ILI9341.h pentru realizarea comunicării cu ecranul LCD ILI9341 și pentru desenarea, actulizarea și ștergerea tuturor obiectelor și formelor(ce constituie jocul propriu-zis) pe acesta.

La nivel global au fost salvate poziția jucătorului (pe Ox și Oy), coordonatele și dimensiunile obstacolelor(zidurilor).
De asemenea, se va salva un array is_drawn pentru punctele de colectat, ce va spune dacă punctul respectiv a fost colectat sau nu.
Se vor salva și alte variabile la nivel global, acestea trebuind să fie independente de iterația funcției loop() (scor, număr de vieți, puncte rămase de colectat, counter pentru timpul rămas, counter pentru abilitatea specială și dacă jocul s-a terminat cu victorie/înfrângere sau a început).
Se vor declara global și cele 2 ecrane ILI9341 și LCD 1602.

Funcția setColorRGB() aprinde LED-urile RGB în funcție de numărul de vieți ale jucătorului. Astfel, un LED roșu simbolizează că jucătorul nu are viața respectivă iar un LED aprins verde simbolizează că jucătorul are viața respectivă. Jucătorul are 2 vieți maxim și schimbarea culorilor se realizează cu funcția analogWrite() pe pinii asociați.

Funcția move_character() este funcția de mișcare a caracterului în care acesta este poziționat la noile coordoanate date de joystick și care verifică coliziunea cu obiectele din jur și cu punctele de acumulat.
Se vor declara mai întai coordonatele pixelilor modelului jucătorului (relative la poziția curentă) și poziția pixelilor ce trebuie ștersi dupa fiecare tip de deplasare. La orice deplasare se vor sterge treptat pixeli poziției anterioare.
Se vor citi coordonatele axelor x si y ale joystick-ului cu funcția analogRead() ce se vor delimita în 4 cazuri: mișcare în sus, in jos, la stânga sau la dreapta.
În funcție de direcția aleasă, se va verifica coliziunea cu toate obstacolele și daca acestea nu sunt valide, nu vor fi acceptate. Apoi se vor verifica coliziunile cu toate punctele de acumulat.
În cazul intersectării cu unul dintre acestea, se va crește scorul, se va șterge punctul și se va transmite un sunet cu ajutorul buzzer-ului prin funcția tone().
În cele din urmă se vor desena pixeli caracterului și se vor șterge pixelii ultimei poziții înainte de deplasare.

În funcția draw_map() vor fi desenate toate zidurile/obstacolele cu ajutorul funcției de bibliotecă drawRoundRect().

În funcția setup() se vor seta pini pentru switch, led de abilitate.
De asemenea, se vor inițializare cele 2 ecrane, se va apela funcția draw_map() pentru desenarea hărții/labirintului și se vor desena punctele de acumulat.

Întreruperea intrerupt() este handler-ului întreruperii externe asociate butonului.
Prin apăsarea butonului se va porni jocul și se va putea activa abilitatea ce oprește pentru o perioadă timpul ce trebuie învins.

În funcția loop() este implementata logica jocului privind scaderea/creșterea timpului jocului, a finalului jocului (victorie/înfrângere) dar și logica folosirii abilității speciale.
Daca jucătorul a acumulat toate punctele de pe hartă, acesta va fi felicitat cu o mică melodie, i se va afișa scorul (la care se adaugă ca bonus viața în plus și timpul rămas) și un mesaj.
În caz că timpul a ajuns la 0 și jucătorul nu mai are nicio viața, procedura este identică dar opusă.
La un anumit număr de iterații timpul va mai scădea cu o unitate.
O data la 3000 de puncte jucătorul mai câstigă o unitate de timp.
La fiecare 5000 de puncte jucătorul poate folosi abilitatea de oprire a timpului marcată prin LED-ul albastru.
În timpul utilizării, LED-ul albastru se va aprinde si se va stinge.
O data la 7000 de puncte jucătorul primeste încă o viață (maxim 2).
Tot aici va fi desenat și personajul la fiecare iterație a funcției loop().

Rezultate obținute

Îmbinând partea hardware cu cea software am reușit implementarea unui joc Pac-Man cu regulile puțin schimbate
dar care în același timp să aducă un mic omagiu jocurilor retro.
La link-ul următor se găsește un demo al jocului cu o victorie și cu o înfrângere evidențiind toate aspectele acestuia.

Link demo joc Pac-Man

Momemente relevante:

Concluzii

În decursul realizării proiectului prinicipala amenințare se anunța a fi memoria restrânsă a plăcuței (2048 de bytes de memorie dinamică).
În cele din urmă această temere s-a adeverit așa că am fost nevoit să schimb anumite aspecte ale jocului.
Am eliminiat existența fantomelor care trebuiau să urmarească jucătorul și le-am înlocuit cu un counter de timp care scade cu câte o unitate de timp.
De asemenea, ecranul ILI9341 prezintă o limitare accentuată privind fluxul de date transmis, astfel fiind nevoit să găsesc o metodă pentru a ascunde această limitare.
În final, mă declar mulțumit de rezultatul proiectului, jocul creat fiind o realizare ce îmi oferă un sentiment de satisfacție după întregul efort depus.

Download

cod sursa

Jurnal

25 aprilie 2021: alegere temă proiect, completare introducere, descriere generală, listă piese
30 mai 2021: realizare cod proiect final
31 mai 2021: completare schemă electrică și hardware design
1 iunie 2021: completare software design, realizare video și poze proiect, completare concluzie, download și rezultate obținute

Bibliografie/Resurse

joc_pac-man.pdf
Biblioteca ecran LCD 1602
Biblioteca Adafruit
Biblioteca ecran ILI9341
Ghid utilizare ecran ILI9341