// Библиотека для работы с текстовым дисплеем по шине I²C #include // Библиотека для работы с кнопкой #include // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); // Даём понятное имя пину с пищалкой constexpr int BUZZER_PIN = 7; // Создаём звуковой шрифт из нот со своими частотами constexpr int NOTE_B3 = 247; constexpr int NOTE_C4 = 262; constexpr int NOTE_D4 = 294; constexpr int NOTE_E4 = 330; constexpr int NOTE_F4 = 349; constexpr int NOTE_G4 = 392; constexpr int NOTE_A4 = 440; constexpr int NOTE_B4 = 494; constexpr int NOTE_C5 = 523; // Создаём символ динозаврика в двоичной системе BIN byte dino[8] = { 0b00000, 0b00111, 0b00111, 0b10110, 0b11111, 0b01010, 0b01010, 0b00000 }; // Создаём символ кактуса в двоичной системе BIN byte cactus[8] = { 0b00100, 0b00101, 0b10101, 0b10101, 0b10111, 0b11100, 0b00100, 0b00000 }; // Назначаем константы с адресами кастомных символов в таблице знакогенератора constexpr byte LCD_ICON_DINO = 0x00; constexpr byte LCD_ICON_CACTUS = 0x01; // Назначаем константы с адресами штатных символов в таблице знакогенератора constexpr byte LCD_ICON_BLOCK = 0xFF; constexpr byte LCD_ICON_EMPTY = 0x20; // Переменные для подсчёта набранных очков и хранения рекордов int score = 0; int bestScore = 0; // Переменная для блокировки очков bool stateFreezeScore = false; // Массив игровой области // Нижняя строка дисплея char gameArea[16]; // Ячейка дисплея // динозаврик в прыжке или нет char gameDinoJump; long millisLastTime = 0; long millisLastJump = 0; // Время зависания динозавра в воздухе int jumpInterval = 50; // Скорость прокрутки дисплея int speedScroller = 300; // Создаём перечисления состояний игры enum { GAME_BEGIN, GAME_PLAY, GAME_OVER } stateGame; // Создаём объект для работы с кнопкой TroykaButton button(5); void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Добавляем собственные символы в ячейки знакогенератора lcd.createChar(LCD_ICON_DINO, dino); lcd.createChar(LCD_ICON_CACTUS, cactus); // Инициализируем кнопку button.begin(); // Задаём зерно генерации случайных чисел randomSeed(analogRead(A0)); // Устанавливаем режим «Начало игры» stateGame = GAME_BEGIN; } void loop() { // Если установлен режим «Начало игры» if (stateGame == GAME_BEGIN) { // Обнуляем текущий результат score = 0; // Очищаем массив игровой зоны for (int i = 0; i <= 15; i++) { gameArea[i] = LCD_ICON_EMPTY; } // Выводим начальную заставку на дисплей printGameBegin(); // Выводим приветственную музыку melodyGameBegin(); // Ожидаем нажатие кнопки для старта while (!button.isClick()) { button.read(); } // Устанавливаем режим «Процесс игры» stateGame = GAME_PLAY; // Очищаем дисплей lcd.clear(); delay(1000); } // Если установлен режим «Процесс игры» if (stateGame == GAME_PLAY) { // и функция playGame выдаёт ложное значение, // переходим в режим «Конец игры». if (playGame() == false) { stateGame = GAME_OVER; } } // Если установлен режим «Конец игры» if (stateGame == GAME_OVER) { // Выводим конечную заставку на дисплей printGameOver(); // Выводим завершающую музыку melodyGameOver(); // Ждём нажатия кнопки для повторной игры while (!button.isClick()) { button.read(); } // Заново переходим в режим «Начало игры» stateGame = GAME_BEGIN; delay(1000); } } // Функция вывода начальной заставки на экран void printGameBegin() { // Очищаем дисплей lcd.clear(); lcd.setCursor(4, 0); lcd.print("Dino Game"); lcd.setCursor(3, 1); lcd.print("Press Start"); // Выводим два барьера по бокам lcd.setCursor(0, 1); lcd.write(LCD_ICON_BLOCK); lcd.setCursor(15, 1); lcd.write(LCD_ICON_BLOCK); // Выводим динозаврика lcd.setCursor(1, 1); lcd.write(LCD_ICON_DINO); } // Функция вывода конечной заставки на экран void printGameOver() { // Выводим сообщение «Игра окончена» lcd.setCursor(4, 1); lcd.print("Game Over"); delay(1000); lcd.clear(); // Выводим набранный счёт lcd.setCursor(4, 0); lcd.print("Score: "); lcd.print(score); // Если счёт превзошёл предыдущий рекорд, обновляем значение рекорда if (score > bestScore) { bestScore = score; } // Выводим лучший результат игры за все попытки lcd.setCursor(5, 1); lcd.print("Best: "); lcd.print(bestScore); } // Функция обработки игрового процесса bool playGame() { button.read(); // Запоминаем текущее время unsigned long millisNowTime = millis(); // Если прошёл заданный интервал if (millisNowTime - millisLastTime >= speedScroller) { millisLastTime = millisNowTime; // Генерируем случайно число от 0 до 10 int randomNumber = random(0, 10); // Если выпало число 0 if (randomNumber == 0) { // Выводим препятствие gameArea[15] = LCD_ICON_CACTUS; } else { // В остальных случаях выводим пустоту gameArea[15] = LCD_ICON_EMPTY; } // Сдвигаем игровую область дисплея на шаг влево for (int i = 0; i <= 15; i++) { gameArea[i] = gameArea[i + 1]; } // Если блокировка очков выключена, увеличиваем очки if (!stateFreezeScore) { score++; } } // Выводим барьеры gameArea[0] = LCD_ICON_BLOCK; gameArea[15] = LCD_ICON_BLOCK; // Если нажата кнопка if (button.isPressed()) { tone(BUZZER_PIN, 1000, 100); // Включаем динозаврика в прыжке gameDinoJump = LCD_ICON_DINO; // Включаем заморозку очков stateFreezeScore = true; // Запоминаем текущее время прыжка millisLastJump = millis(); } // Если время прыжка истекло и внизу нет препятствия if (millis() - millisLastJump >= jumpInterval) { if (gameArea[1] == LCD_ICON_EMPTY || gameArea[1] == LCD_ICON_DINO) { // Возвращаем динозаврика на землю gameArea[1] = LCD_ICON_DINO; gameDinoJump = LCD_ICON_EMPTY; // Выключаем заморозку очков stateFreezeScore = false; } else { return false; } } // Обновляем содержимое на дисплее updateDataLCD(); return true; } // Функция вывода данных на дисплей void updateDataLCD() { // Печатаем игровое поле for (int i = 0; i <= 15; i++) { lcd.setCursor(i, 1); lcd.write(gameArea[i]); } // Выводим ячейку для прыжка и текущее количество очков lcd.setCursor(1, 0); lcd.write(gameDinoJump); lcd.setCursor(4, 0); lcd.print("Score: "); lcd.print(score); } // Функция вступительной мелодии void melodyGameBegin() { tone(BUZZER_PIN, NOTE_E4, 100); delay(150); tone(BUZZER_PIN, NOTE_E4,100); delay(300); tone(BUZZER_PIN, NOTE_E4,100); delay(300); tone(BUZZER_PIN, NOTE_C4,100); delay(100); tone(BUZZER_PIN, NOTE_E4,100); delay(300); tone(BUZZER_PIN, NOTE_G4, 100); delay(550); tone(BUZZER_PIN, NOTE_C5, 100); delay(575); } // Функция завершающей мелодии void melodyGameOver() { for (int i = 0; i <= 2; i++) { tone(BUZZER_PIN, NOTE_G4, 300); delay(200); tone(BUZZER_PIN, NOTE_C4, 300); delay(200); } }