Sommario:

Gioco platform controllato da Arduino con joystick e ricevitore IR: 3 passaggi (con immagini)
Gioco platform controllato da Arduino con joystick e ricevitore IR: 3 passaggi (con immagini)

Video: Gioco platform controllato da Arduino con joystick e ricevitore IR: 3 passaggi (con immagini)

Video: Gioco platform controllato da Arduino con joystick e ricevitore IR: 3 passaggi (con immagini)
Video: SH119 - Arduino TV Game - Hackvision 2024, Luglio
Anonim
Gioco platform controllato da Arduino con joystick e ricevitore IR
Gioco platform controllato da Arduino con joystick e ricevitore IR

Oggi useremo un microcontrollore Arduino per controllare un semplice gioco platform basato su C#. Sto usando Arduino per ricevere input da un modulo joystick e inviare quell'input all'applicazione C# che ascolta e decodifica l'input tramite una connessione seriale. Sebbene non sia necessaria alcuna esperienza precedente nella creazione di videogiochi per completare il progetto, potrebbe essere necessario del tempo per assorbire alcune delle cose che accadono nel "ciclo di gioco", di cui parleremo più avanti.

Per completare questo progetto, avrai bisogno di:

  • Comunità di Visual Studio
  • Un Arduino Uno (o simile)
  • Un modulo controller joystick
  • Pazienza

Se sei pronto per iniziare, continua!

Passaggio 1: collegare il joystick e il LED IR

Collegare il joystick e il LED IR
Collegare il joystick e il LED IR
Collegare il joystick e il LED IR
Collegare il joystick e il LED IR

Qui, il collegamento è abbastanza semplice. Ho incluso diagrammi che mostrano solo il joystick collegato, così come la configurazione che sto usando, che include il joystick più un LED a infrarossi per controllare il gioco con un telecomando, che viene fornito con molti kit Arduino. Questo è facoltativo, ma mi è sembrata un'idea interessante poter giocare senza fili.

I pin utilizzati nella configurazione sono:

  • A0 (analogico) <- Orizzontale o asse X
  • A1 (analogico) <- Verticale o asse Y
  • Pin 2 <- Ingresso interruttore joystick
  • Pin 2 <- Ingresso LED infrarossi
  • VCC <- 5V
  • Terreno
  • Terra #2

Passaggio 2: crea un nuovo schizzo

Crea un nuovo schizzo
Crea un nuovo schizzo

Inizieremo con la creazione del nostro file di schizzo Arduino. Questo esegue il polling del joystick per le modifiche e invia tali modifiche al programma C# ogni diversi millisecondi. In un vero videogioco, controlliamo la porta seriale in un ciclo di gioco per l'input, ma ho iniziato il gioco come un esperimento, quindi il framerate è in realtà basato sul numero di eventi sulla porta seriale. In realtà avevo iniziato il progetto nel progetto gemello Arduino, Processing, ma si è scoperto che era molto, molto più lento e non poteva gestire il numero di caselle sullo schermo.

Quindi, prima crea un nuovo schizzo nel programma di editor di codice Arduino. Mostrerò il mio codice e poi spiegherò cosa fa:

#include "IRremote.h"

// Variabili IR int ricevitore = 3; // Pin segnale del ricevitore IR IRrecv irrecv(ricevitore); // crea un'istanza dei risultati di decode_results 'irrecv'; // crea un'istanza di 'decode_results' // Variabili joystick/gioco int xPos = 507; int yPos = 507; byte gioiaXPin = A0; byte gioiaYPin = A1; byte joySwitch = 2; contatore clic byte volatile = -1; int minMoveHigh = 530; int minMoveLow = 490; int currentSpeed = 550; // Predefinito = una velocità media int speedIncrement = 25; // Importo da aumentare/diminuire velocità con ingresso Y unsigned long current = 0; // Mantiene il timestamp corrente int wait = 40; // ms di attesa tra i messaggi [Nota: attesa inferiore = framerate più veloce] volatile bool buttonPressed = false; // Misura se il pulsante viene premuto void setup() { Serial.begin(9600); pinMode(joySwitch, INPUT_PULLUP); attachInterrupt(0, salto, CADUTA); corrente = millis(); // Imposta l'ora corrente // Imposta il ricevitore a infrarossi: irrecv.enableIRIn(); // Avvia il ricevitore } // setup void loop() { int xMovement = analogRead(joyXPin); int yPos = analogRead(joyYPin); // Gestisce il movimento del Joystick X indipendentemente dalla tempistica: if (xMovement > minMoveHigh || xMovement current + wait) { currentSpeed = yPos > minMoveLow && yPos < minMoveHigh // Se solo spostato un po'… ? currentSpeed // …restituisci semplicemente la velocità corrente: getSpeed(yPos); // Modifica yPos solo se il joystick si è spostato in modo significativo //int distanza =; Serial.print((String) xPos + ", " + (String) yPos + ', ' + (String) currentSpeed + '\n'); corrente = millis(); } } // loop int getSpeed(int yPos) { // I valori negativi indicano che il joystick è stato spostato in alto if(yPos 1023 ? 1023: currentSpeed + speedIncrement; } else if(yPos > minMoveHigh) // Interpretato "Giù" { // Proteggi da andando sotto 0 ritorna currentSpeed - speedIncrement < 0 ? 0: currentSpeed - speedIncrement; } } // getSpeed void jump() { buttonPressed = true; // Indica che il pulsante è stato premuto. } // jump // Quando viene premuto un pulsante sul remoto, gestisce la risposta corretta void translateIR(decode_results results) // intraprende un'azione in base al codice IR ricevuto { 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("altro pulsante"); break; } // Fine switch } //END translateIR

Ho cercato di creare il codice in modo che fosse per lo più autoesplicativo, ma ci sono alcune cose che vale la pena menzionare. Una cosa che ho cercato di spiegare era nelle seguenti righe:

int minYMoveUp = 520;

int minYMoveDown = 500;

Quando il programma è in esecuzione, l'input analogico dal joystick tende a saltare, di solito intorno a 507. Per correggere ciò, l'input non cambia a meno che non sia più grande di minYMoveUp o più piccolo di minYMoveDown.

pinMode(joySwitch, INPUT_PULLUP);

attachInterrupt(0, salto, CADUTA);

Il metodo attachInterrupt() ci consente di interrompere il ciclo normale in qualsiasi momento, in modo da poter ricevere input, come la pressione del pulsante quando si fa clic sul pulsante del joystick. Qui abbiamo attaccato l'interrupt nella riga precedente, usando il metodo pinMode(). Una nota importante qui è che per collegare un'interruzione su Arduino Uno, devi utilizzare il pin 2 o 3. Altri modelli utilizzano pin di interruzione diversi, quindi potresti dover controllare quali pin utilizza il tuo modello sul sito Web di Arduino. Il secondo parametro è per il metodo di callback, qui chiamato ISR o "routine di servizio di interruzione". Non dovrebbe prendere alcun parametro o restituire nulla.

Stampa.seriale(…)

Questa è la linea che invierà i nostri dati al gioco C#. Qui, inviamo al gioco la lettura dell'asse X, la lettura dell'asse Y e una variabile di velocità. Queste letture possono essere espanse per includere altri input e letture per rendere il gioco più interessante, ma qui ne useremo solo un paio.

Se sei pronto per testare il tuo codice, caricalo su Arduino e premi [Shift] + [Ctrl] + [M] per aprire il monitor seriale e vedere se ottieni risultati. Se stai ricevendo dati da Arduino, siamo pronti per passare alla parte C# del codice…

Passaggio 3: creare il progetto C#

Per visualizzare la nostra grafica, ho inizialmente avviato un progetto in Processing, ma in seguito ho deciso che sarebbe stato troppo lento mostrare tutti gli oggetti che dovevamo visualizzare. Quindi, ho scelto di usare C#, che si è rivelato molto più fluido e reattivo durante la gestione del nostro input.

Per la parte C# del progetto, è meglio semplicemente scaricare il file.zip ed estrarlo nella propria cartella, quindi modificarlo. Ci sono due cartelle nel file zip. Per aprire il progetto in Visual Studio, inserisci la cartella RunnerGame_CSharp in Esplora risorse. Qui, fai doppio clic sul file.sln (soluzione) e VS caricherà il progetto.

Ci sono alcune classi diverse che ho creato per il gioco. Non entrerò in tutti i dettagli su ogni classe, ma darò una panoramica di cosa sono le classi principali.

La classe della scatola

Ho creato la classe box per permetterti di creare semplici oggetti rettangolari che possono essere disegnati sullo schermo in un form di Windows. L'idea è di creare una classe che può essere estesa usando altre classi che potrebbero voler disegnare un qualche tipo di grafica. La parola chiave "virtuale" viene utilizzata in modo che altre classi possano sovrascriverle (usando la parola chiave "override"). In questo modo, possiamo ottenere lo stesso comportamento per la classe Player e la classe Platform quando necessario, e anche modificare gli oggetti come necessario.

Non preoccuparti troppo di tutte le proprietà e disegna chiamate. Ho scritto questa classe in modo da poterla estendere per qualsiasi gioco o programma di grafica che potrei voler realizzare in futuro. Se devi semplicemente disegnare un rettangolo al volo, non devi scrivere una classe grande come questa. La documentazione di C# contiene buoni esempi di come eseguire questa operazione.

Tuttavia, esporrò parte della logica della mia classe "Box":

public virtual bool IsCollidedX(Box otherObject) { … }

Qui controlliamo le collisioni con oggetti nella direzione X, perché il giocatore deve solo controllare le collisioni nella direzione Y (su e giù) se è allineato con esso sullo schermo.

public virtual bool IsCollidedY(Box otherObject) { … }

Quando siamo sopra o sotto un altro oggetto di gioco, controlliamo le collisioni Y.

public virtual bool IsCollided(Box otherObject) { … }

Questo combina le collisioni X e Y, restituendo se qualche oggetto è entrato in collisione con questo.

public virtual void OnPaint(Graphics graphics) { … }

Usando il metodo sopra, passiamo qualsiasi oggetto grafico e lo usiamo mentre il programma è in esecuzione. Creiamo tutti i rettangoli che potrebbero aver bisogno di essere disegnati. Tuttavia, questo potrebbe essere utilizzato per una varietà di animazioni. Per i nostri scopi, i rettangoli andranno bene sia per le piattaforme che per il giocatore.

La classe dei personaggi

La classe Personaggio estende la mia classe Scatola, quindi abbiamo una certa fisica fuori dagli schemi. Ho creato il metodo "CheckForCollisions" per controllare rapidamente tutte le piattaforme che abbiamo creato per una collisione. Il metodo "Jump" imposta la velocità verso l'alto del lettore sulla variabile JumpSpeed, che viene quindi modificata fotogramma per fotogramma nella classe MainWindow.

Le collisioni vengono gestite in modo leggermente diverso qui rispetto alla classe Box. Ho deciso in questo gioco che se saltiamo verso l'alto, possiamo saltare attraverso una piattaforma, ma catturerà il nostro giocatore durante la discesa in caso di collisione con essa.

La classe della piattaforma

In questo gioco, utilizzo solo il costruttore di questa classe che accetta una coordinata X come input, calcolando tutte le posizioni X delle piattaforme nella classe MainWindow. Ogni piattaforma è impostata su una coordinata Y casuale da metà dello schermo a 3/4 dell'altezza dello schermo. Anche l'altezza, la larghezza e il colore vengono generati casualmente.

La classe MainWindow

Qui è dove mettiamo tutta la logica da usare mentre il gioco è in esecuzione. Innanzitutto, nel costruttore, stampiamo tutte le porte COM disponibili per il programma.

foreach(porta stringa in SerialPort. GetPortNames())

Console. WriteLine("PORTE DISPONIBILI: " + porta);

Scegliamo su quale accettare le comunicazioni, in base a quale porta il tuo Arduino sta già utilizzando:

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

Presta molta attenzione al comando: SerialPort. GetPortNames()[2]. La [2] indica quale porta seriale utilizzare. Ad esempio, se il programma stampasse "COM1, COM2, COM3", saremmo in ascolto su COM3 perché la numerazione inizia da 0 nell'array.

Sempre nel costruttore, creiamo tutte le piattaforme con spaziatura semi-casuale e posizionamento nella direzione Y sullo schermo. Tutte le piattaforme vengono aggiunte a un oggetto List, che in C# è semplicemente un modo molto intuitivo ed efficiente di gestire una struttura di dati simile a un array. Quindi creiamo il Player, che è il nostro oggetto Character, impostiamo il punteggio su 0 e impostiamo GameOver su false.

private static void DataReceived (mittente oggetto, SerialDataReceivedEventArgs e)

Questo è il metodo che viene chiamato quando i dati vengono ricevuti sulla porta seriale. È qui che applichiamo tutta la nostra fisica, decidiamo se visualizzare il game over, spostare le piattaforme, ecc. Se hai mai creato un gioco, generalmente hai quello che viene chiamato "ciclo di gioco", che viene chiamato ogni volta che il frame rinfresca. In questo gioco, il metodo DataReceived funge da loop di gioco, manipolando solo la fisica quando i dati vengono ricevuti dal controller. Avrebbe potuto funzionare meglio impostare un timer nella finestra principale e aggiornare gli oggetti in base ai dati ricevuti, ma poiché si tratta di un progetto Arduino, volevo creare un gioco che funzionasse effettivamente in base ai dati provenienti da esso.

In conclusione, questa configurazione fornisce una buona base per espandere il gioco in qualcosa di utilizzabile. Sebbene la fisica non sia del tutto perfetta, funziona abbastanza bene per i nostri scopi, ovvero usare Arduino per qualcosa che piace a tutti: giocare!

Consigliato: