«Кнопочные ковбои»‎ — игра для Arduino Uno

Проекты на Arduino Uno и Slot Shield

«Кнопочные ковбои» — простая игра на скорость реакции, кто первый спустил курок, тот и победил. Мы усовершенствовали классическую игру из «Матрёшки». Теперь в неё может играть один человек. Враги будут появляться по сторонам светодиодной матрицы в виде красных меток. На них нужно реагировать с помощью джойстика — с какой стороны появились, в ту и дёргать рычажок. Количество удачных выстрелов выведем на экран компьютера.

Что потребуется

Полный сет компонентов проекта. В сет входят:

Видеоинструкция

Как собрать

Установите Troyka Slot Shield на Arduino Uno

Вставьте RGB матрицу в разъём С. Ножка S должна подключится к пину 4.

3D-джойстик подключите в слот A. Сигнал с X придёт на пин A4, сигнал с Y на A4. Нажатие на джойстик будет передаваться на пин 2.

Скетч

Прошейте контроллер скетчем через Arduino IDE.

wild-gunnman.ino
// библиотека для работы с RGB-матрицей
#include <Adafruit_NeoPixel.h>
 
// номер пина, к которому подключена RGB-матрица
#define MATRIX_C_PIN    4
// количество светодиодов в матрице
#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 matrixOnC = Adafruit_NeoPixel(LED_COUNT, MATRIX_C_PIN, NEO_GRB + NEO_KHZ800);
 
// перечисляем названия мишеней, которые будут выводиться на матрицу
enum {
  NOTOUCH,    // имя для состояния ожидания
  RIGHT,     // имя для мишени, в виде полосы светодиодов справа
  LEFT,      // имя для мишени, в виде полосы светодиодов слева
  DOWN,      // имя для мишени, в виде полосы светодиодов снизу
  UP,        // имя для мишени, в виде полосы светодиодов сверху
  CENTER    // имя для мишени, в виде квадрата светодиодов в центре
};
 
// создаем массив с мишенями
int target[] = {RIGHT, LEFT, DOWN, UP, CENTER};
// задаем цвет мишени в RGB
int color [] = {255, 0, 0};
// создаем переменную для хранения состояния мишени
int shot = 0;
// создаем маркер игрока
int player = NOTOUCH;
// создаем счетчик очков
int score = 0;
 
// создаем объект класса long для хранения времени
unsigned long timeOfReaction = 0;
// создаем счетчик реакции, который сокращается с каждой победой
int reaction = 2000;
 
// создаем флаг для победы
bool flag = false;
// создаем флаги для джойстика
bool stateStickOldX, stateStickOldY, stateStickOldZ = true;
 
void setup() {
  // открываем последовательный порт
  Serial.begin(9600);
  // инициализируем матрицу
  matrixOnC.begin();
  // задаем придельную яркость
  matrixOnC.setBrightness(BRIGHT);
  // очищаем матрицу
  matrixOnC.clear();
  matrixOnC.show();
  // "Давай сыграем!"
  Serial.println("Let's play!");
  delay(1000);
}
 
void loop() {
  // инициализируем последовательность случайных чисел
  randomSeed(analogRead(ANALOG_PIN_FOR_RND));
  // даём сигнал «пли!», выждав случайное время от 0,5 до 1,5 сек
  delay(random(500, 1500));
  // записываем в мишень одно из состояний
  shot = target[random(0, sizeof(target) / sizeof(int))];
  // выводим картинку на матрицу
  matrix_show (shot);
  matrixOnC.show();
  // запускаем таймер реакции
  timeOfReaction = millis();
  while (millis() - timeOfReaction < reaction) {
    // считываем действия игрока
    player_move ();
    if (player == shot) {
      // если успел попасть, то поднимаем флаг победы и выходим
      flag = 1;
      break;
    } else if (player != NOTOUCH) {
      // если промахнулся просто выходим
      break;
    }
    // если не успел, также просто выходим
  }
  if (flag) {
    // победа, показываем зеленый, опускаем флаг, уменьшаем время на реакцию
    you_win();
    flag = 0;
    //    выводим счет в Serial port
    //    Serial.print("You win! Your score: ");
    // увеличиваем счет
    score++;
    //    Serial.println(score);
    reaction -= (reaction / 10);
  } else {
    // поражение, показываем синий, возвращаем время на реакцию к начальному значению
    game_over();
  }
  // ставим фишку игрока в состояние ожидания
  player = NOTOUCH;
  // очищаем матрицу
  matrixOnC.clear();
  matrixOnC.show();
}
 
// функция проигрыша
void game_over() {
  for (int i = 0; i < LED_COUNT; i++) {
    matrixOnC.setPixelColor(i, 0, 0, 255);
  }
  matrixOnC.show();
  // выводим счет в Serial port
  Serial.print("You lose. Your score: ");
  Serial.println(score);
  // обнуляем счет
  score = 0;
  // возвращаем реакцию к начальному значению
  reaction = 2000;
  delay(1000);
}
 
// функция победы
void you_win() {
  for (int i = 0; i < LED_COUNT; i++) {
    matrixOnC.setPixelColor(i, 0, 255, 0);
  }
  matrixOnC.show();
  delay(1000);
}
 
// функция для показа нужной мишени на матрице
void matrix_show(int shot) {
  switch (shot) {
    case RIGHT: {
        // рисуем линию от правого нижнего угла к правому верхнему
        draw_line(0, 0, 0, 3);
        break;
      }
    case LEFT: {
        // рисуем линию от левого нижнего угла к правому верхнему
        draw_line(3, 0, 3, 3);
        break;
      }
    case DOWN: {
        // рисуем линию от правого нижнего угла к левому нижнему
        draw_line(0, 0, 3, 0);
        break;
      }
    case UP: {
        // рисуем линию от левого верхнего угла к правому верхнему
        draw_line(0, 3, 3, 3);
        break;
      }
    case CENTER: {
        // рисуем квадрат в центре
        draw_line(1, 1, 2, 1);
        draw_line(1, 2, 2, 2);
        break;
      }
  }
  matrixOnC.show();
}
 
 
// функция для обработки 3D джойстика
void player_move () {
  int x, y, z;
  // считываем текущее значение джойстика по Х
  x = analogRead(X);
  if (x < 100 && stateStickOldX) {
    player = LEFT;
    stateStickOldX = false;
  }
  if (x > 924 && stateStickOldX) {
    player = RIGHT;
    stateStickOldX = false;
  }
  if (100 < x && x < 924) {
    stateStickOldX = true;
  }
  // считываем текущее значение джойстика по Y
  y = analogRead(Y);
  if (y < 100 && stateStickOldY) {
    player = UP;
    stateStickOldY = false;
  }
  if (y > 924 && stateStickOldY) {
    player = DOWN;
    stateStickOldY = false;
  }
  if (100 < y && y < 924) {
    stateStickOldY = true;
  }
  // считываем текущее значение джойстика по Z
  z = digitalRead(Z);
  if (z == 1 && stateStickOldZ) {
    player = CENTER;
    stateStickOldZ = false;
  }
  if (z == 0)
    stateStickOldZ = true;
}
 
// функция для преобразования номера светодиода в координаты
int xyHelper(int x, int y) {
  return y * 4 + x;
}
 
// функция для рисования линий по алгоритму Брезенхэма
void draw_line(int x1, int y1, int x2, int y2) {
  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;
  matrixOnC.setPixelColor(xyHelper(x2, y2), color[0], color[1], color[2]);
  while (x1 != x2 || y1 != y2)
  {
    matrixOnC.setPixelColor(xyHelper(x1, y1), color[0], color[1], color[2]);
    const int error2 = error * 2;
    if (error2 > -deltaY)
    {
      error -= deltaY;
      x1 += signX;
    }
    if (error2 < deltaX)
    {
      error += deltaX;
      y1 += signY;
    }
  }
  matrixOnC.show();
}

Часто задаваемые вопросы

Где скачать необходимые библиотеки и как их установить?

Что дальше

  • Добавьте четырёхразрядный индикатор. На него можно вывести текущий счёт или время реакции.
  • Подключите Power Shield — игру можно будет взять с собой в дорогу.
  • Вставьте устройство в корпус из #cтруктора. Slot Box придаст игре законченный вид.