„Cărămidă” cu jocuri stil brick 9999 games, cu
Propun implemenarea unui nucleu scris în C++, ce va apela fișierele de joc - implementate într-un limbaj dinamic ușor de înglobat în partea compilată (JavaScript), implementând un API abstractizat pentru logica jocurilor a.î. logica jocului să nu se ocupe direct de citirea de registre și alte astfel de detalii.
Elemente folosite:
Piese folosite:
Schemă electrică:
Codul este disponibil la https://github.com/ALEX11BR/proiect-pm/tree/main/cod-rpipico.
Pentru dezvoltarea codului am folosit Visual Studio Code cu extensia PlatformIO.
Ca biblioteci terțe folosite avem:
Las mai jos argumentele pentru folosirea bibliotecilor folosite:
Bodmer/TFT_eSPI
este exact ce căutam.Bodmer/TFT_eSPI
folosește API-urile Arduino, așa că se impunea ca proiectul meu să le folosească, prin intermediul implementării pentru Pi Pico - Arduino-PicoanalogWrite
sau ceva de genul), până când am încercat platforma lui maxgerhardt și m-am convins că ea e de folosit cănd folosim PlatformIO pe Raspberry Pi Pico..c
și cele .h
în directorul lib
al codului sursă); într-adevăr, pentru JavaScript - o versiune mai veche a limbajului fără anumite caracteristici moderne cu care eram obișnuit din lucrul meu anterior cu JavaScript (let
, arrow functions), dar pentru scopul proiectului este suficient..js
, care să fie incluse ulterior în codul C++ ca șiruri de caractere și încărcate în interpretor. Am găsit ca soluție pentru acest lucru funcționalitatea PlatformIO de imagini ale sistemului de fișiere LittleFS cu fișierele aflate în directorul data
, unde am pus fișierele .js
cu logica jocurilor. Un dezavantaj al acestei abordări este că încărcarea codului pe plăcuță se face în 2 pași (în funcție de ce parte de cod s-a actualizat):Upload
pentru încărcarea codului C++ compilatUpload filesystem
pentru încărcarea logicii JavaScript a jocurilorMeniul principal este, de asemenea implementat ca script JavaScript.
Jocurile au acces la 2 ecrane virtuale, cu celule indexate de la 0:
Ecranele au 3 culori:
Există un sistem de tick-uri care se execută după fiecare TICK_TIME
(500) milisecunde
Runtime-ul implementează următoarele funcții:
brickVibrate(intensity: number, duration: number)
- Motorul cu vibrații va vibra la intensitatea intensity
(număr între 0 și 255; 255 = maxim, 180 = valoare intermediară bună) timp de duration
milisecunde.brickMainDraw(x: number, y: number, color: number)
- desenează pe ecranul principal la celula specificată (linia y
, coloana x
) culoarea specificatăbrickSecondaryDraw(x: number, y: number, color: number)
- desenează pe ecranul principal la celula specificată (linia y
, coloana x
) culoarea specificatăbrickTickReset()
- nu se va mai executa tick-ul următor, se va executa peste TICK_TIME
milisecunde (dacă nu se mai apelează brickTickReset()
încă o dată până atunci)brickGameOver(x: number, y: number, noRestart: boolean)
- inițializează ecranul de game over cu „X”-ul în punctul specificat (linia y
, coloana x
) și vibrație maximă. După un timp se ve reporni jocul în același context (variabilele globale sunt păstrate, dar se va rula handleInit()
) dacă noRestart
e false
, altfel se va reseta consola la meniul principal.brickLoad(gamePath: string)
- încarcă jocul din calea specificată din sistemul de fișiere LittelFS încărcat pe plăcuță, resetând contextul JavaScript. Un API „intern”, nu e intenționată folosirea lui în jocuri, doar în meniul de start.Jocurile trebuie să implementeze următoarele funcții, ce se apelează în cardul unor evenimente relevante în joc:
handleInit()
- se apelează inițializarea jocului la pornire sau după o pierdere de viață prin brickGameOver(x, y, false)
. Aici se inițializează variabilele ce indică starea vieții curente. Inițializarea numărului de vieți sau alte chestii ce se păstrează de-a lungul vieților se face direct în corpul scriptului.handleTick()
- se apelează la fiecare tick - bun pentru aplicarea mișcărilor automate etc.handleAction()
- se apelează la apăsarea butonului acțiune (cel mai din dreapta).handleUp()
- se apelează la deplasarea în sus a joystick-ului.handleLeft()
- se apelează la deplasarea în stânga a joystick-ului.handleRight()
- se apelează la deplasarea în dreapta a joystick-ului.handleDown()
- se apelează la deplasarea în jos a joystick-ului.Aceste funcții nu trebuie să dureze mult, doar cât trebuie pentru a administra evenimentul în cauză. Pentru a încuraja bunele practici, există un watchdog ce nu poate fi resetat de codul Javascript, doar de alte timere interne, care să reseteze consola dacă JavaScriptul durează prea mult.
În cadrul laboratorului am prezentat o versiune cu componentele pe breadboard și cablate conform primei versiuni.
Deoarece după laborator mi-am dat seama că, pentru ca LCD-ul să funcționeze cu biblioteca folosită trebuia să pun pinii de SPI la modulul SPI0 al Raspberry Pi Pico, am rearanjat puțin breadboardul conform schemei finale ilustrate în schema electrică.
A fost o perioadă chinuitoare pentru mine, plină de burn-out, și nu am putut implementa toate jocurile ce le-aș fi dorit, dar am eliminat cam toate obstacolele în implementarea unor jocuri noi. Am reușit să îmi evoluez visul meu de a avea o clonă a acelor console Brick 9999 games cu jocuri implementabile într-un limbaj dinamic, venind cu un API complet pentru scrierea în limbaj dinamic a jocurilor; într-adevăr, optimizat pentru un mediu cu randare lentă a ecranului.
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.