ESP32 Dino Run (Часть 2): Добавляем звук и систему рекордов

Прокачайте свою DIY-консоль: оживляем игру звуковыми эффектами и сохраняем лучшие результаты в памяти ESP32.

В первой части мы создали основу игры ESP32 Dino Run: настроили графику на OLED-дисплее и прописали физику прыжка. Но какая игра обойдется без звука «Пип!» при прыжке и соревновательного элемента? Сегодня мы добавим пьезо-пищалку (Buzzer) и научим игру запоминать ваш High Score даже после выключения питания.

Новые компоненты для ESP32 Dino Run

Для этого этапа нам понадобятся всего два дополнительных элемента:

Список компонентов:

  • Резистор 100-220 Ом — для защиты порта ESP32.
  • Пьезо-излучатель (Buzzer) — активный или пассивный (мы будем использовать пассивный для управления тональностью
  • ESP32 DevKit V1
  • OLED 0.96″ (SSD1306)
  • Кнопка (Tactile Switch)
  • Макетная плата
  • Провода для подключения

Схема подключения компонентов

Для подключения мы используем интерфейс I2C. Это стандарт для OLED-дисплеев, который требует всего два сигнальных провода, помимо питания.

Важно: В коде мы будем использовать внутреннюю подтяжку пина кнопки (INPUT_PULLUP), поэтому дополнительный резистор на кнопку ставить не обязательно — просто подключаем её напрямую между пином 15 и землей (GND).

КомпонентПин дисплея / кнопкиПин ESP32 (GPIO)Описание
OLED DisplayVCC3.3VПитание дисплея
OLED DisplayGNDGNDОбщий минус
OLED DisplaySCLGPIO 22Тактовая линия I2C
OLED DisplaySDAGPIO 21Линия данных I2C
КнопкаPin 1GPIO 15Сигнал прыжка
КнопкаPin 2GNDЗамыкание на землю
BuzzerРезистор 100-220 ОмGPIO 25Выход звукового сигнала
BuzzerNegative (-)GNDОбщая земля

Симуляция в Wokwi: Тестируем без железа

Перед тем как брать в руки паяльник, я рекомендую проверить всё в онлайн-симуляторе. Это сэкономит время на отладку.

Попробовать игру в браузере:

Работа с памятью EEPROM (Preferences)

Особенность ESP32 Dino Run во второй части — использование библиотеки Preferences.h. В отличие от старой EEPROM, она позволяет сохранять данные в виде пар «ключ-значение» во флеш-память. Это значит, что ваш рекорд не сотрется при перезагрузке.

Логика работы с рекордами:

  • При старте игры считываем значение highscore из памяти.
  • Во время игры проверяем: если текущий score > highscore, обновляем значение на экране.
  • При наступлении Game Over записываем новый рекорд в память ESP32.

Исходный код: ESP32 Dino Run (Часть 2)

Скопируйте этот код в Arduino IDE. Обратите внимание на использование функции tone() для генерации звука.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Preferences.h> // Библиотека для сохранения рекордов

// Настройки дисплея и звука
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define BUZZER_PIN 25
#define BUTTON_PIN 15

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
Preferences preferences;

int dinoY = 45;
int velocity = 0;
bool isJumping = false;
int obstacleX = 128;
int score = 0;
int highScore = 0;

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(BUZZER_PIN, OUTPUT);
  
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  
  // Работа с рекордами
  preferences.begin("dino-game", false);
  highScore = preferences.getInt("highscore", 0);
  
  showStartScreen();
}

void playSound(int freq, int duration) {
  tone(BUZZER_PIN, freq, duration);
}

void loop() {
  // Прыжок и звук
  if (digitalRead(BUTTON_PIN) == LOW && !isJumping) {
    velocity = -12;
    isJumping = true;
    playSound(600, 100); // Звук прыжка
  }

  // Физика и логика препятствий (как в Части 1)
  // ... (код перемещения кактуса)

  // Проверка столкновения
  if (obstacleX > 15 && obstacleX < 25 && dinoY > 35) {
    handleGameOver();
  }

  drawGame();
  delay(30);
}

void handleGameOver() {
  playSound(150, 500); // Грустный звук проигрыша
  
  if (score > highScore) {
    highScore = score;
    preferences.putInt("highscore", highScore); // Сохраняем рекорд
  }
  
  // Вывод экрана Game Over...
}

void drawGame() {
  display.clearDisplay();
  // Отрисовка Score и High Score
  display.setCursor(0,0);
  display.print("S: "); display.print(score);
  display.setCursor(60,0);
  display.print("HI: "); display.print(highScore);
  
  // Отрисовка Дино и Кактуса...
  display.display();
}

Почему звук важен для ESP32 Dino Run?

Звуковой фидбек улучшает игровой опыт (UX). Игрок лучше чувствует момент прыжка и столкновения. В следующей части мы пойдем еще дальше и заменим пищалку на полноценную музыку!

Dino Run на ESP32

AndiBond.com


Поддержите проект AndiBond

Создание качественных гайдов, поиск рабочих решений и отладка кода занимают много времени. Все мои проекты остаются открытыми и бесплатными, чтобы каждый мог войти в мир электроники с минимальным порогом входа.

Если этот туториал сэкономил ваше время или помог запустить вашу первую игру на ESP32, вы можете поддержать развитие блога. Ваша поддержка помогает мне покупать новые датчики, дисплеи и контроллеры для будущих обзоров.

Каждый донат — это топливо для новых статей и видео. Спасибо, что вы со мной!»

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Level up your DIY console: bring your game to life with sound effects and save your best results in the ESP32’s memory.

In Part 1, we built the core of ESP32 Dino Run: setting up graphics on the OLED display and coding jump physics. But what is a game without a «Beep!» when you jump and a competitive element? Today, we will add a piezo buzzer and teach the game to remember your High Score even after the power is turned off.

New Components for ESP32 Dino Run

For this stage, we only need two additional items:

Список компонентов:

  • 100-220 Ohm Resistor – to protect the ESP32 GPIO port.
  • Piezo Buzzer – active or passive (we will use a passive one for pitch control).
  • ESP32 DevKit V1
  • OLED 0.96″ (SSD1306)
  • Tactile Switch (Push Button)
  • Breadboard
  • Jumper Wires

Component Wiring Diagram

We use the I2C interface for the display. This is standard for OLEDs and requires only two signal wires plus power.

Important: A 100–220 Ohm resistor between GPIO 25 and the positive (+) pin of the buzzer is necessary to limit current and prevent overloading the ESP32 port.

Buzzer Type: Our code uses the tone() function, so a passive buzzer is best as it allows for varying pitches. An active buzzer will only produce a single-tone beep.

ComponentPin (Device/Button)ESP32 Pin (GPIO)Description
OLED DisplayVCC3.3VDisplay Power
OLED DisplayGNDGNDCommon Ground
OLED DisplaySCLGPIO 22I2C Clock Line
OLED DisplaySDAGPIO 21I2C Data Line
ButtonPin 1GPIO 15Jump Signal
ButtonPin 2GNDClose to Ground
BuzzerVia 100 Ohm resistorGPIO 25Audio Signal Output
BuzzerNegative (-)GNDCommon Ground

Wokwi Simulation: Testing Without Hardware

Before picking up a soldering iron, I recommend testing everything in an online simulator. This saves time on debugging.

Try the game in your browser:

👉 Wokwi Project Link

Working with Memory (Preferences.h)

The standout feature of ESP32 Dino Run (Part 2) is the use of the Preferences.h library. Unlike the old EEPROM, it allows you to save data as «key-value» pairs in flash memory. This means your high score will not be erased when the device reboots.

High Score Logic:

  • At Startup: Read the highscore value from memory.
  • During Gameplay: Check if current score > highscore; if so, update the display.
  • On Game Over: Write the new record into the ESP32’s flash memory.

Source Code: ESP32 Dino Run (Part 2)

Copy this code into your Arduino IDE. Note the use of the tone() function for sound generation.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Preferences.h>

// Настройки дисплея и пинов
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define BUZZER_PIN 25
#define BUTTON_PIN 15

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
Preferences preferences;

// Игровые переменные
int dinoY = 45;
int velocity = 0;
int gravity = 2;
bool isJumping = false;
int obstacleX = 128;
int score = 0;
int highScore = 0;

// Прототипы функций (чтобы компилятор точно их видел)
void showStartScreen();
void handleGameOver();
void drawGame();
void playSound(int freq, int duration);

void setup() {
  Serial.begin(115200);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(BUZZER_PIN, OUTPUT);
  
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("OLED не найден"));
    for(;;);
  }

  // Загружаем рекорд
  preferences.begin("dino-game", false);
  highScore = preferences.getInt("highscore", 0);
  
  showStartScreen();
}

void playSound(int freq, int duration) {
  tone(BUZZER_PIN, freq, duration);
}

void loop() {
  // 1. ВВОД
  if (digitalRead(BUTTON_PIN) == LOW && !isJumping) {
    velocity = -12;
    isJumping = true;
    playSound(600, 100);
  }

  // 2. ЛОГИКА
  if (isJumping) {
    dinoY += velocity;
    velocity += gravity;
    if (dinoY >= 45) {
      dinoY = 45;
      isJumping = false;
    }
  }

  obstacleX -= 7; 
  if (obstacleX < -10) {
    obstacleX = 128;
    score++;
  }

  if (obstacleX > 15 && obstacleX < 25 && dinoY > 35) {
    handleGameOver();
  }

  // 3. ОТРИСОВКА
  drawGame();
  delay(30); 
}

void showStartScreen() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  
  display.setTextSize(1);
  display.setCursor(25, 5);
  display.print("DINO RUN ESP32");
  
  display.setCursor(18, 18);
  display.print("v1.0 by AndiBond");
  
  display.setCursor(28, 38);
  display.print("HI-SCORE: ");
  display.print(highScore);
  
  display.setCursor(20, 54);
  display.print("Press to Start");
  
  display.display();

  while(digitalRead(BUTTON_PIN) == HIGH) {
    delay(10);
  }
  playSound(800, 100);
  delay(200); 
}

void handleGameOver() {
  playSound(150, 600);
  
  if (score > highScore) {
    highScore = score;
    preferences.putInt("highscore", highScore);
  }
  
  display.clearDisplay();
  display.setCursor(10, 10);
  display.setTextSize(2);
  display.print("GAME OVER");
  
  display.setTextSize(1);
  display.setCursor(40, 35);
  display.print("Score: "); display.print(score);
  display.setCursor(35, 47);
  display.print("HI-Score: "); display.print(highScore);
  
  display.setCursor(30, 57);
  display.print("andibond.com");
  
  display.display();
  delay(2000);
  
  // Сброс и возврат в начало
  score = 0;
  obstacleX = 128;
  dinoY = 45;
  showStartScreen(); 
}

void drawGame() {
  display.clearDisplay();
  
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print("S:"); display.print(score);
  display.setCursor(65, 0);
  display.print("HI:"); display.print(highScore);
  
  display.drawLine(0, 55, 128, 55, WHITE);
  display.fillRect(20, dinoY, 10, 10, WHITE);
  display.fillRect(obstacleX, 45, 6, 10, WHITE);
  
  display.display();
}

Why Sound Matters in ESP32 Dino Run?

Звуковой фидбек уAudio feedback significantly improves the UX (User Experience). The player can better feel the timing of jumps and collisions. In the next part, we will take it even further and replace the simple beeps with full 8-bit music!

ESP32 Dino Run

AndiBond.com


Поддержите проект AndiBond

Creating high-quality guides, finding working solutions, and debugging code takes a lot of time. All my projects remain open-source and free, so that everyone can enter the world of electronics with the lowest possible barrier to entry.

If this tutorial saved you time or helped you launch your first game on the ESP32, you can support the blog’s development. Your support helps me purchase new sensors, displays, and controllers for future reviews and projects.

Support Project Development

Every donation is fuel for new articles and videos. Thank you for being with me!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *