Snake Game
Autor
Nume: Olaru Alexandru
Grupa: 336CA
Introducere
Fiind pasionat de jocuri video și de console, m-am gândit să retrăiesc bucuria din copilărie atunci când mă jucam Snake pe diversele mini-console de la acea vreme. Așadar, proiectul reprezintă un joc Snake, ce incearcă să fie cât mai asemănător cu un joc pe o consolă portabilă.
Descriere generală
Schema bloc este compusă din urmatoarele componente: un Arduino Uno, o matrice de leduri 8×8, un potențiometru, un buzzer și un joystick.
Matricea de leduri stă in locul ecranului, pe care m-am gândit să îl înlocuiesc, oferindu-i jocului un aspect mai retro. Ledurile aprinse vor semnifica atât șarpele, cât și merele la care acesta va trebui să ajungă. Joystick-ul reprezintă singura modalitate de input a utilizatorului, prin care acesta poate mișca șarpele in 4 direcții: sus, jos, stânga și dreapta. Nu în ultimul rând, prin intermediul potențiometrului se poate regla viteza de joc, iar buzzer-ul este post de difuzor.
Hardware Design
Lista de piese folosite:
Arduino Uno
Breadboard
Modul joystick compatibil Arduino
Matrice 8×8 leduri OKY3523
Buzzer activ OKY0151
Potențiometru rotativ OKY0107
Schema electrica:
Matrice Led:
VCC → 5V
GND → GND
DIN → D12
CS → D11
CLK → D10
Modul Joystick:
GND → GND
VCC → 5V
VRX → A2
VRY → A3
SW → D2
Buzzer:
Potentiometer:
VCC → 5V
VOUT → A5
GND → GND
Software Design
Descrierea codului aplicaţiei:
Structura codului arată astfel:
void setup() {
initialize();
display_snake_message(message_snake);
}
void loop() {
/* Generate food on the display */
generate_food();
/* Scan joystick input and move the snake */
scan_input();
/* Snake movement */
move_snake();
/* Check and handle game over situation */
check_game_over();
}
Implementare:
Funcția initialize() inițializeaza variabilele ce se ocupă cu stările jocului, configureaza ADC(Analog-to-Digital Converter), ce va fi folosit pentru a citi inputul de la joystick, precum și matricea de led-uri.
Funcția display_snake_message(message_snake) va afișa un text dinamic atunci când ”consola” va fi pornită. Acest text va dispărea atunci când se înregistrează un input de la joystick (fie o mișcare a manetei, fie o apăsare a butonului acesteia).
Funcția generate_food() generează mâncarea șarpelui într-un loc random (unul din cele 64 led-uri de pe matrice), care este valid și care nu este ocupat de șarpe. Aceasta se va genera, schimbându-și locul, doar după ce șarpele o va mânca.
Funcția scan_input() va citi input-ul de la joystick, calculând direcția de deplasare a șarpelui. Aceasta se va ocupa și de viteza jocului, care poate fi setată cu ajutorul potențiometrului (cu cât viteza este mai mică, cu atât funcția se termină mai greu, dictând intervalul de timp în care se aprind și se sting led-urile).
Funcția move_snake() este cea care controlează locația în care se află șarpele, în funcție de direcția acestuia. Aceasta tratează cazurile în care șarpele iși mușcă propria coadă, mănâncă mărul, trece prin marginile ”display-ului” și apare din părțile opuse, și se ocupă de aprinderea corespunzătoare a led-urilor în funcție de poziția corpului șarpelui. Atunci când șarpele va mânca mărul, un sunet scurt va fi emis de către buzzer.
Funcția check_game_over() verifica dacă jocul este încheiat, caz în care va afișa un mesaj de ”GAME OVER”. Buzzer-ul va emite un sunet ceva mai lung, pe o tonalitate mai joasă ,iar variabilele de joc vor fi resetate, așteptând input de la joystick pentru ca jocul sa poată fi jucat din nou.
Citirea input-ului de la joystick (valorile X si Y) este făcută folosind ADC:
uint8_t adc_read(uint8_t pin) {
/* Select ADC channel (pin) for conversion */
ADMUX = (ADMUX & 0xF0) | (pin & 0x0F);
/* Start the conversion */
ADCSRA |= (1 << ADSC);
/* Wait for the conversion to complete */
while (ADCSRA & (1 << ADSC));
/* Return the result */
return ADCH;
}
Pentru a aprinde și a stinge led-urile matricei, am folosit funcții din biblioteca https://github.com/wayoda/LedControl, după mai multe încercări de a implementa această funcționalitate ”de mână”, folosind SPI (Serial Peripheral Interface), care este de asemenea folosit și în funcțiile bibliotecii, într-o modalitate asemanătoare cu cea prezentată mai jos:
void send_data(uint8_t address, uint8_t value) {
digitalWrite(CS, LOW);
/* Send address */
SPI.transfer(address);
/* Send value */
SPI.transfer(value);
/* Finish transfer */
digitalWrite(CS, HIGH);
}
Astfel, pinul Chip-Select (CS) este configurat, trimițându-se mai apoi prin SPI adresa registrului lui MAX7219 al matricei de leduri, precum și valoarea dorită. Dacă se dorește aprinderea sau stingerea led-urilor, conform specificațiilor din datasheet (https://www.analog.com/media/en/technical-documentation/data-sheets/max7219-max7221.pdf), adresa va fi un număr de la 1 la 8, reprezentând linia din matrice pe care se vor aplica modificări, iar valoarea un număr de la 0 la 255, indicând ce led-uri vor fi aprinse, respectiv stinse.
Rezultate Obţinute
Concluzii
În concluzie, am reușit sa creez ceva asemanator unei mini-console portabile mai vechi cu manetă, pe care se poate juca doar jocul Snake. Viteza de joc, adică de deplasare a șarpelui, poate fi setată cu ajutorul rotiței potențiometrului, în acest sens putând fi schimbata dificultatea jocului. Este un proiect care va putea fi folosit și în viitorul îndepărtat, atunci când ți se face dor sa rejoci unul din jocurile alături de care ai copilărit.
Download
Jurnal
3 Mai 2023: M1 - Introducere, Descriere generală, Schemă bloc, Listă componente hardware
14 Mai 2023: M2 - Schemă electrică, Hardware Design
21 Mai 2023: M3 - Implementare software, Software design
22 Mai 2023: Rezultate obținute
Bibliografie/Resurse