Игра Тетрис на Arduino и OLED дисплее


В данной статье мы рассмотрим создание всемирно известной игры "Тетрис" с помощью платы Arduino и OLED дисплее. На первый взгляд эта задача может показаться невообразимо сложной, но не волнуйтесь, я думаю с помощью нашего руководства ее решение не доставит вам особых хлопот.

Игра Тетрис на Arduino и OLED дисплее

Кратко принцип работы нашего проекта можно посмотреть в следующем видео.

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

  1. Плата Arduino Nano (купить на AliExpress).
  2. OLED дисплей с интерфейсом I2C и диагональю экрана 1.3 дюйма (1.3 inch I2C OLED display) (купить на AliExpress - для данного проекта выбирайте вариант дисплея с 4 контактами).
  3. Кнопки - 4 шт.
  4. Зуммер (купить на AliExpress).
  5. Батарейка на 9 В.
  6. Макетная плата.
  7. Соединительные провода.

Распиновка OLED дисплея

Назначение контактов (распиновка) OLED дисплея с интерфейсом I2C приведена на следующем рисунке.

Назначение контактов (распиновка) OLED дисплея с интерфейсом I2C

GND (Ground) - общий провод (земля).

VCC - контакт подачи питания на дисплей.

SCL (Serial Clock) - контакт для передачи синхросигналов в интерфейсе I2C.

SDA (Serial Data) - контакт для передачи данных в интерфейсе I2C.

Схема проекта

Схема проекта игры Тетрис на Arduino и OLED дисплее приведена на следующем рисунке.

Схема проекта игры Тетрис на Arduino и OLED дисплее

OLED-дисплей в нашем проекте подключается к плате Arduino Nano с помощью 4-контактного интерфейса I2C. Что касается кнопок, то вам необходимо подключить их к четырем цифровым входам на Arduino Nano. Этот процесс достаточно прост — просто подключите один контакт каждой кнопки к цифровому входу платы, а другой контакт — к контакту GND платы.

Затем подключите зуммер к цифровому выходу плате Arduino Nano. Наконец, чтобы подать питание на проект, подключите батарею 9 В к контакту Vin платы Arduino Nano (положительный провод) и контакту GND платы (отрицательный провод).

Объяснение кода программы для Arduino

Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.

Первым делом в коде программы подключим необходимые библиотеки для работы с OLED дисплеем.

Затем зададим ширину и высоту OLED дисплея в пикселях.

После этого инициализируем объект для работы с OLED дисплеем с помощью библиотеки Wire, подавая на контакт сброса (reset pin) -1, что значит что он не будет использоваться.

Шестнадцатеричное представление лого сайта circuit digest (откуда взят оригинал данной статьи):

3D массив, задающий форму деталей тетриса. Он представляет собой форму буквы "S" если ее повернуть влево. Его первый индекс (2) задает количество вращений фигуры, второй индекс (2) задает количество строк, а третий индекс (4) представляет собой количество столбцов. Значения в этом массиве представляют положение блоков внутри фрагмента.

3D массив, который задает форму буквы "S" повернутой вправо.

3D массив, который задает форму буквы "L" повернутой влево.

3D массив задающий квадрат 2x2, ему необходимо только одно вращение.

3D массив, который задает форму буквы "T" повернутой во всех возможных 4-х направлениях.

3D массив, который задает линию ("line") размером 1x4. Он включает две ротации - одну горизонтальную и одну вертикальную.

Далее инициализируем необходимые переменные, среди которых четыре переменных целого типа: для движения влево, вправо, изменения и скорости. Зададим им начальные значения 11, 9, 12 и 10 соответственно.

Следующий фрагмент кода реализует функции checkLines() и breakLine(short line) для очистки заполненных строк в игре.

checkLines() перебирает каждую строку сетки снизу вверх, проверяя, заполнена ли строка (все ячейки заняты) или нет. Если это так, она вызывает функцию BreakLine(), передавая номер строки в качестве параметра, чтобы очистить эту строку.

breakLine(short line) сначала воспроизводит звук «стирания», используя функцию tone() на контакте SPEAKER_PIN, который будет указывать на то, что линия очищена. Затем он сдвигает все строки выше очищенной строки на одну ячейку и очищает верхнюю строку. Это также добавляет 10 к счету за очистку линии. Наконец, функция инвертирует отображение светодиодной матрицы на 50 мс, чтобы создать визуальный эффект очищенной линии, а затем возвращает отображение в нормальное состояние.

refresh(): эта функция очищает дисплей, рисует макет, рисует сетку и, наконец, рисует текущую фигуру на дисплее.

drawGrid(): эта функция перебирает всю сетку и, если ячейка заполнена, рисует белый прямоугольник размера ячейки.

nextHorizontalCollision(short piece[2][4], int amount): эта функция проверяет, есть ли какое-либо столкновение с сеткой в ​​горизонтальном направлении. Для этого он проверяет каждую ячейку текущей фигуры и добавляет сумму к ее позиции x. Если новая позиция x находится за пределами сетки или уже занята, происходит столкновение, и функция возвращает true.

nextCollision(): эта функция проверяет, есть ли какие-либо столкновения с сеткой в ​​вертикальном направлении. Она делает это, проверяя каждую ячейку в текущей фигуре и добавляя единицу к ее позиции y. Если новая позиция y находится за пределами сетки или уже занята, происходит столкновение, и функция возвращает true.

generate() - эта функция устанавливает переменные для следующей фигуры тетриса, которая будет введена на игровое поле. Она устанавливает currentType таким же, как nextType, а затем генерирует новый nextType с помощью функции random().

Если currentType не является частью «O» (представленной значением типа 5), то для значения PieceX устанавливается случайное число от 0 до 8 (включительно), поскольку часть «O» всегда центрируется. В противном случае для фрагмента «O» значение PieceX устанавливается в случайное число от 0 до 6 (включительно).

Значение PieceY установлавивается равным 0, что указывает на то, что фигура будет начинаться с верха игрового поля. Значение поворота (rotation value) устанавливается в 0, что указывает на то, что фигура не была повернута.

Наконец, вызывается функция copyPiece() для копирования соответствующего фрагмента в массив фрагментов.

Функция drawPiece() принимает тип фигуры тетриса, ее вращение и текущие координаты x и y на игровом поле. Затем она использует цикл for для перебора четырех блоков фигуры и рисования каждого блока на игровом поле с помощью функции display.fillRect().

Функция drawNextPiece() рисует следующий фрагмент тетриса в поле предварительного просмотра в правой части игрового поля. Сначала она копирует следующий фрагмент в массив nPiece с помощью функции copyPiece(). Затем она проходит через четыре блока фрагмента и рисует каждый блок в поле предварительного просмотра с помощью функции display.fillRect().

refresh(): эта функция вызывается для обновления изображения на дисплее. Сначала она очищает дисплей, а затем рисует макет, сетку и текущию фигуру на экране.

drawGrid(): эта функция вызывается функцией Refresh() для рисования сетки на экране, представляющей уже упавшие блоки.

nextHorizontalCollision(): This function checks if the current piece will collide with any block in the next horizontal move. It does this by checking each of the 4 blocks that make up the piece, and determining whether moving it by amount spaces would make it overlap with any blocks that have already fallen.

nextCollision(): эта функция проверяет, столкнется ли текущая фигура с каким-либо блоком при следующем горизонтальном движении (то есть когда фигура опустится на одну строчку вниз). Она делает это, проверяя каждый из 4 блоков, составляющих фигуру, и определяя, приведет ли перемещение ее на количество ячеек к перекрытию с любыми уже упавшими блоками.

generate(): эта функция генерирует новую фигуру для управления игроком. Она устанавливает для currentType тип следующей фигуры, выбирает случайную позицию для начала фигуры (pieceX), устанавливает для PieceY значение 0 (верхняя часть игровой области), устанавливает вращение на 0 и вызывает copyPiece() для заполнения в массиве частей с соответствующими блоками для новой фигуры.

drawPiece(): эта функция вызывается для рисования текущей фигуры на экране. Для этого она перебирает каждый из 4 блоков, составляющих фигуру, и рисует для каждого из них белый квадрат в соответствующей позиции на экране.

drawNextPiece(): эта функция вызывается для рисования следующей фигуры на экране. Для этого она копирует позиции блоков для следующей фигуры в новый массив nPiece, а затем рисует небольшой предварительный просмотр фигуры в правом верхнем углу экрана.

copyPiece(): эта функция заполняет массив фигур соответствующими блоками для данного типа фигуры и направления вращения. Для этого она включает тип, чтобы определить, какой набор блоков использовать, а затем копирует их по частям на основе текущего вращения.

getMaxRotation(): эта функция возвращает максимальное количество вращений, которое может иметь данный тип фигуры. Она возвращает 2 для типов 1, 2 и 5, 4 для типов 0 и 4, 1 для типа 3 и 0 для любого другого типа.

canRotate(): эта функция проверяет, можно ли повернуть текущую фигуру на величину поворота. Это делается путем вызова функции copyPiece() для создания нового массива частей с повернутыми блоками, а затем вызова nextHorizontalCollision(), чтобы проверить, будет ли повернутая часть перекрываться с какими-либо упавшими блоками.

drawLayout() - эта функция отвечает за отрисовку основного макета игрового экрана, включая границу, разделительную линию, счет и следующую фигуру. Она вызывает функции drawNextPiece() и drawText() для рисования следующей фигуры и счета соответственно.

getNumberLength(int n) - вспомогательная функция, которая принимает на вход целое число и возвращает количество его цифр. Это полезно для определения длины счета, который необходимо вывести на экран.

drawText(char text[], short length, int x, int y) - рисует строку текста на экране по заданным координатам x и y. В качестве параметров она принимает текст, который нужно отрисовать, его длину и координаты x и y. Эта функция устанавливает размер, цвет, положение курсора и шрифт текста перед рисованием текста.

В функции setup() мы инициализируем входные и выходные контакты, устанавливаем связь через последовательный порт, инициализируем и очищаем OLED-дисплей и устанавливаем вращение дисплея. Затем на две секунды отображается логотип, а затем очищается дисплей и вызывается функция drawLayout() для рисования макета игры. Наконец, устанавливается случайное начальное число, генерируется первая фигура и запускается таймер.

В функции loop() мы проверяем, прошел ли заданный интервал времени, и выполняем необходимые действия, такие как проверка и очистка строк или генерация новой фигуры. Также мы считываем состояния кнопок и обрабатываем движение текущей фигуры, включая вращение и горизонтальное перемещение. Наконец, мы регулируем скорость игры в зависимости от нажатия определенной кнопки. Также мы генерируем звуковые эффекты с помощью функций tone() и noTone().

Заключение

В этом проекте мы рассмотрели как создать игру «Тетрис», используя плату Arduino и OLED-дисплей 128x64. Для программирования игры мы использовали язык C++ и Arduino IDE.

Мы начали с настройки оборудования, включая дисплей, кнопки и зуммер, а затем определили игровую логику. Мы использовали сетку для представления игрового поля и задали семь фигур с помощью массивов. Мы также создали функции для создания и вращения фигур, проверки столкновений и обновления игрового поля.

Далее мы определили игровой цикл и реализовали кнопки управления. Мы также добавили звуковые эффекты с помощью функции tone().

Наконец, мы создали простой пользовательский интерфейс игры с использованием OLED-дисплея. Мы отображали текущий счет и следующую фигуру, а также логотип Тетриса в начале игры.

В целом, этот проект демонстрирует, как создать базовую аркадную игру с использованием Arduino и некоторых простых компонентов. Код можно расширять и модифицировать для создания более сложных игр или добавления новых функций.

Код программы (скетча)

Источник статьи

(Проголосуй первым!)
Загрузка...
911 просмотров

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *