Innehållsförteckning:

Arduino-kontrollerat plattformsspel med joystick och IR-mottagare: 3 steg (med bilder)
Arduino-kontrollerat plattformsspel med joystick och IR-mottagare: 3 steg (med bilder)

Video: Arduino-kontrollerat plattformsspel med joystick och IR-mottagare: 3 steg (med bilder)

Video: Arduino-kontrollerat plattformsspel med joystick och IR-mottagare: 3 steg (med bilder)
Video: SKR 1.4 - TMC2209 v1.2 2024, Juli
Anonim
Arduino-kontrollerat plattformsspel med joystick och IR-mottagare
Arduino-kontrollerat plattformsspel med joystick och IR-mottagare

Idag ska vi använda en Arduino-mikrokontroller för att styra ett enkelt C#-baserat plattformsspel. Jag använder Arduino för att ta ingång från en joystick -modul och skicka den indatan till C# -programmet som lyssnar och avkodar ingång över en seriell anslutning. Även om du inte behöver någon tidigare erfarenhet av att bygga videospel för att slutföra projektet, kan det ta lite tid att absorbera några av de saker som händer i "spelslingan", som vi kommer att diskutera senare.

För att slutföra detta projekt behöver du:

  • Visual Studio Community
  • En Arduino Uno (eller liknande)
  • En styrspakmodul
  • Tålamod

Om du är redo att börja, fortsätt!

Steg 1: Anslut joysticken och IR -lysdioden

Anslut joysticken och IR -lysdioden
Anslut joysticken och IR -lysdioden
Anslut joysticken och IR -lysdioden
Anslut joysticken och IR -lysdioden

Här är anslutningen ganska enkel. Jag har inkluderat diagram som visar bara den joystick som är ansluten, liksom den installation jag använder, som inkluderar joysticken plus en infraröd LED för att styra spelet med en fjärrkontroll, som levereras med många Arduino -kit. Detta är valfritt, men det verkade som en cool idé att kunna spela trådlöst.

Stiften som används i installationen är:

  • A0 (analog) <- Horisontell eller X-axel
  • A1 (analog) <- Vertikal eller Y-axel
  • Pin 2 <- Joystick Switch ingång
  • Pin 2 <- Infraröd LED-ingång
  • VCC <- 5V
  • Jord
  • Mark #2

Steg 2: Skapa en ny skiss

Skapa en ny skiss
Skapa en ny skiss

Vi börjar med att skapa vår Arduino -skissfil. Detta undersöker joysticken för ändringar och skickar dessa ändringar till C# -programmet var flera millisekunder. I ett riktigt videospel skulle vi kontrollera ingången i seriell port i en spelslinga, men jag började spelet som ett experiment, så frameraten är faktiskt baserad på antalet händelser på serieporten. Jag hade faktiskt påbörjat projektet i systerprojektet Arduino, Processing, men det visar sig att det var mycket, mycket långsammare och kunde inte hantera antalet lådor på skärmen.

Så, skapa först en ny skiss i Arduino -kodredigeringsprogrammet. Jag visar min kod och förklarar sedan vad den gör:

#inkludera "IRremote.h"

// IR -variabler int mottagare = 3; // Signalstift för IR -mottagare IRrecv irrecv (mottagare); // skapa instans av 'irrecv' resultat för decode_results; // skapa instans av 'decode_results' // Joystick/game variables int xPos = 507; int yPos = 507; byte joyXPin = A0; byte joyYPin = A1; byte joySwitch = 2; flyktig byte clickCounter = -1; int minMoveHigh = 530; int minMoveLow = 490; int currentSpeed = 550; // Standard = en genomsnittlig hastighet int speedIncrement = 25; // Belopp för att öka/minska hastigheten med Y -ingång osignerad långström = 0; // Håller aktuell tidsstämpel int vänta = 40; // ms för att vänta mellan meddelanden [Obs: lägre väntetid = snabbare framerate] volatile bool buttonPressed = false; // Mätare om knappen trycks in void setup () {Serial.begin (9600); pinMode (joySwitch, INPUT_PULLUP); attachInterrupt (0, hoppa, FALLA); ström = millis (); // Ställ in aktuell tid // Konfigurera infraröd mottagare: irrecv.enableIRIn (); // Starta mottagaren} // setup void loop () {int xMovement = analogRead (joyXPin); int yPos = analogRead (joyYPin); // Hantera Joystick X -rörelsen oavsett tidpunkt: om (xMovement> minMoveHigh || xMovement current + wait) {currentSpeed = yPos> minMoveLow && yPos <minMoveHigh // Om bara flyttat lite …? currentSpeed // … returnera bara aktuell hastighet: getSpeed (yPos); // Ändra bara yPos om joysticken flyttas avsevärt // int distance =; Serial.print ((String) xPos + "," + (String) yPos + ',' + (String) currentSpeed + '\ n'); ström = millis (); }} // loop int getSpeed (int yPos) {// Negativa värden indikerar att joysticken flyttas upp om (yPos 1023? 1023: currentSpeed + speedIncrement;} annars om (yPos> minMoveHigh) // Tolkas "ner" {// Skydda från går under 0 return currentSpeed - speedIncrement <0? 0: currentSpeed - speedIncrement;}} // getSpeed void jump () {buttonPressed = true; // Ange knapp trycktes.} // hopp // När en knapp trycks på fjärrkontroll, hantera rätt svar void translateIR (decode_results results) // vidtar åtgärder baserat på mottagen IR -kod {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 (" annan knapp "); break;} // End switch} // END translateIR

Jag försökte skapa koden för att vara mest självförklarande, men det finns några saker som är värda att nämna. En sak jag försökte redogöra för var i följande rader:

int minYMoveUp = 520;

int minYMoveDown = 500;

När programmet körs tenderar den analoga ingången från joysticken att hoppa runt, vanligtvis på cirka 507. För att korrigera detta ändras inte ingången om den inte är större än minYMoveUp eller mindre än minYMoveDown.

pinMode (joySwitch, INPUT_PULLUP);

attachInterrupt (0, hoppa, FALLA);

Metoden attachInterrupt () gör att vi kan avbryta den normala slingan när som helst, så att vi kan ta in inmatning, som knapptryckning när joystick -knappen klickas. Här har vi bifogat avbrottet i raden före det, med hjälp av pinMode () -metoden. En viktig anmärkning här är att för att fästa ett avbrott på Arduino Uno måste du använda antingen stift 2 eller 3. Andra modeller använder olika avbrottsstift, så du kan behöva kontrollera vilka stift din modell använder på Arduinos webbplats. Den andra parametern är för återuppringningsmetoden, här kallad en ISR eller en "Avbrottsrutin". Det ska inte ta några parametrar eller returnera någonting.

Serial.print (…)

Detta är linjen som skickar våra data till C# -spelet. Här skickar vi avläsningen av X-axeln, avläsningen av Y-axeln och en hastighetsvariabel till spelet. Dessa avläsningar kan utökas till att inkludera andra ingångar och avläsningar för att göra spelet mer intressant, men här kommer vi bara att använda ett par.

Om du är redo att testa din kod laddar du upp den till Arduino och trycker på [Skift] + [Ctrl] + [M] för att öppna den seriella bildskärmen och se om du får utdata. Om du tar emot data från Arduino är vi redo att gå vidare till C# -delen av koden …

Steg 3: Skapa C# -projektet

För att visa vår grafik startade jag inledningsvis ett projekt inom Processing, men bestämde mig senare för att det skulle vara för långsamt att visa alla objekt vi behöver visa. Så jag valde att använda C#, vilket visade sig vara mycket smidigare och mer lyhörd när vi hanterade våra input.

För C# -delen av projektet är det bäst att helt enkelt ladda ner.zip -filen och extrahera den till sin egen mapp och sedan ändra den. Det finns två mappar i zip -filen. Om du vill öppna projektet i Visual Studio anger du mappen RunnerGame_CSharp i Utforskaren. Dubbelklicka här på filen.sln (lösning) och VS laddar projektet.

Det finns några olika klasser jag skapade för spelet. Jag kommer inte att gå in på alla detaljer om varje klass, men jag kommer att ge en översikt över vad huvudklasserna är till för.

Boxklassen

Jag skapade boxklassen för att du ska kunna skapa enkla rektangelobjekt som kan ritas på skärmen i en Windows-form. Tanken är att skapa en klass som kan utökas med andra klasser som kanske vill rita någon form av grafik. Det "virtuella" sökordet används så att andra klasser kan åsidosätta dem (med sökordet "åsidosätt"). På det sättet kan vi få samma beteende för spelarklassen och plattformsklassen när vi behöver, och även ändra objekten hur vi än behöver.

Oroa dig inte för mycket om alla egenskaper och dra samtal. Jag skrev den här klassen så att jag kunde förlänga den för alla spel eller grafikprogram jag kanske vill göra i framtiden. Om du bara behöver rita en rektangel i farten behöver du inte skriva ut en stor klass som denna. C# -dokumentationen har bra exempel på hur man gör detta.

Jag kommer dock att beskriva några av logiken i min "Box" -klass:

offentlig virtuell bool IsCollidedX (Box otherObject) {…}

Här kontrollerar vi kollisioner med objekt i X-riktning, eftersom spelaren bara behöver kolla efter kollisioner i Y-riktningen (upp och ner) om han är uppradad med den på skärmen.

offentlig virtuell bool IsCollidedY (Box otherObject) {…}

När vi är över eller under ett annat spelobjekt, letar vi efter Y -kollisioner.

offentlig virtuell bool IsCollided (Box otherObject) {…}

Detta kombinerar X- och Y -kollisioner, vilket returnerar om något objekt har kolliderat med detta.

offentligt virtuellt tomrum OnPaint (grafikgrafik) {…}

Med ovanstående metod skickar vi in grafikobjekt och använder det medan programmet körs. Vi skapar alla rektanglar som kan behöva ritas. Detta kan dock användas för en mängd olika animationer. För våra ändamål kommer rektanglar att fungera bra för både plattformarna och spelaren.

Teckenklassen

Teckenklassen utökar min Box -klass, så vi har viss fysik ur lådan. Jag skapade metoden "CheckForCollisions" för att snabbt kontrollera alla plattformar vi har skapat för en kollision. "Jump" -metoden sätter spelarens uppåtgående hastighet till JumpSpeed-variabeln, som sedan modifieras bild för bild i MainWindow-klassen.

Kollisioner hanteras något annorlunda här än i Box -klassen. Jag bestämde mig i det här spelet att om vi hoppar uppåt kan vi hoppa genom en plattform, men det kommer att fånga vår spelare på vägen ner om den krockar med den.

Plattformsklassen

I det här spelet använder jag bara konstruktören för den här klassen som tar en X-koordinat som en ingång, som beräknar alla plattformars X-platser i MainWindow-klassen. Varje plattform är inställd på en slumpmässig Y-koordinat från 1/2 skärmen till 3/4 av skärmens höjd. Höjd, bredd och färg genereras också slumpmässigt.

MainWindow -klassen

Det är här vi lägger all logik som ska användas medan spelet körs. Först i konstruktören skriver vi ut alla COM -portar som är tillgängliga för programmet.

foreach (strängport i SerialPort. GetPortNames ())

Console. WriteLine ("TILLGÄNGLIGA PORTER:" + port);

Vi väljer vilken vi kommer att acceptera kommunikation på, enligt vilken port din Arduino redan använder:

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

Var noga med kommandot: SerialPort. GetPortNames () [2]. [2] anger vilken serieport som ska användas. Till exempel, om programmet skrev ut "COM1, COM2, COM3", skulle vi lyssna på COM3 eftersom numreringen börjar med 0 i arrayen.

Även i konstruktören skapar vi alla plattformar med semi-slumpmässigt avstånd och placering i Y-riktningen på skärmen. Alla plattformar läggs till i ett List-objekt, vilket i C# helt enkelt är ett mycket användarvänligt och effektivt sätt att hantera en matrisliknande datastruktur. Vi skapar sedan spelaren, som är vårt karaktärsobjekt, sätter poängen till 0 och ställer GameOver till falskt.

private static void DataReceived (objektavsändare, SerialDataReceivedEventArgs e)

Detta är metoden som kallas när data tas emot på den seriella porten. Det är här vi tillämpar all vår fysik, bestämmer om vi ska visa spelet över, flytta plattformarna etc. Om du någonsin har byggt ett spel har du i allmänhet det som kallas en "spelslinga", som kallas varje gång ramen uppdaterar. I detta spel fungerar DataReceived -metoden som spelslinga och manipulerar endast fysiken när data tas emot från kontrollen. Det kanske hade fungerat bättre att ställa in en timer i huvudfönstret och uppdatera objekten baserat på den mottagna data, men eftersom det här är ett Arduino -projekt ville jag göra ett spel som faktiskt kördes baserat på data som kommer in från det.

Sammanfattningsvis ger denna inställning en bra grund för att expandera spelet till något användbart. Även om fysiken inte är helt perfekt, fungerar den tillräckligt bra för våra ändamål, det vill säga att använda Arduino för något som alla gillar: att spela spel!

Rekommenderad: