[050] Wyświetlacz z Nokii cz. 3

[050] Wyświetlacz z Nokii cz. 3

Po solidnej dawce wiedzy czas na obiecaną grę. Użyjemy tutaj większości elementów poznanych w dwóch poprzednich artykułach. Gra oczywiście będzie prosta, a pomysł – często spotykany w historii gier jeszcze z czasów pierwszych komputerów. Mówiąc najprościej, zbieramy do kubka spadające krople. Od razu mówię, że na dłuższą metę jest to nudne, ale wprowadziłem pewne dodatki, które nieco ożywiają akcję. Wkleję teraz cały szkic gry, a poniżej będę już omawiał poszczególne fragmenty.


#include <Adafruit_GFX.h>                                  // Biblioteka obsługująca rysowanie grafik na wyświetlaczach.
#include <Adafruit_PCD8544.h>                              // Biblioteka obsługująca ten konkretny wyświetlacz.
Adafruit_PCD8544 nokia = Adafruit_PCD8544(3, 4, 5, 6, 7);  // Nazwa wyświetlacza, piny: SCLK, MOSI, D/C, CE, RST
#include <Fonts/TomThumb.h>                                // Niestandardowy zestaw czcionek.

const byte potencjometr = A1;  // Port, który będzie czytać położenie potencjometru.
const byte podswietlenie = 9;  // Port, który będzie sterował podświetleniem.

byte punkty = 50;    // Licznik punktów.
byte pozycjaPoziom;  // Pozycja spadającej kropli - poziom.
byte pozycjaPion;    // Pozycja spadającej kropli - pion.
byte pozycjaKubka;   // Pozycja kubka zbierającego krople.

const byte tlo[] PROGMEM = {  // Tablica z obrazkiem tła
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x80, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xa4, 0x8d, 0x04, 0xb4, 0x18, 0x92, 0x49, 0xa3, 0x21, 0xa3,
  0x50, 0x91, 0x10, 0xc8, 0x08, 0xc4, 0x49, 0x32, 0x44, 0x1a, 0x54, 0x50, 0xa2, 0x21, 0x21, 0x61,
  0x24, 0x90, 0x44, 0x90, 0xc4, 0x08, 0x90, 0x91, 0x52, 0x24, 0x92, 0x22, 0x88, 0xa1, 0x11, 0x2a,
  0x45, 0x10, 0x92, 0x94, 0x12, 0x14, 0x11, 0x09, 0x16, 0x08, 0x91, 0x2a, 0x50, 0x8c, 0x08, 0x14,
  0x08, 0x0a, 0x05, 0x08, 0x05, 0x01, 0x51, 0x50, 0x82, 0x00, 0x08, 0x00, 0x04, 0x02, 0x00, 0x02,
  0x00, 0x80, 0x90, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00,
  0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x10, 0x84, 0x00, 0x04, 0x0a, 0x02, 0x02, 0x00, 0x0a,
  0x01, 0x00, 0x10, 0x8a, 0x00, 0x0a, 0x12, 0x05, 0x05, 0x02, 0x11, 0x02, 0x80, 0x10, 0x92, 0x80,
  0x11, 0x29, 0x09, 0x09, 0x45, 0x10, 0x84, 0xa0, 0x10, 0x91, 0x42, 0x20, 0xc4, 0x88, 0xd0, 0xa8,
  0x64, 0x88, 0x51, 0x10, 0xa9, 0x35, 0x4c, 0x20, 0xb4, 0x02, 0x92, 0x82, 0x52, 0x0a, 0x90, 0xa4,
  0x88, 0x93, 0x0d, 0x49, 0x8c, 0x4c, 0x4c, 0x24, 0xd4, 0x50, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0
};
const byte kropla[] PROGMEM = {
  B00100000,  // Tablica z grafiką kropli.
  B01110000,
  B11111000,
  B01110000,
};
const byte kubek[] PROGMEM = {
  B11111110,  // Tablica z grafiką kubka.
  B01111100,
  B01111100,
  B00111000,
};

void setup() {
  nokia.begin();                           // Inicjuj wyświetlacz.
  nokia.setContrast(60);                   // Ustaw kontrast.
  nokia.clearDisplay();                    // Wyczyść wyświetlacz.
  nokia.drawBitmap(0, 0, tlo, 84, 48, 1);  // Rysuj tło.
  nokia.setFont(&TomThumb);                // Wybierz zestaw czcionek.
  pinMode(podswietlenie, OUTPUT);          // Zadeklaruj port podświetlenia jako wyjście.
  digitalWrite(podswietlenie, LOW);        // Włącz podświetlenie.
}

void loop() {
  pozycjaPoziom = random(8, 75);  // Losuj początkową pozycję kropli w poziomie;

  for (pozycjaPion = 10; pozycjaPion < 37; pozycjaPion = pozycjaPion + 1 + (punkty < 41 || punkty > 59)) {  // Procedurę będziemy powtarzać dla kolejnych wierszy ekranu wyznaczonego tymi wartościami. Procedura będzie przyspieszać powyżej i poniżej wyznaczonej granicy punktów.

    nokia.fillRect(73, 0, 9, 6, 0);  // Wyczyść część ekranu, w której będzie rysowany stan punktów.
    nokia.setCursor(74, 5);          // Ustaw kursor.
    nokia.print(punkty);             // Rysuj stan punktów.

    if (punkty < 1) {                    // Przegrałeś!
      nokia.fillRect(1, 10, 82, 30, 0);  // Wyczyść aktywną część ekranu.
      nokia.setCursor(23, 27);           // Ustaw kursor.
      nokia.print(F("PRZEGRALES!"));     // Rysuj komunikat.
      nokia.display();                   // Wyświetl załadowane grafiki.
      digitalWrite(podswietlenie, LOW);  // Włącz podświetlenie.
      delay(3000);                       // Zaczekaj chwilę.
      punkty = 50;                       // Resetuj stan punktów.
    }

    if (punkty > 99) {                   // Wygrałeś!
      nokia.fillRect(1, 10, 82, 30, 0);  // Wyczyść aktywną część ekranu.
      nokia.setCursor(27, 27);           // Ustaw kursor.
      nokia.print(F("WYGRALES!"));       // Rysuj komunikat.
      nokia.display();                   // Wyświetl załadowane grafiki.
      delay(3000);                       // Zaczekaj chwilę.
      punkty = 50;                       // Resetuj stan punktów.
    }

    pozycjaKubka = map(analogRead(potencjometr), 0, 1023, 6, 80) + (punkty < 21 || punkty > 79) * random(-3, 3);  // Odczytaj wartość potencjometru sterującego pozycją kubka, dodając zakłócenia po przekroczeniu wyznaczonej granicy punktów.
    nokia.fillRect(1, 10, 82, 30, 0);                                                                             // Wyczyść aktywną część ekranu.
    digitalWrite(podswietlenie, LOW);                                                                             // Włącz podświetlenie (na wypadek, gdyby został wyłączony stratą punktu)
    nokia.drawBitmap(pozycjaKubka - 5, 35, kubek, 7, 4, 1);                                                       // Rysuj kubek w pozycji określonej poterncjometrem.
    nokia.drawBitmap(pozycjaPoziom, pozycjaPion, kropla, 5, 4, 1);                                                // Rysuj grafikę kropli.
    nokia.display();                                                                                              // Wyświetl załadowane grafiki.

    if (pozycjaKubka > pozycjaPoziom + 2 && pozycjaKubka < pozycjaPoziom + 6 && pozycjaPion > 33) {  // Wykryj interakcję między kroplą, a kubkiem.
      punkty = punkty + 1;                                                                           // Kropla została złapana, Zwiększ liczbę punktów.
      pozycjaPion = 36;                                                                              // Zwiększ liczbę przebiegów pętli do wartości ostatniej, by przerwać jej działanie i zaliczyć tylko jeden punkt.
    } else if (pozycjaPion == 36) {                                                                  // Kropla nie została złapana.
      punkty = punkty - 1;                                                                           // Zmniejsz liczbę punktów.
      digitalWrite(podswietlenie, HIGH);                                                             // Wyłącz podświetlenie.
    }
  }
}

Zaczniemy od grafiki, zatem należy udać się na przykład do Photoshopa. Zaprojektujemy sobie obrazek udający wnętrze jaskini. Ze stropu będą nam spadać krople, które będziemy łapać do kubka. Kropla złapana – punkt, przeoczona – strata punktu.

Na tym etapie ważne jest, by określić aktywne pole ekranu (zaznaczyłem kolorem) i stałe, które będzie tylko tłem. W sposób przedstawiony w poprzednim artykule, korzystając ze strony zamienimy nasz obrazek w paczkę bajtów i wkleimy ją do szkicu. Tę grafikę nazwiemy: tło.

const byte tlo[] PROGMEM = {  // Tablica z obrazkiem tła
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x80, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xa4, 0x8d, 0x04, 0xb4, 0x18, 0x92, 0x49, 0xa3, 0x21, 0xa3,
  0x50, 0x91, 0x10, 0xc8, 0x08, 0xc4, 0x49, 0x32, 0x44, 0x1a, 0x54, 0x50, 0xa2, 0x21, 0x21, 0x61,
  0x24, 0x90, 0x44, 0x90, 0xc4, 0x08, 0x90, 0x91, 0x52, 0x24, 0x92, 0x22, 0x88, 0xa1, 0x11, 0x2a,
  0x45, 0x10, 0x92, 0x94, 0x12, 0x14, 0x11, 0x09, 0x16, 0x08, 0x91, 0x2a, 0x50, 0x8c, 0x08, 0x14,
  0x08, 0x0a, 0x05, 0x08, 0x05, 0x01, 0x51, 0x50, 0x82, 0x00, 0x08, 0x00, 0x04, 0x02, 0x00, 0x02,
  0x00, 0x80, 0x90, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00,
  0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x10, 0x84, 0x00, 0x04, 0x0a, 0x02, 0x02, 0x00, 0x0a,
  0x01, 0x00, 0x10, 0x8a, 0x00, 0x0a, 0x12, 0x05, 0x05, 0x02, 0x11, 0x02, 0x80, 0x10, 0x92, 0x80,
  0x11, 0x29, 0x09, 0x09, 0x45, 0x10, 0x84, 0xa0, 0x10, 0x91, 0x42, 0x20, 0xc4, 0x88, 0xd0, 0xa8,
  0x64, 0x88, 0x51, 0x10, 0xa9, 0x35, 0x4c, 0x20, 0xb4, 0x02, 0x92, 0x82, 0x52, 0x0a, 0x90, 0xa4,
  0x88, 0x93, 0x0d, 0x49, 0x8c, 0x4c, 0x4c, 0x24, 0xd4, 0x50, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0
};

Skorzystamy tutaj z możliwości sterowania podświetleniem oraz wykorzystamy potencjometr, który będzie sterował pozycją kubka. Zaimportujemy też charakterystyczny, pomniejszony krój pisma.

#include <Fonts/TomThumb.h>    // Niestandardowy zestaw czcionek.
const byte potencjometr = A1;  // Port, który będzie czytać położenie potencjometru.
const byte podswietlenie = 9;  // Port, który będzie sterował podświetleniem.

Ze zmiennych potrzebne będą: punkty – początkowo 50, z możliwością dojścia do 100 jak i straty wszystkich oraz trzy zmienne pozycji: kropli i kubka. Ponieważ ten ostatni porusza się tylko w poziomie, czwartej zmiennej nie potrzeba.

byte punkty = 50;    // Licznik punktów.
byte pozycjaPoziom;  // Pozycja spadającej kropli - poziom.
byte pozycjaPion;    // Pozycja spadającej kropli - pion.
byte pozycjaKubka;   // Pozycja kubka zbierającego krople.

Kolejne dwie grafiki to kropla – o rozmiarze 5x4 piksele oraz kubek – o rozmiarze 7x4 piksele. Są to tak proste grafiki, że można je zaprojektować bez programu graficznego, wpisując wprost jedynki i zera.

const byte kropla[] PROGMEM = {
  B00100000,  // Tablica z grafiką kropli.
  B01110000,
  B11111000,
  B01110000,
};
const byte kubek[] PROGMEM = {
  B11111110,  // Tablica z grafiką kubka.
  B01111100,
  B01111100,
  B00111000,
};

Wstęp obejmuje przygotowanie wyświetlacza, podświetlacza, wyświetlenie tła i zaimportowanie czcionek.

void setup() {
  nokia.begin();                           // Inicjuj wyświetlacz.
  nokia.setContrast(60);                   // Ustaw kontrast.
  nokia.clearDisplay();                    // Wyczyść wyświetlacz.
  nokia.drawBitmap(0, 0, tlo, 84, 48, 1);  // Rysuj tło.
  nokia.setFont(&TomThumb);                // Wybierz zestaw czcionek.
  pinMode(podswietlenie, OUTPUT);          // Zadeklaruj port podświetlenia jako wyjście.
  digitalWrite(podswietlenie, LOW);        // Włącz podświetlenie.
}

Pętlę główną rozpoczyna losowanie wyjściowej pozycji kropli. Jak widać, przedział jest nieco węższy od obszaru aktywnego wyświetlacza, bo kropla ma szerokość pięciu pikseli i trzeba uwzględnić dla niej marginesy – inaczej wlazłaby nam na ramkę albo wręcz poza wyświetlacz.

pozycjaPoziom = random(8, 75);  // Losuj początkową pozycję kropli w poziomie;

Jedyna duża pętla będzie wykonywać się tyle razy, ile pikseli ma do przebycia spadająca kropla. Ale nie do końca, gdy licznik punktów przekroczy 59 będziemy przeskakiwać o dwa piksele, dzięki czemu animacja przyspieszy. Co ciekawe, stanie się tak także gdy zaczniemy przegrywać i licznik osiągnie 40 punktów i mniej. Innymi słowy – silnym jest trudniej, ale i dla słabych miejsca nie ma.

for (pozycjaPion = 10; pozycjaPion < 37; pozycjaPion = pozycjaPion + 1 + (punkty < 41 || punkty > 59)) {  // Procedurę będziemy powtarzać dla kolejnych wierszy ekranu wyznaczonego tymi wartościami. Procedura będzie przyspieszać powyżej i poniżej wyznaczonej granicy punktów.
                                                                                                          // Dalszy ciąg programu...

Zaczynamy od wyczyszczenia małego obszaru na górze, w którym umieścimy licznik punktów. Popsuje on naszą grafikę, jednak tak ma być, gdzieś ten licznik trzeba wstawić i robimy to zaraz po wyczyszczeniu poprzedniego wpisu.

nokia.fillRect(73, 0, 9, 6, 0);  // Wyczyść część ekranu, w której będzie rysowany stan punktów.
nokia.setCursor(74, 5);          // Ustaw kursor.
nokia.print(punkty);             // Rysuj stan punktów.

Teraz zastawimy dwie pułapki. Gdy licznik osiągnie zero, wyczyścimy aktywną część ekranu i wyświetlimy na środku demotywujący napis. Po trzech sekundach gra wystartuje od nowa.

if (punkty < 1) {                   // Przegrałeś!
  nokia.fillRect(1, 10, 82, 30, 0);  // Wyczyść aktywną część ekranu.
  nokia.setCursor(23, 27);           // Ustaw kursor.
  nokia.print(F("PRZEGRALES!"));     // Rysuj komunikat.
  nokia.display();                   // Wyświetl załadowane grafiki.
  digitalWrite(podswietlenie, LOW);  // Włącz podświetlenie.
  delay(3000);                       // Zaczekaj chwilę.
  punkty = 50;                       // Resetuj stan punktów.
}

Druga pułapka włącza się po osiągnięciu stu punktów. Tym razem napis jest motywujący, ale reszta – identyczna.

if (punkty > 99) {                  // Wygrałeś!
  nokia.fillRect(1, 10, 82, 30, 0);  // Wyczyść aktywną część ekranu.
  nokia.setCursor(27, 27);           // Ustaw kursor.
  nokia.print(F("WYGRALES!"));       // Rysuj komunikat.
  nokia.display();                   // Wyświetl załadowane grafiki.
  delay(3000);                       // Zaczekaj chwilę.
  punkty = 50;                       // Resetuj stan punktów.
}

W końcu przechodzimy do obsługi kropli i kubka.

pozycjaKubka = map(analogRead(potencjometr), 0, 1023, 6, 80) + (punkty < 21 || punkty > 79) * random(-3, 3);  // Odczytaj wartość potencjometru sterującego pozycją kubka, dodając zakłócenia po przekroczeniu wyznaczonej granicy punktów.

Najpierw łączymy pozycję rysowania tego ostatniego z pozycją potencjometru. Trzeba więc przemapować typową wartość z przetwornika A/D do zakresu, w którym kubek może się poruszać, nie wjeżdżając w marginesy. Ale żeby łatwo nie było, gdy licznik punktów przekroczy 79 albo opadnie poniżej 21, kubkiem zacznie miotać w zakresie sześciu pikseli. To dodatkowe utrudnienie włącza się później niż przyspieszacz gry, przez co dobicie do setki będzie trudne, a do zera – dużo łatwiejsze niestety. Warunki takie realizuje się w ten sposób, że przesunięcie może być równe operacji – tutaj losowaniu – jeśli wyrażenie logiczne jest prawdziwe, a więc równe jeden. Gdy jest fałszywe, więc równe zero, cokolwiek dalej przemnożone przez zero da zero, więc losowanie nie będzie mieć wpływu na położenie kubka.

nokia.fillRect(1, 10, 82, 30, 0);  // Wyczyść aktywną część ekranu.

Teraz wyczyścimy aktywną część ekranu.

nokia.drawBitmap(pozycjaKubka - 5, 35, kubek, 7, 4, 1);         // Rysuj kubek w pozycji określonej poterncjometrem.
nokia.drawBitmap(pozycjaPoziom, pozycjaPion, kropla, 5, 4, 1);  // Rysuj grafikę kropli.
nokia.display();                                                // Wyświetl załadowane grafiki.

Tutaj rysujemy kubek zgodnie z obliczoną wcześniej pozycją. Potem rysujemy kroplę. Przy czym pozycja w poziomie była losowana przed pętlą animacji, a w pionie jest zgodna z kolejnym przejściem tej pętli. W końcu, gdy wszystko już zostało wrzucone do bufora wyświetlacza, aktywujemy obraz. Pozostało wykrycie interakcji między kroplą, a kubkiem.

if (pozycjaKubka > pozycjaPoziom + 2 && pozycjaKubka < pozycjaPoziom + 6 && pozycjaPion > 33) {  // Wykryj interakcję między kroplą, a kubkiem.
                                                                                                 // Dalszy ciąg programu... 

Mamy tu trzy warunki do spełnienia jednocześnie, z których wynika, że pozycja kubka i kropli musi być zbliżona. Nie identyczna, bo ograniczyłoby to nam zaliczenie punktu do jednego tylko piksela, co byłoby trudne. Obszar zaliczenia ma szerokość trzech pikseli i wysokość dwóch. Jeśli obiekty znajdą się w tym obszarze, licznik punktów zwiększy się, a pozycja kropli w pionie zostanie przesunięta na koniec, by licznik nie dodawał nam więcej niż jeden punkt i by pętlę przerwać.

punkty = punkty + 1;  // Kropla została złapana, Zwiększ liczbę punktów.
pozycjaPion = 36;     // Zwiększ liczbę przebiegów pętli do wartości ostatniej, by przerwać jej działanie i zaliczyć tylko jeden punkt.

Natomiast gdy nie mamy interakcji, a kropla osiągnie ostatnią pozycję na dole ekranu, nastąpi odjęcie punku i zgaszenie podświetlenia ekranu.

else if (pozycjaPion == 36) {        // Kropla nie została złapana.
  punkty = punkty - 1;               // Zmniejsz liczbę punktów.
  digitalWrite(podswietlenie, HIGH); // Wyłącz podświetlenie.
}

Stąd wcześniej istniejące funkcje włączające podświetlenie, którą należy umieścić gdziekolwiek, by uzyskać efekt mrugnięcia widoczny, ale nie za długi.

digitalWrite(podswietlenie, LOW);  // Włącz podświetlenie.

Po kompilacji okazuje się, że program pracuje „na styk”. Samo środowisko do szybkich nie należy, ale dodatkowo procedury, zwłaszcza wypełniania figur geometrycznych, sprowadza całość do szybkości ledwo wystarczających przy tego typu grach, w końcu nieskomplikowanych i pracujących z ekranem o niskiej rozdzielczości. Sam ekran także ma sporą latencję i szybsze ruchy wykazują smużenie. Gra jest tylko wstępem do ciekawszej fabuły i może być rozbudowywana w nieskończoność, ale to już pozostawiam miłym czytelnikom.

Powiązane tematy

Płytka edukacyjna TME-EDU-ARD-2Płytka edukacyjna TME-EDU-ARD-2Sprawdź tutaj

Przeczytaj również

Nasi partnerzy

TMETech Master EventTME EducationPoweredby
Copyright © 2025 arduino.pl