[CZĘŚĆ 2] STM32Butterfly2: Tetris na STM32 – interfejs użytkownika
Tym razem korzystamy wykonujemy operację AND na buforze i odwróconych danych tła napisu. Odejmowanie bajtów obrazka od wartości 0xFF tworzy jego negatyw. Dodajemy przed poleceniem nałożenia napisu, polecenie „odjęcia tła”:
LcdLoadSubBMP(pauza2); //nałożenie białego tła pod napis PAUSE
Analogicznie dodamy słynny napis „Game Over” na zakończenie gry. Wystarczą dwie tablice z grafiką tła i napisu oraz dodanie trzech poleceń do funkcji main.
Pozostała kwestia strony tytułowej. Tu chciałbym zaproponować coś ciekawszego niż statyczny obrazek. Wymyśliłem prostą animację, napisy tytułowe, wykonane tę samą techniką, co poprzednie napisy, na przesuwającym się tle.
Na wstępie, należy tu zwrócić uwagę, że wpisani jednego bajtu danych do wyświetlacza zmienia stan 8 pikseli usytuowanych pionowo. Z tego względu najłatwiej będzie nam przesuwać tło w poziomie. Jest nam to nawet na rękę, uzyskamy efekt czołówek rodem z komputerów 8-bitowych. Przygotowujemy obrazek z tłem, ale nie standardowych wymiarów 84×48 punktów tylko szerszy, aby można było go przesuwać. Ponadto początek grafiki powinien ładnie się zgrywać po połączeniu z początkiem. Moje tło jest szerokie na 126 pikseli i wygląda jak na rysunku 6.
Rys. 6. Animowane tło czołówki gry
Konwertujemy grafikę na tablicę danych identycznie jak w przypadku poprzednich grafik. Jednak w tym przypadku uzyskamy większy rozmiar tablicy, ze względu na zwiększony rozmiar grafiki. Warto przy okazji zadeklarować nietypową szerokość obrazka, ułatwi nam to eksperymenty z różnymi tłami:
#define intro_tlo_sz 126 const unsigned char intro_tlo[intro_tlo_sz*6]={ 0x00,0x00,0x00,0x00,0x00,0x00, ...
Potrzebujemy także dodatkowej zmiennej, w której będzie przechowywane aktualne przesunięcie tła. Deklarujemy ją jako zmienną globalną w pliku main:
unsigned char introbgoff=0;
Do biblioteki z funkcjami graficznymi dodajemy trzecią, dodatkową funkcję, tym razem rysującą przesunięte tło. Tym razem jesteśmy zmuszeni operować dwoma współrzędnymi x i y, dlatego użyjemy dwóch pętli. Pierwsze będzie odliczać kolejnych 6 wierszy, a druga 84 bajty każdego wiersza. Przy czym do indeksowania tablicy z grafiką nie użyjemy zmiennej y, tylko dodatkowej zmiennej i do której wpiszemy na początku żądane przesunięcie. Oto kompletna funkcja:
void LcdLoadBG (const unsigned char* bitmap, unsigned char width, unsigned char offset) { unsigned char x,y,i; for(y=0;y<6;y++) //Odliczanie wierszy { i=offset; //Załadowanie przesunięcia do zmiennej i for ( x = 0; x < 84; x++ ) //Odliczanie kolejnych bajtów w wierszu { LcdCache[x+(y*84)] = bitmap[i+(y*width)]; //kopiowanie danych i++; //zwiększenie zmiennej i if(i>=width) i=0; //jeżeli zmienne i większa niż szerokość tła, } // to wpisz zero } }
Jeżeli zmienna i osiągnie wartość szerokości obrazka tła, wpisywana jest do niej wartość zero. Dzięki temu obrazek połączy się końcem ze swoim początkiem i będzie się wydawało, że tło nie ma końca.
Kolejną kwestią wymagającą wyjaśnienia jest bardziej skomplikowane indeksowanie tablic. Użyty przeze mnie sposób pozwala indeksować tablicę jednowymiarową, tak jakby była dwuwymiarowa. Oczywiście, jeżeli w tym drugim przypadku tablica zostanie zadeklarowana jako dwuwymiarowa.
Nasza nowa funkcja będzie wywoływana z głównej pętli w funkcji main. Należy ją tam umieścić analogicznie do pozostałych funkcji rysujących w następującej postaci:
LcdLoadBG(intro_tlo, intro_tlo_sz, introbgoff);
Aby tło się zaczęło przesuwać należy jeszcze cyklicznie zwiększać wartość zmiennej introbgoff. Oczywiście najwygodniej będzie to robić w obsłudze przerwania timer SysTick:
introbgoff++; //zwiększenie zmiennej przesunięcia tła if(introbgoff>=126) introbgoff=0;
Dobrana wcześniej częstotliwość wywołań od timera bardzo pasuje do przesuwania tła. Prędkość jest na tyle mała, że tworzy to dobry efekt i nie powoduje rozmazywania przesuwanego obrazka. Oczywiście nic nie stoi na przeszkodzie, aby zwiększyć prędkość, z jaką przesuwa się tło, skracając okres wywołań timera SysTick. Należy jednak wtedy pamiętać, aby wyrównać prędkość gry, dobierając odpowiednio początkową wartość zmiennej szybkosc.
Mając już ładnie przesuwające się tło, zaprojektowałem grafiki z napisami tytułowymi. Tak jak uprzednio w przypadku innych „nakładanych grafik” zastosowałem odejmowane tło dla grafiki uzyskując efekt pokazany na rysunku 7.
Rys. 7. Grafika czołówki gry
W rzeczywistości efekt jest dużo milszy dla oka. Przesuwające się tło, wyraźniej oddziela się od umieszczonych na nim napisów. Pozostało nam zadbać, aby zmienna stan_gry zaraz po uruchomieniu miała wartość INTRO tak, aby program zawsze rozpoczynał się od naszej efektownej czołówki.
Rafał Kędzierski