Table des matières:

Jeu de plateforme contrôlé par Arduino avec joystick et récepteur IR : 3 étapes (avec photos)
Jeu de plateforme contrôlé par Arduino avec joystick et récepteur IR : 3 étapes (avec photos)

Vidéo: Jeu de plateforme contrôlé par Arduino avec joystick et récepteur IR : 3 étapes (avec photos)

Vidéo: Jeu de plateforme contrôlé par Arduino avec joystick et récepteur IR : 3 étapes (avec photos)
Vidéo: Les bases de l'Arduino ! 2024, Juillet
Anonim
Jeu de plateforme contrôlé par Arduino avec joystick et récepteur IR
Jeu de plateforme contrôlé par Arduino avec joystick et récepteur IR

Aujourd'hui, nous allons utiliser un microcontrôleur Arduino pour contrôler un simple jeu de plateforme basé sur C#. J'utilise l'Arduino pour prendre l'entrée d'un module de manette de jeu et envoyer cette entrée à l'application C# qui écoute et décode l'entrée via une connexion série. Bien que vous n'ayez besoin d'aucune expérience préalable dans la création de jeux vidéo pour terminer le projet, cela peut prendre un certain temps pour absorber certaines des choses qui se passent dans la "boucle de jeu", dont nous parlerons plus tard.

Pour mener à bien ce projet, vous aurez besoin de:

  • Communauté Visual Studio
  • Un Arduino Uno (ou similaire)
  • Un module de contrôleur de joystick
  • Patience

Si vous êtes prêt à commencer, continuez !

Étape 1: Branchez le joystick et la LED IR

Branchez le joystick et la LED IR
Branchez le joystick et la LED IR
Branchez le joystick et la LED IR
Branchez le joystick et la LED IR

Ici, le branchement est assez simple. J'ai inclus des schémas montrant uniquement le joystick connecté, ainsi que la configuration que j'utilise, qui comprend le joystick et une LED infrarouge pour contrôler le jeu avec une télécommande, fournie avec de nombreux kits Arduino. C'est facultatif, mais cela semblait être une bonne idée de pouvoir faire des jeux sans fil.

Les broches utilisées dans la configuration sont:

  • A0 (analogique) <- Horizontal ou axe X
  • A1 (analogique) <- Vertical ou axe Y
  • Broche 2 <- Entrée de commutateur de manette
  • Broche 2 <- Entrée LED infrarouge
  • VCC <- 5V
  • Sol
  • Terre #2

Étape 2: Créer une nouvelle esquisse

Créer une nouvelle esquisse
Créer une nouvelle esquisse

Nous allons commencer par créer notre fichier de croquis Arduino. Cela interroge le joystick pour les modifications et envoie ces modifications au programme C# toutes les quelques millisecondes. Dans un jeu vidéo réel, nous vérifierions le port série dans une boucle de jeu pour l'entrée, mais j'ai commencé le jeu à titre expérimental, donc la fréquence d'images est en fait basée sur le nombre d'événements sur le port série. J'avais en fait commencé le projet dans le projet frère Arduino, Processing, mais il s'est avéré qu'il était beaucoup, beaucoup plus lent et ne pouvait pas gérer le nombre de boîtes à l'écran.

Alors, créez d'abord un nouveau Sketch dans le programme de l'éditeur de code Arduino. Je vais montrer mon code et ensuite expliquer ce qu'il fait:

#include "IRremote.h"

// Variables IR int récepteur = 3; // Broche de signal du récepteur IR IRrecv irrecv(receiver); // crée une instance des résultats de decode_results 'irrecv'; // crée une instance de 'decode_results' // Joystick/variables de jeu int xPos = 507; int yPos = 507; octet joyXPin = A0; octet joyYPin = A1; octet joySwitch = 2; octet volatile clickCounter = -1; int minMoveHaut = 530; int minMoveLow = 490; int currentSpeed = 550; // Par défaut = une vitesse moyenne int speedIncrement = 25; // Quantité pour augmenter/diminuer la vitesse avec l'entrée Y courant long non signé = 0; // Contient l'horodatage actuel int wait = 40; // ms à attendre entre les messages [Remarque: attente inférieure = fréquence d'images plus rapide] volatile bool buttonPressed = false; // Jauge si le bouton est enfoncé void setup() { Serial.begin(9600); pinMode(joySwitch, INPUT_PULLUP); attachInterrupt(0, saut, TOMBE); courant = millis(); // Configurer l'heure actuelle // Configurer le récepteur infrarouge: irrecv.enableIRIn(); // Démarrer le récepteur } // configurer void loop() { int xMovement = analogRead(joyXPin); int yPos = analogRead(joyYPin); // Gère le mouvement du Joystick X quel que soit le timing: if (xMovement > minMoveHigh || xMovement current + wait) { currentSpeed = yPos > minMoveLow && yPos < minMoveHigh // Si seulement déplacé un peu… ? currentSpeed // …retourne juste la vitesse actuelle: getSpeed(yPos); // Ne change yPos que si le joystick s'est déplacé de manière significative // int distance =; Serial.print((String) xPos + ", " + (String) yPos + ', ' + (String) currentSpeed + '\n'); courant = millis(); } } // boucle int getSpeed(int yPos) { // Les valeurs négatives indiquent que le joystick est déplacé vers le haut if(yPos 1023 ? 1023: currentSpeed + speedIncrement; } else if(yPos > minMoveHigh) // Interprété "Down" { // Protéger de passer en dessous de 0 return currentSpeed - speedIncrement < 0 ? 0: currentSpeed - speedIncrement; } } // getSpeed void jump() { buttonPressed = true; // Indique que le bouton a été enfoncé. } // jump // Lorsqu'un bouton est enfoncé sur le distant, gère la bonne réponse void translateIR(decode_results results) // prend des mesures en fonction du code IR reçu { 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(" other button "); break; }// Commutateur de fin } //END translateIR

J'ai essayé de créer le code pour qu'il soit principalement explicite, mais il y a quelques choses qui méritent d'être mentionnées. Une chose que j'ai essayé de prendre en compte était dans les lignes suivantes:

int minYMoveUp = 520;

int minYMoveDown = 500;

Lorsque le programme est en cours d'exécution, l'entrée analogique du joystick a tendance à sauter, restant généralement autour de 507. Pour corriger cela, l'entrée ne change que si elle est supérieure à minYMoveUp ou inférieure à minYMoveDown.

pinMode(joySwitch, INPUT_PULLUP);

attachInterrupt(0, saut, TOMBE);

La méthode attachInterrupt () nous permet d'interrompre la boucle normale à tout moment, afin que nous puissions prendre une entrée, comme la pression d'un bouton lorsque le bouton du joystick est cliqué. Ici, nous avons attaché l'interruption dans la ligne qui la précède, en utilisant la méthode pinMode(). Une note importante ici est que pour attacher une interruption sur l'Arduino Uno, vous devez utiliser la broche 2 ou 3. D'autres modèles utilisent des broches d'interruption différentes, vous devrez donc peut-être vérifier quelles broches votre modèle utilise sur le site Web Arduino. Le deuxième paramètre concerne la méthode de rappel, appelée ici ISR ou "Interrupt Service Routine". Il ne doit prendre aucun paramètre ni retourner quoi que ce soit.

Impression.série(…)

C'est la ligne qui enverra nos données au jeu C#. Ici, nous envoyons la lecture de l'axe X, la lecture de l'axe Y et une variable de vitesse au jeu. Ces lectures peuvent être étendues pour inclure d'autres entrées et lectures pour rendre le jeu plus intéressant, mais ici, nous n'en utiliserons que quelques-unes.

Si vous êtes prêt à tester votre code, téléchargez-le sur l'Arduino et appuyez sur [Maj] + [Ctrl] + [M] pour ouvrir le moniteur série et voir si vous obtenez une sortie. Si vous recevez des données de l'Arduino, nous sommes prêts à passer à la partie C# du code…

Étape 3: Créer le projet C#

Pour afficher nos graphiques, j'ai d'abord commencé un projet dans Processing, mais j'ai ensuite décidé qu'il serait trop lent d'afficher tous les objets que nous devons afficher. J'ai donc choisi d'utiliser C#, qui s'est avéré beaucoup plus fluide et réactif lors de la gestion de nos entrées.

Pour la partie C# du projet, il est préférable de simplement télécharger le fichier.zip et de l'extraire dans son propre dossier, puis de le modifier. Il y a deux dossiers dans le fichier zip. Pour ouvrir le projet dans Visual Studio, entrez le dossier RunnerGame_CSharp dans l'Explorateur Windows. Ici, double-cliquez sur le fichier.sln (solution) et VS chargera le projet.

Il y a quelques classes différentes que j'ai créées pour le jeu. Je n'entrerai pas dans tous les détails de chaque classe, mais je donnerai un aperçu de ce à quoi servent les classes principales.

La classe de boîte

J'ai créé la classe box pour vous permettre de créer des objets rectangles simples qui peuvent être dessinés à l'écran dans un formulaire Windows. L'idée est de créer une classe qui peut être étendue en utilisant d'autres classes qui peuvent vouloir dessiner une sorte de graphique. Le mot-clé "virtual" est utilisé pour que d'autres classes puissent les remplacer (en utilisant le mot-clé "override"). De cette façon, nous pouvons obtenir le même comportement pour la classe Player et la classe Platform lorsque nous en avons besoin, et également modifier les objets selon nos besoins.

Ne vous inquiétez pas trop de toutes les propriétés et dessinez des appels. J'ai écrit cette classe pour pouvoir l'étendre à n'importe quel jeu ou programme graphique que je souhaiterais créer à l'avenir. Si vous devez simplement dessiner un rectangle à la volée, vous n'avez pas besoin d'écrire une grande classe comme celle-ci. La documentation C# contient de bons exemples de la façon de procéder.

Cependant, je vais exposer une partie de la logique de ma classe "Box":

bool virtuel public IsCollidedX(Box otherObject) { … }

Ici, nous vérifions les collisions avec des objets dans la direction X, car le joueur n'a besoin de vérifier les collisions dans la direction Y (haut et bas) que s'il est aligné avec lui sur l'écran.

bool virtuel public IsCollidedY(Box otherObject) { … }

Lorsque nous sommes au-dessus ou en dessous d'un autre objet du jeu, nous vérifions les collisions Y.

bool virtuel public IsCollided(Box otherObject) { … }

Cela combine les collisions X et Y, indiquant si un objet est entré en collision avec celui-ci.

vide virtuel public OnPaint (graphiques graphiques) { … }

En utilisant la méthode ci-dessus, nous passons n'importe quel objet graphique et l'utilisons pendant que le programme s'exécute. Nous créons tous les rectangles qui pourraient avoir besoin d'être dessinés. Cela pourrait être utilisé pour une variété d'animations, cependant. Pour nos besoins, les rectangles conviendront à la fois aux plates-formes et au joueur.

La classe de personnage

La classe Character étend ma classe Box, nous avons donc une certaine physique prête à l'emploi. J'ai créé la méthode "CheckForCollisions" pour vérifier rapidement toutes les plates-formes que nous avons créées pour une collision. La méthode "Jump" définit la vitesse ascendante du joueur sur la variable JumpSpeed, qui est ensuite modifiée image par image dans la classe MainWindow.

Les collisions sont gérées légèrement différemment ici que dans la classe Box. J'ai décidé dans ce jeu que si nous sautons vers le haut, nous pouvons sauter à travers une plate-forme, mais cela attrapera notre joueur en descendant s'il entre en collision avec elle.

La classe de plate-forme

Dans ce jeu, j'utilise uniquement le constructeur de cette classe qui prend une coordonnée X en entrée, calculant tous les emplacements X des plates-formes dans la classe MainWindow. Chaque plate-forme est configurée à une coordonnée Y aléatoire de la moitié de l'écran aux 3/4 de la hauteur de l'écran. La hauteur, la largeur et la couleur sont également générées aléatoirement.

La classe MainWindow

C'est là que nous mettons toute la logique à utiliser pendant que le jeu est en cours d'exécution. Tout d'abord, dans le constructeur, nous imprimons tous les ports COM disponibles pour le programme.

foreach (chaîne de port dans SerialPort. GetPortNames())

Console. WriteLine("PORTS DISPONIBLES: " + port);

Nous choisissons celui sur lequel nous accepterons les communications, en fonction du port déjà utilisé par votre Arduino:

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

Faites très attention à la commande: SerialPort. GetPortNames()[2]. Le [2] signifie quel port série utiliser. Par exemple, si le programme affichait "COM1, COM2, COM3", nous serions à l'écoute sur COM3 car la numérotation commence à 0 dans le tableau.

Toujours dans le constructeur, nous créons toutes les plates-formes avec un espacement semi-aléatoire et un placement dans la direction Y sur l'écran. Toutes les plates-formes sont ajoutées à un objet List, qui en C# est simplement un moyen très convivial et efficace de gérer une structure de données de type tableau. Nous créons ensuite le Player, qui est notre objet Character, définissons le score sur 0 et définissons GameOver sur false.

Private static void DataReceived (expéditeur d'objet, SerialDataReceivedEventArgs e)

C'est la méthode qui est appelée lorsque des données sont reçues sur le port série. C'est là que nous appliquons toute notre physique, décidons d'afficher le game over, de déplacer les plates-formes, etc. Si vous avez déjà construit un jeu, vous avez généralement ce qu'on appelle une "boucle de jeu", qui est appelée à chaque rafraîchit. Dans ce jeu, la méthode DataReceived agit comme une boucle de jeu, manipulant uniquement la physique au fur et à mesure que les données sont reçues du contrôleur. Cela aurait peut-être mieux fonctionné de configurer une minuterie dans la fenêtre principale et d'actualiser les objets en fonction des données reçues, mais comme il s'agit d'un projet Arduino, je voulais créer un jeu qui s'exécutait en fonction des données qui en provenaient..

En conclusion, cette configuration donne une bonne base pour étendre le jeu en quelque chose d'utilisable. Bien que la physique ne soit pas tout à fait parfaite, cela fonctionne assez bien pour nos besoins, qui sont d'utiliser l'Arduino pour quelque chose que tout le monde aime: jouer à des jeux !

Conseillé: