В этом уроке
- Создание игры
- Работа с массивами
Видео версия урока
Принцип работы игры
Мы научились выводить на светодиодную матрицу изображения и даже делать несложную анимацию. Давайте пойдём дальше и создадим небольшую игру, в которой нужно будет уклоняться от летящих пикселей. Они, разумеется, будут появляться случайным образом, а скорость игры будет меняться в зависимости от успешности её прохождения — замедляться с каждой ошибкой и расти после нескольких успешных действий.
А что же насчёт управления? Можно сделать управление игроком через ИК-пульт, но почему бы не использовать бампер в качестве датчика, который будет реагировать на перемещающийся под ним палец!
Скетч проекта
Давайте загрузим скетч. Мы постарались сделать его как можно более простым, чтобы в нём было несложно разобраться. Комментарии помогут Вам.
#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); // Задержка для стабильности работы (если её не ставить, игрок будет заметно мерцать из-за частого обновления матрицы }
Обсуждение