Игра «Тетрис» на Arduino

В этом проекте мы создадим классическую игру «Тетрис» на платформе Arduino. Игровое поле и фигуры будут отображаться на LED матрице 8x8 с драйвером MAX7219, а для управления движением и вращением будет использоваться джойстик. Этот проект поможет вам освоить работу с матрицами LED и управлением пользователем с использованием Arduino.

1. Необходимые компоненты

  • Arduino Uno

  • LED матрица 8x8 с драйвером MAX7219

  • Джойстик

  • Соединительные провода

  • Макетная плата

2. Подключение компонентов

Подключение LED матрицы MAX7219:

  • VCC к 5V Arduino

  • GND к GND Arduino

  • DIN к цифровому пину 12 Arduino

  • CS к цифровому пину 10 Arduino

  • CLK к цифровому пину 11 Arduino

Подключение джойстика:

  • VCC к 5V Arduino

  • GND к GND Arduino

  • Ось X к аналоговому пину A0 Arduino

  • Ось Y к аналоговому пину A1 Arduino

  • Кнопка джойстика к цифровому пину 2 Arduino

Схема подключения матричной клавиатуры на Arduino

3. Установка библиотеки LedControl

Для работы с матрицей MAX7219 нам понадобится библиотека LedControl. Установите её следующим образом:

  • Откройте Arduino IDE

  • Перейдите в «Скетч» → «Подключить библиотеку» → «Управлять библиотеками»

  • Введите “LedControl” в строке поиска

  • Найдите библиотеку LedControl by Eberhard Fahle и установите её

4. Программирование Arduino

Загрузите следующий код в ваш Arduino. Этот код реализует простую игру «Тетрис» с управлением через джойстик и отображением на матрице MAX7219.

#include <LedControl.h>
#define DATA_IN 12
#define CLK 11
#define LOAD 10
LedControl lc = LedControl(DATA_IN, CLK, LOAD, 1);

#define JOYSTICK_X A0
#define JOYSTICK_Y A1
#define JOYSTICK_BTN 2

#define MATRIX_SIZE 8
int field[MATRIX_SIZE][MATRIX_SIZE] = {0};
int tetromino[4][4];
int tetrominoX = 2, tetrominoY = 0;
unsigned long lastMoveDown = 0;
unsigned long lastMoveSide = 0;
bool gameOver = false;

void setup() {
  lc.shutdown(0, false);
  lc.setIntensity(0, 8);
  lc.clearDisplay(0);
  pinMode(JOYSTICK_BTN, INPUT_PULLUP);
  newTetromino();
}

void loop() {
  if (gameOver) {
    delay(2000);
    resetGame();
    return;
  }
  int xValue = analogRead(JOYSTICK_X);
  int yValue = analogRead(JOYSTICK_Y);
  bool buttonPressed = digitalRead(JOYSTICK_BTN) == LOW;

  if (millis() - lastMoveSide > 250) {
    if (xValue < 400) moveLeft();
    else if (xValue > 600) moveRight();
    lastMoveSide = millis();
  }
  if (millis() - lastMoveDown > 700) {
    moveDown();
    lastMoveDown = millis();
  }
  if (buttonPressed) {
    rotateTetromino();
    delay(200);
  }
  drawField();
  delay(100);
}

void drawField() {
lc.clearDisplay(0);
// Отображение игрового поля
for (int y = 0; y < MATRIX_SIZE; y++) {
    for (int x = 0; x < MATRIX_SIZE; x++) {
    if (field[y][x]) lc.setLed(0, y, x, true);
    }
}
// Отображение текущей фигуры
for (int y = 0; y < 4; y++) {
    for (int x = 0; x < 4; x++) {
    if (tetromino[y][x]) {
        lc.setLed(0, tetrominoY + y, tetrominoX + x, true);
    }
    }
}
}
void newTetromino() {
// Определение форм тетромино
int tetrominoShapes[7][4][4] = {
    {{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}},
    {{0, 0, 0, 0}, {1, 1, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}},
    {{0, 0, 0, 0}, {1, 1, 1, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}},
    {{0, 0, 0, 0}, {1, 1, 1, 0}, {0, 0, 1, 0}, {0, 0, 0, 0}},
    {{0, 0, 0, 0}, {0, 1, 1, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}},
    {{0, 0, 0, 0}, {1, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}},
    {{0, 0, 0, 0}, {1, 1, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}}
};
// Выбор случайной формы
int shape = random(7);
for (int y = 0; y < 4; y++) {
    for (int x = 0; x < 4; x++) {
    tetromino[y][x] = tetrominoShapes[shape][y][x];
    }
}
// Начальная позиция фигуры
tetrominoX = 2;
tetrominoY = 0;
if (collides()) {
    gameOver = true; // Игра окончена, если новая фигура сталкивается
}
}
void rotateTetromino() {
int rotated[4][4];
// Вращение фигуры
for (int y = 0; y < 4; y++) {
    for (int x = 0; x < 4; x++) {
    rotated[x][3 - y] = tetromino[y][x];
    }
}
for (int y = 0; y < 4; y++) {
    for (int x = 0; x < 4; x++) {
    tetromino[y][x] = rotated[y][x];
    }
}
// Проверка столкновений после вращения
if (collides()) {
    // Восстановление предыдущего состояния, если есть столкновение
    for (int y = 0; y < 4; y++) {
    for (int x = 0; x < 4; x++) {
        tetromino[y][x] = rotated[3 - x][y];
    }
    }
}
}
void moveLeft() {
tetrominoX--; // Движение влево
if (collides()) {
    tetrominoX++; // Отмена движения, если есть столкновение
}
}
void moveRight() {
tetrominoX++; // Движение вправо
if (collides()) {
    tetrominoX--; // Отмена движения, если есть столкновение
}
}
void moveDown() {
tetrominoY++; // Движение вниз
if (collides()) {
    tetrominoY--; // Отмена движения, если есть столкновение
    placeTetromino(); // Установка фигуры на поле
    clearLines(); // Очистка заполненных линий
    newTetromino(); // Создание новой фигуры
}
}
void placeTetromino() {
// Размещение текущей фигуры на поле
for (int y = 0; y < 4; y++) {
    for (int x = 0; x < 4; x++) {
    if (tetromino[y][x]) {
        field[tetrominoY + y][tetrominoX + x] = 1;
    }
    }
}
}
void clearLines() {
// Очистка заполненных линий
for (int y = 0; y < MATRIX_SIZE; y++) {
    bool fullLine = true;
    for (int x = 0; x < MATRIX_SIZE; x++) {
    if (!field[y][x]) {
        fullLine = false;
        break;
    }
    }
    // Сдвиг линий вниз, если линия заполнена
    if (fullLine) {
    for (int ny = y; ny > 0; ny--) {
        for (int x = 0; x < MATRIX_SIZE; x++) {
        field[ny][x] = field[ny - 1][x];
        }
    }
    // Очистка верхней линии
    for (int x = 0; x < MATRIX_SIZE; x++) {
        field[0][x] = 0;
    }
    }
}
}
bool collides() {
// Проверка на столкновения
for (int y = 0; y < 4; y++) {
    for (int x = 0; x < 4; x++) {
    if (tetromino[y][x]) {
        int nx = tetrominoX + x;
        int ny = tetrominoY + y;
        if (nx < 0 || nx >= MATRIX_SIZE || ny < 0 || ny >= MATRIX_SIZE || field[ny][nx]) {
        return true;
        }
    }
    }
}
return false;
}
void resetGame() {
// Сброс игрового поля
for (int y = 0; y < MATRIX_SIZE; y++) {
    for (int x = 0; x < MATRIX_SIZE; x++) {
    field[y][x] = 0;
    }
}
gameOver = false; // Сброс флага окончания игры
newTetromino(); // Создание новой фигуры
}

5. Пояснения к коду

  • Инициализация библиотеки LedControl: используется для управления матрицей MAX7219

  • Настройка матрицы: установка яркости и очистка экрана

  • Создание переменных: игровое поле, фигура, координаты

  • Управление джойстиком: считывание X, Y и кнопки

  • Движение и вращение: реализация движений и поворотов фигуры

  • Отображение: функция drawField() рисует поле и фигуру

  • Обработка столкновений: проверка границ и наложения

  • Перезапуск игры: функция resetGame() очищает поле и начинает заново

6. Заключение

Проект «Тетрис на Arduino с использованием матрицы MAX7219 и джойстика» предоставляет увлекательный способ изучения основ программирования и работы с аппаратными компонентами. Этот проект подходит как для начинающих, так и для опытных пользователей, интересующихся созданием интерактивных игр и работой с микроконтроллерами.

Вы также можете расширить проект:

  • Подключить дополнительные матрицы

  • Добавить LCD-дисплей

  • Реализовать уровни сложности и счёт

  • Добавить музыку и звуковые эффекты

✨ Всё ограничивается лишь вашей фантазией!