| <!DOCTYPE html> |
| <html lang="en" class="dark"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>SkyFlap Odyssey: Aerial Avian Adventure</title> |
| <link rel="icon" type="image/x-icon" href="/static/favicon.ico"> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
| <script src="https://unpkg.com/feather-icons"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/phaser/3.60.0/phaser.min.js"></script> |
| <style> |
| @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap'); |
| |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| } |
| |
| body { |
| font-family: 'Poppins', sans-serif; |
| background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #334155 100%); |
| color: #f8fafc; |
| overflow-x: hidden; |
| } |
| |
| .game-container { |
| position: relative; |
| max-width: 1200px; |
| margin: 0 auto; |
| border-radius: 20px; |
| overflow: hidden; |
| box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); |
| } |
| |
| #gameCanvas { |
| display: block; |
| margin: 0 auto; |
| border: 3px solid #475569; |
| border-radius: 15px; |
| background: linear-gradient(180deg, #1e40af 0%, #3b82f6 100%); |
| } |
| |
| .stats-overlay { |
| position: absolute; |
| top: 20px; |
| left: 20px; |
| z-index: 10; |
| background: rgba(15, 23, 42, 0.8); |
| padding: 15px; |
| border-radius: 12px; |
| backdrop-filter: blur(10px); |
| border: 1px solid rgba(255, 255, 255, 0.1); |
| } |
| |
| .score-animation { |
| animation: scorePop 0.6s ease-out; |
| } |
| |
| @keyframes scorePop { |
| 0% { transform: scale(1); } |
| 50% { transform: scale(1.3); } |
| 100% { transform: scale(1); } |
| } |
| |
| .power-up-indicator { |
| animation: pulse 2s infinite; |
| } |
| |
| @keyframes pulse { |
| 0% { opacity: 1; } |
| 50% { opacity: 0.6; } |
| 100% { opacity: 1; } |
| } |
| |
| .menu-transition { |
| transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1); |
| } |
| |
| .parallax-bg { |
| position: absolute; |
| width: 100%; |
| height: 100%; |
| top: 0; |
| left: 0; |
| z-index: -1; |
| } |
| |
| .bg-layer-1 { animation: scrollBg 60s linear infinite; } |
| .bg-layer-2 { animation: scrollBg 40s linear infinite; } |
| .bg-layer-3 { animation: scrollBg 20s linear infinite; } |
| |
| @keyframes scrollBg { |
| from { transform: translateX(0); } |
| to { transform: translateX(-100%); } |
| } |
| </style> |
| </head> |
| <body class="min-h-screen flex items-center justify-center p-4"> |
| |
| <div class="parallax-bg"> |
| <div class="bg-layer-1 absolute inset-0 opacity-20"> |
| <div class="flex"> |
| <div class="w-64 h-64 bg-gradient-to-br from-blue-400 to-purple-500 rounded-full blur-3xl"></div> |
| <div class="w-48 h-48 bg-gradient-to-br from-green-400 to-blue-500 rounded-full blur-2xl ml-32"></div> |
| </div> |
| </div> |
| <div class="bg-layer-2 absolute inset-0 opacity-15"> |
| <div class="flex mt-32"> |
| <div class="w-32 h-32 bg-gradient-to-br from-yellow-400 to-red-500 rounded-full blur-2xl"></div> |
| <div class="w-56 h-56 bg-gradient-to-br from-pink-400 to-red-500 rounded-full blur-3xl ml-64"></div> |
| </div> |
| </div> |
| <div class="bg-layer-3 absolute inset-0 opacity-10"> |
| <div class="flex mt-64"> |
| <div class="w-40 h-40 bg-gradient-to-br from-purple-400 to-pink-500 rounded-full blur-2xl ml-96"></div> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="game-container"> |
| |
| <div class="stats-overlay"> |
| <div class="flex items-center space-x-6"> |
| <div class="text-center"> |
| <div class="text-sm text-gray-400">SCORE</div> |
| <div id="currentScore" class="text-2xl font-bold text-white score-animation">0</div> |
| </div> |
| <div class="text-center"> |
| <div class="text-sm text-gray-400">BEST</div> |
| <div id="bestScore" class="text-xl font-semibold text-yellow-400">0</div> |
| </div> |
| <div id="powerUpDisplay" class="hidden"> |
| <div class="text-center"> |
| <div class="text-sm text-gray-400">POWER</div> |
| <div class="text-lg font-semibold text-green-400 power-up-indicator">ACTIVE</div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="gameCanvas" class="w-full h-auto"></div> |
|
|
| |
| <div id="startMenu" class="absolute inset-0 flex flex-col items-center justify-center bg-gradient-to-br from-black/80 to-blue-900/80 backdrop-blur-sm"> |
| <div class="text-center space-y-6"> |
| <h1 class="text-5xl font-bold bg-gradient-to-r from-blue-400 to-purple-500 bg-clip-text text-transparent"> |
| SkyFlap Odyssey |
| </h1> |
| <p class="text-xl text-gray-300">Aerial Avian Adventure</p> |
| |
| <div class="space-y-4 mt-8"> |
| <button onclick="startGame()" class="w-64 bg-gradient-to-r from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700 text-white font-semibold py-3 px-6 rounded-xl transition-all duration-300 transform hover:scale-105"> |
| <i data-feather="play" class="inline mr-2"></i> |
| Start Flight |
| </button> |
| |
| <button onclick="showSettings()" class="w-64 bg-gray-700 hover:bg-gray-600 text-white font-semibold py-3 px-6 rounded-xl transition-all duration-300"> |
| <i data-feather="settings" class="inline mr-2"></i> |
| Game Settings |
| </button> |
| </div> |
| |
| <div class="mt-6 text-sm text-gray-400"> |
| <p>Tap, Click, or Press SPACE to flap</p> |
| <p>Gamepad supported</p> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="gameOverMenu" class="absolute inset-0 flex flex-col items-center justify-center bg-gradient-to-br from-red-900/80 to-black/80 backdrop-blur-sm hidden"> |
| <div class="text-center space-y-6"> |
| <h2 class="text-4xl font-bold text-red-400">Flight Terminated</h2> |
| |
| <div class="bg-black/50 p-6 rounded-2xl space-y-4"> |
| <div class="text-2xl font-semibold">Final Score: <span id="finalScore" class="text-yellow-400">0</span></div> |
| <div class="text-lg">Best Score: <span id="finalBestScore" class="text-green-400">0</span></div> |
| |
| <div class="space-y-3"> |
| <button onclick="restartGame()" class="w-64 bg-gradient-to-r from-green-500 to-blue-600 hover:from-green-600 hover:to-blue-700 text-white font-semibold py-3 px-6 rounded-xl transition-all duration-300"> |
| <i data-feather="refresh-cw" class="inline mr-2"></i> |
| Fly Again |
| </button> |
| |
| <button onclick="showStartMenu()" class="w-64 bg-gray-700 hover:bg-gray-600 text-white font-semibold py-3 px-6 rounded-xl transition-all duration-300"> |
| <i data-feather="home" class="inline mr-2"></i> |
| Main Menu |
| </button> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="settingsMenu" class="absolute inset-0 flex flex-col items-center justify-center bg-gradient-to-br from-gray-900/80 to-black/80 backdrop-blur-sm hidden"> |
| <div class="bg-black/70 p-8 rounded-2xl max-w-md w-full space-y-6"> |
| <h2 class="text-3xl font-bold text-center text-white">Settings</h2> |
| |
| <div class="space-y-4"> |
| <div> |
| <label class="block text-sm font-medium text-gray-300 mb-2">Music Volume</label> |
| <input type="range" min="0" max="100" value="50" class="w-full h-2 bg-gray-600 rounded-lg appearance-none cursor-pointer slider"> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium text-gray-300 mb-2">SFX Volume</label> |
| <input type="range" min="0" max="100" value="70" class="w-full h-2 bg-gray-600 rounded-lg appearance-none cursor-pointer slider"> |
| </div> |
| |
| <div class="flex items-center justify-between"> |
| <span class="text-sm font-medium text-gray-300">Visual Effects</span> |
| <label class="relative inline-flex items-center cursor-pointer"> |
| <input type="checkbox" checked class="sr-only peer"> |
| <div class="w-11 h-6 bg-gray-700 peer-focus:outline-none rounded-full peer-checked:after:translate-x-full after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-500"></div> |
| </label> |
| </div> |
| </div> |
| |
| <div class="flex space-x-4 pt-4"> |
| <button onclick="saveSettings()" class="flex-1 bg-gradient-to-r from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700 text-white font-semibold py-2 px-4 rounded-xl transition-all duration-300"> |
| Save |
| </button> |
| |
| <button onclick="showStartMenu()" class="flex-1 bg-gray-700 hover:bg-gray-600 text-white font-semibold py-2 px-4 rounded-xl transition-all duration-300"> |
| Cancel |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="mt-8 text-center space-y-2"> |
| <div class="flex justify-center space-x-8 text-sm text-gray-400"> |
| <div class="flex items-center space-x-2"> |
| <i data-feather="mouse-pointer"></i> |
| <span>Click/Tap</span> |
| </div> |
| <div class="flex items-center space-x-2"> |
| <i data-feather="square"></i> |
| <span>Space Bar</span> |
| </div> |
| <div class="flex items-center space-x-2"> |
| <i data-feather="gamepad"></i> |
| <span>Gamepad A</span> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| let game; |
| let currentScore = 0; |
| let bestScore = 0; |
| let isGameRunning = false; |
| |
| |
| if (localStorage.getItem('skyflapBestScore')) { |
| bestScore = parseInt(localStorage.getItem('skyflapBestScore')); |
| document.getElementById('bestScore').textContent = bestScore; |
| } |
| |
| |
| const config = { |
| type: Phaser.AUTO, |
| width: 800, |
| height: 600, |
| parent: 'gameCanvas', |
| physics: { |
| default: 'arcade', |
| arcade: { |
| gravity: { y: 800 }, |
| debug: false |
| } |
| }, |
| scene: { |
| preload: preload, |
| create: create, |
| update: update |
| }, |
| scale: { |
| mode: Phaser.Scale.FIT, |
| autoCenter: Phaser.Scale.CENTER_BOTH |
| } |
| }; |
| |
| function preload() { |
| |
| this.load.image('bird', 'http://static.photos/nature/64x64/1'); |
| this.load.image('pipe', 'http://static.photos/green/128x512/2'); |
| this.load.image('background', 'http://static.photos/blue/800x600/3'); |
| } |
| |
| function create() { |
| |
| this.add.image(400, 300, 'background'); |
| |
| |
| this.bird = this.physics.add.sprite(100, 300, 'bird'); |
| this.bird.setCollideWorldBounds(true); |
| |
| |
| this.pipes = this.physics.add.group(); |
| |
| |
| this.input.on('pointerdown', flap, this); |
| this.input.keyboard.on('keydown-SPACE', flap, this); |
| |
| |
| this.pipeTimer = this.time.addEvent({ |
| delay: 1500, |
| callback: generatePipe, |
| callbackScope: this, |
| loop: true |
| }); |
| |
| |
| this.physics.add.collider(this.bird, this.pipes, gameOver, null, this); |
| |
| |
| this.scoreText = this.add.text(400, 50, '0', { |
| fontSize: '32px', |
| fill: '#ffffff', |
| stroke: '#000000', |
| strokeThickness: 4 |
| }).setOrigin(0.5); |
| } |
| |
| function update() { |
| if (!isGameRunning) return; |
| |
| |
| this.bird.rotation = Phaser.Math.Clamp(this.bird.body.velocity.y * 0.01, -0.5, 0.5); |
| } |
| |
| function flap() { |
| if (!isGameRunning) return; |
| this.bird.setVelocityY(-300); |
| } |
| |
| function generatePipe() { |
| if (!isGameRunning) return; |
| |
| const gap = 200; |
| const pipeX = 800; |
| const pipeY = Phaser.Math.Between(100, 500); |
| |
| |
| const topPipe = this.pipes.create(pipeX, pipeY - gap / 2, 'pipe'); |
| topPipe.setScale(1, -1); |
| topPipe.setVelocityX(-200); |
| |
| |
| const bottomPipe = this.pipes.create(pipeX, pipeY + gap / 2, 'pipe'); |
| bottomPipe.setVelocityX(-200); |
| |
| |
| this.time.delayedCall(4000, () => { |
| topPipe.destroy(); |
| bottomPipe.destroy(); |
| }); |
| |
| |
| this.time.delayedCall(2000, () => { |
| currentScore++; |
| updateScore(); |
| }); |
| } |
| |
| function gameOver() { |
| isGameRunning = false; |
| |
| |
| if (currentScore > bestScore) { |
| bestScore = currentScore; |
| localStorage.setItem('skyflapBestScore', bestScore); |
| |
| |
| document.getElementById('finalScore').textContent = currentScore; |
| document.getElementById('finalBestScore').textContent = bestScore; |
| document.getElementById('gameOverMenu').classList.remove('hidden'); |
| } |
| |
| function updateScore() { |
| document.getElementById('currentScore').textContent = currentScore; |
| document.getElementById('bestScore').textContent = bestScore; |
| |
| |
| document.getElementById('currentScore').classList.add('score-animation'); |
| setTimeout(() => { |
| document.getElementById('currentScore').classList.remove('score-animation'); |
| }, 600); |
| } |
| |
| |
| function startGame() { |
| document.getElementById('startMenu').classList.add('hidden'); |
| document.getElementById('gameOverMenu').classList.add('hidden'); |
| isGameRunning = true; |
| currentScore = 0; |
| updateScore(); |
| |
| |
| if (!game) { |
| game = new Phaser.Game(config); |
| } else { |
| |
| game.scene.restart(); |
| } |
| } |
| |
| function restartGame() { |
| document.getElementById('gameOverMenu').classList.add('hidden'); |
| isGameRunning = true; |
| currentScore = 0; |
| updateScore(); |
| game.scene.restart(); |
| } |
| |
| function showStartMenu() { |
| document.getElementById('startMenu').classList.remove('hidden'); |
| document.getElementById('settingsMenu').classList.add('hidden'); |
| document.getElementById('gameOverMenu').classList.add('hidden'); |
| isGameRunning = false; |
| } |
| |
| function showSettings() { |
| document.getElementById('startMenu').classList.add('hidden'); |
| document.getElementById('settingsMenu').classList.remove('hidden'); |
| } |
| |
| function saveSettings() { |
| |
| showStartMenu(); |
| } |
| |
| |
| window.addEventListener("gamepadconnected", (e) => { |
| console.log("Gamepad connected:", e.gamepad.id); |
| }); |
| |
| window.addEventListener("gamepaddisconnected", (e) => { |
| console.log("Gamepad disconnected:", e.gamepad.id); |
| }); |
| |
| |
| document.addEventListener('DOMContentLoaded', function() { |
| feather.replace(); |
| }); |
| |
| |
| function handleResize() { |
| const container = document.querySelector('.game-container'); |
| const canvas = document.getElementById('gameCanvas'); |
| |
| if (window.innerWidth < 768) { |
| container.style.maxWidth = '100%'; |
| if (game) { |
| game.scale.setGameSize(400, 300); |
| } |
| } else { |
| container.style.maxWidth = '1200px'; |
| if (game) { |
| game.scale.setGameSize(800, 600); |
| } |
| } |
| } |
| |
| window.addEventListener('resize', handleResize); |
| handleResize(); |
| </script> |
| </body> |
| </html> |
|
|