Arduino Retro Gaming avec un écran OLED

Arduino Retro Gaming avec un écran OLED / DIY

Vous êtes-vous déjà demandé combien de travail il fallait pour écrire vos propres jeux rétro? Est-ce que Pong est facile à coder pour l'Arduino? Rejoignez-moi pour vous montrer comment construire une mini-console de jeux rétro alimentée par Arduino et comment coder Pong à partir de zéro. Voici le résultat final:

Plan de construction

C'est un circuit assez simple. UNE potentiomètre (pot) contrôlera le jeu, et un écran OLED sera piloté par Arduino. Ceci sera produit sur une planche à pain, mais vous voudrez peut-être en faire un circuit permanent et l'installer dans un boîtier. Comment recréer le jeu Pong classique en utilisant Arduino Comment recréer le jeu Pong classique en utilisant Arduino Pong a été le premier jeu vidéo jamais atteint le marché de masse. Pour la première fois de l'histoire, le concept de "jeu vidéo" a été introduit dans la maison familiale grâce à l'Atari 2600 -… En savoir plus, cependant, aujourd'hui, je vais vous montrer comment écrire le code à partir de rien, et casser bas chaque partie.

De quoi as-tu besoin

Voici ce dont vous avez besoin:

  • 1 x Arduino (tout modèle)
  • 1 x 10k potentiomètre
  • 1 écran OLED I2C 0,96 "
  • 1 x planche à pain
  • Fils assortis> fils de raccordement mâles

DIYmall 0.96 "pouce I2c IIC Série 128x64 Oled LCD Module d'affichage blanc à LED pour Arduino 51 Msp420 Stim32 SCR DIYmall 0.96" pouce I2c IIC Série 128x64 Oled LCD Module d'affichage blanc à LED pour Arduino 51 Msp420 Stim32 SCR Acheter maintenant sur Amazon $ 8.99

N'importe quel Arduino devrait fonctionner, alors consultez notre guide d'achat Guide d'achat Arduino: Quelle carte choisir? Guide d'achat Arduino: Quel conseil choisir? Il y a tellement de types de cartes Arduino que vous ne pouvez pas vous tromper Que devriez-vous acheter pour votre projet? Laissez-nous vous aider avec ce guide d'achat Arduino! Lire la suite si vous ne savez pas quel modèle acheter.

Ces écrans OLED sont très cool. Ils peuvent généralement être achetés en blanc, bleu, jaune ou un mélange des trois. Ils existent en couleur, mais ils ajoutent un tout autre niveau à la complexité et au coût de ce projet..

Le circuit

C'est un circuit assez simple. Si vous n'avez pas beaucoup d'expérience avec Arduino, consultez ces projets pour débutants. 15 Grands projets Arduino pour les débutants 15 Grands Projets Arduino pour les débutants Intéressé par Arduino mais vous ne savez pas par où commencer? Voici quelques-uns de nos meilleurs projets Arduino pour les débutants! Lire plus en premier.

C'est ici:

En regardant à l’avant du pot, connectez la broche gauche à +5V et la bonne épingle à sol. Connectez la broche centrale à broche analogique 0 (A0).

L'écran OLED est connecté à l'aide du protocole I2C. Relier VCC et GND à l'Arduino +5V et sol. Relier SCL à analogique cinq (A5). Relier SDA à analogique 4 (A4). La raison pour laquelle cela est connecté aux broches analogiques est simple; ces broches contiennent les circuits requis pour le protocole I2C. Assurez-vous que ceux-ci sont correctement connectés et non traversés. Les broches exactes varieront selon le modèle, mais les formats A4 et A5 sont utilisés sur les modèles Nano et Uno. Consultez la documentation de la bibliothèque Wire de votre modèle si vous n’utilisez ni Arduino ni Nano..

Test de pot

Téléchargez ce code de test (assurez-vous de sélectionner le bon conseil et le bon port Outils > Planche et Outils > Port menus):

void setup () // placez votre code de configuration ici, pour exécuter une fois: Serial.begin (9600); // setup serial void loop () // place ton code principal ici, pour l'exécuter de manière répétée: Serial.println (analogRead (A0)); // affiche la valeur du délai du pot (500); 

Ouvrez maintenant le moniteur série (En haut à droite > Moniteur série) et tourner le pot. Vous devriez voir la valeur affichée sur le moniteur série. Complètement dans le sens anti-horaire devrait être zéro, et complètement dans le sens horaire devrait être 1023:

Vous ajusterez cela plus tard, mais pour l'instant ça va. Si rien ne se passe ou si la valeur change sans que vous ne fassiez rien, débranchez et vérifiez le circuit..

Test OLED

L’affichage OLED est légèrement plus complexe à configurer. Vous devez d'abord installer deux bibliothèques pour piloter l'affichage. Téléchargez les bibliothèques Adafruit_SSD1306 et Adafruit-GFX à partir de Github. Copiez les fichiers dans votre dossier de bibliothèques. Cela varie en fonction de votre système d'exploitation:

  • Mac OS: / Utilisateurs / Nom d'utilisateur / Documents / Arduino / Bibliothèques
  • Linux: / home / Nom d'utilisateur / Carnet de croquis
  • Les fenêtres: / Utilisateurs / Arduino / Bibliothèques

Maintenant, téléchargez une esquisse de test. Aller à Fichier > Exemples > Adafruit SSD1306 > ssd1306_128x64_i2c. Cela devrait vous donner un grand croquis contenant beaucoup de graphiques:

Si rien ne se passe après le téléchargement, déconnectez-vous et vérifiez vos connexions. Si les exemples ne figurent pas dans les menus, vous devrez peut-être redémarrer votre IDE Arduino..

Le code

Il est maintenant temps pour le code. Je vais vous expliquer chaque étape, alors sautez à la fin si vous voulez juste le faire fonctionner. C’est une bonne quantité de code. Si vous ne vous sentez pas en confiance, consultez ces 10 ressources gratuites. Découvrez le code: 10 ressources en ligne gratuites et fantastiques pour améliorer vos compétences. Découvrez le code: 10 ressources en ligne gratuites et fantastiques pour améliorer votre Compétences de codage. Un sujet qui est évité par beaucoup. Il existe une abondance de ressources et d'outils gratuits, tous disponibles en ligne. Bien sûr, vous pourriez suivre des cours sur le sujet dans un endroit proche… En savoir plus pour apprendre à coder.

Commencez par inclure les bibliothèques nécessaires:

#comprendre  #comprendre  #comprendre  #comprendre 

SPI et CÂBLE existe deux bibliothèques Arduino pour gérer la communication I2C. Adafruit_GFX et Adafruit_SSD1306 sont les bibliothèques que vous avez installées précédemment.

Ensuite, configurez l'affichage:

Adafruit_SSD1306 affichage (4);

Puis configurez toutes les variables nécessaires au lancement du jeu:

résolution int [2] = 128, 64, boule [2] = 20, (résolution [1] / 2); const int PIXEL_SIZE = 8, WALL_WIDTH = 4, PADDLE_WIDTH = 4, BALL_SIZE = 4, SPEED = 3; int playerScore = 0, aiScore = 0, playerPos = 0, aiPos = 0; char ballDirectionHori = 'R', ballDirectionVerti = 'S'; booléen inProgress = true;

Ceux-ci stockent toutes les données nécessaires au fonctionnement du jeu. Certains d'entre eux stockent l'emplacement de la balle, la taille de l'écran, l'emplacement du joueur, etc. Remarquez comment certains sont const ce qui signifie qu'ils sont constants et ne changeront jamais. Cela permet au compilateur Arduino d’accélérer les choses.

La résolution de l'écran et l'emplacement de la balle sont stockés dans tableaux. Les tableaux sont des collections d'objets similaires, et pour le ballon, stockez les coordonnées (X et Y). L'accès aux éléments dans les tableaux est simple (n'incluez pas ce code dans votre fichier):

résolution [1];

Comme les tableaux commencent à zéro, cela retournera le deuxième élément du tableau de résolution (64). La mise à jour des éléments est encore plus facile (encore une fois, n'incluez pas ce code):

balle [1] = 15;

À l'intérieur void setup(), configurer l'affichage:

void setup () display.begin (SSD1306_SWITCHCAPVCC, 0x3C); display.display (); 

La première ligne indique à la bibliothèque Adafruit les dimensions et le protocole de communication utilisés par votre moniteur (dans ce cas,, 128 X 64 et I2C). La deuxième ligne (display.display ()) indique à l'écran d'afficher tout ce qui est stocké dans la mémoire tampon (ce qui n'est rien).

Créez deux méthodes appelées DrawBall et eraseBall:

void drawBall (int x, int y) display.drawCircle (x, y, BALL_SIZE, WHITE);  void eraseBall (int x, int y) display.drawCircle (x, y, BALL_SIZE, BLACK); 

Ceux-ci prennent la X et y coordonnées de la balle et dessinez-la à l'écran à l'aide du bouton drawCircle méthode à partir des bibliothèques d’affichage. Cela utilise la constante BALL_SIZE défini plus tôt. Essayez de changer cela et voyez ce qui se passe. Cette méthode drawCircle accepte une couleur de pixel - NOIR ou BLANC. Comme il s'agit d'un affichage monochrome (une couleur), le blanc équivaut à l'activation d'un pixel et le noir l'éteint..

Maintenant, créez une méthode appelée moveAi:

null moveAi () eraseAiPaddle (aiPos); if (ball [1]> aiPos) ++ aiPos;  sinon si (balle [1] < aiPos)  --aiPos;  drawAiPaddle(aiPos); 

Cette méthode gère le déplacement du Intelligence artificielle ou AI joueur. Il s’agit d’un adversaire informatique assez simple: si la balle est au-dessus de la raquette, avancez. Si c'est en dessous de la pagaie, descendez. Assez simple, mais ça marche bien. Les symboles d’incrémentation et de décrémentation sont utilisés (++aiPos et -aiPos) pour ajouter ou soustraire un de la aiPosition. Vous pouvez ajouter ou soustraire un nombre plus important pour que l'IA se déplace plus rapidement, et donc être plus difficile à battre. Voici comment vous feriez cela:

aiPos + = 2;

Et:

aiPos - = 2;

le Plus égal et Moins égal les signes sont un raccourci pour en ajouter ou en soustraire deux de / à la valeur actuelle de aiPos. Voici une autre façon de faire cela:

aiPos = aiPos + 2;

et

aiPos = aiPos - 1;

Notez que cette méthode efface d'abord la palette, puis la dessine à nouveau. Cela doit être fait comme ça. Si la nouvelle position de la palette était dessinée, il y aurait deux palettes qui se chevauchent sur l'écran.

le drawNet méthode utilise deux boucles pour dessiner le filet:

void drawNet () for (int i = 0; i < (resolution[1] / WALL_WIDTH); ++i)  drawPixel(((resolution[0] / 2) - 1), i * (WALL_WIDTH) + (WALL_WIDTH * i), WALL_WIDTH);  

Cela utilise le WALL_WIDTH variables pour définir sa taille.

Créer des méthodes appelées drawPixels et effacerPixels. Tout comme les méthodes de boule, la seule différence entre ces deux est la couleur des pixels:

void drawPixel (int posX, int posY, int dimensions) pour (int x = 0; x < dimensions; ++x)  for (int y = 0; y < dimensions; ++y)  display.drawPixel((posX + x), (posY + y), WHITE);    void erasePixel(int posX, int posY, int dimensions)  for (int x = 0; x < dimensions; ++x)  for (int y = 0; y < dimensions; ++y)  display.drawPixel((posX + x), (posY + y), BLACK);   

Encore une fois, ces deux méthodes utilisent deux pour boucles pour dessiner un groupe de pixels. Plutôt que d'avoir à dessiner chaque pixel en utilisant les bibliothèques drawPixel méthode, les boucles dessinent un groupe de pixels basé sur les dimensions données.

le drawScore Cette méthode utilise les fonctions de texte de la bibliothèque pour écrire le lecteur et le score d’IA sur l’écran. Ceux-ci sont stockés dans playerScore et aiScore:

void drawScore () display.setTextSize (2); display.setTextColor (WHITE); display.setCursor (45, 0); display.println (playerScore); display.setCursor (75, 0); display.println (aiScore); 

Cette méthode a aussi un eraseScore contrepartie, qui active ou désactive les pixels.

Les quatre dernières méthodes sont très similaires. Ils dessinent et effacent le joueur et les pagaies de l'IA:

void erasePlayerPaddle (int row) erasePixel (0, row - (PADDLE_WIDTH * 2), PADDLE_WIDTH); erasePixel (0, row - PADDLE_WIDTH, PADDLE_WIDTH); erasePixel (0, row, PADDLE_WIDTH); erasePixel (0, row + PADDLE_WIDTH, PADDLE_WIDTH); erasePixel (0, row + (PADDLE_WIDTH + 2), PADDLE_WIDTH); 

Remarquez comment ils appellent le effacerPixel méthode créer plus tôt. Ces méthodes dessinent et effacent la palette appropriée.

Il y a un peu plus de logique dans la boucle principale. Voici le code complet:

#comprendre  #comprendre  #comprendre  #comprendre  Adafruit_SSD1306 affichage (4); résolution int [2] = 128, 64, boule [2] = 20, (résolution [1] / 2); const int PIXEL_SIZE = 8, WALL_WIDTH = 4, PADDLE_WIDTH = 4, BALL_SIZE = 4, SPEED = 3; int playerScore = 0, aiScore = 0, playerPos = 0, aiPos = 0; char ballDirectionHori = 'R', ballDirectionVerti = 'S'; booléen inProgress = true; void setup () display.begin (SSD1306_SWITCHCAPVCC, 0x3C); display.display ();  void loop () if (aiScore> 9 || playerScore> 9) // vérifie l'état du jeu inProgress = false;  if (inProgress) eraseScore (); eraseBall (balle [0], balle [1]); if (ballDirectionVerti == 'U') // déplace la balle vers le haut balle en diagonale [1] = balle [1] - SPEED;  if (ballDirectionVerti == 'D') // déplace la balle en diagonale balle [1] = balle [1] + VITESSE;  si (balle [1] <= 0)  // bounce the ball off the top ballDirectionVerti = 'D';  if (ball[1] >= résolution [1]) // faire rebondir la balle par le bas ballDirectionVerti = 'U';  if (ballDirectionHori == 'R') ball [0] = ball [0] + SPEED; // déplace la balle si (balle [0]> = (résolution [0] - 6)) // la balle est au bord AI de l'écran si ((aiPos + 12)> = balle [1] && (aiPos - 12) <= ball[1])  // ball hits AI paddle if (ball[1] > (aiPos + 4)) // dévie la balle vers le bas ballDirectionVerti = 'D';  sinon si (balle [1] < (aiPos - 4))  // deflect ball up ballDirectionVerti = 'U';  else  // deflect ball straight ballDirectionVerti = 'S';  // change ball direction ballDirectionHori = 'L';  else  // GOAL! ball[0] = 6; // move ball to other side of screen ballDirectionVerti = 'S'; // reset ball to straight travel ball[1] = resolution[1] / 2; // move ball to middle of screen ++playerScore; // increase player score    if (ballDirectionHori == 'L')  ball[0] = ball[0] - SPEED; // move ball if (ball[0] <= 6)  // ball is at the player edge of the screen if ((playerPos + 12) >= balle [1] && (playerPos - 12) <= ball[1])  // ball hits player paddle if (ball[1] > (playerPos + 4)) // dévie la balle vers le bas ballDirectionVerti = 'D';  sinon si (balle [1] < (playerPos - 4))  // deflect ball up ballDirectionVerti = 'U';  else  // deflect ball straight ballDirectionVerti = 'S';  // change ball direction ballDirectionHori = 'R';  else  ball[0] = resolution[0] - 6; // move ball to other side of screen ballDirectionVerti = 'S'; // reset ball to straight travel ball[1] = resolution[1] / 2; // move ball to middle of screen ++aiScore; // increase AI score    drawBall(ball[0], ball[1]); erasePlayerPaddle(playerPos); playerPos = analogRead(A2); // read player potentiometer playerPos = map(playerPos, 0, 1023, 8, 54); // convert value from 0 - 1023 to 8 - 54 drawPlayerPaddle(playerPos); moveAi(); drawNet(); drawScore();  else  // somebody has won display.clearDisplay(); display.setTextSize(4); display.setTextColor(WHITE); display.setCursor(0, 0); // figure out who if (aiScore > playerScore) display.println ("VOUS PERDEZ!");  else if (playerScore> aiScore) display.println ("VOUS GAGNEZ!");  display.display ();  void moveAi () // déplace le curseur AI eraseAiPaddle (aiPos); if (ball [1]> aiPos) ++ aiPos;  sinon si (balle [1] < aiPos)  --aiPos;  drawAiPaddle(aiPos);  void drawScore()  // draw AI and player scores display.setTextSize(2); display.setTextColor(WHITE); display.setCursor(45, 0); display.println(playerScore); display.setCursor(75, 0); display.println(aiScore);  void eraseScore()  // erase AI and player scores display.setTextSize(2); display.setTextColor(BLACK); display.setCursor(45, 0); display.println(playerScore); display.setCursor(75, 0); display.println(aiScore);  void drawNet()  for (int i = 0; i < (resolution[1] / WALL_WIDTH); ++i)  drawPixel(((resolution[0] / 2) - 1), i * (WALL_WIDTH) + (WALL_WIDTH * i), WALL_WIDTH);   void drawPixel(int posX, int posY, int dimensions)  // draw group of pixels for (int x = 0; x < dimensions; ++x)  for (int y = 0; y < dimensions; ++y)  display.drawPixel((posX + x), (posY + y), WHITE);    void erasePixel(int posX, int posY, int dimensions)  // erase group of pixels for (int x = 0; x < dimensions; ++x)  for (int y = 0; y < dimensions; ++y)  display.drawPixel((posX + x), (posY + y), BLACK);    void erasePlayerPaddle(int row)  erasePixel(0, row - (PADDLE_WIDTH * 2), PADDLE_WIDTH); erasePixel(0, row - PADDLE_WIDTH, PADDLE_WIDTH); erasePixel(0, row, PADDLE_WIDTH); erasePixel(0, row + PADDLE_WIDTH, PADDLE_WIDTH); erasePixel(0, row + (PADDLE_WIDTH + 2), PADDLE_WIDTH);  void drawPlayerPaddle(int row)  drawPixel(0, row - (PADDLE_WIDTH * 2), PADDLE_WIDTH); drawPixel(0, row - PADDLE_WIDTH, PADDLE_WIDTH); drawPixel(0, row, PADDLE_WIDTH); drawPixel(0, row + PADDLE_WIDTH, PADDLE_WIDTH); drawPixel(0, row + (PADDLE_WIDTH + 2), PADDLE_WIDTH);  void drawAiPaddle(int row)  int column = resolution[0] - PADDLE_WIDTH; drawPixel(column, row - (PADDLE_WIDTH * 2), PADDLE_WIDTH); drawPixel(column, row - PADDLE_WIDTH, PADDLE_WIDTH); drawPixel(column, row, PADDLE_WIDTH); drawPixel(column, row + PADDLE_WIDTH, PADDLE_WIDTH); drawPixel(column, row + (PADDLE_WIDTH * 2), PADDLE_WIDTH);  void eraseAiPaddle(int row)  int column = resolution[0] - PADDLE_WIDTH; erasePixel(column, row - (PADDLE_WIDTH * 2), PADDLE_WIDTH); erasePixel(column, row - PADDLE_WIDTH, PADDLE_WIDTH); erasePixel(column, row, PADDLE_WIDTH); erasePixel(column, row + PADDLE_WIDTH, PADDLE_WIDTH); erasePixel(column, row + (PADDLE_WIDTH * 2), PADDLE_WIDTH);  void drawBall(int x, int y)  display.drawCircle(x, y, BALL_SIZE, WHITE);  void eraseBall(int x, int y)  display.drawCircle(x, y, BALL_SIZE, BLACK); 

Voici ce que vous vous retrouvez avec:

Une fois que vous êtes sûr du code, vous pouvez effectuer de nombreuses modifications:

  • Ajouter un menu pour les niveaux de difficulté (changer l'IA et la vitesse du ballon).
  • Ajoutez des mouvements aléatoires à la balle ou à l'IA.
  • Ajouter un autre pot pour deux joueurs.
  • Ajouter un bouton de pause.

Maintenant, jetez un coup d'œil à ces projets de jeu rétro Pi Zero. 5 projets de jeu rétro avec le Raspberry Pi Zero. 5 projets de jeu rétro avec le Raspberry Pi Zero. et inspirants nouveaux venus, en particulier dans l'esprit fébrile des fans de jeux rétro. Lire la suite .

Avez-vous codé Pong en utilisant ce code? Quelles modifications avez-vous apportées? Laissez-moi savoir dans les commentaires ci-dessous, j'aimerais regarder quelques photos!

Explorer plus sur: Arduino, Electronics, Retro Gaming.