[CZĘŚĆ 1] STM32Butterfly2: Tetris na STM32 – wprowadzenie do mechanizmu gry
W przypadku, kiedy równanie w warunku jest prawdziwe (wystąpiła kolizja) ustawiana jest zmienna kolizja zwracana później w wyniku działania funkcji.
W tej samej funkcji umieścimy także warunki sprawdzające czy klocek nie wystaje poza boczne ścianki planszy:
if(x > 9) kolizja=1;
…i czy nie przeszedł poniżej jej „dna”:
if(y > 19) kolizja=1;
Gotowa funkcja będzie wyglądała następująco:
unsigned char Kolizja(unsigned char kx, unsigned char ky, unsigned char r) { unsigned char x,y,i,kolizja; kolizja=0; for(i=0;i<4;i++) //pętla odlicza kolejne cztery segmenty klocka { //(każdy klocek składa się z czterech segmentów) x=klocki[kloceknr][r][i][0]+kx-2; //obliczanie współrzędnych na planszy y=klocki[kloceknr][r][i][1]+ky-2; danego segmentu klocka if(x > 9) kolizja=1; //sprawdzenie czy segment jest poza ściankami bocznymi planszy if(y > 19) kolizja=1; //sprawdzenie czy segment jest poniżej dna planszy if(plansza[x][y]) kolizja=1; //sprawdzenie czy segment //nie nakłada się na istniejący punkt na planszy } return kolizja; //jeżeli wykryto kolizję //to funkcja zwraca 1 w innym razie 0 }
Teraz wystarczy, że przed każdym kolejnym ruchem klocka będziemy sprawdzać czy nie wywoła od kolizji. Przykładowo przed opuszczeniem klocka w dół sprawdzimy następujący warunek:
if(Kolizja(klocekx,kloceky+1,klocekr) == 0) kloceky++;
Konstrukcja taka zwiększy zmienną kloceky o jeden, tylko w przypadku, kiedy funkcja sprawdzająca zwróci wartość 0. Podstawiając do funkcji współrzędna y klocka zwiększona o jeden sprawdzamy czy opuszczenie klocka spowoduje kolizję, zanim opuścimy klocek. Analogicznie będziemy sprawdzać wystąpienie kolizji podczas wykonywania klockiem ruchów na boki. Przygotujmy zatem komplet funkcji odpowiedzialnych za poszczególne ruchy klocka. Pierwsza funkcja przemieszcza klocek o jedno pole w lewo:
void KlocekLewo(void) { if(Kolizja(klocekx-1,kloceky,klocekr) == 0) //sprawdzenie czy przesunięty klocek nie wywoła kolizji { //jeżeli nie to: klocekx--; //zmniejsz zmienną x położenia klocka } }
Proste, prawda? Myślę, że już każdy wie jak będzie wyglądała funkcja przemieszczająca klocek w prawo. Równie proste będzie obracanie klocka. Wystarczy tylko zmienić wartość jednej zmiennej, na kolejną z 4 wartości, jakie może przyjąć:
void KlocekObroc(void) { if(Kolizja(klocekx,kloceky,klocekr+1) == 0) //sprawdzenie czy obrócony klocek //nie wywoła kolizji { //jeżeli nie to: klocekr++; //zwiększ zmienną obrotu klocka if(klocekr > 3) klocekr = 0; } }
Mam nadzieję, że już widzicie zalety proponowanego rozwiązania. Pozostała nam jeszcze do napisania funkcja przemieszczająca klocek o jedno pole w dół. Tutaj sytuacja będzie nieco bardziej skomplikowana. Zastanówmy się jak nasz program powinien zareagować, jeżeli podczas opuszczania klocka kolejny ruch nie będzie możliwy. Klocek, który do tej pory był w ruchu powinien zostać odwzorowany na planszy w postaci stałych jej segmentów. Napiszmy wiec funkcję, która będzie tego dokonywała. Podobnie jak w przypadku sprawdzania kolizji zbudujemy pętlę odliczającą kolejne segmenty klocka, wewnątrz której będziemy obliczać ich współrzędne na planszy. Z tą jednak różnicą, że ustawimy odpowiednie komórki w tablicy odwzorowującej planszę.
void KlocekWklej(void) { unsigned char x,y,i; for(i=0;i<4;i++) //pętla odlicza kolejne 4 segmenty klocka { x=klocki[kloceknr][klocekr][i][0] + klocekx-2; //oblicz położenie segmentu na planszy y=klocki[kloceknr][klocekr][i][1] + kloceky-2; plansza[x][y]=1; //zapal punk na planszy w miejscu segmentu klocka } }
Kolejnym działaniem po odwzorowaniu klocka na planszę będzie wygenerowanie kolejnego klocka. Będzie to polegało na ustawieniu współrzędnych klocka na położenie początkowe. Wyzerowaniu zmiennej obrotu klocka, oraz zmiany rodzaju klocka na inny, najlepiej w sposób losowy. Tworzymy kolejna funkcję, która będzie za to odpowiedzialna:
void KlocekNowy(void) { kloceknr=nkloceknr; //kopiuj rodzaj klocka jaki był następny jako bierzący nkloceknr=rkloceknr; //kopiuj rodzaj następnego klocka z zmiennej losującej if(kloceknr==nkloceknr) //jeżeli następny klocek jest taki sam jak bierzący { rkloceknr++; //zwiększ zmienną losującą if(rkloceknr>6) rkloceknr=0; nkloceknr++; //zwiększ zmienną z rodzajem kolejnego klocka if(nkloceknr>6) nkloceknr=0; } klocekr=0; //zerowanie położenia nowego klocka na planszy klocekx=5; kloceky=2; }