// библиотека для работы с RGB-матрицей
#include <Adafruit_NeoPixel.h>
// номер пина, к которому подключена 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();
}
}