Cuprins:

Joc de platformă controlat de Arduino cu joystick și receptor IR: 3 pași (cu imagini)
Joc de platformă controlat de Arduino cu joystick și receptor IR: 3 pași (cu imagini)

Video: Joc de platformă controlat de Arduino cu joystick și receptor IR: 3 pași (cu imagini)

Video: Joc de platformă controlat de Arduino cu joystick și receptor IR: 3 pași (cu imagini)
Video: CS50 Live, Episode 003 2024, Noiembrie
Anonim
Joc de platformă controlat de Arduino cu joystick și receptor IR
Joc de platformă controlat de Arduino cu joystick și receptor IR

Astăzi, vom folosi un microcontroler Arduino pentru a controla un simplu joc platformer bazat pe C #. Folosesc Arduino pentru a prelua intrarea de la un modul joystick și a trimite acea intrare către aplicația C # care ascultă și decodează intrarea printr-o conexiune Serial. Deși nu aveți nevoie de experiență anterioară în construirea de jocuri video pentru a finaliza proiectul, poate necesita ceva timp pentru a absorbi unele dintre lucrurile care se întâmplă în „bucla jocului”, despre care vom discuta mai târziu.

Pentru a finaliza acest proiect, veți avea nevoie de:

  • Comunitatea Visual Studio
  • Un Arduino Uno (sau similar)
  • Un modul de control al joystick-ului
  • Răbdare

Dacă sunteți gata să începeți, continuați!

Pasul 1: Conectați joystick-ul și LED-ul IR

Conectați joystick-ul și LED-ul IR
Conectați joystick-ul și LED-ul IR
Conectați joystick-ul și LED-ul IR
Conectați joystick-ul și LED-ul IR

Aici, conectarea este destul de simplă. Am inclus diagrame care arată doar joystick-ul conectat, precum și configurarea pe care o folosesc, care include joystick-ul plus un LED cu infraroșu pentru controlul jocului cu o telecomandă, care vine cu multe kituri Arduino. Acest lucru este opțional, dar mi s-a părut o idee interesantă să poți face jocuri wireless.

Pinii utilizați în configurare sunt:

  • A0 (analog) <- Axa orizontală sau X
  • A1 (analogic) <- Axa verticală sau Y
  • Pinul 2 <- Intrare comutator joystick
  • Pin 2 <- Intrare LED cu infraroșu
  • VCC <- 5V
  • Sol
  • Terenul # 2

Pasul 2: Creați o schiță nouă

Creați o schiță nouă
Creați o schiță nouă

Vom începe cu crearea fișierului nostru de schiță Arduino. Aceasta sondează joystick-ul pentru modificări și trimite aceste modificări programului C # la fiecare câteva milisecunde. Într-un joc video propriu-zis, am verifica portul serial într-o buclă de joc pentru intrare, dar am început jocul ca un experiment, deci frecvența se bazează de fapt pe numărul de evenimente de pe portul serial. De fapt, începusem proiectul în cadrul proiectului sora Arduino, Procesare, dar se pare că a fost mult, mult mai lent și nu putea rezolva numărul de cutii de pe ecran.

Deci, mai întâi creați o nouă schiță în programul de editare a codului Arduino. Îmi voi arăta codul și apoi voi explica ce face:

#include „IRremote.h”

// Variabile IR int receptor = 3; // Pinul de semnal al receptorului IR IRrecv irrecv (receptor); // creați instanța rezultatelor 'irrecv' decode_results; // creați instanța „decode_results” // Joystick / variabile de joc int xPos = 507; int yPos = 507; octet joyXPin = A0; octet joyYPin = A1; octet joySwitch = 2; octet volatil clickCounter = -1; int minMoveHigh = 530; int minMoveLow = 490; int CurrentSpeed = 550; // Implicit = o viteză medie int speedIncrement = 25; // Suma pentru creșterea / scăderea vitezei cu intrarea Y curent lung nesemnat = 0; // Păstrează timestamp curent int wait = 40; // ms pentru a aștepta între mesaje [Notă: așteptare mai mică = framerate mai rapid] buton volatil bool Apăsat = fals; // Măsurați dacă butonul este apăsat setare nulă () {Serial.begin (9600); pinMode (joySwitch, INPUT_PULLUP); attachInterrupt (0, jump, FALLING); curent = milis (); // Configurați ora curentă // Configurați receptorul infraroșu: irrecv.enableIRIn (); // Porniți receptorul} // setup void loop () {int xMovement = analogRead (joyXPin); int yPos = analogRead (joyYPin); // Manevrați mișcarea Joystick X indiferent de momentul: if (xMovement> minMoveHigh || xMovement current + wait) {currentSpeed = yPos> minMoveLow && yPos <minMoveHigh // Dacă s-ar muta doar puțin …? currentSpeed // … doar returnează viteza curentă: getSpeed (yPos); // Schimbați yPos numai dacă joystick-ul s-a mutat semnificativ // int distance =; Serial.print ((String) xPos + "," + (String) yPos + ',' + (String) currentSpeed + '\ n'); curent = milis (); }} // buclă int getSpeed (int yPos) {// Valorile negative indică joystick-ul deplasat în sus dacă (yPos 1023? 1023: currentSpeed + speedIncrement;} else if (yPos> minMoveHigh) // Interpretat „Down” {// Protejați de mergând sub 0 return currentSpeed - speedIncrement <0? 0: currentSpeed - speedIncrement;}} // getSpeed void jump () {buttonPressed = true; // Indicați butonul a fost apăsat.} // jump // Când este apăsat un buton pe la distanță, gestionați răspunsul corespunzător void translateIR (decode_results results) // ia măsuri pe baza codului IR primit {switch (results.value) {case 0xFF18E7: //Serial.println("2 "); currentSpeed + = speedIncrement * 2; break; case 0xFF10EF: //Serial.println("4 "); xPos = -900; break; case 0xFF38C7: //Serial.println("5"); jump (); break; case 0xFF5AA5: // Serial. println ("6"); xPos = 900; break; case 0xFF4AB5: //Serial.println("8 "); currentSpeed - = speedIncrement * 2; break; implicit: //Serial.println (" alt buton "); pauză;} // Comutator de sfârșit} // ÎNCHEI traducereIR

Am încercat să creez codul pentru a fi în mare parte auto-explicativ, dar sunt câteva lucruri demne de menționat. Un lucru pe care am încercat să-l explic a fost în următoarele rânduri:

int minYMoveUp = 520;

int minYMoveDown = 500;

Când programul rulează, intrarea analogică de la joystick tinde să sară în jur, rămânând de obicei la aproximativ 507. Pentru a corecta acest lucru, intrarea nu se schimbă decât dacă este mai mare decât minYMoveUp sau mai mică decât minYMoveDown.

pinMode (joySwitch, INPUT_PULLUP);

attachInterrupt (0, jump, FALLING);

Metoda attachInterrupt () ne permite să întrerupem bucla normală în orice moment, astfel încât să putem prelua date, cum ar fi apăsarea butonului când se face clic pe butonul joystick-ului. Aici am atașat întreruperea în linia dinaintea ei, folosind metoda pinMode (). O notă importantă aici este că pentru a atașa o întrerupere pe Arduino Uno, trebuie să utilizați fie pinul 2, fie 3. Alte modele utilizează pini de întrerupere diferiți, deci poate fi necesar să verificați ce pini utilizează modelul dvs. pe site-ul web Arduino. Al doilea parametru este pentru metoda de apel invers, numită aici un ISR sau o „rutină de întrerupere a serviciului”. Nu ar trebui să ia parametri sau să returneze nimic.

Serial.print (…)

Aceasta este linia care ne va trimite datele către jocul C #. Aici, trimitem jocului citirea axei X, citirea axei Y și o variabilă de viteză. Aceste citiri pot fi extinse pentru a include alte intrări și citiri pentru a face jocul mai interesant, dar aici vom folosi doar câteva.

Dacă sunteți gata să vă testați codul, încărcați-l pe Arduino și apăsați [Shift] + [Ctrl] + [M] pentru a deschide monitorul serial și a vedea dacă obțineți ieșiri. Dacă primiți date de la Arduino, suntem gata să trecem la partea C # a codului …

Pasul 3: Creați proiectul C #

Pentru a afișa grafica noastră, am început inițial un proiect în Procesare, dar ulterior am decis că ar fi prea lent să arăt toate obiectele pe care trebuie să le afișăm. Așadar, am ales să folosesc C #, care s-a dovedit a fi mult mai ușor și mai receptiv la manipularea informațiilor noastre.

Pentru partea C # a proiectului, cel mai bine este să descărcați pur și simplu fișierul.zip și să îl extrageți în propriul folder, apoi să îl modificați. Există două dosare în fișierul zip. Pentru a deschide proiectul în Visual Studio, introduceți folderul RunnerGame_CSharp în Windows Explorer. Aici, faceți dublu clic pe fișierul.sln (soluție), iar VS va încărca proiectul.

Există câteva clase diferite pe care le-am creat pentru joc. Nu voi intra în toate detaliile despre fiecare clasă, dar voi oferi o imagine de ansamblu a ceea ce sunt clasele principale.

Clasa Box

Am creat clasa casetă pentru a vă permite să creați obiecte dreptunghiulare simple care pot fi desenate pe ecran într-o formă de ferestre. Ideea este de a crea o clasă care poate fi extinsă folosind alte clase care ar putea dori să deseneze un fel de grafică. Cuvântul cheie „virtual” este utilizat astfel încât alte clase să le suprascrie (folosind cuvântul cheie „suprascrie”). În acest fel, putem obține același comportament pentru clasa Player și clasa Platform atunci când avem nevoie și, de asemenea, putem modifica obiectele oricum ar trebui.

Nu vă faceți griji prea mult cu privire la toate proprietățile și trageți apeluri. Am scris această clasă, astfel încât să o pot extinde pentru orice joc sau program grafic pe care aș vrea să îl realizez în viitor. Dacă trebuie să desenați pur și simplu un dreptunghi din mers, nu trebuie să scrieți o clasă mare ca aceasta. Documentația C # conține exemple bune despre cum să faceți acest lucru.

Cu toate acestea, voi expune o parte din logica clasei mele „Box”:

public virtual bool IsCollidedX (Caseta otherObject) {…}

Aici verificăm dacă există coliziuni cu obiecte în direcția X, deoarece jucătorul trebuie să verifice dacă există coliziuni în direcția Y (în sus și în jos) dacă este aliniat cu acesta pe ecran.

public virtual bool IsCollidedY (Caseta otherObject) {…}

Când suntem peste sau sub un alt obiect de joc, verificăm dacă există coliziuni Y.

public virtual bool IsCollided (Caseta otherObject) {…}

Aceasta combină coliziunile X și Y, returnând dacă vreun obiect este ciocnit cu acesta.

gol virtual virtual OnPaint (grafică grafică) {…}

Folosind metoda de mai sus, trecem orice obiect grafic și îl folosim pe măsură ce programul rulează. Creăm orice dreptunghi care ar putea fi necesar să fie desenat. Totuși, aceasta ar putea fi utilizată pentru o varietate de animații. În scopurile noastre, dreptunghiurile vor merge bine atât pentru platforme, cât și pentru jucător.

Clasa personajului

Clasa Character extinde clasa mea Box, așa că avem anumite fizici în afara cutiei. Am creat metoda „CheckForCollisions” pentru a verifica rapid toate platformele pe care le-am creat pentru o coliziune. Metoda „Salt” setează viteza ascendentă a jucătorului la variabila JumpSpeed, care este apoi modificată cadru cu cadru în clasa MainWindow.

Coliziunile sunt tratate ușor diferit aici decât în clasa Box. Am decis în acest joc că, dacă sărim în sus, putem sări printr-o platformă, dar acesta ne va prinde jucătorul la coborâre dacă se ciocnește cu ea.

Clasa platformei

În acest joc, folosesc doar constructorul acestei clase care ia o coordonată X ca intrare, calculând toate locațiile X ale platformelor din clasa MainWindow. Fiecare platformă este configurată la o coordonată Y aleatorie de la 1/2 ecran la 3/4 din înălțimea ecranului. Înălțimea, lățimea și culoarea sunt, de asemenea, generate aleatoriu.

Clasa MainWindow

Aici punem toată logica pe care trebuie să o folosim în timp ce jocul rulează. În primul rând, în constructor, imprimăm toate porturile COM disponibile pentru program.

foreach (port șir în SerialPort. GetPortNames ())

Console. WriteLine ("PORTURI DISPONIBILE:" + port);

Alegem pe care vom accepta comunicările, în funcție de portul pe care îl folosește deja Arduino:

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

Acordați o atenție deosebită comenzii: SerialPort. GetPortNames () [2]. [2] semnifică portul serial de utilizat. De exemplu, dacă programul ar fi tipărit „COM1, COM2, COM3”, am asculta pe COM3, deoarece numerotarea începe de la 0 în matrice.

De asemenea, în constructor, creăm toate platformele cu spațierea semi-aleatorie și plasarea în direcția Y pe ecran. Toate platformele sunt adăugate unui obiect List, care în C # este pur și simplu un mod foarte ușor de utilizat și eficient de a gestiona o structură de date de tip matrice. Apoi creăm Playerul, care este obiectul nostru Personaj, setăm scorul la 0 și setăm GameOver la fals.

private static nul DataReceived (expeditor obiect, SerialDataReceivedEventArgs e)

Aceasta este metoda care este apelată atunci când datele sunt primite pe portul serial. Aici aplicăm toată fizica noastră, decidem dacă afișăm jocul, mutăm platformele etc. Dacă ați construit vreodată un joc, în general aveți ceea ce se numește „buclă de joc”, care se numește de fiecare dată cadru reîmprospătează. În acest joc, metoda DataReceived acționează ca buclă de joc, manipulând fizica doar pe măsură ce datele sunt primite de la controler. S-ar putea să fi funcționat mai bine să configurați un cronometru în fereastra principală și să reîmprospătați obiectele pe baza datelor primite, dar, deoarece acesta este un proiect Arduino, am vrut să fac un joc care să ruleze de fapt pe baza datelor care vin din acesta.

În concluzie, această configurație oferă o bază bună pentru extinderea jocului în ceva utilizabil. Deși fizica nu este destul de perfectă, funcționează suficient de bine pentru scopurile noastre, adică să folosim Arduino pentru ceva ce îi place tuturor: jocuri!

Recomandat: