Arduino Breakout на сенсорном TFT 2.4″: классическая аркада своими руками
Компоненты и материалы
| Компонент | Количество | Примечание |
|---|---|---|
| Arduino UNO | 1 | |
| AZ-Delivery 2.4" TFT LCD Touch Display Shield (ILI9341, 240x320, 8-бит параллель) | 1 |
Приложения и онлайн-сервисы
- Arduino IDE
О проекте
Мини-версия классического Breakout для Arduino UNO и TFT-экрана 2.4" (240x320), драйвер ILI9341, 8-бит параллельный интерфейс.
Игровой процесс
Несколько уровней с настраиваемым числом строк/столбцов кирпичей (до 8 строк), каждые две строки — свой цвет. Кирпичи можно включать/выключать разными паттернами. Одним мячом, управляя платформой сенсорным экраном, нужно сбить максимум кирпичей, используя стены и/или платформу для рикошета. Промах — потеря жизни.
Очки за строки:
uint8_t pointsForRow[] = {7, 7, 5, 5, 3, 3, 1, 1};
Каждый уровень настраивает размер платформы и мяча. Скорость мяча растёт при каждом ударе. Горизонтальная скорость зависит от точки удара платформы.
Можно добавлять новые уровни с разными паттернами стен:
Как играть
Держите устройство в руках, большими пальцами двигайте платформу влево/вправо по экрану.
Создание нового уровня
Структура для определения уровня:
typedef struct game_type {
int ballsize;
int playerwidth;
int playerheight;
int exponent;
int top;
int rows;
int columns;
int brickGap;
int lives;
int wall[GAMES_NUMBER];
int initVelx;
int initVely;
} game_type;
Добавить в массив:
game_type games[GAMES_NUMBER] = {
// ballsize, playerwidth, playerheight, exponent, top, rows, columns, brickGap, lives, wall[8], initVelx, initVely
{ 10, 60, 8, 6, 40 , 8, 8, 3, 3, {0x18, 0x66, 0xFF, 0xDB, 0xFF, 0x7E, 0x24, 0x3C} , 28, -28},
// ... остальные уровни
};
Паттерн стен
Стена — массив 8x8 бит.
Пример:
{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}
Соответствует битовому массиву:
1,0,1,0,1,0,1,0
1,0,1,0,1,0,1,0
... (8 строк)
Результат (зеркалируется):
Железо
Используется AZ-Delivery 2.4" TFT LCD с резистивным 4-проводным тачскрином и встроенным SD-ридером.
Подробнее в моей статье «Road testing an AZ-Delivery 2.4 TFT LCD Touch Display».
Сборка шилда
Просто вставляем шилд сверху на Arduino.
Библиотеки:
#include <Adafruit_GFX.h>
#include <Adafruit_TFTLCD.h>
#include <TouchScreen.h>
Калибровка тачскрина
Нужно откалибровать, чтобы координаты касания были точными. Библиотека MCUFriend_kbv даёт пример TouchScreen_Calibr_native. Результаты в Serial Monitor. Следуйте инструкциям на экране: нажимайте и удерживайте белые маркеры. После завершения калибровка выводится на экран и в порт.
Для проекта нужны данные PORTRAIT CALIBRATION:
const int XP=8,XM=A2,YP=A3,YM=9; //240x320 ID=0x9341
const int TS_LEFT=118,TS_RT=931,TS_TOP=72,TS_BOT=887;
PORTRAIT CALIBRATION 240 x 320
x = map(p.x, LEFT=118, RT=931, 0, 240)
y = map(p.y, TOP=72, BOT=887, 0, 320)
Анимация
Движение изображения: pos += vel * dt.
Избегание float-арифметики
Разрешение 240x320 → 9 бит на координату. В 16-битном числе 6 бит остаются для дробной части.
Формат: nnnn nnnn nndd dddd — 6 бит = бинарный экспонента. Дробная часть 0.000–0.63. Целая часть — арифметический сдвиг вправо: number >> exponent.
state.ballx += state.velx;
state.bally += state.vely;
checkBallCollisions(game, &state, state.ballx >> game->exponent, state.bally >> game->exponent);
checkBallExit(game, &state, state.ballx >> game->exponent, state.bally >> game->exponent);
Демо-режим
Раскомментируйте #define DEMO_MODE — платформа будет следовать за мячом (как в демо-видео).
Наслаждайтесь!
Создавайте новые паттерны и уровни, делитесь!
Код
Arduino Breakout (Arduino)
/* Arduino Touch TFT Breakout
Classic breakout game
Parts: Ardunio UNO + AZ-Delivery 2.4 TFT LCD Touch Shield
Public domain. Modified 07 11 2020 By Enrique Albertos */
// #define DEMO_MODE
#include <Adafruit_GFX.h>
#include <Adafruit_TFTLCD.h>
#include <TouchScreen.h>
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
#define PRIMARY_COLOR 0x4A11
#define PRIMARY_LIGHT_COLOR 0x7A17
#define PRIMARY_DARK_COLOR 0x4016
#define PRIMARY_TEXT_COLOR 0x7FFF
#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
#define LCD_RESET A4
Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);
#define LOWFLASH (defined(__AVR_ATmega328P__) && defined(MCUFRIEND_KBV_H_))
#define MINPRESSURE 40
#define MAXPRESSURE 1000
const int16_t XP = 8, XM = A2, YP = A3, YM = 9;
const int16_t TS_LEFT = 122, TS_RT = 929, TS_TOP = 77, TS_BOT = 884;
const TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
#define SCORE_SIZE 30
char scoreFormat[] = "%04d";
typedef struct gameSize_type { int16_t x, y, width, height; } gameSize_type;
gameSize_type gameSize;
uint16_t backgroundColor = BLACK;
int level;
const uint8_t BIT_MASK[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
uint8_t pointsForRow[] = {7, 7, 5, 5, 3, 3, 1, 1};
#define GAMES_NUMBER 16
typedef struct game_type { ... } game_type;
game_type games[GAMES_NUMBER] = { ... }; // 16 уровней
game_type* game;
typedef struct game_state_type { ... } game_state_type;
game_state_type state;
// SETUP
void setup() {
initTft(tft);
gameSize = {0, 0, tft.width(), tft.height()};
newGame(&games[0], &state, tft);
}
// LOOP
int selection = -1;
void loop(void) {
selection = readUiSelection(game, &state, selection);
drawPlayer(game, &state);
state.playerxold = state.playerx;
// ограничение макс скорости
if (abs(state.vely) > ((1 << game->exponent) - 1)) state.vely = ((1 << game->exponent) - 1) * ((state.vely > 0) - (state.vely < 0));
if (abs(state.velx) > ((1 << game->exponent) - 1)) state.velx = ((1 << game->exponent) - 1) * ((state.velx > 0) - (state.velx < 0));
state.ballx += state.velx;
state.bally += state.vely;
checkBallCollisions(game, &state, state.ballx >> game->exponent, state.bally >> game->exponent);
checkBallExit(game, &state, state.ballx >> game->exponent, state.bally >> game->exponent);
drawBall(state.ballx >> game->exponent, state.bally >> game->exponent, state.ballxold >> game->exponent, state.ballyold >> game->exponent, game->ballsize);
state.ballxold = state.ballx; state.ballyold = state.bally;
// ускорение
state.velx = (20 + (state.score >> 3)) * ((state.velx > 0) - (state.velx < 0));
state.vely = (20 + (state.score >> 3)) * ((state.vely > 0) - (state.vely < 0));
// следующий уровень
if (noBricks(game, &state) && level < GAMES_NUMBER) {
level++; newGame(&games[level], &state, tft);
} else if (state.remainingLives <= 0) {
gameOverTouchToStart(); state.score = 0; level = 0; newGame(game, &state, tft);
}
}
// Вспомогательные функции: newGame, setupState, updateLives, setupWall, drawBrick, noBricks, drawPlayer, drawBall, touchToStart, gameOverTouchToStart, updateScore, checkBrickCollision, checkCornerCollision, hitBrick, checkBorderCollision, checkBallCollisions, checkBallExit, setBrick, unsetBrick, isBrickIn, initTft, clearDialog, drawBoxedString, readUiSelection, waitForTouch
// ... (полный код в оригинале)
Схемы
Производственный процесс
- Создание игры в реальном времени на Arduino Nano с гироскопом MPU-6050 и OLED-дисплеем
- Создание классической игры Pong на Arduino UNO с OLED-дисплеем: пошаговый урок
- Создание портативного ёмкостного сенсорного пианино на Arduino: пошаговое руководство
- DIY USB-геймпад на Arduino: собственный высокопроизводительный контроллер
- Сборка консоли Pong 24×16 на Arduino Nano и MAX7219
- Гигантский анимированный LEGO-человечек: игра «Операция» на Arduino
- USB-BLE MIDI-адаптеры на Arduino: беспроводное подключение инструментов к iOS
- Pixel Chaser: игра на один тап с NeoPixel Ring и Arduino Nano
- Освоение распознавания и синтеза речи на Arduino Due
- Автоматизация игры «Динозавр» в Chrome на Arduino и LDR с Python