Indholdsfortegnelse:

Arduino-kontrolleret platformspil med joystick og IR-modtager: 3 trin (med billeder)
Arduino-kontrolleret platformspil med joystick og IR-modtager: 3 trin (med billeder)

Video: Arduino-kontrolleret platformspil med joystick og IR-modtager: 3 trin (med billeder)

Video: Arduino-kontrolleret platformspil med joystick og IR-modtager: 3 trin (med billeder)
Video: Lesson 92: Controlling Multiple servo each with potentiometer| Arduino Step By Step Course 2024, November
Anonim
Arduino-kontrolleret platformspil med joystick og IR-modtager
Arduino-kontrolleret platformspil med joystick og IR-modtager

I dag vil vi bruge en Arduino mikrokontroller til at styre et simpelt C#-baseret platformspil. Jeg bruger Arduino til at tage input fra et joystick -modul og sende dette input til C# -programmet, der lytter og afkoder input over en seriel forbindelse. Selvom du ikke har brug for nogen tidligere erfaring med at bygge videospil for at fuldføre projektet, kan det tage noget tid at absorbere nogle af de ting, der foregår i "game loop", som vi vil diskutere senere.

For at fuldføre dette projekt skal du bruge:

  • Visual Studio -fællesskab
  • En Arduino Uno (eller lignende)
  • Et joystick controller modul
  • Tålmodighed

Hvis du er klar til at begynde, så fortsæt!

Trin 1: Tilslut joysticket og IR -LED

Tilslut joysticket og IR -LED
Tilslut joysticket og IR -LED
Tilslut joysticket og IR -LED
Tilslut joysticket og IR -LED

Her er tilslutningen ret simpel. Jeg har inkluderet diagrammer, der kun viser det joystick, der er tilsluttet, samt det setup, jeg bruger, som indeholder joysticket plus en infrarød LED til styring af spillet med en fjernbetjening, der leveres med mange Arduino -sæt. Dette er valgfrit, men det virkede som en fed idé at kunne spille trådløst.

De ben, der blev brugt i opsætningen, er:

  • A0 (analog) <- Vandret eller X-akse
  • A1 (analog) <- Lodret eller Y-akse
  • Pin 2 <- Joystick Switch input
  • Pin 2 <- Infrarød LED-indgang
  • VCC <- 5V
  • Jord
  • Grund #2

Trin 2: Opret en ny skitse

Opret en ny skitse
Opret en ny skitse

Vi starter med at oprette vores Arduino -skitsefil. Dette undersøger joysticket for ændringer og sender disse ændringer til C# -programmet hvert flere millisekunder. I et egentligt videospil ville vi tjekke den serielle port i en spilloop for input, men jeg begyndte spillet som et eksperiment, så frameraten er faktisk baseret på antallet af hændelser på den serielle port. Jeg var faktisk begyndt på projektet i Arduino søsterprojektet Processing, men det viser sig, at det var meget, meget langsommere og ikke kunne klare antallet af kasser på skærmen.

Så opret først en ny skitse i Arduino -kodeditorprogrammet. Jeg viser min kode og forklarer derefter, hvad den gør:

#include "IRremote.h"

// IR -variabler int modtager = 3; // Signalpind af IR -modtager IRrecv irrecv (modtager); // oprette forekomst af 'irrecv' decode_results resultater; // opret forekomst af 'decode_results' // Joystick/game variables int xPos = 507; int yPos = 507; byte joyXPin = A0; byte joyYPin = A1; byte joySwitch = 2; flygtig byte clickCounter = -1; int minMoveHigh = 530; int minMoveLow = 490; int currentSpeed = 550; // Standard = en gennemsnitshastighed int speedIncrement = 25; // Beløb for at øge/sænke hastigheden med Y -input usigneret langstrøm = 0; // Holder det aktuelle tidsstempel int wait = 40; // ms for at vente mellem meddelelser [Bemærk: lavere ventetid = hurtigere framerate] volatile bool buttonPressed = false; // Måler, hvis der trykkes på knappen void setup () {Serial.begin (9600); pinMode (joySwitch, INPUT_PULLUP); attachInterrupt (0, hop, FALLING); strøm = millis (); // Opsæt den aktuelle tid // Opsæt infrarød modtager: irrecv.enableIRIn (); // Start modtageren} // setup void loop () {int xMovement = analogRead (joyXPin); int yPos = analogRead (joyYPin); // Håndter joystick X -bevægelsen uanset timing: hvis (xMovement> minMoveHigh || xMovement current + wait) {currentSpeed = yPos> minMoveLow && yPos <minMoveHigh // Hvis bare flyttet lidt …? currentSpeed // … bare returner den aktuelle hastighed: getSpeed (yPos); // Skift kun yPos, hvis joystick flyttede sig markant // int distance =; Serial.print ((String) xPos + "," + (String) yPos + ',' + (String) currentSpeed + '\ n'); strøm = millis (); }} // loop int getSpeed (int yPos) {// Negative værdier angiver, at joysticket flyttede op, hvis (yPos 1023? 1023: currentSpeed + speedIncrement;} ellers hvis (yPos> minMoveHigh) // Fortolket "Ned" {// Beskyt mod går under 0 return currentSpeed - speedIncrement <0? 0: currentSpeed - speedIncrement;}} // getSpeed void jump () {buttonPressed = true; // Angiv knap blev trykket.} // hop // Når der trykkes på en knap på fjernbetjening, håndter det korrekte svar void translateIR (decode_results results) // tager handling baseret på modtaget IR -kode {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; default: //Serial.println (" anden knap "); pause;} // Afslut switch} // END translateIR

Jeg forsøgte at oprette koden for at være mest selvforklarende, men der er et par ting, der er værd at nævne. En ting jeg forsøgte at redegøre for var i følgende linjer:

int minYMoveUp = 520;

int minYMoveDown = 500;

Når programmet kører, har det analoge input fra joysticket en tendens til at hoppe rundt, normalt på omkring 507. For at korrigere for dette ændres input ikke, medmindre det er større end minYMoveUp eller mindre end minYMoveDown.

pinMode (joySwitch, INPUT_PULLUP);

attachInterrupt (0, hop, FALLING);

Metoden attachInterrupt () giver os mulighed for at afbryde den normale sløjfe når som helst, så vi kan tage input, som f.eks. Tryk på knappen, når der klikkes på joystick -knappen. Her har vi vedhæftet afbrydelsen i linjen før den ved hjælp af pinMode () -metoden. En vigtig bemærkning her er, at for at vedhæfte en afbrydelse på Arduino Uno skal du bruge enten pin 2 eller 3. Andre modeller bruger forskellige interrupt pins, så du skal muligvis kontrollere, hvilke pins din model bruger på Arduino -webstedet. Den anden parameter er til tilbagekaldelsesmetoden, her kaldet en ISR eller en "Interrupt Service Routine". Det bør ikke tage nogen parametre eller returnere noget.

Serial.print (…)

Dette er den linje, der sender vores data til C# -spillet. Her sender vi aflæsning af X-aksen, aflæsning af Y-aksen og en hastighedsvariabel til spillet. Disse aflæsninger kan udvides til at omfatte andre input og aflæsninger for at gøre spillet mere interessant, men her vil vi kun bruge et par.

Hvis du er klar til at teste din kode, skal du uploade den til Arduino og trykke på [Shift] + [Ctrl] + [M] for at åbne den serielle skærm og se, om du får noget output. Hvis du modtager data fra Arduino, er vi klar til at gå videre til C# -delen af koden …

Trin 3: Opret C# -projektet

For at vise vores grafik startede jeg oprindeligt et projekt i Processing, men besluttede senere, at det ville være for langsomt at vise alle de objekter, vi skal vise. Så jeg valgte at bruge C#, som viste sig at være meget glattere og mere lydhør, når vi håndterede vores input.

For C# -delen af projektet er det bedst blot at downloade.zip -filen og udpakke den til sin egen mappe og derefter ændre den. Der er to mapper i zip -filen. For at åbne projektet i Visual Studio skal du indtaste mappen RunnerGame_CSharp i Windows Stifinder. Dobbeltklik her på.sln (løsning) filen, og VS indlæser projektet.

Der er et par forskellige klasser, jeg har oprettet til spillet. Jeg vil ikke gå ind på alle detaljer om hver klasse, men jeg vil give et overblik over, hvad hovedklasserne er til.

Box -klassen

Jeg oprettede bokseklassen for at give dig mulighed for at oprette enkle rektangelobjekter, der kan tegnes på skærmen i en Windows-form. Ideen er at oprette en klasse, der kan udvides ved hjælp af andre klasser, der måske vil tegne en form for grafik. Det "virtuelle" søgeord bruges, så andre klasser kan tilsidesætte dem (ved hjælp af søgeordet "tilsidesæt"). På den måde kan vi få den samme adfærd for Player -klassen og Platform -klassen, når vi har brug for det, og også ændre objekterne, som vi skal.

Du skal ikke bekymre dig for meget om alle egenskaberne og trække opkald. Jeg skrev denne klasse, så jeg kunne udvide den til ethvert spil eller grafikprogram, jeg måske vil lave i fremtiden. Hvis du bare skal tegne et rektangel i fluen, behøver du ikke skrive en stor klasse som denne. C# -dokumentationen har gode eksempler på, hvordan man gør dette.

Jeg vil dog lægge nogle af logikken i min "Box" -klasse op:

offentlig virtuel bool IsCollidedX (Box otherObject) {…}

Her kontrollerer vi for kollisioner med objekter i X-retningen, fordi spilleren kun behøver at kontrollere for kollisioner i Y-retningen (op og ned), hvis han er stillet op med det på skærmen.

offentlig virtuel bool IsCollidedY (Box otherObject) {…}

Når vi er over eller under et andet spilobjekt, kontrollerer vi for Y -kollisioner.

offentlig virtuel bool IsCollided (Box otherObject) {…}

Dette kombinerer X- og Y -kollisioner og returnerer, om et objekt er kollideret med dette.

offentligt virtuelt tomrum OnPaint (grafikgrafik) {…}

Ved hjælp af ovenstående metode sender vi ethvert grafisk objekt ind og bruger det, mens programmet kører. Vi opretter alle rektangler, som muligvis skal tegnes. Dette kan dog bruges til en række forskellige animationer. Til vores formål vil rektangler gøre det godt for både platforme og afspilleren.

Karakterklassen

Character -klassen udvider min Box -klasse, så vi har en vis fysik ud af boksen. Jeg oprettede metoden "CheckForCollisions" for hurtigt at kontrollere alle de platforme, vi har oprettet for en kollision. "Jump" -metoden indstiller spillerens hastighed opad til JumpSpeed-variablen, som derefter ændres frame-for-frame i MainWindow-klassen.

Sammenstød håndteres lidt anderledes her end i Box -klassen. Jeg besluttede i dette spil, at hvis vi hopper opad, kan vi springe gennem en platform, men det vil fange vores spiller på vej ned, hvis den kolliderer med den.

Platformklassen

I dette spil bruger jeg kun konstruktøren af denne klasse, der tager en X-koordinat som input, der beregner alle platformens X-placeringer i MainWindow-klassen. Hver platform er sat op med en tilfældig Y-koordinat fra 1/2 skærmen til 3/4 af skærmens højde. Højden, bredden og farven genereres også tilfældigt.

MainWindow -klassen

Det er her, vi lægger al den logik, der skal bruges, mens spillet kører. Først i konstruktøren udskriver vi alle de COM -porte, der er tilgængelige for programmet.

foreach (strengport i SerialPort. GetPortNames ())

Console. WriteLine ("TILGÆNGELIGE PORTER:" + port);

Vi vælger hvilken vi vil acceptere kommunikation på, i henhold til hvilken port din Arduino allerede bruger:

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

Vær meget opmærksom på kommandoen: SerialPort. GetPortNames () [2]. [2] angiver hvilken seriel port der skal bruges. For eksempel, hvis programmet udskrev "COM1, COM2, COM3", ville vi lytte på COM3, fordi nummereringen begynder med 0 i arrayet.

Også i konstruktøren opretter vi alle platformene med semi-tilfældig afstand og placering i Y-retningen på skærmen. Alle platformene tilføjes til et listeobjekt, som i C# simpelthen er en meget brugervenlig og effektiv måde at styre en matrix-lignende datastruktur på. Vi opretter derefter afspilleren, som er vores karakterobjekt, sætter scoren til 0 og sætter GameOver til falsk.

private static void DataReceived (objekt afsender, SerialDataReceivedEventArgs e)

Dette er den metode, der kaldes, når data modtages på den serielle port. Det er her, vi anvender hele vores fysik, beslutter, om spillet skal vises, flyttes platforme osv. Hvis du nogensinde har bygget et spil, har du generelt det, der kaldes en "game loop", som kaldes hver gang rammen opfrisker. I dette spil fungerer DataReceived -metoden som game loop, og manipulerer kun fysikken, da data modtages fra controlleren. Det kunne have fungeret bedre at oprette en timer i hovedvinduet og opdatere objekterne baseret på de modtagne data, men da dette er et Arduino -projekt, ville jeg lave et spil, der faktisk kørte baseret på dataene, der kom ind fra det.

Afslutningsvis giver dette setup et godt grundlag for at udvide spillet til noget brugbart. Selvom fysikken ikke er helt perfekt, fungerer den godt nok til vores formål, det vil sige at bruge Arduino til noget alle kan lide: at spille spil!

Anbefalede: