Создадим игровую консоль с игрой "арканоид" на основе Arduino UNO с TFT LCD экраном (240x320 пикселей), драйвером ILI9341 с 8-битной параллельной связью.
Демонстрация
Демонстрация игровой консоли представлена на видео ниже. Данный урок был опубликован благодаря поддержке бюро АСК Лаб - лаборатории по разработке электронных устройств на заказ.
Компоненты
Для создания нашей игровой консоли нам нужны будут следующие компоненты:
- Arduino Uno
- AZ-Delivery 2,4-дюймовый TFT ЖК-дисплей и шилд
В этом проекте используется 2,4-дюймовый TFT ЖК-дисплей AZ-Delivery с резистивным 4-проводным сенсорным экраном и встроенным устройством чтения SD-карт.
Из программного обеспечения нам традиционно нужна среда разработки Arduino IDE.
Сборка игровой консоли
С распиновкой нашего шилда можно ознакомиться ниже:
Чтобы собрать нашу игровую консоль всё что нам нужно только соединить экран с нашей платой Arduino Uno.
Игра
В этой игре есть несколько экранов с различными настраиваемыми рядами и столбцами кирпичей, до восьми рядов. Каждые две строки имеют разный цвет, который можно запрограммировать (включить или выключить) с помощью различных шаблонов.
Используя единственный мяч, используя сенсорную панель, игрок должен сбить как можно больше кирпичей, используя стены и ракетку внизу игрового поля, ударив мяч о кирпичи и уничтожив их.
Если ракетка игрока не попадает по мячу, - игрок теряет ход. Каждый кирпичик ряда приносит разные очки:
uint8_t pointsForRow[] = {7, 7, 5, 5, 3, 3 , 1, 1};
На каждом уровне можно настроить размер ракетки и размер мяча. Скорость мяча увеличивается с каждым ударом, вы можете настроить начальную скорость для каждого экрана. В зависимости от того, под каким углом падает мяч на ракетку и в какое место, - изменяется скорость мяча.
Вы можете определить различный вид игровых экранов:
Как играть?
Держите устройство руками, а большими пальцами по экрану перемещайте ракетку влево или вправо.

Код проекта
Скачать файл .ino для игровой консоли вы можете ниже. Код проекта целиком:
Определение нового экрана
Эта структура используется для определения нового экрана:
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
1,0,1,0,1,0,1,0
1,0,1,0,1,0,1,0
1,0,1,0,1,0,1,0
1,0,1,0,1,0,1,0
1,0,1,0,1,0,1,0
1,0,1,0,1,0,1,0
Получаем в итоге такую стену (обратите внимание, что она зеркальная):

Библиотеки
В начале программы мы подключаем нужные нам библиотеки:
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_TFTLCD.h>
#include <TouchScreen.h>
Калибровка сенсорного экрана
Вы должны откалибровать дисплей так, чтобы информация о местоположении была правильной при прикосновении к дисплею. Библиотека MCUFriend_kbv предоставляет пример с именем «TouchScreen_Calibr_native».
Пример отправляет результаты в последовательный порт. Запустите последовательный монитор IDE Arduino, чтобы вы могли скопировать код, созданный в примере.
Следуйте инструкциям на сенсорном дисплее, нажмите и удерживайте отображаемые маркеры положения, которые выделены белым. После того, как вы закончите с отметками положения, калибровка дисплея выводится вам на сенсорный дисплей и через последовательный порт.
Для этого проекта вам понадобятся данные для «портретной калибровки».
TouchScreen.h GFX Calibration
Making all control and bus pins INPUT_PULLUP
Typical 30k Analog pullup with corresponding pin
would read low when digital is written LOW
e.g. reads ~25 for 300R X direction
e.g. reads ~30 for 500R Y direction
Testing : (A2, D8) = 26
Testing : (A3, D9) = 28
ID = 0x9341
cx=153 cy=103 cz=534 X, Y, Pressure
cx=150 cy=475 cz=406 X, Y, Pressure
cx=155 cy=868 cz=231 X, Y, Pressure
cx=517 cy=103 cz=561 X, Y, Pressure
cx=535 cy=855 cz=364 X, Y, Pressure
cx=884 cy=88 cz=650 X, Y, Pressure
cx=908 cy=478 cz=557 X, Y, Pressure
cx=902 cy=864 cz=488 X, Y, Pressure
*** COPY-PASTE from Serial Terminal:
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)
LANDSCAPE CALIBRATION 320 x 240
x = map(p.y, LEFT=72, RT=887, 0, 320)
y = map(p.x, TOP=931, BOT=118, 0, 240)
Анимация
Чтобы перемещать изображение по экрану с течением времени, нужно использовать статическую скорость и применять ее к положению изображения на каждом временном шаге.
pos += vel * dt;
Избегаем арифметики с плавающей запятой
Разрешение ILI9341 составляет 240 x 320, поэтому нам нужны два 9-битных целых числа для ссылки на пиксель на экране. Использование 16-битных целых чисел позволяет освободить 6 бит для представления десятичной части.
nnnn nnnn nndd dddd
Мы называем это число 6 двоичной экспонентой. И мы можем использовать эти шесть битов для получения десятичной части от 0,000 до 0,63. Таким образом, мы можем использовать целочисленную математику, избегая арифметики с плавающей запятой.
Чтобы получить целую часть числа, делаем арифметический сдвиг вправо.
число >> экспонента (number >> exponent)
state.ballx += state.velx;
state.bally += state.vely;
// check ball collisions and exit
checkBallCollisions(game, &state, state.ballx >> game->exponent, state.bally >> game->exponent);
checkBallExit(game, &state, state.ballx >> game->exponent, state.bally >> game->exponent);
Демо-режим
Раскомментируйте директиву define
, и ракетка будет следовать за мячом, как показано в демо-видео:
#define DEMO_MODE
Теперь вы можете самостоятельно создавать новые шаблоны, уровни и усложнять игру.