Введение
Со второго класса я увлекся созданием электронных игр в программной среде Scrаth. Scratch - инструмент создания разнообразных программных проектов: мультфильмов, игр, рекламных роликов, музыки, “живых” рисунков, интерактивных историй и презентаций, компьютерных моделей, обучающих программ. Но этот инструмент не позволяет создать новое программируемое устройство. А мне хотелось всегда создать какое-либо электронное и обязательно программируемое устройство, полезное для меня и моих друзей. Как – то я прочитал про Arduino – это электронный конструктор и удобная платформа быстрой разработки электронных устройств для новичков и профессионалов. Платформа пользуется огромной популярностью во всем мире, благодаря удобству и простоте языка программирования, а также открытой архитектуре и программному коду. Устройство программируется через USB без использования программаторов. Воссоединив пристрастие к играм, и возможности платформы Arduino, мы решили создать проект игры Тетрис.
Почему именно Тетрис? Многие великие вещи создаются случайно. Затем эта случайность становится мировым достоянием и меняет жизнь многих людей. Тетрис — одна из таких случайностей. Это простая логическая головоломка, написанная в 1985 году сотрудником Вычислительного центра при Академии наук СССР Алексеем, за небольшой промежуток времени обрела мировую известность. Ни одно поколение со времен 90-х играет с огромным довольствием в эту игру-головоломку и сегодня. Мы решили вдохнуть в нее еще одну жизнь и создали игру на базе платформы Arduino.
Цель проекта разработать программно - аппаратный комплекс электронной игры Тетрис на базе Arduino.
Задачи проекта:
исследовать платформу Arduino и ее возможности;
изучить язык программирования Arduino;
изучить принцип работы светодиодной матрицы и электронных часов, способ их подключения к платформе Arduino;
проанализировать принцип работы игры Тетрис
запрограммировать используемые модули и датчики;
модифицировать программу для управления игрой с мобильного телефона;
сделать выводы о проделанной работе и об актуальности применения проекта.
Объект исследования – логическая игра-головоломка Тетрис.
Предмет исследования: аппаратная вычислительная платформа Arduino и ее программная среда.
Методы исследования:
1. Теоретический метод – по обучающей и технической литературе, а также материалам сети Internet проведен анализ имеющегося в продаже оборудования для конструкции игры Тетрис
2. Практический метод исследования:
- была спроектирована и собрана электронная схема с подключением к персональному компьютеру;
- внесены существенные изменения в специализированное программное обеспечение игры;
- проведение испытания;
- корректировка ошибок.
3. Проектный метод – спроектировано устройство для игры Тетрис.
Актуальность данного проекта в том, что данная модель игры – головоломки Тетрис, не требует применение компьютера и подключения к интернету,что исключает необходимость времяпрепровождения ребенка за компьютером, а значит и снижает риск приобретения «компьютерной зависимости».
Проблема: можно ли создать игру тетрис на базе платформы Arduino, с использованием светодиодной матрицы и управлением с мобильного телефона.
«Тетрис» представляет собой головоломку, построенную на использовании геометрических фигур «тетрамино» - разновидности полимино, состоящих из четырёх квадратов. Полимино в том или ином виде использовались в настольных играх и головоломках задолго до создания «Тетриса». Идею «Тетриса» Пажитнову подсказала игра в петамино. Первоначальная версия игры была написана Пажитновым на языке Паскаль для компьютера «Электроника-60». Коммерческая версия игры — первая из многих последующих — была выпущена американской компанией SpectrumHoloByte (англ.) в 1987 году. В последующие годы «Тетрис» во множестве различных версий был портирован на великое множество устройств, включая все возможные компьютеры и игровые консоли, а также такие устройства, как графические калькуляторы, мобильные телефоны, ПК. Интересная история создания тетриса увлекла меня и мы решили вдохнуть в него еще одну новую жизнь, реализовав игру на платформе Arduino.
Для выполнения данного проекта необходимы базовые знания схемотехники электроники, некоторые умения по пайке компонентов и знание микроконтроллера Arduino и его язык программирования.
Arduino – это электронный конструктор, небольшая плата с собственным процессором и памятью. На плате также есть пара десятков контактов, к которым можно подключать всевозможные компоненты: лампочки, датчики, моторы, чайники, роутеры, магнитные дверные замки и вообще всё, что работает от электричества. В процессор Arduino можно загрузить программу, которая будет управлять всеми этими устройствами по заданному алгоритму. Таким образом, можно создать бесконечное количество уникальных классных гаджетов, сделанных своими руками и по собственной задумке. Arduino как и RaspberryPi относится к одноплатным компьютерам. Ядро команды разработчиков Arduino составляют: Массимо Банци, Девид Куартиллье, Том Иго, Джанлука Мартино, Девид Меллис и Николас Замбетти. С 2008 года в компании начался раскол. Джанлука Мартино зарегистрировал другую фирму, на которую сумел оформить авторские права на торговую марку Arduino в некоторых странах. Новая компания создала альтернативную ветвь продаж оригинальных продуктов Arduino на сайте arduino.org. Первоначальная компания контролирует продажи через сайт arduino.cc. Набор новых изделий на сайтах различается. Также существует две ветви Arduino IDE, поддерживающие разный набор плат и библиотек. Одинаковые названия и пересекающиеся номера версий IDE вносят путаницу. Насчитывается 20 разновидных плат Arduino, но большинство были лишь пробными, несерьёзными проектами, и на этих платформах никто особо не программирует. Однако, 5 проектов получились удачными: Arduino UNO, Arduino Leonardo, Arduino Mega, Arduino Mini, ArduinoNano. Рассмотрим принцип работы Arduino на плате – ArduinoMega.
К Arduino можно подключить всевозможные сенсоры. В русском языке существуют слова «датчик» и «сенсор», это синонимы и означают по сути одно и то же. Это отдельное устройство, которое способно измерять определённую физическую величину или реагировать на физическое явление и поставлять информацию об этом в виде электрического сигнала. Сенсоры различаются по тому, что они фиксируют. Существуют сенсоры для измерения температуры, расстояния, вибрации, звука, влажности, движения, магнитного поля и многие другие. Также они различаются по типу сигнала. Некоторые передают сигнал в виде переменного напряжения (аналоговые), некоторые в виде последовательности низкого и высокого напряжения (цифровые), другие меняют собственное сопротивление. Тип сигнала определяет, как физические показания проецируются на электрические величины пригодные для считывания. Далее, сенсоры различаются по протоколу. Он определяет как принимающая сторона (например, Arduino) должна интерпретировать передаваемый сенсором сигнал, чтобы получить значение измеряемой величины. Для одних сенсоров измеряемая величина прямо пропорциональна передаваемому напряжению, другие передают только «да / нет» с помощью двух величин напряжения, третьи передают свои показания в виде последовательности бит, где отсутствию напряжения соответствует 0, а высокому напряжению — 1. Протокол у каждого сенсора свой и определяется производителем датчика. Кроме того, сенсоры различаются по способу физического подключения. Большое количество Arduino-совместимых сенсоров легко подключаются по трём проводам. Другие сенсоры имеют контакты, подходящие для подключение через breadboard.
Для проекта была выбрана плата ArduinoMega. Мы изучили основные характеристики этой платы:
Питание
Arduino Mega может быть запитан от USB либо от внешнего источника питания - тип источника выбирается автоматически. В качестве внешнего источника питания (не USB) может использоваться сетевой AC/DC-адаптер или аккумулятор/батарея.
Напряжение внешнего источника питания может быть в пределах от 6 до 20 В. Однако, уменьшение напряжения питания ниже 7В приводит к уменьшению напряжения на выводе 5V, что может стать причиной нестабильной работы устройства. Использование напряжения больше 12В может приводить к перегреву стабилизатора напряжения и выходу платы из строя. С учетом этого, рекомендуется использовать источник питания с напряжением в диапазоне от 7 до 12В.
Выводы питания, расположенные на плате, перечислены ниже:
VIN. Напряжение, поступающее в Arduino непосредственно от внешнего источника питания (не связано с 5В от USB или другим стабилизированным напряжением). Через этот вывод можно как подавать внешнее питание, так и потреблять ток, когда устройство запитано от внешнего адаптера.
5V. На этот вывод поступает напряжение 5В от стабилизатора напряжения на плате, вне независимости от того, как запитано устройство: от адаптера (7 - 12В), от USB (5В) или через вывод VIN (7 - 12В). Запитывать устройство через выводы 5V или 3V3 не рекомендуется, поскольку в этом случае не используется стабилизатор напряжения, что может привести к выходу платы из строя.
3V3. 3.3В, поступающие от стабилизатора напряжения на плате. Максимальный ток, потребляемый от этого вывода, составляет 50 мА.
GND. Выводы земли.
IOREF. Этот вывод предоставляет платам расширения информацию о рабочем напряжении микроконтроллера Ардуино. В зависимости от напряжения, считанного с вывода IOREF, плата расширения может переключиться на соответствующий источник питания либо задействовать преобразователи уровней, что позволит ей работать как с 5В, так и с 3.3В-устройствами.
Память
В микроконтроллере ATmega2560 есть 256 КБ флеш-памяти программ (из которых 8 КБ используются загрузчиком), 8 КБ памяти SRAM и 4 КБ EEPROM
Входы и выходы
Уровень напряжения на выводах ограничен 5В. Максимальный ток, который может отдавать или потреблять один вывод, составляет 40 мА. Все выводы сопряжены с внутренними подтягивающими резисторами (по умолчанию отключенными) номиналом 20-50 кОм. Помимо этого, некоторые выводы Ардуино могут выполнять дополнительные функции:
Светодиод: 13. Встроенный светодиод, подсоединенный к выводу 13. При отправке значения HIGH светодиод включается, при отправке LOW - выключается.
В Arduino Mega 2560 есть 16 аналоговых входов, каждый из которых может представить аналоговое напряжение в виде 10-битного числа (1024 различных значения). По умолчанию, измерение напряжения осуществляется относительно диапазона от 0 до 5 В. Тем не менее, верхнюю границу этого диапазона можно изменить, используя вывод AREF и функцию analogReference().
Мы провели сравнительный аналих с другими платами Ардуино. Вот список основных отличий:
Плата Mega использует иной микроконтроллер: ATMega 2560. Но тактовая частота его равна 16МГц, так же как и в Уно.
В плате Mega большее количество цифровых пинов — 54 вместо 14 у платы Uno. И аналоговых — 16 / 6.
У платы Mega больше контактов, поддерживающих аппаратные прерывания: 6 против 2. Больше Serial портов — 4 против 1.
По объему памяти Uno тоже существенно уступает Megа. Flash -память 32/256, SRAM — 2/8, EEPROM — 4/1.
Исходя из всего этого можно сделать вывод, что для больших сложных проектов с программами большого размера и активным использованием различных коммуникационных портов лучше выбирать Mega.
После выбора платы, изучили характеристики и принцип работы светодиодной матрицы 16х16 адресных светодиодов WS2812b, Bluetooth модуль HC-06 для управления с телефона и модуля часов DS3231.Разработав схему подключения устройств (приложение №1 и №2), перешли к программированию.
Для начала программирования необходимо установить среду программирования на компьютер.
Язык программирования Arduino является стандартным C++ (используется компилятор AVR-GCC) с некоторыми особенностями, облегчающими новичкам написание первой работающей программы. Программы, написанные программистом Arduino, называются наброски (или иногда скетчи — варваризм от англ. sketch) и сохраняются в файлах с расширением ino. Эти файлы перед компиляцией обрабатываются препроцессором Arduino. Также существует возможность создавать и подключать к проекту стандартные файлы C++. Обязательную в C++ функцию main() препроцессор Arduino создает сам, вставляя туда необходимые «черновые» действия. Программист должен написать две обязательные для Arduino функции setup() и loop(). Первая вызывается однократно при старте, вторая выполняется в бесконечном цикле. В текст своей программы (скетча) программист не обязан вставлять заголовочные файлы используемых стандартных библиотек. Эти заголовочные файлы добавит препроцессор Arduino в соответствии с конфигурацией проекта. Однако пользовательские библиотеки нужно указывать. Менеджер проекта Arduino IDE имеет нестандартный механизм добавления библиотек. Библиотеки в виде исходных текстов на стандартном C++ добавляются в специальную папку в рабочем каталоге IDE. При этом название библиотеки добавляется в список библиотек в меню IDE. Программист отмечает нужные библиотеки, и они вносятся в список компиляции. Arduino IDE не предлагает никаких настроек компилятора и минимизирует другие настройки, что упрощает начало работы для новичков и уменьшает риск возникновения проблем. Простейшая Arduino-программа состоит из двух функций:
setup(): функция вызывается однократно при старте микроконтроллера
loop(): функция вызывается после setup () в бесконечном цикле все время работы микроконтроллера.
После установки ПО необходимо выбрать нужный тип платформы Arduino.
Программный код мы скачали в интернете, но после тестирования оказалось, что фигурки тетриса, которые двигаются у края матрицы и их надо перевернуть, пропадают за край матрицы.
Например:
До поворота:
б |
|||||
После поворота:
Или:
Для решения этой проблемы мы изменили программный код (приложение 2.)
В процедуре управление движением фигурок (tetris Routine) при нажатии кнопки поворот добавлен вызов функции (check Area(3)) проверки столкновения фигурки с полом, стенками или другими фигурками в куче при повороте. Изменения выделены красным.
В функции checkArea выполняется имитация поборота фигуры. Мы задаем вопрос: «А что будет, если мы повернем фигуру?»
if (check_type == 3) { this_ang = ++this_ang % 4; offset = 0; }
и выполняем проверки на столкновение с другими фигурами или выход за пределы экрана для каждого элемента вращаемой фигуры.
Если поле выполнения всех проверок переменная flag будет содержать значение – Истина (true), то в процедуре управления движением фигурок будет выполнен поворот фигуры, а в противном случае нажатие кнопки «поворот» не будет обработано.
После отладки программы немного отредактировали схему сборки, дополнив еще ее электронными часами.
Для управление игрой с мобильного телефона скачали и доработали программу с сайта Thunkable. После успешного тестирования всех аппартно - программных средств, начертили чертёж задней части корпуса, (внутри которого расположился процессор и вся остальная электроника) в редакторе SolidWorks, а переднюю часть в редакторе Tinkercad. Готовые чертежи корпусов распечатали на 3D принтере PLA пластиком чёрного цвета. Между передней частью корпуса и матрицей установили экран из белого матового прозрачного пластика для улучшения наглядности игры. Собрали и скрутили болтами все части и внутрь задней части корпуса поместили электронику. Провели повторный тест, который дал положительный результат.
В процессе работы над проектом мною сделаны следующие выводы:
Для больших сложных проектов с программами большого размера и активным использованием различных коммуникационных портов лучше выбирать Arduino Mega;
На базе микроконтроллера Arduino с подключенной светодиодной матрицей и Bluetooth-модулем, можно создать не только модель игры Тетрис, но и много других визуальных игр, световых эффектов;
Если собрать очень большую светодиодную матрицу, то можно ее использовать в качестве монитора, но управлять такой матрицей с помощью Arduino не получится. Нужен более мощный процессор.
На данный момент игра апробирована и пользуется большой популярностью среди моих сверстников. Учителя и родители довольны нашим полезным времяпровождением на переменах.
http://wiki.amperka.ru
http://arduino.ru
http://robocraft.ru
https://ru.wikipedia.org/wiki/Arduino
https://ru.wikipedia.org/wiki/Тетрис
Приложение 1. Схема сборки аппаратной части игры Тетрис.
Приложение 2.
Приложение 3. Программный код скетча. Красным цветом выделена измененная и доработанная часть программы.
// **************** НАСТРОЙКИ ****************
#define FAST_SPEED 20 // скорость падения при удержании "вниз" (меньше - быстрее)
#define DEMO_TETRIS 0 // позиция новой фигуры в верху экрана случайна. 1 - вкл, 0 - выкл
#define STEER_SPEED 40 // скорость перемещения в бок при удержании кнопки (меньше - быстрее) на BT версии не работает!
// --------------------- ДЛЯ РАЗРАБОТЧИКОВ ----------------------
#define ADD_COLOR 0x010101
int8_t fig = 0, ang = 0, pos = WIDTH / 2, height = HEIGHT - 1;
int8_t prev_ang, prev_pos, prev_height;
uint32_t colors[6] {0x0000EE, 0xEE0000, 0x00EE00, 0x00EEEE, 0xEE00EE, 0xEEEE00};
uint32_t color = 0x000088;
bytecolor_index;
bytelinesToClear;
booleandown_flag = true;
bytelineCleanCounter;
// самаяважнаячастьпрограммы! Координаты пикселей фигур
// 0 - палка
// 1 - кубик
// 2 - Г
// 3 - Г обратная
// 4 - Z
// 5 - Z обратная
// 6 - Т
const int8_t figures[7][12][2] PROGMEM = {
{
{ -1, 0}, {1, 0}, {2, 0},
{0, 1}, {0, 2}, {0, 3},
{ -1, 0}, {1, 0}, {2, 0},
{0, 1}, {0, 2}, {0, 3},
},
{
{0, 1}, {1, 0}, {1, 1},
{0, 1}, {1, 0}, {1, 1},
{0, 1}, {1, 0}, {1, 1},
{0, 1}, {1, 0}, {1, 1},
},
{
{ -1, 0}, { -1, 1}, {1, 0},
{0, 1}, {0, 2}, {1, 2},
{ -2, 1}, { -1, 1}, {0, 1},
{ -1, 0}, {0, 1}, {0, 2},
},
{
{ -1, 0}, {1, 0}, {1, 1},
{0, 1}, {0, 2}, {1, 0},
{0, 1}, {1, 1}, {2, 1},
{0, 1}, {0, 2}, { -1, 2},
},
{
{ -1, 0}, {0, 1}, {1, 1},
{0, 1}, { -1, 1}, { -1, 2},
{ -1, 0}, {0, 1}, {1, 1},
{0, 1}, { -1, 1}, { -1, 2},
},
{
{ -1, 1}, {0, 1}, {1, 0},
{0, 1}, {1, 1}, {1, 2},
{ -1, 1}, {0, 1}, {1, 0},
{0, 1}, {1, 1}, {1, 2},
},
{
{ -1, 0}, {0, 1}, {1, 0},
{0, 1}, {0, 2}, {1, 1},
{ -1, 1}, {0, 1}, {1, 1},
{ -1, 1}, {0, 1}, {0, 2},
}
};
voidtetrisRoutine() {
if (checkButtons()) {
if (buttons[3]) { // кнопканажата
buttons[3] = 0;
stepLeft();
}
if (buttons[1]) {
buttons[1] = 0;
stepRight();
}
if (buttons[0]) {
buttons[0] = 0;
if (checkArea(3)) { // проверкавозможностиповорота
prev_ang = ang; // запоминаем старый угол
ang = ++ang % 4; // изменяем ang от 0 до 3
redrawFigure(prev_ang, pos, height); // перерисоватьфигуру
}
}
if (buttons[2]) { // кнопкавнизудерживается
buttons[2] = 0;
gameTimer.setInterval(FAST_SPEED); // увеличитьскорость
}
}
/*
if (bt_left.isStep()) { // кнопка нажата и удерживается
stepLeft();
}
if (bt_right.isStep()) {
stepRight();
}
*/
if (gameTimer.isReady()) { // главныйтаймеригры
prev_height = height;
if(!checkArea(0)) { // проверяем столкновение с другими фигурами
if (height>= HEIGHT - 2) { // проиграл по высоте
gameOver(); // игра окончена, очистить всё
newGameTetris(); // новый раунд
} else{ // если не достигли верха
fixFigure(); // зафиксировать
checkAndClear(); // проверить ряды и очистить если надо
newGameTetris(); // новый раунд
}
} elseif (height == 0) { // если достигли дна
fixFigure(); // зафиксировать
checkAndClear(); // проверить ряды и очистить если надо
newGameTetris(); // новый раунд
} else{ // если путь свободен
height--; // идёмвниз
redrawFigure(ang, pos, prev_height); // перерисовка
}
}
}
// поиск и очистка заполненных уровней
voidcheckAndClear() {
linesToClear = 0; // счётчик заполненных строк по вертикали
booleanfull_flag; // флаг заполненности
bytelineNum = 255; // высота, с которой начинаются заполненные строки (искусственно увеличена)
for (byte Y = 0; Y < HEIGHT; Y++) { // сканируем от точки падения
full_flag = true; // поднимаемфлаг. Будет сброшен, если найдём чёрный пиксель
for (byte X = 0; X < WIDTH; X++) { // проходимся по строкам
if ((long)getPixColorXY(X, Y) == (long)0x000000) { // если хоть один пиксель чёрный
full_flag = false; // считаем строку неполной
}
}
if (full_flag) { // если нашлась заполненная строка
linesToClear++; // увеличиваем счётчик заполненных строк
if (lineNum == 255) // если это первая найденная строка
lineNum = Y; // запоминаем высоту. Значение 255 было просто "заглушкой"
} else{ // если строка не полная
if (lineNum != 255) // если lineNum уже не 255 (значит строки были найдены!!)
break; // покинуть цикл
}
}
if (linesToClear> 0) { // если найденных полных строк больше 1
lineCleanCounter += linesToClear; // суммируем количество очищенных линий (игровой "счёт")
// заполняем весь блок найденных строк белым цветом слева направо
for (byte X = 0; X < WIDTH; X++) {
for (byte i = 0; i<linesToClear; i++) {
leds[getPixelNumber(X, lineNum + i)] = CHSV(0, 0, 255); // закрашиваемегобелым
}
FastLED.show();
delay(5); // задержка между пикселями слева направо
}
delay(10);
// теперь плавно уменьшаем яркость всего белого блока до нуля
for (byte val = 0; val<= 30; val++) {
for (byte X = 0; X < WIDTH; X++) {
for (byte i = 0; i<linesToClear; i++) {
leds[getPixelNumber(X, lineNum + i)] = CHSV(0, 0, 240 - 8 * val); // гасимбелыйцвет
}
}
FastLED.show();
delay(5); // задержка между сменой цвета
}
delay(10);
// и теперь смещаем вниз все пиксели выше уровня с первой найденной строкой
for (byte i = 0; i<linesToClear; i++) {
for (byte Y = lineNum; Y < HEIGHT - 1; Y++) {
for (byte X = 0; X < WIDTH; X++) {
drawPixelXY(X, Y, getPixColorXY(X, Y + 1)); // сдвигаемвниз
}
FastLED.show();
}
delay(100); // задержка между "сдвигами" всех пикселей на один уровень
}
}
gameTimer.reset();
}
// функция фиксации фигуры
voidfixFigure() {
color += ADD_COLOR; // чутка перекрасить
redrawFigure(ang, pos, prev_height); // перерисовать
}
// проигрыш
voidgameOver() {
FastLED.clear();
FastLED.show();
// тутможновывестисчётlineCleanCounter
displayScore(lineCleanCounter);
delay(1000);
lineCleanCounter = 0; // сброссчёта
FastLED.clear();
FastLED.show();
delay(20);
}
// новыйраунд
voidnewGameTetris() {
Serial.println("lolkek");
delay(10);
height = HEIGHT; // высота = высоте матрицы
pos = WIDTH / 2; // фигура появляется в середине
fig = random(7); // выбираем случайную фигуру
ang = random(4); // и угол поворота
//color = colors[random(6)]; // случайныйцвет
color_index = ++color_index % 6; // всецветапоочереди
color = colors[color_index];
// если включен демо-режим, позицию по горизонтали выбираем случайно
if (DEMO_TETRIS) pos = random(1, WIDTH - 1);
// возвращаем обычную скорость падения
gameTimer.setInterval(globalSpeed * 4);
down_flag = false; // разрешаем ускорять кнопкой "вниз"
delay(10);
}
// управление фигурами вправо и влево
voidstepRight() {
if (checkArea(1)) {
prev_pos = pos;
if (++pos> WIDTH) pos = WIDTH;
redrawFigure(ang, prev_pos, height);
}
}
voidstepLeft() {
if (checkArea(2)) {
prev_pos = pos;
if (--pos< 0) pos = 0;
redrawFigure(ang, prev_pos, height);
}
}
// проверканастолкновения
booleancheckArea(int8_t check_type) {
// checktype:
// 0 - проверка лежащих фигур и пола
// 1 - проверка стенки справа и фигур
// 2 - проверка стенки слева и фигур
// 3 - проверка обеих стенок и пола
boolean flag = true;
int8_t X, Y;
boolean offset = 1;
int8_t this_ang = ang;
// этот режим для проверки поворота. Поэтому "поворачиваем"
// фигуру на следующий угол чтобы посмотреть, не столкнётся ли она с чем
if (check_type == 3) {
this_ang = ++this_ang % 4;
offset = 0; // разрешаем оказаться вплотную к стенке
}
for (byte i = 0; i< 4; i++) {
// проверяем точки фигуры
// pos, height - координаты главной точки фигуры в ГЛОБАЛЬНОЙ системе координат
// X, Y - координаты остальных трёх точек в ГЛОБАЛЬНОЙ системе координат
if (i == 0) { // стартовая точка фигуры (начало отсчёта)
X = pos;
Y = height;
} else{ // остальные три точки
// получаем глобальные координаты точек, прибавив их положение в
// системе координат главной точки к координатам самой главной
// точки в глобальной системе координат.
X = pos + (int8_t)pgm_read_byte(&figures[fig][this_ang * 3 + i - 1][0]);
Y = height + (int8_t)pgm_read_byte(&figures[fig][this_ang * 3 + i - 1][1]);
// восстанавливаем из прогмема данные и берём нужное число в массиве.
}
// границыполя
if (check_type == 1 || check_type == 3) {
if (X + 1 > WIDTH - 1) flag = false; // смотрим следующий справа
uint32_tgetColor;
if (Y < HEIGHT)
getColor = getPixColorXY(X + offset, Y);
if (getColor != color &&getColor != 0x000000) {
flag = false; // если не СВОЙ цвет и не чёрный
}
}
if (check_type == 2 || check_type == 3) {
if (X - 1 < 0) flag = false; // смотрим следующий слева
uint32_tgetColor;
if (Y < HEIGHT)
getColor = getPixColorXY(X - offset, Y);
if (getColor != color &&getColor != 0x000000) {
flag = false; // если не СВОЙ цвет и не чёрный
}
}
if (check_type == 0 || check_type == 3) {
uint32_tgetColor;
if (Y < HEIGHT) {
getColor = getPixColorXY(X, Y - 1);
if (getColor != color &&getColor != 0x000000) {
flag = false; // если не СВОЙ цвет и не чёрный
}
}
}
}
returnflag; // возвращаем глобальный флаг, который говорит о том, столкнёмся мы с чем то или нет
}
// функция, которая удаляет текущую фигуру (красит её чёрным)
// а затем перерисовывает её в новом положении
voidredrawFigure(int8_t clr_ang, int8_t clr_pos, int8_t clr_height) {
drawFigure(fig, clr_ang, clr_pos, clr_height, 0x000000); // стеретьфигуру
drawFigure(fig, ang, pos, height, color); // обрисовать
FastLED.show();
}
// функция, отрисовывающая фигуру заданным цветом и под нужным углом
voiddrawFigure(byte figure, byte angle, byte x, byte y, uint32_t color) {
drawPixelXY(x, y, color); // рисуем точку начала координат фигуры
int8_t X, Y; // вспомогательные
for (bytei = 0; i< 3; i++) { // рисуем 4 точкифигуры
// что происходит: рисуем фигуру относительно текущей координаты падающей точки
// просто прибавляем "смещение" из массива координат фигур
// для этого идём в прогмем (функция pgm_read_byte)
// обращаемся к массиву по адресу &figures
// преобразовываем число в int8_t (так как progmem работает только с "unsigned"
// angle * 3 + i - обращаемся к координатам согласно текущему углу поворота фигуры
X = x + (int8_t)pgm_read_byte(&figures[figure][angle * 3 + i][0]);
Y = y + (int8_t)pgm_read_byte(&figures[figure][angle * 3 + i][1]);
if (Y > HEIGHT - 1) continue; // если выходим за пределы поля, пропустить отрисовку
drawPixelXY(X, Y, color);
}
}