====== «Flappy Bird»‎ на Arduino Uno ====== **[[arduino-projects:start|Проекты на Arduino Uno и Slot Shield]]** «Flappy Bird» — звезда вьетнамского игростроя. Она произвела революцию на рынке мобильных игр. За неказистой графикой и простым геймплеем скрывалась невероятно азартная и увлекательная игра. Говорят, она стоила геймерам не одного разбитого телефона. Мы портировали игру на Ардуино. {{ :arduino-projects:arduino-snake:arduino-games-flappy-bird.png |}} Игровое поле — это пара [[amp>product/troyka-rgb-led-4x4?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|RGB матриц]]. Они превратятся в экран разрешением 4 на 8 пикселей. Мозг проекта — оригинальная [[amp>product/arduino-uno?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|Arduino Uno]]. Полётом жёлтой птички управляет [[amp>product/troyka-3d-joystick?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|3D-джойстик]]. Нажмёте джойстик вверх, птичка взмахнёт крыльями и взлетит. Оставите в покое, и она начнёт снижаться. Всё просто. Главное — не врезайтесь в зелёные трубы. * Платформы: [[amp>product/arduino-uno?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|Arduino Uno]], [[amp>product/arduino-leonardo?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|Arduino Leonardo]], [[amp>product/iskra-neo?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|Iskra Neo]] * Язык программирования: Arduino (C++) ===== Что потребуется ===== [[amp>collection/arduino-games?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki | Полный сет]] компонентов проекта. В сет входят: * [[amp>product/arduino-uno?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|Arduino Uno]] * [[amp>product/arduino-troyka-slot-shield?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|Troyka Slot Shield]] * 2× [[amp>product/troyka-rgb-led-4x4?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|RGB матрица 4×4]] * [[amp>product/troyka-3d-joystick?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|3D-джойстик]] ===== Видеоинструкция ===== {{youtube>SkbAgCVzeLU?large}} ===== Как собрать ===== Установите [[amp>product/arduino-troyka-slot-shield?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|Troyka Slot Shield]] на [[amp>product/arduino-uno?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|Arduino Uno]] {{ :arduino-projects:arduino-snake:fourgames0.png |}} Вставьте [[amp>product/troyka-rgb-led-4x4?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|RGB матрицу]] в разъём ''С''. Ножка ''S'' должна подключится к пину ''4''. {{ :arduino-projects:arduino-snake:fourgames1.png |}} [[amp>product/troyka-3d-joystick?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|3D-джойстик]] подключите в слот ''A''. Сигнал с ''X'' придёт на пин ''A4'', сигнал с ''Y'' на ''A4''. Нажатие на джойстик будет передаваться на пин ''2''. {{ :arduino-projects:arduino-snake:fourgames2.png |}} Вставьте вторую [[amp>product/troyka-rgb-led-4x4?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|RGB матрицу]] в разъём ''F''. Сигнальный пин ''S'' модуля должен подключится к пину "10" платы. {{ :arduino-projects:arduino-snake:fourgames3.png |}} ===== Скетч ===== Прошейте контроллер скетчем через [[articles:arduino-ide-install|Arduino IDE]]. // библиотека для работы с RGB-матрицей #include // номер пина, к которому подключена RGB-матрица #define MATRIX_C_PIN 4 #define MATRIX_F_PIN 10 // количество светодиодов в матрице #define LED_COUNT 16 // зерно для генератора случайных чисел #define ANALOG_PIN_FOR_RND A3 // максимальная яркость матрицы от 0 до 255 #define BRIGHT 10 // даём разумные имена пинам, к которым подключён джойстик #define X A5 #define Y A4 #define Z 2 // создаём объект класса Adafruit_NeoPixel Adafruit_NeoPixel matrix [] = {Adafruit_NeoPixel(LED_COUNT, MATRIX_C_PIN, NEO_GRB + NEO_KHZ800), Adafruit_NeoPixel(LED_COUNT, MATRIX_F_PIN, NEO_GRB + NEO_KHZ800) }; // создаем структуру для размещения в ней цветов struct Light { uint32_t Y = 0xffff00; uint32_t G = 0x00ff00; uint32_t B = 0x0000ff; }; // создаем структуру для размещения в ней параметров счета struct Store { int value; bool flag; }; // создаем структуру для размещения в ней параметров игровых объектов struct GameObjects { int x; int y; bool flag; }; // создаем объект структуры Light Light light; // создаем объект структуры Store Store store; // создаем объекты структуры GameObjects: игрока и стену GameObjects player, wall; // создаем объект класса long для хранения времени unsigned long timeOfGravity = 0; // создаем счетчик, по которому действует гравитация int gravity = 500; // создаем объект класса long для хранения времени unsigned long timeOfSide = 0; // создаем счетчик, который определяет "скорость" стены int side; void setup() { // инициализируем последовательность случайных чисел randomSeed(analogRead(ANALOG_PIN_FOR_RND)); // инициализируем все матрицы for (int i = 0; i < sizeof(matrix) / sizeof(Adafruit_NeoPixel); i++) { matrix[i].begin(); matrix[i].setBrightness(10); } // открываем последовательный порт Serial.begin(9600); // очищаем матрицы draw_clear (); } void loop() { // запускаем функцию предстартовой подготовки at_the_start(); // пока флаг счета опущен, игра не начнется // когда же он поднят, то не игра закончится while (store.flag) { // очищаем матрицы draw_clear (); // отслеживаем перемещение стены wall_move (); // рисуем стену на матрицах wall_draw(); // считываем движения джойстика player_move (); // рисуем на матрице положение игрока draw_point(player.x, player.y, light.Y); // проверяем, не вышел ли игрок за пределы поля if ( player.y < 0 || player.y >= 8) { // если вышел, то воспроизводим проигрыш game_over(); } // когда стена и игрок сравняются по x... if (player.x == wall.x ) { // ...проверяем, не врезался ли игрок в стену if (player.y != wall.y && player.y != wall.y + 1) { // если врезался, то воспроизводим проигрыш game_over(); } // ... если игра все же не проиграна то однократно выполняем условие победы if (wall.flag) { // увеличиваем счет store.value ++; // увеличиваем "скорость" side -= side / 10; // опускаем флаг однократного выполнения wall.flag = false; } } else { // держим флаг поднятым до тех пор, пока не наступит победа wall.flag = true; } } } // функция приготовления к игре void at_the_start() { // очищаем матрицы draw_clear (); // задаем начальное значение счетчика "скорости" стены side = 1500; // обнуляем значение счета и держим флаг счетчика опущенным store = { 0, false}; // задаем начальное значение координат игрока player = {3, 4, true}; // задаем начальное значение координат стены wall = { -1, random(0, 6), true}; // считываем движения джойстика player_move (); // рисуем на матрице положение игрока draw_point(player.x, player.y, light.Y); delay (250); } // функция окончания игры void game_over() { // очищаем матрицы draw_clear (); // заливаем матрицы синим цветом draw_pouring (light.B); // выводим счет в Serial port Serial.print ("Your store: "); Serial.println (store.value); // опускаем флаг счета store.flag = false; // инициализируем последовательность случайных чисел randomSeed(analogRead(ANALOG_PIN_FOR_RND)); delay (250); } // функция движения игрока void player_move () { int y, z; // считываем текущее значение джойстика по Y y = analogRead(Y); if (y < 100 && player.flag) { player.y ++; player.flag = false; } if (y > 924 && player.flag) { player.y --; player.flag = false; } if (100 < y && y < 924) { player.flag = true; } // создаем постоянную простую гравитацию if (millis() - timeOfGravity > gravity) { player.y --; timeOfGravity = millis(); } // считываем текущее значение джойстика по Z z = digitalRead(Z); if (z == 1 && store.flag == false) { store.flag = true; } } // функция движения стены void wall_move () { // запускаем таймер, по которому стена будет перемещаться if (millis() - timeOfSide > side) { // если стена за пределами видимости игрока if (wall.x >= 4) { // то изменяем ее и перемещаем в начало wall.x = 0; wall.y = random(0, 7); } else { // если нет, то двигаем ее на игрока wall.x++; } timeOfSide = millis(); } } // функция рисования препятствия void wall_draw () { // проверяем координату стены, в зависимости от этого рисуем одну или 2 линии switch (wall.y) { case 0: { draw_line(wall.x, 2, wall.x, 7, light.G); break; } case 6: { draw_line(wall.x, 0, wall.x, 5, light.G); break; } default : { draw_line(wall.x, 0, wall.x, wall.y - 1, light.G); draw_line(wall.x, wall.y + 2, wall.x, 7, light.G); break; } } } // функция для перевода координат в № матрицы int nHelper (int x, int y) { return y / 4 + ((x / 4) * 2); } // функция для перевода координат в № светодиода int mHelper(int x, int y) { switch (nHelper (x, y)) { case 0: { return {y * 4 + x}; break; } case 1: { return {abs (y - 8) * 4 - x - 1}; break; } } } // функция для рисования линий по алгоритму Брезенхэма void draw_line(int x1, int y1, int x2, int y2, uint32_t RGB) { const int deltaX = abs(x2 - x1); const int deltaY = abs(y2 - y1); const int signX = x1 < x2 ? 1 : -1; const int signY = y1 < y2 ? 1 : -1; int error = deltaX - deltaY; matrix[nHelper(x2, y2)].setPixelColor(mHelper(x2, y2), RGB); while (x1 != x2 || y1 != y2) { matrix[nHelper(x1, y1)].setPixelColor(mHelper(x1, y1), RGB); const int error2 = error * 2; if (error2 > -deltaY) { error -= deltaY; x1 += signX; } if (error2 < deltaX) { error += deltaX; y1 += signY; } } for (int i = 0; i < sizeof(matrix) / sizeof(Adafruit_NeoPixel); i++) { matrix[i].show(); } } // функция для рисования сплошной заливкой void draw_pouring (uint32_t RGB) { for (int i = 0; i < sizeof(matrix) / sizeof(Adafruit_NeoPixel); i++) { for (int j = 0; j < LED_COUNT; j++) { matrix[i].setPixelColor(j, RGB); matrix[i].show(); } } } // функция для рисования точки void draw_point (int x, int y, uint32_t RGB) { matrix[nHelper(x, y)].setPixelColor(mHelper(x, y), RGB); matrix[nHelper(x, y)].show(); } // функция для очистки матриц void draw_clear () { for (int i = 0; i < sizeof(matrix) / sizeof(Adafruit_NeoPixel); i++) { matrix[i].clear(); matrix[i].show(); } } ====== Часто задаваемые вопросы ====== > Где скачать необходимые библиотеки и как их установить? * [[https://github.com/adafruit/Adafruit_NeoPixel|Библиотека Adafruit NeoPixel]] * [[программирование:библиотеки|Как установить библиотеку]] ===== Что дальше ===== * Замените 3D-джойстик на [[amp>product/troyka-sound-loudness-sensor?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|датчик шума (Troyka-модуль) ]] и сможете управлять полётом птички крича на неё. * Добавьте [[amp>product/arduino-power-shield-li-ion?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|Power Shield]] — игру можно будет взять с собой в дорогу. * Вставьте устройство в корпус из #cтруктора. [[amp>product/structor-slot-box?utm_source=proj&utm_campaign=arduino-slot-proj-13&utm_medium=wiki|Slot Box]] придаст игре законченный вид.