Elektroda.pl
Elektroda.pl
X
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

Arduino Uno R4 WiFi - tworzymy grę snake na wyświetlaczu matrycowym

p.kaczmarek2 19 Sep 2023 11:06 1332 3
  • Arduino Uno R4 WiFi - tworzymy grę snake na wyświetlaczu matrycowym
    Zapraszam na kolejny projekt zrealizowany w oparciu o Arduino R4 WiFi. Tym razem wykonamy prostą grę "snake" na wyświetlaczu matrycowym znajdującym się na pokładzie Arduino. Gra będzie polegać na sterowaniu tytułowym "wężem", który może "zjadać pokarm" i zwiększać swoją długość. Gra kończy się gdy dojdzie do kolizji węża z samym sobą, co tutaj też obsłużę. Zaczynamy!

    Krok 1: przyciski
    Zacznijmy od czegoś bardzo przyziemnego. Od przycisków. Potrzebujemy "strzałek", czyli góra, dół, prawo i lewo. Nie przejmujemy się drganiem styków. Starczą zwykłe GPIO w trybie input pullup i cztery przyciski zwierające je do masy gdy je wciśniemy. Oto krótki kod testowy przycisków:
    Code: c
    Log in, to see the code

    Po sprawdzeniu czy wszystko działa, przechodzimy dalej.

    Krok 2: Ruch punktu
    W tym momencie wprowadziłem już obsługę macierzy LED. Bazowałem na przykładzie Game Of Life z przykładów Arduino R4 WiFi.
    Tak jak w Game Of Life, nasza plansza to tablica dwuwymiarowa odpowiadająca poszczególnym LEDom:
    Code: c
    Log in, to see the code

    Dla uproszczenia przyjąłem tutaj, że jeden piksel to jeden bajt, ale tu zasadniczo mamy tylko dwa stany, więc można by to zoptymalizować.
    Na początek poruszamy samym punktem. Dla uproszczenia po prostu przyjąłem, że opisują go dwie zmienne globalne, koordynaty X i Y:
    Code: c
    Log in, to see the code

    W pętli odpowiednio nim poruszamy:
    Code: c
    Log in, to see the code

    Linijki z dodawaniem i modulo zapewniają "zapętlenie się" planszy.
    Dodatkowo mamy jeszcze wyświetlanie, czyli czyszczenie mapy i nakładanie tego punktu:
    Code: c
    Log in, to see the code

    Całość wykonuję w pętli ze sztucznym opóźnieniem, aby narzucić ilość klatek animacji na sekundę.
    Cały kod:
    Code: c
    Log in, to see the code

    Rezultat:





    Krok 3: Ruch węża
    Niestety jednak sam jeden punkt nam nie wystarczy. Chcemy móc mieć całego węża i to o zmiennej długości. Trzeba zmienić podejście i mechanikę. Postawiłem zatem na tablicę koordynatów punktów:
    Code: c
    Log in, to see the code

    Mamy osobno ustawiony na sztywno limit rozmiaru tablicy (całość pamięci jest od razu alokowana), oraz mamy zmienną przechowującą bieżący rozmiar węża.
    Struktura Point reprezentuje jeden punkt węża.
    W setup tworzę przykładowego węża, dla testu:
    Code: c
    Log in, to see the code

    Zostaje poruszanie. Trzeba poruszać głowę węża a potem przesuwać kolejne punkty o krok dalej. Myślałem tu, by użyć bufora kołowego, ale uznałem, że łatwiej będzie tak:
    Code: c
    Log in, to see the code

    Osobno obsługuję głowę i osobno przesuwam punkty. Tutaj, w przeciwieństwie do demka z punktem nie pozwalam już na ruchy po skosie. Oprócz tego analogicznie.
    Jeszcze wyświetlanie:
    Code: c
    Log in, to see the code

    Rozumiecie ten zapis, prawda? Przechodzę tablicę punktów węża i korzystam z koordynatów x i y do indeksowania tablicy dwuwymiarowej pikseli i tak zapalam odpowiednie piksele.
    Całość:
    Code: c
    Log in, to see the code

    Filmik:




    Krok 4: Jedzenie dla węża
    Teraz trzeba dodać mechanizm pozwalający wydłużać węża. Trzeba mu tworzyć punkciki do jedzenia oraz pozwalać mu je jeść. Zatem najpierw może załóżmy, że na planszy może być tylko jeden punkcik pokarmowy w danym momencie:
    Code: c
    Log in, to see the code

    A potem trzeba w pętli sprawdzać czy głowa węża jest na tym samym pikselu co ten punkt i jeśli tak to wydłużać węża i tworzyć punkcik z jedzeniem na innej, losowej pozycji:
    Code: c
    Log in, to see the code

    To naprawdę tylko tyle! Ważne jest tylko jeszcze tutaj przekopiowanie jednego piksela węża przed wydłużeniem, bo inaczej miałby on nieokreśloną zawartość. Chociaż może starczyłoby "karmić węża" przed wykonaniem ruchu, hmm...

    To oczywiście prosta implementacja, można by jeszcze badać czy nowy wylosowany punkt nie znajduje się na wężu, ale tu to pominąłem.
    Cały kod:
    Code: c
    Log in, to see the code

    Rezultat:





    Krok 5: Kolizja
    Została jeszcze jedna, też bardzo ważna mechanika gry. Wąż nie może sam z sobą kolidować. W przeciwnym razie gra by wręcz nie miała sensu. Trzeba zatem sprawdzać, czy taka kolizja występuje.
    Po chwili namysłu łatwo dojść do wniosku, że musimy sprawdzać tylko kolizję "łba" z resztą ciała. Nie musimy sprawdzać kolizji pozostałych pikseli między sobą.
    Code: c
    Log in, to see the code

    Wystarczy tylko jedna pętla. Dodałem jeszcze w niej wywołanie funkcji tworzącej efekt "końca gry", który tak naprawdę tego rozpoczyna w kółko miganie ekranem dopóki nie zresetujemy Arduino:
    Code: c
    Log in, to see the code

    Cały kod:
    Code: c
    Log in, to see the code

    Rezultat:




    Podsumowanie
    To był tylko przykład, ale i tak podstawowa, lecz działająca wersja gry i tak została napisana. Przy ekranie "game over" można by jeszcze wyświetlać ilość zdobytych punktów, chociażby w oparciu o mój wcześniejszy temat o czcionce. Sam kod można by usprawnić, chociażby poprzez zwiększenie rozmiaru tablicy punktów węża i dodanie limitu jego długości (na ten moment zakres tablicy zostanie przekroczony a dalsze działanie programu jest niezdefiniowane - zależy, co będzie w pamięci za tą tablicą). Można by też ulepszyć dodanie nagród itd, ale to pozostawiam czytelnikowi.
    Zapraszam do komentowania. Może wkrótce warto zrobić wersję na większym wyświetlaczu, np na układach MAX7219?

    Cool? Ranking DIY
    Do you have a problem with Arduino? Ask question. Visit our forum Arduino.
    About Author
    p.kaczmarek2
    Moderator Smart Home
    Offline 
  • #2
    acctr
    Level 28  
    p.kaczmarek2 wrote:
    Zostaje poruszanie. Trzeba poruszać głowę węża a potem przesuwać kolejne punkty o krok dalej. Myślałem tu, by użyć bufora kołowego, ale uznałem, że łatwiej będzie tak:

    Niepotrzebna komplikacja. Wystarczy zaświecić jedną diodę reprezentującą głowę i jedną zgasić.
  • #3
    ken-wawa
    Level 12  
    Fajna zabawa! I fajnie wyszło! Choć wiadomo jak zawsze w takich projektach wiele rzeczy można zrobić na wiele sposobów...

    Skoro już mowa o wężu, to kiedyś pokazywałem taką o to chińską zabawkę do samodzielnego montażu, też całkiem fajną:



  • #4
    p.kaczmarek2
    Moderator Smart Home
    acctr wrote:

    Niepotrzebna komplikacja. Wystarczy zaświecić jedną diodę reprezentującą głowę i jedną zgasić.

    Czy masz na myśli odświeżanie ekranu? Generalnie tak, można albo zawsze od 0 "rysować" scenę, albo robić tylko zmiany. Swego czasu nawet pisałem coś chyba w Pascalu/na Lazarusie z grafiką gdzie nie było double bufferingu i okazało się, że wygodniej jest tylko aktualizować zmienione części ekranu.

    Czy mowa o samym buforze? W jego przypadku chciałem, by głowa była zawsze pierwsza.