В этом уроке
- Создание игры
- Работа с массивами
Видео версия урока
Принцип работы игры
Мы научились выводить на светодиодную матрицу изображения и даже делать несложную анимацию. Давайте пойдём дальше и создадим небольшую игру, в которой нужно будет уклоняться от летящих пикселей. Они, разумеется, будут появляться случайным образом, а скорость игры будет меняться в зависимости от успешности её прохождения — замедляться с каждой ошибкой и расти после нескольких успешных действий.
А что же насчёт управления? Можно сделать управление игроком через ИК-пульт, но почему бы не использовать бампер в качестве датчика, который будет реагировать на перемещающийся под ним палец!

Скетч проекта
Давайте загрузим скетч. Мы постарались сделать его как можно более простым, чтобы в нём было несложно разобраться. Комментарии помогут Вам.
#include <Wire.h> // Подключаем библиотеку для работы с шиной I2C
#include <iarduino_I2C_Bumper.h> // Подключаем библиотеку для работы с бампером I2C-flash
#include <iarduino_I2C_Matrix_8x8.h> // Подключаем библиотеку для работы с LED матрицей 8x8
iarduino_I2C_Bumper bum (0x0C); // Объявляем объект bum для работы с бампером I2C-flash
iarduino_I2C_Matrix_8x8 disp(0x0D); // Объявляем объект disp для работы с LED матрицей 8x8
uint8_t sensNum; // Номер сработавшего датчика бампера
uint32_t timeFrame = 400; // Начальное время задержки смены кадров (скорость летящих пикселей)
uint32_t timeUpdate; // Время, когда картинка на матрице была обновлена
uint32_t soundTime; // Время, когда прозвучал первый сигнал зуммера
uint8_t score = 0; // Число "победных" строчек подряд
uint8_t par = 0; // Число, показывающее чётность строк
bool soundBad = false; // Флаг — указывает, что нужно включить звук ошибки
bool secondTimeFlag = false; // Флаг — показывает, что нужно повторно включить звук ошибки
byte matrix[8] = {0b10000000, // Начальное изображение на матрице
0b00000000,
0b00100000,
0b00000000,
0b10000000,
0b00000000,
0b00100000,
0b00000000};
void setup(){
delay(500);
bum.begin(); // Инициируем работу с бампером (датчиком линии)
disp.begin(); // Инициируем работу с дисплеем
randomSeed(analogRead(A2)); // Выбираем случайную точку в последовательности чисел
}
void loop() {
uint16_t sensMax = 0; // Максимальное из считанных значений с бампера
for (uint8_t i = 2; i <= 7; i++){ // Проходим по всем датчикам бампера с 2 по 7
if (sensMax < bum.getLineAnalog(i)){ // Ищем большее значение
sensMax = bum.getLineAnalog(i);
sensNum = i; // Записываем номер датчика, который регистрирует максимальное значение
}
}
switch (sensNum){ // В зависимости от номера максимума (там, где находится палец), выбираем рисунок нижней строки
case 2: matrix[7] = 0b00000111; break;
case 3: matrix[7] = 0b00001110; break;
case 4: matrix[7] = 0b00011100; break;
case 5: matrix[7] = 0b00111000; break;
case 6: matrix[7] = 0b01110000; break;
case 7: matrix[7] = 0b11100000; break;
}
if (timeUpdate + timeFrame <= millis()){ // Если пришло время обновить матрицу
timeUpdate = millis(); // Записываем время обновления
for (uint8_t i = 7; i >= 1; i--){ // Проходимся по строкам массива с пикселями снизу вверх (от 7 до 1)
if (i==7) matrix[7] |= matrix[6]; // Если 7 строка (там, где находится игрок), "накладываем" изображение строки игрока на строку с летящим пикселем (операция "Логическое ИЛИ")
else matrix[i] = matrix[i-1]; // Для всех остальных строк: сдвигаем все вниз на 1
}
par = par + 1; // Увеличиваем число, обозначающее чётность на 1
if (par > 1){ // Если строка четная
par = 0; // Обнуляем значение
uint8_t rnd = random(1,9); // Генерируем случайное число от 1 до 8
switch (rnd){ // В зависимости от числа отрисовываем пиксель на первой строке
case 1: matrix[0] = 0b00000001; break;
case 2: matrix[0] = 0b00000010; break;
case 3: matrix[0] = 0b00000100; break;
case 4: matrix[0] = 0b00001000; break;
case 5: matrix[0] = 0b00010000; break;
case 6: matrix[0] = 0b00100000; break;
case 7: matrix[0] = 0b01000000; break;
case 8: matrix[0] = 0b10000000; break;
}
}
else matrix[0] = 0b00000000; // Если строка нечётная, рисуем пустую строку
// Если на строке должен быть пиксель и на последней строке находится только игрок (значит, пиксель "упал" на игрока)
if (par == 1 && (matrix[7] == 0b00000111 || matrix[7] == 0b00001110 || matrix[7] == 0b00011100 || matrix[7] == 0b00111000 || matrix[7] == 0b01110000 || matrix[7] == 0b11100000)){
soundBad = true; // Флаг звука проигрыша
if (timeFrame < 800) timeFrame = timeFrame + 40; // Увеличиваем время (уменьшаем скорость), если оно меньше максимального
score = 0; // Сбрасываем заработанные очки
}
else { // Иначе, если пиксель не попал в игрока
score = score + 1; // Увеличиваем очки
if (score >= 20 && timeFrame >= 100){ // Если 20 раз подряд попаданий в игрока не было
score = 0; // Обнуляем очки
timeFrame = timeFrame - 20; // Уменьшаем время (увеличиваем скорость игры)
tone (6, 400, 200); // Издаём звук "Новый уровень"
}}}
disp.drawImage(matrix); // Обновляем картинку на матрице
if (soundBad){ // Если флаг звука проигрыша равен true
tone(6, 150, 100); // Издаём звук
soundBad = false; // Меняем флаг
soundTime = millis(); // Записываем время звука
secondTimeFlag = true; // Записываем флаг, обозначающий, что нужен повторный звук
}
if (secondTimeFlag && soundTime + 150 <= millis()){ // Если secondTimeFlag = true и прошло 150мс тишины после первого звука
secondTimeFlag = false; // Флаг secondTimeFlag = false
tone(6, 150, 100); // Издаём звук
}
delay(20); // Задержка для стабильности работы (если её не ставить, игрок будет заметно мерцать из-за частого обновления матрицы
}
Обсуждение