#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <SPI.h>
// Пины для дисплея ILI9341
#define TFT_CS 10
#define TFT_RST 9
#define TFT_DC 8
// Пины для кнопок и зуммера
#define UP_BUTTON_PIN 2
#define DOWN_BUTTON_PIN 3
#define BUZZER_PIN 4
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
// Параметры игры
#define PADDLE_WIDTH 10
#define PADDLE_HEIGHT 30
#define BALL_SIZE 5
int playerY = 60; // Начальная позиция игрока
int playerX = 10;
int botY = 60; // Начальная позиция бота
int botX = 118;
int ballX = 64;
int ballY = 80;
int ballSpeedX = 2;
int ballSpeedY = 2;
int playerScore = 0;
int botScore = 0;
void setup() {
pinMode(UP_BUTTON_PIN, INPUT_PULLUP);
pinMode(DOWN_BUTTON_PIN, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
tft.begin();
tft.setRotation(3);
tft.fillScreen(ILI9341_BLACK);
drawField();
drawPaddle(playerX, playerY, ILI9341_RED);
drawPaddle(botX, botY, ILI9341_BLUE);
drawBall(ballX, ballY);
updateScore();
}
void loop() {
if (digitalRead(UP_BUTTON_PIN) == LOW) {
playerY = max(0, playerY - 2);
}
if (digitalRead(DOWN_BUTTON_PIN) == LOW) {
playerY = min(tft.height() - PADDLE_HEIGHT, playerY + 2);
}
// Обновление позиции бота
botY = constrain(ballY - PADDLE_HEIGHT / 2, 0, tft.height() - PADDLE_HEIGHT);
// Обновление позиции мяча
ballX += ballSpeedX;
ballY += ballSpeedY;
// Проверка столкновений с верхним и нижним краем
if (ballY <= 0 || ballY >= tft.height() - BALL_SIZE) {
ballSpeedY = -ballSpeedY;
wallTone();
}
// Проверка столкновений с ракетками
if (ballX <= playerX + PADDLE_WIDTH && ballY + BALL_SIZE >= playerY && ballY <= playerY + PADDLE_HEIGHT) {
ballSpeedX = -ballSpeedX;
ballX = playerX + PADDLE_WIDTH; // чтобы избежать застревания мяча в ракетке
mcuPaddleTone();
}
if (ballX + BALL_SIZE >= botX && ballY + BALL_SIZE >= botY && ballY <= botY + PADDLE_HEIGHT) {
ballSpeedX = -ballSpeedX;
ballX = botX - BALL_SIZE; // чтобы избежать застревания мяча в ракетке
mcuPaddleTone();
}
// Проверка гола
if (ballX <= 0) {
botScore++;
resetBall();
} else if (ballX >= tft.width() - BALL_SIZE) {
playerScore++;
resetBall();
}
// Перерисовка игрового поля
tft.fillScreen(ILI9341_BLACK);
drawField();
drawPaddle(playerX, playerY, ILI9341_RED);
drawPaddle(botX, botY, ILI9341_BLUE);
drawBall(ballX, ballY);
updateScore();
delay(30);
}
void drawPaddle(int x, int y, uint16_t color) {
tft.fillRect(x, y, PADDLE_WIDTH, PADDLE_HEIGHT, color);
}
void drawBall(int x, int y) {
tft.fillRect(x, y, BALL_SIZE, BALL_SIZE, ILI9341_WHITE);
}
void drawField() {
tft.drawRect(0, 0, tft.width(), tft.height(), ILI9341_WHITE);
}
void updateScore() {
tft.setCursor(30, 10);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.print(playerScore);
tft.setCursor(90, 10);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.print(botScore);
}
void resetBall() {
ballX = tft.width() / 2 - BALL_SIZE / 2;
ballY = tft.height() / 2 - BALL_SIZE / 2;
ballSpeedX = random(2) == 0 ? 2 : -2;
ballSpeedY = random(2) == 0 ? 2 : -2;
}
void mcuPaddleTone() {
tone(BUZZER_PIN, 225, 25);
delay(25);
noTone(BUZZER_PIN);
}
void wallTone() {
tone(BUZZER_PIN, 200, 25);
delay(25);
noTone(BUZZER_PIN);
}