Spisu treści:

Gra platformowa kontrolowana przez Arduino z joystickiem i odbiornikiem podczerwieni: 3 kroki (ze zdjęciami)
Gra platformowa kontrolowana przez Arduino z joystickiem i odbiornikiem podczerwieni: 3 kroki (ze zdjęciami)

Wideo: Gra platformowa kontrolowana przez Arduino z joystickiem i odbiornikiem podczerwieni: 3 kroki (ze zdjęciami)

Wideo: Gra platformowa kontrolowana przez Arduino z joystickiem i odbiornikiem podczerwieni: 3 kroki (ze zdjęciami)
Wideo: W jaki sposób studenci Politechniki Rzeszowskiej grają w gry. 2024, Grudzień
Anonim
Sterowana przez Arduino gra platformowa z joystickiem i odbiornikiem podczerwieni
Sterowana przez Arduino gra platformowa z joystickiem i odbiornikiem podczerwieni

Dzisiaj użyjemy mikrokontrolera Arduino do sterowania prostą platformówką opartą na C#. Używam Arduino do pobierania danych z modułu joysticka i wysyłania tych danych do aplikacji C#, która nasłuchuje i dekoduje dane wejściowe przez połączenie szeregowe. Chociaż nie potrzebujesz żadnego wcześniejszego doświadczenia w tworzeniu gier wideo, aby ukończyć projekt, może zająć trochę czasu, aby wchłonąć niektóre rzeczy zachodzące w „pętli gry”, którą omówimy później.

Aby ukończyć ten projekt, będziesz potrzebować:

  • Społeczność Visual Studio
  • Arduino Uno (lub podobny)
  • Moduł kontrolera joysticka
  • Cierpliwość

Jeśli jesteś gotowy, aby rozpocząć, kontynuuj!

Krok 1: Podłącz joystick i diodę podczerwieni

Podłącz joystick i diodę podczerwieni
Podłącz joystick i diodę podczerwieni
Podłącz joystick i diodę podczerwieni
Podłącz joystick i diodę podczerwieni

Tutaj podłączenie jest dość proste. Dołączyłem schematy pokazujące tylko podłączony joystick, a także konfigurację, której używam, która obejmuje joystick oraz diodę podczerwieni do sterowania grą za pomocą pilota, który jest dostarczany z wieloma zestawami Arduino. Jest to opcjonalne, ale możliwość grania w gry bezprzewodowe wydawała się fajnym pomysłem.

Piny użyte w konfiguracji to:

  • A0 (analogowy) <- Oś pozioma lub X
  • A1 (analogowy) <- Oś pionowa lub Y
  • Pin 2 <- wejście przełącznika joysticka
  • Pin 2 <- Wejście LED na podczerwień
  • VCC <-5V
  • Grunt
  • Ziemia #2

Krok 2: Utwórz nowy szkic

Utwórz nowy szkic
Utwórz nowy szkic

Zaczniemy od stworzenia naszego pliku szkicu Arduino. To odpytuje joystick o zmiany i wysyła te zmiany do programu C# co kilka milisekund. W rzeczywistej grze wideo sprawdzalibyśmy port szeregowy w pętli gry pod kątem danych wejściowych, ale zacząłem grę jako eksperyment, więc liczba klatek na sekundę jest w rzeczywistości oparta na liczbie zdarzeń na porcie szeregowym. Właściwie zacząłem projekt w siostrzanym projekcie Arduino, Processing, ale okazuje się, że był znacznie wolniejszy i nie mógł obsłużyć liczby pudełek na ekranie.

Tak więc najpierw utwórz nowy szkic w programie do edycji kodu Arduino. Pokażę mój kod, a następnie wyjaśnię, co robi:

#include "IRremote.h"

// zmienne IR int receiver = 3; // Pin sygnału odbiornika podczerwieni IRrecv irrecv(odbiornik); // utwórz instancję wyników decode_results 'irrecv'; // utwórz instancję 'decode_results' // Zmienne joysticka/gry int xPos = 507; int yPoz = 507; bajt joyXPin = A0; bajt joyYPin = A1; bajt joySwitch = 2; volatile byte clickCounter = -1; int minMoveHigh = 530; int minMoveLow = 490; int currentPrędkość = 550; // Domyślnie = średnia prędkość int speedIncrement = 25; // Kwota do zwiększenia/zmniejszenia prędkości z wejściem Y unsigned long current = 0; // Przechowuje aktualny znacznik czasu int wait = 40; // ms oczekiwania między wiadomościami [Uwaga: niższe czekanie = szybsza liczba klatek na sekundę] volatile bool buttonPressed = false; // Sprawdź, czy przycisk jest wciśnięty void setup() { Serial.begin(9600); pinMode(joySwitch, INPUT_PULLUP); attachInterrupt(0, skok, Opadające); prąd = mili(); // Ustaw aktualny czas // Ustaw odbiornik podczerwieni: irrecv.enableIRIn(); // Uruchom odbiornik } // ustaw void loop() { int xMovement = analogRead(joyXPin); int yPos = analogRead(joyYPin); // Obsługa ruchu Joystick X niezależnie od czasu: if (xMovement > minMoveHigh || xMovement current + wait) { currentSpeed = yPos > minMoveLow && yPos < minMoveHigh // Gdybyś tylko trochę się poruszył… ? currentSpeed // …po prostu zwróć aktualną prędkość: getSpeed(yPos); // Zmieniaj pozycję yPos tylko wtedy, gdy joystick poruszał się znacząco //int distance =; Serial.print((String) xPos + ", " + (String) yPos + ', ' + (String) currentSpeed + '\n'); prąd = mili(); } } // loop int getSpeed(int yPos) { // Wartości ujemne oznaczają przesunięcie joysticka w górę if(yPos 1023 ? 1023: currentSpeed + speedIncrement; } else if(yPos > minMoveHigh) // Interpretacja "Down" { // Ochrona przed przejście poniżej 0 zwróć currentSpeed - speedIncrement < 0 ? 0: currentSpeed - speedIncrement; } } // getSpeed void jump() { buttonPressed = true; // wskaż, że przycisk został naciśnięty } // skok // po naciśnięciu przycisku na remote, obsłuż poprawną odpowiedź void translateIR(decode_results results) // podejmuje działanie na podstawie otrzymanego kodu IR { switch(results.value) { case 0xFF18E7: //Serial.println("2"); currentSpeed += speedIncrement * 2; przerwa; przypadek 0xFF10EF: //Serial.println("4"); xPos = -900; przerwa; przypadek 0xFF38C7: //Serial.println("5"); jump();przerwa; przypadek 0xFF5AA5: //Serial. println("6"); xPos = 900; break; case 0xFF4AB5: //Serial.println("8"); currentSpeed -= speedIncrement * 2; break; default: //Serial.println("inny przycisk"); break; }// Koniec przełącznika } //END translateIR

Starałem się, aby kod był w większości zrozumiały, ale jest kilka rzeczy, o których warto wspomnieć. Jedną rzeczą, którą starałem się wyjaśnić, były następujące wiersze:

int minMoveUp = 520;

int minPrzesuń w dół = 500;

Gdy program jest uruchomiony, wejście analogowe z joysticka ma tendencję do przeskakiwania, zwykle pozostając na poziomie około 507. Aby to naprawić, wejście nie zmienia się, chyba że jest większe niż minYMoveUp lub mniejsze niż minYMoveDown.

pinMode(joySwitch, INPUT_PULLUP);

attachInterrupt(0, skok, Opadające);

Metoda attachInterrupt() pozwala nam przerwać normalną pętlę w dowolnym momencie, dzięki czemu możemy pobierać dane, tak jak naciśnięcie przycisku po kliknięciu przycisku joysticka. Tutaj dołączyliśmy przerwanie w wierszu przed nim, używając metody pinMode(). Ważną uwagą jest to, że aby dołączyć przerwanie do Arduino Uno, musisz użyć pinu 2 lub 3. Inne modele używają różnych pinów przerwań, więc być może będziesz musiał sprawdzić, których pinów używa twój model na stronie Arduino. Drugi parametr dotyczy metody wywołania zwrotnego, zwanej tutaj ISR lub „Procedurą usługi przerwania”. Nie powinien przyjmować żadnych parametrów ani niczego zwracać.

Druk.seryjny(…)

To jest linia, która prześle nasze dane do gry w C#. Tutaj wysyłamy odczyt z osi X, odczyt z osi Y i zmienną prędkości do gry. Te odczyty można rozszerzyć o inne dane wejściowe i odczyty, aby uczynić grę bardziej interesującą, ale tutaj użyjemy tylko kilku.

Jeśli jesteś gotowy, aby przetestować swój kod, prześlij go do Arduino i naciśnij [Shift] + [Ctrl] + [M], aby otworzyć monitor szeregowy i sprawdzić, czy otrzymujesz jakieś dane wyjściowe. Jeśli otrzymujesz dane z Arduino, jesteśmy gotowi przejść do części kodu C#…

Krok 3: Utwórz projekt C#

Aby wyświetlić naszą grafikę, początkowo rozpocząłem projekt w Przetwarzaniu, ale później zdecydowałem, że pokazanie wszystkich obiektów, które musimy wyświetlić, byłoby zbyt wolne. Zdecydowałem się więc na użycie C#, który okazał się znacznie płynniejszy i bardziej responsywny podczas obsługi naszych danych wejściowych.

W przypadku części projektu w języku C# najlepiej jest po prostu pobrać plik.zip i rozpakować go do własnego folderu, a następnie zmodyfikować. W pliku zip znajdują się dwa foldery. Aby otworzyć projekt w programie Visual Studio, wprowadź folder RunnerGame_CSharp w Eksploratorze Windows. W tym miejscu kliknij dwukrotnie plik.sln (rozwiązanie), a program VS załaduje projekt.

W grze stworzyłem kilka różnych klas. Nie będę wchodził w szczegóły dotyczące każdej klasy, ale przedstawię ogólny zarys tego, do czego służą główne zajęcia.

Klasa Pudełkowa

Stworzyłem klasę box, aby umożliwić Ci tworzenie prostych obiektów prostokątnych, które można narysować na ekranie w postaci okna. Pomysł polega na stworzeniu klasy, którą można rozszerzyć o inne klasy, które mogą chcieć rysować jakiś rodzaj grafiki. Słowo kluczowe „virtual” jest używane, aby inne klasy mogły je zastąpić (używając słowa kluczowego „override”). W ten sposób możemy uzyskać to samo zachowanie dla klasy Player i klasy Platform, kiedy zajdzie taka potrzeba, a także modyfikować obiekty w dowolny sposób.

Nie przejmuj się zbytnio wszystkimi właściwościami i rysuj wywołania. Napisałem tę klasę, aby móc ją rozszerzyć o dowolną grę lub program graficzny, który chciałbym stworzyć w przyszłości. Jeśli chcesz po prostu narysować prostokąt w locie, nie musisz pisać tak dużej klasy. Dokumentacja C# zawiera dobre przykłady, jak to zrobić.

Jednak przedstawię trochę logiki mojej klasy „Box”:

public virtual bool IsCollidedX(Box otherObject) { … }

Tutaj sprawdzamy kolizje z obiektami w kierunku X, ponieważ gracz musi sprawdzić kolizje tylko w kierunku Y (góra i dół), jeśli znajduje się w jednej linii z nim na ekranie.

public virtual bool IsCollidedY(Box otherObject) { … }

Kiedy jesteśmy nad lub pod innym obiektem gry, sprawdzamy kolizje Y.

public virtual bool IsCollided(Box otherObject) { … }

Łączy to kolizje X i Y, zwracając, czy jakikolwiek obiekt jest zderzył się z tym.

public virtual void OnPaint(Grafika) { … }

Używając powyższej metody, przekazujemy dowolny obiekt graficzny i używamy go podczas działania programu. Tworzymy dowolne prostokąty, które mogą wymagać narysowania. Można to jednak wykorzystać do różnych animacji. Dla naszych celów prostokąty będą odpowiednie zarówno dla platform, jak i gracza.

Klasa postaci

Klasa Character rozszerza moją klasę Box, więc mamy pewną fizykę po wyjęciu z pudełka. Stworzyłem metodę "CheckForCollisions", aby szybko sprawdzić wszystkie utworzone przez nas platformy pod kątem kolizji. Metoda "Jump" ustawia prędkość w górę odtwarzacza na zmienną JumpSpeed, która jest następnie modyfikowana klatka po klatce w klasie MainWindow.

Tutaj kolizje są obsługiwane nieco inaczej niż w klasie Box. Zdecydowałem w tej grze, że skacząc w górę, możemy przeskoczyć przez platformę, ale złapie ona naszego gracza schodząc w dół, jeśli się z nią zderzymy.

Klasa platformy

W tej grze używam tylko konstruktora tej klasy, który pobiera współrzędną X jako dane wejściowe, obliczając wszystkie lokalizacje X platform w klasie MainWindow. Każda platforma jest ustawiona na losowej współrzędnej Y od 1/2 ekranu do 3/4 wysokości ekranu. Wysokość, szerokość i kolor są również generowane losowo.

Klasa MainWindow

To tutaj umieszczamy całą logikę, która ma być używana podczas działania gry. Najpierw w konstruktorze wypisujemy wszystkie dostępne dla programu porty COM.

foreach(string port w SerialPort. GetPortNames())

Console. WriteLine("DOSTĘPNE PORTY: " + port);

Wybieramy, na którym będziemy akceptować komunikację, w zależności od portu, z którego korzysta już Twoje Arduino:

SerialPort = new SerialPort(SerialPort. GetPortNames()[2], 9600, Parity. None, 8, StopBits. One);

Zwróć szczególną uwagę na polecenie: SerialPort. GetPortNames()[2]. [2] oznacza, którego portu szeregowego użyć. Na przykład, jeśli program wypisze "COM1, COM2, COM3", będziemy nasłuchiwać na COM3, ponieważ numeracja zaczyna się od 0 w tablicy.

Również w konstruktorze tworzymy wszystkie platformy z półlosowymi odstępami i rozmieszczeniem w kierunku Y na ekranie. Wszystkie platformy są dodawane do obiektu List, który w C# jest po prostu bardzo przyjaznym dla użytkownika i wydajnym sposobem zarządzania strukturą danych podobną do tablicy. Następnie tworzymy Player, który jest naszym obiektem Character, ustawiamy wynik na 0 i ustawiamy GameOver na false.

private static void DataReceived(object sender, SerialDataReceivedEventArgs e)

Jest to metoda wywoływana, gdy dane są odbierane przez port szeregowy. To tutaj stosujemy całą naszą fizykę, decydujemy, czy wyświetlić grę, przesunąć platformy itp. Jeśli kiedykolwiek zbudowałeś grę, zazwyczaj masz coś, co nazywa się „pętlą gry”, która jest wywoływana za każdym razem, gdy klatka odświeża. W tej grze metoda DataReceived działa jak pętla gry, manipulując fizyką tylko podczas odbierania danych z kontrolera. Być może lepiej byłoby ustawić Timer w głównym oknie i odświeżyć obiekty na podstawie otrzymanych danych, ale ponieważ jest to projekt Arduino, chciałem stworzyć grę, która faktycznie działała na podstawie danych z niego pochodzących.

Podsumowując, ta konfiguracja daje dobrą podstawę do rozszerzenia gry na coś użytecznego. Chociaż fizyka nie jest idealna, działa wystarczająco dobrze dla naszych celów, czyli używania Arduino do czegoś, co każdy lubi: grania w gry!

Zalecana: