Tetris sur Arduino Uno avec écran OLED SSD1306

Le jeu Tetris sur Arduino Uno avec écran OLED i2c 128×64 et boutons est un projet simple pour les débutants. Le schéma d’assemblage du jeu Tetris sur Arduino Uno avec écran est aussi simple que possible, de sorte que n’importe quel débutant sera en mesure de le répéter. Nous avons placé ici le schéma de connexion de l’écran au microcontrôleur Arduino, ainsi que le programme de création d’un jeu Tetris sur display SSD1306 OLED i2c.


Pour cette activité, nous aurons besoin:

  • Arduino Uno / Arduino Nano / Arduino Mega
  • oled display 128×64 i2c
  • boutons-poussoirs
  • buzzer
  • breadboard
  • fils de connexion
  • librairies Adafruit_SSD1306.h, Adafruit_GFX.h

Matériel supplémentaire sur ce sujet:

  1. Branchement afficheur OLED i2c avec Arduino
  2. Projet Arduino avec boutons «Déchiffrez le code»
  3. Jeu Arduino avec LEDs «Répétez la séquence»

Jeu Tetris Arduino avec écran OLED 128×64

Jeu Tetris Arduino avec écran OLED SSD1306 128×64
Jeu Tetris Arduino avec écran OLED SSD1306 128×64
OLED i2c 0,96 Arduino Uno Arduino Nano Arduino Mega
GND GND GND GND
VDD 5V 5V 5V
SDA A4 A4 20
SCL A5 A5 21

L’écran OLED est connecté à la carte Arduino via le protocole i2c (voir le tableau de connexion de l’écran à Arduino Mega). Les boutons sont connectés aux broches numériques de la carte et les broches peuvent être modifiées si nécessaire. Les broches des boutons sont configurées en mode input_pullup, donc aucune résistance n’est nécessaire pour la connexion. Après avoir assemblé le circuit, téléchargez le code « Tetris » sur l’Arduino Uno / Nano.

Programme pour jeu Tetris Arduino avec OLED i2c

#include "Wire.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"

#define WIDTH 64   // largeur de l'écran en pixels
#define HEIGHT 128 // hauteur de l'écran en pixels
#define BUZZER 12  // port de connexion du buzzer
#define left 2     // port de connexion du boutons
#define right 5
#define change 3
#define down 4

Adafruit_SSD1306 display(HEIGHT, WIDTH, &Wire, -1);

// matrice de la forme "S"
const char pieces_S_l[2][2][4] = {{ {0, 0, 1, 1}, {0, 1, 1, 2} },
                                  { {0, 1, 1, 2}, {1, 1, 0, 0} }};
// matrice de la forme "S"
const char pieces_S_r[2][2][4] = {{ {1, 1, 0, 0}, {0, 1, 1, 2} },
                                  { {0, 1, 1, 2}, {0, 0, 1, 1} }};
// matrice de la forme "L"
const char pieces_L_l[4][2][4] = {{ {0, 0, 0, 1}, {0, 1, 2, 2} },
                                  { {0, 1, 2, 2}, {1, 1, 1, 0} },
                                  { {0, 1, 1, 1}, {0, 0, 1, 2} },
                                  { {0, 0, 1, 2}, {1, 0, 0, 0} }};
// matrice de la forme "carrée"
const char pieces_Sq[1][2][4] = {{ {0, 1, 0, 1}, {0, 0, 1, 1} }};
// matriz de forma "T"
const char pieces_T[4][2][4] = {{ {0, 0, 1, 0}, {0, 1, 1, 2} },
                                { {0, 1, 1, 2}, {1, 0, 1, 1} },
                                { {1, 0, 1, 1}, {0, 1, 1, 2} },
                                { {0, 1, 1, 2}, {0, 0, 1, 0} }};
// matrice de la forme "I"
const char pieces_l[2][2][4] = {{ {0, 1, 2, 3}, {0, 0, 0, 0} },
                                { {0, 0, 0, 0}, {0, 1, 2, 3}}};
 
const short MARGIN_TOP = 19;
const short MARGIN_LEFT = 3;
const short SIZE = 5;
const short TYPES = 6;
const int MELODY_LENGTH = 10;
const int MELODY_NOTES[MELODY_LENGTH] = {262, 294, 330, 262};
const int MELODY_DURATIONS[MELODY_LENGTH] = {500, 500, 500, 500};

int click[] = { 1047 };
int click_duration[] = { 100 };
int erase[] = { 2093 };
int erase_duration[] = { 100 };
word currentType, nextType, rotation;
short pieceX, pieceY;
short piece[2][4];
int interval = 20, score;
long timer, delayer;
boolean grid[10][18];
boolean b1, b2, b3;

void checkLines() {
  boolean full;
  for (short y = 17; y >= 0; y--) {
    full = true;
    for (short x = 0; x < 10; x++) {
      full = full && grid[x][y];
    }
    if (full) { breakLine(y); y++; }
  }
}

void breakLine(short line) {
  tone(BUZZER, erase[0], 1000 / erase_duration[0]);
  delay(100);
  noTone(BUZZER);
  for (short y = line; y >= 0; y--) {
    for (short x = 0; x < 10; x++) {
      grid[x][y] = grid[x][y - 1];
    }
  }
  for (short x = 0; x < 10; x++) {
    grid[x][0] = 0;
  }
  display.invertDisplay(true);
  delay(50);
  display.invertDisplay(false);
  score += 10;
}

void refresh() {
  display.clearDisplay();
  drawLayout();
  drawGrid();
  drawPiece(currentType, 0, pieceX, pieceY);
  display.display();
}

void drawGrid() {
  for (short x = 0; x < 10; x++)
    for (short y = 0; y < 18; y++)
      if (grid[x][y])
        display.fillRect(MARGIN_LEFT + (SIZE + 1)*x, MARGIN_TOP + (SIZE + 1)*y, SIZE, SIZE, WHITE);
}

boolean nextHorizontalCollision(short piece[2][4], int amount) {
  for (short i = 0; i < 4; i++) {
    short newX = pieceX + piece[0][i] + amount;
    if (newX > 9 || newX < 0 || grid[newX][pieceY + piece[1][i]])
      return true;
  }
  return false;
}

boolean nextCollision() {
  for (short i = 0; i < 4; i++) {
    short y = pieceY + piece[1][i] + 1;
    short x = pieceX + piece[0][i];
    if (y > 17 || grid[x][y])
      return true;
  }
  return false;
}

void generate() {
  currentType = nextType;
  nextType = random(TYPES);
  if (currentType != 5)
    pieceX = random(9);
  else
    pieceX = random(7);
  pieceY = 0;
  rotation = 0;
  copyPiece(piece, currentType, rotation);
}
 
void drawPiece(short type, short rotation, short x, short y) {
  for (short i = 0; i < 4; i++)
    display.fillRect(MARGIN_LEFT + (SIZE + 1) * (x + piece[0][i]), MARGIN_TOP + (SIZE + 1) * (y + piece[1][i]), SIZE, SIZE, WHITE);
}
 
void drawNextPiece() {
  short nPiece[2][4];
  copyPiece(nPiece, nextType, 0);
  for (short i = 0; i < 4; i++)
    display.fillRect(50 + 3 * nPiece[0][i], 4 + 3 * nPiece[1][i], 2, 2, WHITE);
}

void copyPiece(short piece[2][4], short type, short rotation) {
  switch (type) {
    case 0: //L_l
      for (short i = 0; i < 4; i++) {
        piece[0][i] = pieces_L_l[rotation][0][i];
        piece[1][i] = pieces_L_l[rotation][1][i];
      }
      break;
    case 1: //S_l
      for (short i = 0; i < 4; i++) {
        piece[0][i] = pieces_S_l[rotation][0][i];
        piece[1][i] = pieces_S_l[rotation][1][i];
      }
      break;
    case 2: //S_r
      for (short i = 0; i < 4; i++) {
        piece[0][i] = pieces_S_r[rotation][0][i];
        piece[1][i] = pieces_S_r[rotation][1][i];
      }
      break;
    case 3: //Sq
      for (short i = 0; i < 4; i++) {
        piece[0][i] = pieces_Sq[0][0][i];
        piece[1][i] = pieces_Sq[0][1][i];
      }
      break;
    case 4: //T
      for (short i = 0; i < 4; i++) {
        piece[0][i] = pieces_T[rotation][0][i];
        piece[1][i] = pieces_T[rotation][1][i];
      }
      break;
    case 5: //l
      for (short i = 0; i < 4; i++) {
        piece[0][i] = pieces_l[rotation][0][i];
        piece[1][i] = pieces_l[rotation][1][i];
      }
      break;
  }
}

short getMaxRotation(short type) {
  if (type == 1 || type == 2 || type == 5)
    return 2;
  else if (type == 0 || type == 4)
    return 4;
  else if (type == 3)
    return 1;
  else
    return 0;
}

boolean canRotate(short rotation) {
  short piece[2][4];
  copyPiece(piece, currentType, rotation);
  return !nextHorizontalCollision(piece, 0);
}

void drawLayout() {
  display.drawLine(0, 15, WIDTH, 15, WHITE);
  display.drawRect(0, 0, WIDTH, HEIGHT, WHITE);
  drawNextPiece();
  char text[6];
  itoa(score, text, 10);
  drawText(text, getNumberLength(score), 7, 4);
}

short getNumberLength(int n) {
  short counter = 1;
  while (n >= 10) {
    n /= 10;
    counter++;
  }
  return counter;
}

void drawText(char text[], short length, int x, int y) {
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(x, y);
  display.cp437(true);
  for (short i = 0; i < length; i++)
    display.write(text[i]);
}

void setup() {
  pinMode(left, INPUT_PULLUP);
  pinMode(right, INPUT_PULLUP);
  pinMode(change, INPUT_PULLUP);
  pinMode(down, INPUT_PULLUP);
  pinMode(BUZZER, OUTPUT);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.setRotation(1);

  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(18, 40);
  display.println("TETRIS");
  display.setCursor(15, 60);
  display.println("ARDUINO");
  display.display();
 
  // appuyer sur le bouton "change" pour démarrer le jeu
  while (true) {
    if (!digitalRead(change)) {
      if (b3) { break; }
      b3 = false;
    }
    else { b3 = true; }
  }

  display.clearDisplay();

  drawLayout();
  display.display();
  randomSeed(analogRead(0));
  nextType = random(TYPES);
  generate();
  timer = millis();
}

void loop() {
  if (millis() - timer > interval) {
    checkLines();
    refresh();
    if (nextCollision()) {
      for (short i = 0; i < 4; i++)
        grid[pieceX + piece[0][i]][pieceY + piece[1][i]] = 1;
      generate();
    } else
      pieceY++;
    timer = millis();
  }

  // en cas d'appui sur le bouton "left"
  if (!digitalRead(left)) {
    tone(BUZZER, click[0], 1000 / click_duration[0]);
    delay(100);
    noTone(BUZZER);
    if (b1) {
      if (!nextHorizontalCollision(piece, -1)) {
        pieceX--;
        refresh();
      }
      b1 = false;
    }
  } else {
    b1 = true;
  }

  // en cas d'appui sur le bouton "right"
  if (!digitalRead(right)) {
    tone(BUZZER, click[0], 1000 / click_duration[0]);
    delay(100);
    noTone(BUZZER);
    if (b2) {
      if (!nextHorizontalCollision(piece, 1)) {
        pieceX++;
        refresh();
      }
      b2 = false;
    }
  } else {
    b2 = true;
  }

  // en cas d'appui sur le bouton "down"
  if (!digitalRead(down)) {
    interval = 20;
  } else {
    interval = 400;
  }

  // en cas d'appui sur le bouton "change"
  if (!digitalRead(change)) {
    tone(BUZZER, click[0], 1000 / click_duration[0]);
    delay(100);
    noTone(BUZZER);
    if (b3) {
      if (rotation == getMaxRotation(currentType) - 1 && canRotate(0)) {
        rotation = 0;
      } else if (canRotate(rotation + 1)) {
        rotation++;
      }
      copyPiece(piece, currentType, rotation);
      refresh();
      b3 = false;
      delayer = millis();
    }
  } else if (millis() - delayer > 50) {
    b3 = true;
  }
}

Explication du code pour jeu Tetris sur Arduino avec OLED i2c:

  1. les ports de connexion des boutons-poussoirs et du buzzer sont spécifiés dans le programme à l’aide de la directive #define;
  2. après le chargement du programme Tetris sur OLED SSD1306, pour démarrer le jeu, vous devez appuyer sur le bouton du haut (change).

Conclusion. Le but du jeu est de placer des blocs tombants de différentes formes sur le terrain de jeu de telle sorte qu’après chaque déplacement, vous obteniez une ligne régulière sans trous. Lorsque la ligne est entièrement remplie, elle disparaît et le joueur marque des points. Le but du jeu est de marquer le plus de points possible avant que toute la ligne ne soit remplie. Si vous avez des questions sur le projet Arduino, laissez-les dans les commentaires.





4 Replies to “Tetris sur Arduino Uno avec écran OLED SSD1306 i2c”

    1. Bonjour.
      Je n’ai pas travaillé avec ce module, je ne peux donc pas me prononcer.
      Mais il me semble qu’il est possible de faire du tetris sur cet écran

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. Découvrez comment les données de vos commentaires sont traitées.