КОРЗИНА
магазина
8 (499) 500-14-56 | ПН. - ПТ. 12:00-18:00
ЛЕСНОРЯДСКИЙ ПЕРЕУЛОК, 18С2, БЦ "ДМ-ПРЕСС"

Игра "Бегущий огонёк"

Общие сведения

«Бегущий огонёк» - игра, собранная на основе NeoPixel-кольца, Piranha UNO, и тактовой кнопки. Суть игры: необходимо с помощью нажатия кнопки останавливать бегущий огонёк внутри светящегося интервала. Трудность в том, что сложность игры постоянно растёт, а при ошибке происходит откат назад. 

Видео

Нам понадобится

Также понадобится библиотека iarduino_NeoPixel

При необходимости, ознакомьтесь с нашей инструкцией по установке библиотек в Arduino IDE.

 Подключение

Мы предполагаем, что Вы уже спаяли NeoPixel-кольцо. Если нет, сперва соберите его по этой инструкции, а затем вернитесь к данному проекту. 

Соедините все компоненты при помощи проводов «папа-мама», также можете для удобства воспользоваться макетной платой Breadboard mini.

К 3 выводу контроллера подключена кнопка. Вторым контактом она подключена к GND. NeoPixel-кольцо подключено к 5 выводу контроллера.

Минус питания подаётся на GND контроллера и кольца, а плюс - на 5V.

Для питания рекомендуем использовать отдельный блок питания с напряжением 5В и током не менее 3А.

Для проекта можно использовать любой контроллер. Не забудьте выбрать используемую плату в меню Инструменты -> Плата

Скетч проекта

#include <iarduino_NeoPixel.h>      // подключаем библиотеку iarduino_NeoPixel для работы со светодиодами NeoPixel
iarduino_NeoPixel led(5,60);       // указываем, что к 5 выводу подключено 60 светодиодов

#define buttonNextPin 3             // Пин кнопки "Далее"

bool btnNextFlag = false;           // флаг нажатия кнопки
uint32_t btnNextTimer = 0;          // время с момента нажатия (необходимо для устранения дребезга контактов

int8_t pointPosition=0;             // позиция бегущей точки
bool clickFlag = false;             // флаг нажатия кнопки
uint8_t startInterval=0;            // начало интервала (генерируется случайным образом)
uint8_t endInterval;                // конец интервала (расчитывается исходя из начала интервала и его длины)
uint8_t interval = 10;              // "окно" зоны для ловли точки
uint8_t speedTime = 50;             // "скорость" двидения точки (мс). Чем меньше значение, тем быстрее скорость
int diff=0;                         // не поместившиеся точки интервала
bool newGameFlag = true;            // флаг новой игры (для единоразовой генерации начала интервала)
uint8_t currentLedInterval;         // растущее от startInterval до endInterval число (инжекс светодиода)
uint8_t ledInterval[60];            // массив номеров светодиодов интервала
uint8_t currentLedDiff = 0;         // растущее от 0 до diff число - непоместившаяся часть интервала
int32_t colorInterval = 0x0000ff;   // цвет интервала (изначально синий) 
uint32_t timeNewLevel;              // время старта нового уровня
uint32_t timeMinLimit = 3000;       // мс, 0...65535. Минимальное время до "убегания" интервала 
uint32_t timeMaxLimit = 7000;       // мс, 0...65535. Максимальное время до "убегания" интервала 
uint32_t timeRunning;               // время "убегания" интервала между минимальным и максимальным, вичисленное случайным образом
uint8_t randomStepsRun;             // число шагов, на которое будет "убегать" интервал
uint8_t n = 0;                      // число пикселей, на которые уже "убежал" интервал
uint32_t timeStartRunning;          // время начала "убегания" интервала

void setup(){
    Serial.begin(9600);                                                                // инициируем передачу данных в монитор последовательного порта
    if(led.begin()) {Serial.println("Ok");}                                            // инициируем работу со светодиодами и выводим сообщение об успешной инициализации
    else            {Serial.println("Err");}                                           // если инициализация невозможна (не хватает памяти для хранения цветов всех светодиодов), 
                                                                                       // то выводим сообщение об ошибке
    pinMode(buttonNextPin, INPUT_PULLUP);                                              // пин, к которому подключена кнопка - вход, подтиягивающий резистор полюкчен програмно 
}

void loop(){                                                                           
  randomSeed(analogRead(A0));                                                          // новая опорная точка для генератора случайных чисел (на "висящем в воздухе" выводе А0 присутствует случайный сигнал)

  bool buttonNextState = !digitalRead(buttonNextPin);                                  // состояние кнопки - сигнал с пина кнопки (инвертируем для удобства)
  
  if (buttonNextState && !btnNextFlag && millis() - btnNextTimer > 100) {              // если кнорка нажата И до этого нажата не была И она нажата уже 100мс (задержка от дребезга контактов)
    btnNextTimer = millis();                                                           // сбрасываем таймер
    Serial.println("press");                                                           // выводим инфомацию о том что кнопка нажата     
    btnNextFlag = true;                                                                // флаг нажатия кнопки - true
    clickFlag=true;                                                                    // флаг нажатия кнопки - true
  }
  if (!buttonNextState && btnNextFlag) {                                               // если кнопку отпустили
    btnNextFlag = false;                                                               // флаг нажатия кнопки false - теперь она отпущена
    btnNextTimer = millis();                                                           // сбрасываем таймер
  }
  
  if (newGameFlag){                                                                    // если начался новый уровень
    newGameFlag=false;                                                                 // меняем флаг, чтобы не заходить в цикл до новой смены уровней
    currentLedDiff = 0;                                                                // текущая разница "непоместившихся" пикселей
    startInterval = random(led.count());                                               // начало интервала - случайное число от 0 до последнего светодиода в круге
    currentLedInterval = startInterval;                                                // начало интервала на старте
    endInterval = startInterval+interval;                                              // конец интервала
    timeNewLevel = millis();                                                           // время старта нового уровня
    timeRunning = random(timeMinLimit, timeMaxLimit);                                  // случайно определяем время, через которое интервал начнёт "убегать"

    if (endInterval > led.count()){                                                    // если интервал вышел за пределы кольца
      diff = endInterval-led.count();                                                  // вычисляем количесво светодиодов, которые "не поместились" в конце
      endInterval = led.count()-1;                                                     // устанавливаем конец интервала последнему пикселю в кольце
    }
    else diff = 0;                                                                     // иначе не переносим "не поместившиеся" значения (если их нет)
    
    for (uint8_t i=0; i < interval; i++){                                                // формируем массив из номеров светодиодов интервала
      if (currentLedInterval <= endInterval){                                          // если при заполнении интервала не дошли до конца
        ledInterval[i] = currentLedInterval;                                           // i-й элемент массива равен текцщему индексу светодиода
        currentLedInterval++;                                                          // увеличиваем текущий индекс светодиода на 1
      }
      else if (diff != 0){                                                             // иначе, если требуется "перенос" непоместившейся части интервала 
        ledInterval[i] = currentLedDiff;                                               // i-й элемент массива равен первому светодиоду
        currentLedDiff++;                                                              // увеличиваем текущий индекс "непоместившихся" светодиодов на 1
      }
    }
    colorInterval = random(0x777777);                                                  // генерируем случайный цвет
  }
  
  led.setColor(NeoPixelAll, 0,0,0);                                                    // гасим все светодиоды
      
  for (uint8_t i=0; i < interval; i++){                                                  // зажигаем светодиоды интервала
    led.setColor(ledInterval[i], colorInterval);
  }

  led.setColor(pointPosition, 255,255,255);                                            // зажигаем бегущую точку
  led.write();                                                                         // записываем
  delay(speedTime);                                                                    // задержка в перемещении точки (определяет её скорость)
  if (pointPosition >= led.count()-1) pointPosition = -1;                              // если точка дошла до конца, начинем сначала; 
                                                                                       // -1, потому что дальше значение увеличится на 1 и позиция станет равно 0
                                                                                      
  if (clickFlag && isIncludePoint(interval, ledInterval, pointPosition)){              // если клик кнопки в момент, когда точка в зоне интервала
    clickFlag = false;                                                                 // флаг для того, чтобы больше не заходить в этот цикл и не обрабатывать клик повторно
    for (uint8_t i=0; i < interval; i++){                                                // зажигаем интервал зеленым
      led.setColor(ledInterval[i], 0,255,0);                                          
    }
    interval--;                                                                        // уменьшаем интервал на 1 пиксель
    if (interval < 10) speedTime -= speedTime*30/100;                                  // увеличиваем скорость точки на 30%
    led.write();                                                                       // записываем
    delay(1000);                                                                       // "победа" горит 1 секунду
    diff=0;                                                                            // сбрасываем разницу "непоместившихся" пикселей (вдруг она была)
    newGameFlag=true;                                                                  // начинаем новую игру
  }
  else if (clickFlag && !isIncludePoint(interval, ledInterval, pointPosition)){        // если клик кнопки в момент, когда точка НЕ в зоне интервала
    clickFlag = false;                                                                 // флаг для того, чтобы больше не заходить в этот цикл и не обрабатывать клик повторно
    for (uint8_t i=0; i < interval; i++){                                                // зажигаем интервал красным
     led.setColor(ledInterval[i], 255,0,0);                                          
    }
    led.write();                                                                       // записываем
    delay(1000);                                                                       // "проигрыш" горит 1 секунду
    interval++;                                                                        // увеличиваем интервал на 1 пиксель
    if (speedTime < 50) speedTime += speedTime*42.865/100;                             // уменьшаем скорость точки до предыдущего значения 
    diff=0;                                                                            // сбрасываем разницу "непоместившихся" пикселей (вдруг она была)
    newGameFlag=true;                                                                  // начинаем новую игру
  }
  
  pointPosition++;                                                                     // перемещаем точку на новую позицию

  if (interval <= 1) {                                                                 // если интервал равен одной точке
    for (uint8_t i=0; i < led.count(); i++){                                             // зажигаем весь круг зеленым - победа
     led.setColor(i, 0,255,0);
    }
    led.write();                                                                       // записываем
    delay(2000);                                                                       // ждём две секунды 
    newGameFlag = true;                                                                // начинаем новую игру (новый уровень)
    pointPosition=0;                                                                   // устанавливаем точку в 0 положение
    interval = 10;                                                                     // начинаем новую игру - интервал свновь равен 10
    speedTime = 50;                                                                    // начинаем новую игру - скорость точки (задержка перемещения) снова 50
    diff=0;                                                                            // сбрасываем разницу "непоместившихся" пикселей (вдруг она была)
    currentLedDiff = 0;                                                                // сбрасываем разницу "непоместившихся" пикселей (вдруг она была)
  }

  if ((millis()-timeNewLevel) > timeRunning){                                            // если слишком долго целимся
    if(n==0) {                                                                         // если пока не начинали перемещать интервал
      timeStartRunning = millis();                                                     // отмечаем время начала движения интервала
      n++;                                                                             // прибавляем шаг смещения
      randomStepsRun = random(5, 50);                                                  // случайно определяем число пикселей, на которые сдвинется интервал
    }
    if(n < randomStepsRun && (timeStartRunning+speedTime) < millis()){                     // если интервал еще не "доехал" до конца, и пришло время сдвинуть его на шаг         
      timeStartRunning = millis();                                                     // "сбрасываем" время начала движения интервала
      n++;                                                                             // прибавляем шаг смещения
      for (uint8_t j=0; j < interval; j++){                                              // для каждого номера светодиода в интервале,
        if (ledInterval[j] > 0) ledInterval[j]--;                                        // если его значение больше 0, уменьшаем на 1,
        else ledInterval[j]=led.count()-1;                                             // иначе "перепрыгиваем" в конец - приравниваем последнему номеру светодиода в кольце
    }
    }
    else if(n >= randomStepsRun){                                                        // если интервал "доехал" до конца
      n=0;                                                                             // обнуляем число шагов
      timeNewLevel = millis();                                                         // "обнуляем" время начала нового уровня (формально, новый уровень не начинается)
      timeRunning = random(timeMinLimit, timeMaxLimit);                                // определяем случайным образом новое время, через которое интревал начнёт "убегать"
    }
  }
}

bool isIncludePoint(uint8_t interval, uint8_t *ledInterval, int8_t pointPosition){     // функиця, определяющая, находится ли бегущий светодиод внутри интервала
  for (uint8_t i=0; i < interval; i++){                                                  // для каждого значения в интервале, проверить:
    if (ledInterval[i] == pointPosition){                                              // если это значение равно позиции бегущего светодиода (позиции - номера светодиодов в кольце)
      return true;                                                                     // вернуть "true"
    }
  }
  return false;                                                                        // иначе вернуть "false"
}

Ссылки




Обсуждение

Гарантии и возврат Используя сайт Вы соглашаетесь с условями