//PONG GAME COM INTERFACE WEB
#include <WiFi.h>
#include <WebServer.h>
#include <DNSServer.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// WiFi Configuration
const char* ssid = "PongGame-ESP32";
const char* password = "12345678";
// DNS Server for Captive Portal
DNSServer dnsServer;
WebServer server(80);
// Display Configuration
#define largura_tela 128
#define altura_tela 64
#define resetar_oled -1
Adafruit_SSD1306 tela(largura_tela, altura_tela, &Wire, resetar_oled);
// Game Configuration
#define margem_superior 12
#define margem_inferior 63
#define margem_esquerda 0
#define margem_direita 127
// Game Variables
int altura_raquete = 10;
int largura_raquete = 3;
int raquete1_y = (margem_inferior + margem_superior - altura_raquete) / 2;
int raquete2_y = (margem_inferior + margem_superior - altura_raquete) / 2;
int bola_x = largura_tela / 2;
int bola_y = (margem_inferior + margem_superior) / 2;
int velocidade_bola_x = 2;
int velocidade_bola_y = 2;
int pontos_esquerda = 0;
int pontos_direita = 0;
bool jogo_iniciado = false;
// Player Connection Tracking
bool player1_conectado = false;
bool player2_conectado = false;
// Web Control Variables
int player1_input = 0;
int player2_input = 0;
// Client IP Tracking
IPAddress player1_ip;
IPAddress player2_ip;
void configurar_display() {
if (!tela.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("Falha na alocacao do SSD1306"));
for (;;);
}
tela.clearDisplay();
tela.setTextSize(1);
tela.setTextColor(SSD1306_WHITE);
}
void configurar_wifi() {
WiFi.softAP(ssid, password);
// Configurar DNS para Captive Portal
dnsServer.start(53, "*", WiFi.softAPIP());
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
}
void configurar_servidor_web() {
server.on("/", handle_root);
server.on("/move", handle_move);
server.on("/connect", handle_connect);
server.onNotFound(handle_root);
server.begin();
}
void atualizar_status_conexao() {
// Update display to show connection status
tela.clearDisplay();
tela.setTextSize(2);
tela.setCursor(10, 0);
tela.print("Conecte para jogar");
tela.print(player1_conectado ? "1" : "0");
tela.print("/");
tela.print(player2_conectado ? "1" : "0");
tela.display();
}
void handle_root() {
String html = "<!DOCTYPE html>\n";
html += "<html>\n";
html += "<head>\n";
html += " <title>Pong Game</title>\n";
html += " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n";
html += " <style>\n";
html += " body { \n";
html += " font-family: Arial; \n";
html += " text-align: center; \n";
html += " margin: 0; \n";
html += " padding: 0; \n";
html += " height: 100vh; \n";
html += " display: flex; \n";
html += " flex-direction: column; \n";
html += " justify-content: center; \n";
html += " }\n";
html += " .game-container { \n";
html += " display: flex; \n";
html += " justify-content: space-between; \n";
html += " width: 100%; \n";
html += " padding: 0 20px; \n";
html += " box-sizing: border-box; \n";
html += " }\n";
html += " .player-controls { \n";
html += " display: flex; \n";
html += " flex-direction: column; \n";
html += " gap: 10px; \n";
html += " }\n";
html += " .btn { \n";
html += " width: 80px; \n";
html += " height: 50px; \n";
html += " font-size: 16px; \n";
html += " border: none; \n";
html += " cursor: pointer; \n";
html += " touch-action: manipulation; \n";
html += " }\n";
html += " .player1 .btn:first-child { background-color: blue; color: white; }\n";
html += " .player1 .btn:not(:first-child) { background-color: #4CAF50; color: white; }\n";
html += " .player2 .btn:first-child { background-color: red; color: white; }\n";
html += " .player2 .btn:not(:first-child) { background-color: #4CAF50; color: white; }\n";
html += " </style>\n";
html += "</head>\n";
html += "<body>\n";
html += " <h1>Pong Game</h1>\n";
html += " <div class=\"game-container\">\n";
// Player 1 Controls (Left Side)
html += " <div class=\"player-controls player1\">\n";
html += " <button class=\"btn\" onmousedown=\"connect(1)\">Connect</button>\n";
html += " <button class=\"btn\" onmousedown=\"move('p1up')\" onmouseup=\"move('stop')\">Up</button>\n";
html += " <button class=\"btn\" onmousedown=\"move('p1down')\" onmouseup=\"move('stop')\">Down</button>\n";
html += " </div>\n";
// Player 2 Controls (Right Side)
html += " <div class=\"player-controls player2\">\n";
html += " <button class=\"btn\" onmousedown=\"connect(2)\">Connect</button>\n";
html += " <button class=\"btn\" onmousedown=\"move('p2up')\" onmouseup=\"move('stop')\">Up</button>\n";
html += " <button class=\"btn\" onmousedown=\"move('p2down')\" onmouseup=\"move('stop')\">Down</button>\n";
html += " </div>\n";
html += " </div>\n";
html += " <script>\n";
html += " function connect(player) {\n";
html += " fetch('/connect?player=' + player).then(response => response.text())\n";
html += " .then(text => {\n";
html += " alert(text);\n";
html += " });\n";
html += " }\n";
html += " function move(dir) {\n";
html += " fetch('/move?d=' + dir);\n";
html += " }\n";
html += " </script>\n";
html += "</body>\n";
html += "</html>\n";
server.send(200, "text/html", html);
}
void handle_connect() {
String playerParam = server.arg("player");
IPAddress clientIp = server.client().remoteIP();
// Verify if this client is already connected
if (playerParam == "1" && !player1_conectado) {
player1_conectado = true;
player1_ip = clientIp;
server.send(200, "text/plain", "Player 1 Connected");
atualizar_status_conexao();
} else if (playerParam == "2" && !player2_conectado && clientIp != player1_ip) {
player2_conectado = true;
player2_ip = clientIp;
server.send(200, "text/plain", "Player 2 Connected");
atualizar_status_conexao();
} else {
server.send(200, "text/plain", "Already Connected or Game Full");
}
}
void handle_move() {
String direction = server.arg("d");
if (!jogo_iniciado && (direction == "p1up" || direction == "p1down" ||
direction == "p2up" || direction == "p2down")) {
if (player1_conectado || player2_conectado) {
jogo_iniciado = true;
}
}
if (direction == "p1up") player1_input = -1;
else if (direction == "p1down") player1_input = 1;
else if (direction == "p2up") player2_input = -1;
else if (direction == "p2down") player2_input = 1;
else {
player1_input = 0;
player2_input = 0;
}
server.send(200, "text/plain", "OK");
}
void atualizar_raquetes() {
// Player 1 (Left Paddle)
if (player1_input == -1 && raquete1_y > margem_superior) {
raquete1_y -= 5;
} else if (player1_input == 1 && raquete1_y < margem_inferior - altura_raquete) {
raquete1_y += 5;
}
// Player 2 (Right Paddle)
if (player2_input == -1 && raquete2_y > margem_superior) {
raquete2_y -= 5;
} else if (player2_input == 1 && raquete2_y < margem_inferior - altura_raquete) {
raquete2_y += 5;
}
}
void atualizar_jogo() {
// Só atualiza o jogo se já tiver iniciado
if (!jogo_iniciado) return;
// Mover a bola
bola_x += velocidade_bola_x;
bola_y += velocidade_bola_y;
// Colisao da bola com o quadro
if (bola_y <= margem_superior || bola_y >= margem_inferior - 1) {
velocidade_bola_y = -velocidade_bola_y;
bola_y = constrain(bola_y, margem_superior + 1, margem_inferior - 2);
}
// Colisao da bola com a raquete
if ((bola_x <= margem_esquerda + largura_raquete && bola_y >= raquete1_y && bola_y <= raquete1_y + altura_raquete) ||
(bola_x >= margem_direita - largura_raquete - 1 && bola_y >= raquete2_y && bola_y <= raquete2_y + altura_raquete)) {
velocidade_bola_x = -velocidade_bola_x;
}
// Pontuacao e reinicio da bola
if (bola_x <= margem_esquerda) {
pontos_direita++;
bola_x = largura_tela / 2;
bola_y = (margem_inferior + margem_superior) / 2;
velocidade_bola_x = abs(velocidade_bola_x);
} else if (bola_x >= margem_direita) {
pontos_esquerda++;
bola_x = largura_tela / 2;
bola_y = (margem_inferior + margem_superior) / 2;
velocidade_bola_x = -abs(velocidade_bola_x);
}
}
void desenhar_jogo() {
tela.clearDisplay();
if (!player1_conectado) {
// Tela de espera de conexão
tela.setTextSize(2);
tela.setCursor(10, 0);
tela.print("PONG GAME");
tela.setTextSize(1);
tela.setCursor(10, 20);
tela.print("Conecte para jogar");
tela.setTextSize(1);
tela.setCursor(20, 30);
tela.print("Ate 2 jogadores");
tela.setCursor(10, 45);
tela.print("Aguardando conexao");
tela.setCursor(30, 55);
tela.print("do Jogador...");
tela.display();
return;
}
if (!jogo_iniciado) {
// Tela de pronto para iniciar
tela.setTextSize(1);
tela.setCursor(10, 20);
tela.print("Jogador conectado!");
tela.setCursor(10, 30);
tela.print("Aperte um botao");
tela.setCursor(10, 40);
tela.print("para iniciar");
tela.display();
return;
}
// Desenhar quadro
tela.drawRect(margem_esquerda, margem_superior, largura_tela, margem_inferior - margem_superior, SSD1306_WHITE);
// Pontuacao
tela.setCursor(0, 0);
tela.print("Esq.:");
tela.print(pontos_esquerda);
tela.setCursor(largura_tela - 45, 0);
tela.print("Dir.:");
tela.print(pontos_direita);
// Linha divisoria
for (int i = margem_superior; i < margem_inferior; i += 4) {
tela.drawPixel(largura_tela/2, i, SSD1306_WHITE);
}
// Raquetes
tela.fillRect(margem_esquerda, raquete1_y, largura_raquete, altura_raquete, SSD1306_WHITE);
tela.fillRect(margem_direita - largura_raquete, raquete2_y, largura_raquete, altura_raquete, SSD1306_WHITE);
// Bola
tela.fillRect(bola_x, bola_y, 2, 2, SSD1306_WHITE);
tela.display();
}
void desenhar_tela_inicial() {
tela.clearDisplay();
tela.setTextSize(2);
tela.setCursor((largura_tela - 12*8)/2, 10);
tela.print("PONG GAME");
tela.setTextSize(1);
tela.setCursor(20, 30);
tela.print("Conecte em PongGame");
tela.setCursor(25, 40);
tela.print("Acesse 192.168.4.1");
tela.display();
}
void setup() {
Serial.begin(115200);
configurar_display();
configurar_wifi();
configurar_servidor_web();
desenhar_tela_inicial();
}
void loop() {
// Tratar solicitacoes de DNS para Captive Portal
dnsServer.processNextRequest();
// Tratar solicitacoes do servidor web
server.handleClient();
// Atualizar posicao das raquetes baseado na entrada web
atualizar_raquetes();
// Atualizar jogo e desenhar
atualizar_jogo();
desenhar_jogo();
delay(30);
}