Использование нейросетей в разработке игр. Часть 2. Делаем платформер

<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Снежный прыжок</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { background: linear-gradient(to bottom, #1e3c72, #2a5298); overflow: hidden; display: flex; justify-content: center; align-items: center; min-height: 100vh; font-family: Arial, sans-serif; } #game-container { position: relative; width: 400px; height: 600px; background: linear-gradient(to top, #000428, #004e92); border: 4px solid #4facfe; border-radius: 20px; overflow: hidden; box-shadow: 0 0 20px rgba(79, 172, 254, 0.5); } #score { position: absolute; top: 20px; left: 20px; color: white; font-size: 24px; font-weight: bold; text-shadow: 0 0 5px rgba(0, 0, 0, 0.7); z-index: 5; } #best-score { position: absolute; top: 60px; left: 20px; color: #ffd700; font-size: 18px; font-weight: bold; text-shadow: 0 0 5px rgba(0, 0, 0, 0.7); z-index: 5; } #hint { position: absolute; top: 90px; left: 20px; color: rgba(255, 255, 255, 0.7); font-size: 14px; z-index: 5; } #game-over, .level-complete { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.9); color: white; display: flex; flex-direction: column; justify-content: center; align-items: center; z-index: 30; opacity: 0; pointer-events: none; transition: opacity 0.5s; } #game-over.active, .level-complete.active { opacity: 1; pointer-events: all; } #restart-btn, #next-level-btn, #restart-all-btn { margin-top: 20px; padding: 12px 24px; background: #4facfe; color: white; border: none; border-radius: 10px; cursor: pointer; font-size: 18px; } #next-level-btn { background: #00f2fe; box-shadow: 0 0 10px rgba(0, 242, 254, 0.5); } .snow-effect { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1; } .snow { position: absolute; width: 6px; height: 6px; background: white; border-radius: 50%; opacity: 0.8; } #player { position: absolute; width: 40px; height: 50px; transform: translate(-50%, -50%); transition: transform 0.1s; z-index: 10; } .head { position: absolute; width: 24px; height: 24px; background: white; border-radius: 50%; top: 0; left: 8px; box-shadow: 0 0 5px rgba(255, 255, 255, 0.6); } .body { position: absolute; width: 30px; height: 30px; background: white; border-radius: 50%; bottom: 0; left: 5px; box-shadow: 0 0 5px rgba(255, 255, 255, 0.6); } .eye { position: absolute; width: 5px; height: 5px; background: #333; border-radius: 50%; top: 12px; } .eye.left { left: 12px; } .eye.right { left: 19px; } .mouth { position: absolute; width: 10px; height: 3px; background: #333; border-radius: 2px; top: 18px; left: 15px; } .arm { position: absolute; width: 6px; height: 18px; background: #ddd; border-radius: 3px; top: 12px; } .arm.left { left: 2px; transform: rotate(20deg); } .arm.right { right: 2px; transform: rotate(-20deg); } #player.jump .arm.left { animation: wave-left 0.5s infinite alternate; } #player.jump .arm.right { animation: wave-right 0.5s infinite alternate; } @keyframes wave-left { 0% { transform: rotate(20deg); } 100% { transform: rotate(40deg); } } @keyframes wave-right { 0% { transform: rotate(-20deg); } 100% { transform: rotate(-40deg); } } #player.squish { transform: translate(-50%, -50%) scaleY(0.8); } #player.fall { animation: blink 0.3s infinite; } @keyframes blink { 0%, 80% { opacity: 1; } 90%, 100% { opacity: 0.5; } } .platform { position: absolute; width: 100px; height: 15px; background: linear-gradient(to bottom, #a0e7ff, #d0f0ff); border-radius: 10px; box-shadow: 0 0 8px rgba(255, 255, 255, 0.4); opacity: 1; transition: opacity 0.3s; } .flag { position: absolute; width: 12px; height: 20px; background: #ff4757; border-radius: 2px 0 0 2px; } .flag::after { content: ''; position: absolute; top: 0; right: -8px; width: 10px; height: 10px; background: #00f; clip-path: polygon(0 0, 100% 0, 50% 100%); } </style> </head> <body> <div id="game-container"> <div id="score">Счёт: 0</div> <div id="best-score">Рекорд: 0</div> <div id="hint">Подсказка: Доберись до флага!</div> <div id="player"> <div class="head"></div> <div class="body"></div> <div class="eye left"></div> <div class="eye right"></div> <div class="mouth"></div> <div class="arm left"></div> <div class="arm right"></div> </div> <div class="snow-effect" id="snow-effect"></div> <div id="game-over"> <h2>Вы упали!</h2> <p>Счёт: <span id="final-score">0</span></p> <button id="restart-btn">Начать сначала</button> </div> <div class="level-complete" id="level-complete"> <h2>Уровень пройден! 🎉</h2> <button id="next-level-btn">Следующий уровень</button> </div> <div class="level-complete" id="final-win"> <h2>🎉 Победа! Все уровни пройдены!</h2> <button id="restart-all-btn">Начать сначала</button> </div> </div> <script> const player = document.getElementById('player'); const gameContainer = document.getElementById('game-container'); const scoreElement = document.getElementById('score'); const bestScoreElement = document.getElementById('best-score'); const gameOverScreen = document.getElementById('game-over'); const levelCompleteScreen = document.getElementById('level-complete'); const finalWinScreen = document.getElementById('final-win'); const finalScoreElement = document.getElementById('final-score'); const restartBtn = document.getElementById('restart-btn'); const nextLevelBtn = document.getElementById('next-level-btn'); const restartAllBtn = document.getElementById('restart-all-btn'); const snowEffect = document.getElementById('snow-effect'); const gameWidth = 400; const gameHeight = 600; const playerWidth = 40; const playerHeight = 50; const platformWidth = 100; const platformHeight = 15; const playerSpeed = 6; let playerX = gameWidth / 2 - playerWidth / 2; let playerY = 550 - playerHeight / 2; let velocityY = 0; let gravity = 0.6; let jumpPower = -13.5; let isJumping = false; let cameraY = 0; let score = 0; let platforms = []; let gameActive = true; let bestScore = 0; let currentLevel = 1; let levelFinished = false; const keys = { w: false, a: false, d: false }; function createSnowflakes() { for (let i = 0; i < 30; i++) { const snow = document.createElement('div'); snow.classList.add('snow'); snow.style.left = `${Math.random() * 100}%`; snow.style.top = `${Math.random() * 100}%`; snow.style.opacity = Math.random() * 0.7 + 0.3; snow.dataset.speed = Math.random() * 1.5 + 0.5; snowEffect.appendChild(snow); } } function animateSnowflakes() { const snows = document.querySelectorAll('.snow'); if (!snows.length) return; snows.forEach(snow => { let top = parseFloat(snow.style.top) + parseFloat(snow.dataset.speed); if (top > 100) top = -5; snow.style.top = `${top}%`; }); } function createLevel1() { platforms = [ { x: 150, y: 550, touched: false, moving: false }, { x: 250, y: 480, touched: false, moving: false }, { x: 100, y: 410, touched: false, moving: false }, { x: 200, y: 340, touched: false, moving: false }, { x: 300, y: 270, touched: false, moving: false }, { x: 120, y: 200, touched: false, moving: false }, { x: 220, y: 130, touched: false, moving: false }, { x: 150, y: 60, touched: false, moving: false } ]; } function createLevel2() { platforms = [ { x: 180, y: 550, touched: false, moving: false }, { x: 280, y: 480, touched: false, moving: true, direction: 1, speed: 1 }, { x: 80, y: 410, touched: false, moving: true, direction: -1, speed: 1.2 }, { x: 200, y: 340, touched: false, moving: false }, { x: 300, y: 270, touched: false, moving: true, direction: 1, speed: 1.5 }, { x: 100, y: 200, touched: false, moving: true, direction: -1, speed: 1 }, { x: 220, y: 130, touched: false, moving: false }, { x: 150, y: 60, touched: false, moving: false } ]; } // --- ИСПРАВЛЕНИЕ: стартовая платформа — статичная --- function createLevel3() { platforms = [ { x: 180, y: 550, touched: false, moving: false }, // ✅ Статичная { x: 200, y: 480, touched: false, moving: true, direction: -1, speed: 2.2 }, { x: 180, y: 410, touched: false, moving: true, direction: 1, speed: 2.5 }, { x: 200, y: 340, touched: false, moving: true, direction: -1, speed: 2 }, { x: 180, y: 270, touched: false, moving: true, direction: 1, speed: 2.3 }, { x: 200, y: 200, touched: false, moving: true, direction: -1, speed: 2.1 }, { x: 180, y: 130, touched: false, moving: false }, { x: 150, y: 60, touched: false, moving: false } ]; } function renderPlatforms() { document.querySelectorAll('.platform').forEach(p => p.remove()); document.querySelectorAll('.flag').forEach(f => f.remove()); platforms.forEach((p, index) => { const platform = document.createElement('div'); platform.classList.add('platform'); platform.style.left = `${p.x}px`; platform.style.top = `${p.y - cameraY}px`; platform.dataset.y = p.y; gameContainer.appendChild(platform); if (index === platforms.length - 1) { const flag = document.createElement('div'); flag.classList.add('flag'); flag.style.left = `${p.x + platformWidth - 20}px`; flag.style.top = `${p.y - cameraY - 15}px`; gameContainer.appendChild(flag); } }); } function updateMovingPlatforms() { platforms.forEach(p => { if (p.moving) { p.x += p.direction * p.speed; if (p.x <= 50 || p.x >= gameWidth - platformWidth - 50) { p.direction *= -1; } } }); } function jump() { if (isJumping) return; velocityY = jumpPower; isJumping = true; } function checkCollision() { const playerBottom = playerY + playerHeight / 2; const playerCenterX = playerX + playerWidth / 2; for (let p of platforms) { if ( playerBottom >= p.y && playerBottom <= p.y + 10 && playerCenterX >= p.x && playerCenterX <= p.x + platformWidth && velocityY > 0 ) { isJumping = false; velocityY = 0; playerY = p.y - playerHeight / 2; if (!p.touched) { p.touched = true; score++; scoreElement.textContent = `Счёт: ${score}`; setTimeout(() => { const el = document.querySelector(`.platform[data-y="${p.y}"]`); if (el) el.style.opacity = '0'; }, 500); } return true; } } return false; } function gameLoop() { if (!gameActive) return; if (keys.a) playerX = Math.max(0, playerX - playerSpeed); if (keys.d) playerX = Math.min(gameWidth - playerWidth, playerX + playerSpeed); if (keys.w && !isJumping) jump(); velocityY += gravity; playerY += velocityY; checkCollision(); updateMovingPlatforms(); const targetCameraY = Math.max(0, playerY - gameHeight * 0.4); cameraY += (targetCameraY - cameraY) * 0.1; if (playerY > gameHeight + 100) { endGame(); } // --- ПРОВЕРКА ПОБЕДЫ --- const lastPlatform = platforms[platforms.length - 1]; const playerBottom = playerY + playerHeight / 2; const playerCenterX = playerX + playerWidth / 2; if ( !levelFinished && playerBottom >= lastPlatform.y - 10 && playerBottom <= lastPlatform.y + 20 && playerCenterX >= lastPlatform.x && playerCenterX <= lastPlatform.x + platformWidth ) { console.log(`🎉 Уровень ${currentLevel} пройден!`); levelFinished = true; setTimeout(() => { if (currentLevel < 3) { levelCompleteScreen.classList.add('active'); } else { finalWinScreen.classList.add('active'); } }, 600); } player.classList.remove('jump', 'squish', 'fall'); if (velocityY < 0) player.classList.add('jump'); else if (velocityY > 5) player.classList.add('fall'); if (!isJumping) player.classList.add('squish'); player.style.left = `${playerX + playerWidth / 2}px`; player.style.top = `${playerY - cameraY}px`; renderPlatforms(); animateSnowflakes(); requestAnimationFrame(gameLoop); } function endGame() { gameActive = false; finalScoreElement.textContent = score; gameOverScreen.classList.add('active'); } function startLevel(level) { if (level > 3) return; playerX = gameWidth / 2 - playerWidth / 2; playerY = 550 - playerHeight / 2; velocityY = 0; cameraY = 0; isJumping = false; gameActive = true; levelFinished = false; scoreElement.textContent = `Счёт: ${score}`; gameOverScreen.classList.remove('active'); levelCompleteScreen.classList.remove('active'); finalWinScreen.classList.remove('active'); if (level === 1) createLevel1(); else if (level === 2) createLevel2(); else if (level === 3) createLevel3(); currentLevel = level; requestAnimationFrame(gameLoop); } restartBtn.addEventListener('click', () => { score = 0; startLevel(1); }); nextLevelBtn.addEventListener('click', () => { startLevel(currentLevel + 1); }); restartAllBtn.addEventListener('click', () => { score = 0; startLevel(1); }); document.addEventListener('keydown', (e) => { const key = e.key.toLowerCase(); if (key === 'w') keys.w = true; if (key === 'a') keys.a = true; if (key === 'd') keys.d = true; if (key === ' ') { e.preventDefault(); jump(); } }); document.addEventListener('keyup', (e) => { const key = e.key.toLowerCase(); if (key === 'w') keys.w = false; if (key === 'a') keys.a = false; if (key === 'd') keys.d = false; }); // Запуск if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { createSnowflakes(); startLevel(1); }); } else { createSnowflakes(); startLevel(1); } </script> </body> </html>