Как сделать тетрис на ардуино

Обновлено: 05.07.2024

В предыдущих статьях этой серии мы уже успели написать сапёра, змейку и десктопный клон игры 2048. Попробуем теперь написать свой Тетрис.

Нам, как обычно, понадобятся:

  • 30 минут свободного времени;
  • Настроенная рабочая среда, т.е. JDK и IDE (например, Eclipse);
  • Библиотека LWJGL (версии 2.x.x) для работы с графикой (опционально). Обратите внимание, что для LWJGL версий выше 3 потребуется написать код, отличающийся от того, что приведён в статье;
  • Спрайты, т.е. картинки плиток всех возможных состояний (пустая, и со степенями двойки до 2048). Можно нарисовать самому, или скачать использовавшиеся при написании статьи.

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

С чего начать?

Начать стоит с главного управляющего класса, который в нашем проекте находится выше остальных по уровню абстракции. Вообще отличный совет — в начале работы всегда пишите код вида if (getKeyPressed()) doSomething() , так вы быстро определите фронт работ.

Это наш main() . Он ничем принципиально не отличается от тех, что мы писали в предыдущих статьях — мы всё так же инициализируем поля и, пока игра не закончится, осуществляем по очереди: ввод пользовательских данных ( input() ), основные игровые действия ( logic() ) и вызов метода отрисовки у графического модуля ( graphicsModule.draw() ), в который передаём текущее игровое поле ( gameField ). Из нового разве что метод sync — метод, который должен будет гарантировать нам определённую частоту выполнения итераций. С его помощью мы сможем задать скорость падения фигуры в клетках-в-секунду.

Вы могли заметить, что в коде использована константа FPS . Все константы удобно определять в классе с public static final полями. Полный список констант, который нам потребуется в ходе разработки, можно посмотреть в классе Constants на GitHub.

Оставим пока инициализацию полей на потом (мы же ещё не знаем, какие нам вообще понадобятся поля). Разберёмся сначала с input() и logic() .

Получение данных от пользователя

Код, честно говоря, достаточно капитанский:

Все данные от ввода мы просто сохраняем в соответствующие поля, действия на основе них будет выполнять метод logic() .

Теперь уже потихоньку становится понятно, что нам необходимо. Во-первых, нам нужны клавиатурный и графический модули. Во-вторых, нужно как-то хранить направление, которое игрок выбрал для сдвига. Вторая задача решается просто — создадим enum с тремя состояниями: AWAITING, LEFT, RIGHT . Зачем нужен AWAITING ? Чтобы хранить информацию о том, что сдвиг не требуется (использования в программе null следует всеми силами избегать). Перейдём к интерфейсам.

Интерфейсы для клавиатурного и графического модулей

Так как многим не нравится, что я пишу эти модули на LWJGL, я решил в статье уделить время только интерфейсам этих классов. Каждый может написать их с помощью той GUI-библиотеки, которая ему нравится (или вообще сделать консольный вариант). Я же по старинке реализовал их на LWJGL, код можно посмотреть здесь в папках graphics/lwjglmodule и keyboard/lwjglmodule.

Интерфейсы же, после добавления в них всех упомянутых выше методов, будут выглядеть следующим образом:

Отлично, мы получили от пользователя данные. Что дальше?

А дальше мы должны эти данные обработать и что-то сделать с игровым полем. Если пользователь сказал сдвинуть фигуру куда-то, то передаём полю, что нужно сдвинуть фигуру в таком-то направлении. Если пользователь сказал, что нужно фигуру повернуть, поворачиваем, и так далее. Кроме этого нельзя забывать, что 1 раз в FRAMES_PER_MOVE (вы же открывали файл с константами?) итераций нам необходимо сдвигать падающую фигурку вниз.

Сюда же добавим проверку на переполнение поля (в Тетрисе игра завершается, когда фигурам некуда падать):

Так, а теперь мы напишем класс для того магического gameField, в который мы всё это передаём, да?

Не совсем. Сначала мы пропишем поля класса Main и метод initFields() , чтобы совсем с ним закончить. Вот все поля, которые мы использовали:

А инициализировать мы их будем так:

Если вы решили не использовать LWJGL и написали свои классы, реализующие GraphicsModule и KeyboardHandleModule , то здесь нужно указать их конструкторы вместо, соответственно new LwjglGraphicsModule() и new LwjglKeyboardHandleModule() .

А вот теперь мы переходим к классу, который отвечает за хранение информации об игровом поле и её обновление.

Класс GameField

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

Начнём по порядку.

Хранить информацию о поле…

…и о падающей фигуре

TpReadableColor — простой enum, содержащий элементы с говорящими названиями (RED, ORANGE и т.п.) и метод, позволяющий получить случайным образом один из этих элементов. Ничего особенного в нём нет, код можно посмотреть тут.

Это все поля, которые нам понадобятся. Как известно, поля любят быть инициализированными.
Сделать это следует в конструкторе.

Конструктор и инициализация полей

Далее в конструкторе стоит заполнить массив theField значениями константы EMPTINESS_COLOR , а countFilledCellsInLine — нулями (второе в Java не требуется, при инициализации массива все int‘ы равны 0). Или можно создать несколько слоёв уже заполненных ячейкам — на GitHub вы можете увидеть реализацию именно второго варианта.

А что это там за spawnNewFigure()? Почему инициализация фигуры вынесена в отдельный метод?

Вы правильно догадались, spawnNewFigure() действительно инициализирует поле figure . А в отдельный метод это вынесено, потому что нам придётся делать инициализацию каждый раз, когда будет создаваться новая фигура.

На этом с хранением данных мы закончили. Переходим к методам, которые отдают информацию о поле другим классам.

Методы, передающие информацию об игровом поле

Таких метода всего два. Первый возвращает цвет ячейки (для графического модуля):

А второй сообщает, переполнено ли поле (как это происходит, мы разобрали выше):

Методы, обновляющие фигуру и игровое поле

Начнём реализовывать методы, которые мы вызывали из Main.logic() .

Сдвиг фигуры

Что мы сделали в этом методе? Мы запросили у фигуры ячейки, которые бы она заняла в случае сдвига. А затем для каждой из этих ячеек мы проверяем, не выходит ли она за пределы поля, и не находится ли по её координатам в сетке статичный блок. Если хоть одна ячейка фигуры выходит за пределы или пытается встать на место блока — сдвига не происходит. Coord здесь — класс-оболочка с двумя публичными числовыми полями (x и y координаты).

Поворот фигуры

Логика аналогична сдвигу:

Падение фигуры

Сначала код в точности повторяет предыдущие два метода:

Так как в результате переноса ячеек какая-то линия может заполниться полностью, после каждого добавления ячейки мы проверяем линию, в которую мы её добавили, на полноту:

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

Теперь GameField реализован почти полностью — за исключением геттера для фигуры. Её ведь графическому модулю тоже придётся отрисовывать:

Теперь нам нужно написать алгоритмы, по которым фигура определяет свои координаты в разных состояниях. Да и вообще весь класс фигуры.

Класс фигуры

Rotation мод здесь будет выглядеть таким образом:

Соответственно, от самого класса Figure нам нужен только конструктор, инициализирующий поля:

И методы, которыми мы пользовались в GameField следующего вида:

Вдобавок, у фигуры должен быть цвет, чтобы графический модуль мог её отобразить. В тетрисе каждой фигуре соответствует свой цвет, поэтому цвет мы будем запрашивать у формы:

Форма фигуры и маски координат

Чтобы не занимать лишнее место, здесь я приведу реализацию только для двух форм: I-образной и J-образной. Код для остальных фигур принципиально не отличается и выложен на GitHub.

Реализуем методы, которые использовали выше:

Ну а сами маски координат я предлагаю просто захардкодить следующим образом:

Т.е. для каждого объекта enum‘а мы передаём с помощью импровизированных (других в Java нет) делегатов метод, в котором в зависимости от переданного состояния поворота возвращаем разные реальные координаты блоков. В общем-то, можно обойтись и без делегатов, если хранить в каждом элементе отсупы для каждого из режимов поворота.

После первого эксперимента с ардуино, джойстиком и сервомотором я начал думать, как еще можно использовать стик. Так как у меня был дисплей 128x64 для arduino, было решено сделать что-нибудь с ним. Наиболее простой вариант - передвигать фигуру по дисплею вслед за манипуляциями джойстика. Итак, у нас в наличии: аналог arduino uno, джойстик и дисплей 128x64 точек.

Сервомотор для arduino

Дисплей 128x64 для arduino

Arduino uno (клон)

Теперь собираем схему. Подключаем джойстик к arduino:

  • gnd - gnd;
  • 5v - 5v;
  • VRX (сигнал для координат по оси X) - A1;
  • VRY (сигнал для координат по оси Y) - A0;
  • SW (режим кнопки - нажата или отпущена) - 2 пин.

Подключаем дисплей к arduino:

  • gnd - gnd;
  • vcc - 3.3v;
  • scl - A5;
  • sda - A4.

Открываем Arduino IDE, запускаем следующий скетч:

После загрузки скетча в центре дисплея отрисуется прямоугольник, который можно передвигать по осям X и Y манипулируя джойстиком. Вот видео с результатами работы:

Итак, в данной статье рассмотрен процесс подключения дисплея 128x64 точек и джойстика к Arduino Uno, приведен пример скетча, позволяющего двигать прямоугольник на дисплее манипулируя джойстиком.

В дальнейшем планирую написать простую игру наподобие тетриса или арканоида используя arduino uno, джойстик и дисплей.

Установка Webmin для Armbian (Orange Pi / Raspberry Pi)

Установка webmin для операционной системы Armbian (Orange Pi / Raspberry Pi). Install webmin for armbian.

Yii2 advanced доступ из backend к frontend кэшу

В заметке описан способ доступа к фронтэнд (frontend) кэшу (cache) advanced приложения из бэкэнда (backend) для php фреймворка Yii2.

Приехал мне из китая вот такой удобный ЛСД шилд для ардуино Уно , но так же он подходит и для версии MEGA .

Шил по сути удобная платформа с дисплеем 1602 и 6ю кнопками для подключения к ардуино - Бутербродом .

Все это легко монтируется , как я раньше без него то жил ?

Купить платы можно на Алиэкспресс :

Для начала нужно залить скетч игры в вашу плату ардуино и настроить контрастность дисплея , а для игры необходимо нажать кнопку SELECT.



Функционал кнопок:

• UP - движение вверх

• DOWN - движение вниз

• RIGHT - ускорение

• LEFT - пауза

• SELECT - продолжить игру (пауза), старт новой игры



Для динамики игры, помимо ручного ускорения, каждые 5 секунд происходит программное увеличение скорости звездолета. На 25 секунде включается максимальная сложность "UNREAL" и скорость больше не растет.

После каждой игры на дисплее высвечивается время текущей игры, а также время лучшей попытки.

Сам код , работает на любой версии Ардуино ИДЕ

Подписывайся на Geek каналы :

★ Моя партнёрка с Aliexpress ★

★ Получай 10.5% скидку с любой покупки на Aliexpress! ★

★ Полезное браузерное приложение для кэшбэка ★

Похожие статьи

Сенсорная кнопка и Arduino

Сенсорная кнопка и Arduino

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

Вероятно, многим может показаться, что использовать подобный модуль непросто и периодически придется иметь дело с неожиданным результатом. Что ж, оказывается, что все не так. Сенсорная кнопка отлично справляется с поставленными задачами и не преподносит никаких сюрпризов, хотя стоимость и настораживает.

 Подключение OLED I2C дисплея с размером экрана 0,96″ и разрешением 128*64 точек к плате Arduino UNO.

Подключение OLED I2C дисплея с размером экрана 0,96″ и разрешением 128*64 точек к плате Arduino UNO.

Сегодня мы рассмотрим подключение OLED I2C дисплея с размером экрана 0,96″ и разрешением 128*64 точек к плате Arduino UNO.

Делаем весы на ардуино Arduino , работа с тензонометрическим датчиком и платой HX711

Делаем весы на ардуино Arduino , работа с тензонометрическим датчиком и платой HX711

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

Подключение геймпада \ джойстика к Arduino , беспроводной джойстик 2 4 dualshock от PS2

Подключение геймпада \ джойстика к Arduino , беспроводной джойстик 2 4 dualshock от PS2

В данной статье я продемонстрирую простой способ подключения беспроводного геймпада 2.4 от плейстейшен к плате Arduino UNO

Выставка электроники Hong Kong Electronics Fair 2019 которую стоит посетить

Выставка электроники Hong Kong Electronics Fair 2019 которую стоит посетить

Почему стоит посещать выставки? На хорошей Экспо всегда можно увидеть, что нас ждёт в ближайшее время, какие веяния и тенденции будут актуальными в ближайшие полгода. Hong Kong Electronics Fair – как раз одна из таких выставок, где экспоненты демонстрируют на что они способны, а мы – гости мероприятия знакомимся и активно тестируем продукты, оцениваем их и решаем, что станет хитом, что просто заслуживает интереса, а что обречено лежать без внимания на стенде. Напомним, что все это проводится под крышей красивейшего выставочного центра Гонконга – Hong Kong Convention & Exhibition Centre .

  • Опубліковано 22 лис 2018
  • Сегодня будем разбирать игру Tetris и её реализацию на Arduino.
    Качай Vikings: War of Clans и получи 200 💰голды и 🏥 бесплатный щит:
    ➤ Android: bit.ly/2qLTtwU ➤ IOS: bit.ly/2K0MEA3
    Конкурс MacbookPro - club168701342

КОМЕНТАРІ • 1 088

Ошибочка вышла! Всё таки в памяти один светодиод занимает 3 байта, просто хранится это число другим способом, не в одной большой переменной :)

Саш давай уже большую матрицу и игру марио со звуком. Я бы с удовольствием сделал такую по твоей технологии)


Примечание: не подключайте Arduino к компьютеру без внешнего питания 5V, так как лента потребляет большой ток!

Библиотеки

Для работы с кнопкой используется “мини библиотека” кнопки, которая просто обрабатывает клики с гашением дребезга:

Программа

Зададим константами все настройки и пины проекта:

Подключим библиотеку ленты и создадим ленту:

Также создадим две кнопки (структура описана выше в главе Библиотеки):

В блоке setup() настроим ленту, пин пищалки, а также запустим новую игру:

Функция новой игры мигает зонами игроков тремя цветами и пищит, после чего задаёт начальные условия:

Где newRound() и задаёт стартовые значения скорости и позиции шарика:

Выше использовалась функция blinkTone() , давайте посмотрим что у неё внутри:

Данная функция моргает и пищит с заданной частотой и временем. Моргает здесь функция fillZones() , которая просто красит начальный и конечный отрезки ленты указанными цветами. Она выглядит так:

В основном цикле программы у нас вызываются две функции: опрос кнопок и процесс самой игры:

Опрос кнопок – проверяем клик. Если игрок кликнул вне своей зоны – проиграл раунд. Кликнул внутри зоны – мяч отбивается и происходит пересчёт скорости: чем ближе к краю ленты, тем сильнее отскок.

Тут же используется функция gameOver() , которая сигнализирует о проигрыше раунда у конкретного игрока:

Функция реализована не очень красиво, можно сократить её вдвое. Это ваше домашнее задание =)

Процесс игры – тут нас встречает таймер на 10мс, в котором происходит движение “шарика” и его отрисовка:

Внутри таймера двигаем шарик и проверяем, не вылетел ли он за ленту:

Если вылетел – выходим из функции, чтобы не отрисовывать шарик в несуществующем месте ленты!

Дальше очищаем ленту, заново рисуем зоны и поверх – текущею позицию шарика. Координаты у нас десятикратные, поэтому делим на 10:

Видео

Читайте также: