| <!DOCTYPE html> |
| <html lang="pt-br"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>SisPong - Inspirado no Sismetro</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap'); |
| |
| body { |
| font-family: 'Roboto', sans-serif; |
| background-color: #1a1a2e; |
| color: white; |
| overflow: hidden; |
| } |
| |
| #gameCanvas { |
| background-color: #16213e; |
| box-shadow: 0 0 30px rgba(0, 0, 0, 0.5); |
| } |
| |
| .player-paddle { |
| box-shadow: 0 0 15px rgba(74, 201, 195, 0.7); |
| } |
| |
| .ball { |
| box-shadow: 0 0 20px rgba(255, 123, 84, 0.8); |
| } |
| |
| .pulse { |
| animation: pulse 1.5s infinite; |
| } |
| |
| @keyframes pulse { |
| 0% { transform: scale(1); } |
| 50% { transform: scale(1.05); } |
| 100% { transform: scale(1); } |
| } |
| |
| .score-glow { |
| text-shadow: 0 0 10px #4ac9c3, 0 0 20px #4ac9c3; |
| } |
| |
| .button-glow:hover { |
| box-shadow: 0 0 15px rgba(74, 201, 195, 0.7); |
| transform: translateY(-2px); |
| transition: all 0.3s; |
| } |
| </style> |
| </head> |
| <body class="flex flex-col items-center justify-center min-h-screen"> |
| <header class="w-full max-w-4xl px-4 mb-6"> |
| <div class="flex items-center justify-between"> |
| <div class="flex items-center space-x-2"> |
| <i class="fas fa-broadcast-tower text-3xl text-cyan-400"></i> |
| <h1 class="text-3xl font-bold text-gradient bg-gradient-to-r from-cyan-400 to-orange-400 bg-clip-text text-transparent">Sis<span class="text-white">Pong</span></h1> |
| </div> |
| <div class="text-sm text-gray-300 hidden md:block"> |
| <p>Inspirado no visual do <span class="text-cyan-400">Sismetro.com.br</span></p> |
| </div> |
| </div> |
| </header> |
|
|
| <div class="relative w-full max-w-4xl aspect-video"> |
| <canvas id="gameCanvas" class="w-full h-full rounded-xl"></canvas> |
| |
| <div id="startScreen" class="absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-80 rounded-xl p-6 text-center"> |
| <h2 class="text-4xl font-bold mb-4 text-gradient bg-gradient-to-r from-cyan-400 to-orange-400 bg-clip-text text-transparent">SisPong</h2> |
| <p class="text-gray-300 mb-8">Use as teclas <span class="font-mono bg-gray-800 px-2 py-1 rounded">W/S</span> para mover o jogador esquerdo e as teclas <span class="font-mono bg-gray-800 px-2 py-1 rounded">↑/↓</span> para mover o jogador direito</p> |
| <button id="startButton" class="button-glow bg-gradient-to-r from-cyan-500 to-blue-600 text-white px-8 py-3 rounded-full font-bold pulse hover:from-cyan-600 hover:to-blue-700 transition-all"> |
| Começar Jogo |
| </button> |
| |
| <div class="mt-8 flex space-x-4"> |
| <div class="flex flex-col items-center"> |
| <p class="text-gray-400">Jogador 1</p> |
| <div class="flex items-center mt-2"> |
| <div class="w-5 h-16 bg-gradient-to-t from-cyan-400 to-cyan-600 rounded-full mr-2"></div> |
| <div class="text-xs text-gray-300"> |
| <p>Tecla W: ↑</p> |
| <p>Tecla S: ↓</p> |
| </div> |
| </div> |
| </div> |
| |
| <div class="flex flex-col items-center"> |
| <p class="text-gray-400">Jogador 2</p> |
| <div class="flex items-center mt-2"> |
| <div class="text-xs text-gray-300 mr-2"> |
| <p>Tecla ↑: ↑</p> |
| <p>Tecla ↓: ↓</p> |
| </div> |
| <div class="w-5 h-16 bg-gradient-to-t from-orange-400 to-orange-600 rounded-full"></div> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <div id="gameOverScreen" class="absolute inset-0 hidden flex-col items-center justify-center bg-black bg-opacity-80 rounded-xl p-6 text-center"> |
| <h2 class="text-4xl font-bold mb-2 text-gradient bg-gradient-to-r from-cyan-400 to-orange-400 bg-clip-text text-transparent">Fim de Jogo!</h2> |
| <p id="winnerText" class="text-xl mb-6"></p> |
| <div class="flex space-x-4"> |
| <button id="restartButton" class="button-glow bg-gradient-to-r from-cyan-500 to-blue-600 text-white px-6 py-2 rounded-full font-bold"> |
| Jogar Novamente |
| </button> |
| <button id="menuButton" class="button-glow bg-gradient-to-r from-gray-600 to-gray-700 text-white px-6 py-2 rounded-full font-bold"> |
| Menu Principal |
| </button> |
| </div> |
| <div class="mt-8 text-gray-300 text-sm"> |
| <p>Criado com inspiração no visual do Sismetro.com.br</p> |
| <p class="flex items-center justify-center mt-2"> |
| <i class="fas fa-code text-cyan-400 mr-1"></i> HTML, CSS e JavaScript |
| </p> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="mt-6 w-full max-w-4xl px-4"> |
| <div class="flex justify-between items-center"> |
| <div class="flex items-center space-x-2"> |
| <div class="w-5 h-16 bg-gradient-to-t from-cyan-400 to-cyan-600 rounded-full"></div> |
| <span class="text-2xl font-bold score-glow" id="player1Score">0</span> |
| </div> |
| |
| <div class="text-gray-400 text-sm"> |
| <p>Pontuação <span class="text-cyan-400">máxima: 5</span></p> |
| </div> |
| |
| <div class="flex items-center space-x-2"> |
| <span class="text-2xl font-bold score-glow" id="player2Score">0</span> |
| <div class="w-5 h-16 bg-gradient-to-t from-orange-400 to-orange-600 rounded-full"></div> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="mt-4 text-xs text-gray-400 flex flex-wrap justify-center gap-2 px-4"> |
| <div class="flex items-center"><div class="w-3 h-3 rounded-full bg-cyan-400 mr-1"></div> Controles do Jogador 1</div> |
| <div class="flex items-center"><div class="w-3 h-3 rounded-full bg-orange-400 mr-1"></div> Controles do Jogador 2</div> |
| <div class="flex items-center"><i class="fas fa-circle text-white text-xs mr-1"></i> Bola</div> |
| </div> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', () => { |
| const canvas = document.getElementById('gameCanvas'); |
| const ctx = canvas.getContext('2d'); |
| const player1ScoreElement = document.getElementById('player1Score'); |
| const player2ScoreElement = document.getElementById('player2Score'); |
| const startButton = document.getElementById('startButton'); |
| const startScreen = document.getElementById('startScreen'); |
| const gameOverScreen = document.getElementById('gameOverScreen'); |
| const restartButton = document.getElementById('restartButton'); |
| const menuButton = document.getElementById('menuButton'); |
| const winnerText = document.getElementById('winnerText'); |
| |
| |
| function resizeCanvas() { |
| const container = canvas.parentElement; |
| canvas.width = container.clientWidth; |
| canvas.height = container.clientHeight; |
| } |
| |
| resizeCanvas(); |
| window.addEventListener('resize', resizeCanvas); |
| |
| |
| const paddleWidth = 15; |
| const paddleHeight = 100; |
| const ballSize = 15; |
| const paddleSpeed = 8; |
| const initialBallSpeed = 5; |
| |
| let player1Score = 0; |
| let player2Score = 0; |
| let gameRunning = false; |
| |
| |
| let player1 = { |
| x: 30, |
| y: canvas.height / 2 - paddleHeight / 2, |
| speed: 0 |
| }; |
| |
| let player2 = { |
| x: canvas.width - 30 - paddleWidth, |
| y: canvas.height / 2 - paddleHeight / 2, |
| speed: 0 |
| }; |
| |
| |
| let ball = { |
| x: canvas.width / 2, |
| y: canvas.height / 2, |
| speedX: initialBallSpeed, |
| speedY: initialBallSpeed |
| }; |
| |
| |
| const keys = { |
| w: false, |
| s: false, |
| ArrowUp: false, |
| ArrowDown: false |
| }; |
| |
| |
| window.addEventListener('keydown', (e) => { |
| if (e.key in keys) { |
| keys[e.key] = true; |
| } |
| }); |
| |
| window.addEventListener('keyup', (e) => { |
| if (e.key in keys) { |
| keys[e.key] = false; |
| } |
| }); |
| |
| |
| startButton.addEventListener('click', startGame); |
| restartButton.addEventListener('click', startGame); |
| menuButton.addEventListener('click', () => { |
| gameOverScreen.classList.add('hidden'); |
| startScreen.classList.remove('hidden'); |
| resetGame(); |
| }); |
| |
| function startGame() { |
| startScreen.classList.add('hidden'); |
| gameOverScreen.classList.add('hidden'); |
| resetGame(); |
| gameRunning = true; |
| animate(); |
| } |
| |
| function resetGame() { |
| player1Score = 0; |
| player2Score = 0; |
| player1ScoreElement.textContent = '0'; |
| player2ScoreElement.textContent = '0'; |
| |
| player1.y = canvas.height / 2 - paddleHeight / 2; |
| player2.y = canvas.height / 2 - paddleHeight / 2; |
| |
| resetBall(); |
| } |
| |
| function resetBall() { |
| ball.x = canvas.width / 2; |
| ball.y = canvas.height / 2; |
| |
| |
| ball.speedX = (Math.random() > 0.5 ? 1 : -1) * initialBallSpeed; |
| ball.speedY = (Math.random() * 2 - 1) * initialBallSpeed * 0.5; |
| |
| |
| const speed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY); |
| ball.speedX = (ball.speedX / speed) * initialBallSpeed; |
| ball.speedY = (ball.speedY / speed) * initialBallSpeed; |
| } |
| |
| |
| function animate() { |
| if (!gameRunning) return; |
| |
| |
| ctx.clearRect(0, 0, canvas.width, canvas.height); |
| |
| |
| if (keys.w) player1.speed = -paddleSpeed; |
| else if (keys.s) player1.speed = paddleSpeed; |
| else player1.speed = 0; |
| |
| if (keys.ArrowUp) player2.speed = -paddleSpeed; |
| else if (keys.ArrowDown) player2.speed = paddleSpeed; |
| else player2.speed = 0; |
| |
| |
| player1.y += player1.speed; |
| player2.y += player2.speed; |
| |
| if (player1.y < 0) player1.y = 0; |
| if (player1.y > canvas.height - paddleHeight) player1.y = canvas.height - paddleHeight; |
| |
| if (player2.y < 0) player2.y = 0; |
| if (player2.y > canvas.height - paddleHeight) player2.y = canvas.height - paddleHeight; |
| |
| |
| ball.x += ball.speedX; |
| ball.y += ball.speedY; |
| |
| |
| if (ball.y <= 0 || ball.y >= canvas.height - ballSize) { |
| ball.speedY = -ball.speedY; |
| } |
| |
| |
| if ( |
| ball.x <= player1.x + paddleWidth && |
| ball.x + ballSize >= player1.x && |
| ball.y + ballSize >= player1.y && |
| ball.y <= player1.y + paddleHeight |
| ) { |
| |
| const hitPosition = (ball.y - (player1.y + paddleHeight / 2)) / (paddleHeight / 2); |
| const angle = hitPosition * Math.PI / 4; |
| |
| |
| const speed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY) * 1.05; |
| |
| ball.speedX = Math.cos(angle) * speed; |
| ball.speedY = Math.sin(angle) * speed; |
| |
| |
| if (ball.speedX < 0) ball.speedX = -ball.speedX; |
| } |
| |
| if ( |
| ball.x + ballSize >= player2.x && |
| ball.x <= player2.x + paddleWidth && |
| ball.y + ballSize >= player2.y && |
| ball.y <= player2.y + paddleHeight |
| ) { |
| |
| const hitPosition = (ball.y - (player2.y + paddleHeight / 2)) / (paddleHeight / 2); |
| const angle = hitPosition * Math.PI / 4; |
| |
| |
| const speed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY) * 1.05; |
| |
| ball.speedX = Math.cos(angle) * -speed; |
| ball.speedY = Math.sin(angle) * speed; |
| |
| |
| if (ball.speedX > 0) ball.speedX = -ball.speedX; |
| } |
| |
| |
| if (ball.x < 0) { |
| player2Score++; |
| player2ScoreElement.textContent = player2Score; |
| checkGameOver(); |
| resetBall(); |
| } else if (ball.x > canvas.width) { |
| player1Score++; |
| player1ScoreElement.textContent = player1Score; |
| checkGameOver(); |
| resetBall(); |
| } |
| |
| |
| ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)'; |
| ctx.lineWidth = 3; |
| ctx.setLineDash([10, 15]); |
| ctx.beginPath(); |
| ctx.moveTo(canvas.width / 2, 0); |
| ctx.lineTo(canvas.width / 2, canvas.height); |
| ctx.stroke(); |
| ctx.setLineDash([]); |
| |
| |
| |
| const player1Gradient = ctx.createLinearGradient(0, player1.y, 0, player1.y + paddleHeight); |
| player1Gradient.addColorStop(0, '#4ac9c4'); |
| player1Gradient.addColorStop(1, '#1e90ff'); |
| ctx.fillStyle = player1Gradient; |
| ctx.beginPath(); |
| ctx.roundRect(player1.x, player1.y, paddleWidth, paddleHeight, 20); |
| ctx.fill(); |
| |
| |
| const player2Gradient = ctx.createLinearGradient(0, player2.y, 0, player2.y + paddleHeight); |
| player2Gradient.addColorStop(0, '#ff7b54'); |
| player2Gradient.addColorStop(1, '#ff5353'); |
| ctx.fillStyle = player2Gradient; |
| ctx.beginPath(); |
| ctx.roundRect(player2.x, player2.y, paddleWidth, paddleHeight, 20); |
| ctx.fill(); |
| |
| |
| ctx.fillStyle = 'white'; |
| ctx.beginPath(); |
| ctx.arc(ball.x + ballSize/2, ball.y + ballSize/2, ballSize/2, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| requestAnimationFrame(animate); |
| } |
| |
| function checkGameOver() { |
| if (player1Score >= 5 || player2Score >= 5) { |
| gameRunning = false; |
| gameOverScreen.classList.remove('hidden'); |
| |
| if (player1Score >= 5) { |
| winnerText.textContent = 'Jogador 1 venceu!'; |
| winnerText.className = 'text-xl mb-6 text-cyan-400'; |
| } else { |
| winnerText.textContent = 'Jogador 2 venceu!'; |
| winnerText.className = 'text-xl mb-6 text-orange-400'; |
| } |
| } |
| } |
| }); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=tionioliv/sispong" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |