| <!DOCTYPE html> |
| <html lang="zh"> |
| <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 { |
| font-family: 'Arial', sans-serif; |
| background-color: #87CEEB; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| justify-content: center; |
| min-height: 100vh; |
| overflow: hidden; |
| position: relative; |
| background-image: linear-gradient(to bottom, #87CEEB, #E0F7FA); |
| } |
| |
| .game-container { |
| position: relative; |
| width: 800px; |
| height: 500px; |
| border: 3px solid #FF9800; |
| border-radius: 10px; |
| box-shadow: 0 0 20px rgba(0, 0, 0, 0.2); |
| overflow: hidden; |
| background-color: #E0F7FA; |
| } |
| |
| canvas { |
| display: block; |
| background-color: #E0F7FA; |
| } |
| |
| .welcome-screen, .game-over-screen { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| justify-content: center; |
| background-color: rgba(0, 0, 0, 0.7); |
| color: white; |
| z-index: 10; |
| } |
| |
| .game-over-screen { |
| display: none; |
| } |
| |
| h1 { |
| font-size: 36px; |
| margin-bottom: 20px; |
| color: #FFEB3B; |
| text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); |
| } |
| |
| p { |
| font-size: 18px; |
| margin-bottom: 30px; |
| max-width: 80%; |
| text-align: center; |
| } |
| |
| button { |
| background-color: #FF9800; |
| color: white; |
| border: none; |
| padding: 12px 30px; |
| font-size: 18px; |
| border-radius: 30px; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
| } |
| |
| button:hover { |
| background-color: #FF5722; |
| transform: translateY(-2px); |
| box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15); |
| } |
| |
| .score-display { |
| position: absolute; |
| top: 20px; |
| left: 20px; |
| font-size: 24px; |
| color: #333; |
| background-color: rgba(255, 255, 255, 0.8); |
| padding: 10px 20px; |
| border-radius: 10px; |
| display: flex; |
| align-items: center; |
| z-index: 5; |
| } |
| |
| .score-display span { |
| margin-left: 10px; |
| font-weight: bold; |
| color: #FF9800; |
| } |
| |
| .controls { |
| position: absolute; |
| bottom: 20px; |
| left: 50%; |
| transform: translateX(-50%); |
| background-color: rgba(255, 255, 255, 0.8); |
| padding: 10px 20px; |
| border-radius: 10px; |
| font-size: 16px; |
| color: #333; |
| } |
| |
| @keyframes cloudMove { |
| from { transform: translateX(800px); } |
| to { transform: translateX(-200px); } |
| } |
| |
| .cloud { |
| position: absolute; |
| color: white; |
| font-size: 80px; |
| animation: cloudMove linear infinite; |
| z-index: 0; |
| opacity: 0.8; |
| user-select: none; |
| } |
| |
| .cloud:nth-child(1) { |
| top: 50px; |
| animation-duration: 40s; |
| } |
| |
| .cloud:nth-child(2) { |
| top: 150px; |
| animation-duration: 50s; |
| animation-delay: -15s; |
| } |
| |
| .cloud:nth-child(3) { |
| top: 300px; |
| animation-duration: 60s; |
| animation-delay: -30s; |
| } |
| |
| .sound-control { |
| position: absolute; |
| top: 20px; |
| right: 20px; |
| background-color: rgba(255, 255, 255, 0.8); |
| padding: 10px; |
| border-radius: 50%; |
| cursor: pointer; |
| z-index: 5; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| width: 40px; |
| height: 40px; |
| } |
| |
| .sound-control i { |
| font-size: 20px; |
| color: #333; |
| } |
| |
| |
| .flower { |
| position: absolute; |
| font-size: 40px; |
| color: #FF5722; |
| bottom: -10px; |
| z-index: 1; |
| user-select: none; |
| } |
| |
| .flower:nth-child(1) { |
| left: 100px; |
| } |
| |
| .flower:nth-child(2) { |
| right: 150px; |
| } |
| </style> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"> |
| </head> |
| <body> |
| |
| <div class="cloud">☁️</div> |
| <div class="cloud">☁️</div> |
| <div class="cloud">☁️</div> |
| |
| |
| <div class="flower">🌻</div> |
| <div class="flower">🌼</div> |
| |
| <div class="game-container"> |
| <div class="score-display"> |
| 分数: <span id="score">0</span> |
| </div> |
| |
| <div class="sound-control" id="soundControl"> |
| <i class="fas fa-volume-up"></i> |
| </div> |
| |
| <div class="welcome-screen" id="welcomeScreen"> |
| <h1>小蜜蜂吃果子</h1> |
| <p>帮助可爱的小蜜蜂收集甜蜜的花粉果实!使用键盘方向键控制小蜜蜂移动,避开障碍物,吃到越多果实得分越高!</p> |
| <button id="startBtn">开始游戏</button> |
| </div> |
| |
| <div class="game-over-screen" id="gameOverScreen"> |
| <h1>游戏结束!</h1> |
| <p>你的最终分数是: <span id="finalScore">0</span></p> |
| <button id="restartBtn">再来一次</button> |
| </div> |
| |
| <canvas id="gameCanvas" width="800" height="500"></canvas> |
| </div> |
| |
| <div class="controls"> |
| 使用键盘方向键(↑↓←→)控制小蜜蜂移动 |
| </div> |
| |
| <script> |
| |
| const gameState = { |
| score: 0, |
| gameOver: false, |
| soundEnabled: true, |
| gameSpeed: 3, |
| difficultyIncreaseInterval: 10, |
| lastDifficultyIncrease: 0 |
| }; |
| |
| |
| const canvas = document.getElementById('gameCanvas'); |
| const ctx = canvas.getContext('2d'); |
| const scoreDisplay = document.getElementById('score'); |
| const finalScoreDisplay = document.getElementById('finalScore'); |
| const welcomeScreen = document.getElementById('welcomeScreen'); |
| const gameOverScreen = document.getElementById('gameOverScreen'); |
| const startBtn = document.getElementById('startBtn'); |
| const restartBtn = document.getElementById('restartBtn'); |
| const soundControl = document.getElementById('soundControl'); |
| |
| |
| const bee = { |
| x: canvas.width / 2, |
| y: canvas.height / 2, |
| size: 40, |
| speed: 5 |
| }; |
| |
| const fruits = []; |
| const obstacles = []; |
| let fruitSpawnTimer = 0; |
| let obstacleSpawnTimer = 0; |
| |
| |
| const keys = { |
| ArrowUp: false, |
| ArrowDown: false, |
| ArrowLeft: false, |
| ArrowRight: false |
| }; |
| |
| |
| const sounds = { |
| collect: new Audio('data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU...'), |
| gameOver: new Audio('data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU...') |
| }; |
| |
| |
| window.addEventListener('keydown', (e) => { |
| if (keys.hasOwnProperty(e.key)) { |
| keys[e.key] = true; |
| e.preventDefault(); |
| } |
| }); |
| |
| window.addEventListener('keyup', (e) => { |
| if (keys.hasOwnProperty(e.key)) { |
| keys[e.key] = false; |
| e.preventDefault(); |
| } |
| }); |
| |
| |
| startBtn.addEventListener('click', () => { |
| welcomeScreen.style.display = 'none'; |
| resetGame(); |
| gameLoop(); |
| }); |
| |
| |
| restartBtn.addEventListener('click', () => { |
| gameOverScreen.style.display = 'none'; |
| resetGame(); |
| gameLoop(); |
| }); |
| |
| |
| soundControl.addEventListener('click', () => { |
| gameState.soundEnabled = !gameState.soundEnabled; |
| soundControl.innerHTML = gameState.soundEnabled ? |
| '<i class="fas fa-volume-up"></i>' : '<i class="fas fa-volume-mute"></i>'; |
| }); |
| |
| |
| function resetGame() { |
| gameState.score = 0; |
| gameState.gameOver = false; |
| gameState.gameSpeed = 3; |
| gameState.lastDifficultyIncrease = 0; |
| |
| scoreDisplay.textContent = '0'; |
| |
| bee.x = canvas.width / 2; |
| bee.y = canvas.height / 2; |
| |
| fruits.length = 0; |
| obstacles.length = 0; |
| fruitSpawnTimer = 0; |
| obstacleSpawnTimer = 0; |
| } |
| |
| |
| function drawBee() { |
| |
| ctx.fillStyle = '#FFD700'; |
| ctx.beginPath(); |
| ctx.arc(bee.x, bee.y, bee.size / 2, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| ctx.strokeStyle = '#333'; |
| ctx.lineWidth = 3; |
| for (let i = 0; i < 3; i++) { |
| ctx.beginPath(); |
| ctx.arc(bee.x, bee.y, bee.size / 2 - 5, Math.PI * 0.2 + i * Math.PI * 0.5, Math.PI * 0.8 + i * Math.PI * 0.5); |
| ctx.stroke(); |
| } |
| |
| |
| ctx.fillStyle = 'rgba(200, 200, 255, 0.7)'; |
| ctx.beginPath(); |
| ctx.ellipse(bee.x - bee.size * 0.3, bee.y - bee.size * 0.3, bee.size * 0.3, bee.size * 0.5, Math.PI / 4, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| ctx.beginPath(); |
| ctx.ellipse(bee.x + bee.size * 0.3, bee.y - bee.size * 0.3, bee.size * 0.3, bee.size * 0.5, -Math.PI / 4, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| ctx.fillStyle = '#333'; |
| ctx.beginPath(); |
| ctx.arc(bee.x + bee.size * 0.15, bee.y - bee.size * 0.15, bee.size * 0.1, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| ctx.strokeStyle = '#333'; |
| ctx.lineWidth = 2; |
| ctx.beginPath(); |
| ctx.moveTo(bee.x + bee.size * 0.15, bee.y - bee.size * 0.2); |
| ctx.lineTo(bee.x + bee.size * 0.3, bee.y - bee.size * 0.4); |
| ctx.stroke(); |
| |
| ctx.beginPath(); |
| ctx.moveTo(bee.x - bee.size * 0.15, bee.y - bee.size * 0.2); |
| ctx.lineTo(bee.x - bee.size * 0.3, bee.y - bee.size * 0.4); |
| ctx.stroke(); |
| } |
| |
| |
| function spawnFruit() { |
| const size = Math.random() * 20 + 15; |
| const x = Math.random() * (canvas.width - size); |
| const y = Math.random() * (canvas.height - size); |
| const type = Math.floor(Math.random() * 3); |
| |
| fruits.push({ |
| x, |
| y, |
| size, |
| type, |
| speedY: Math.random() * 1 + 0.5 |
| }); |
| } |
| |
| |
| function spawnObstacle() { |
| const width = Math.random() * 80 + 50; |
| const height = Math.random() * 80 + 30; |
| const x = Math.random() > 0.5 ? -width : canvas.width; |
| const y = Math.random() * (canvas.height - height); |
| |
| obstacles.push({ |
| x, |
| y, |
| width, |
| height, |
| speedX: (Math.random() * 2 + 2) * (x < 0 ? 1 : -1), |
| rotation: Math.random() * Math.PI * 2, |
| rotationSpeed: (Math.random() - 0.5) * 0.1 |
| }); |
| } |
| |
| |
| function drawFruits() { |
| fruits.forEach((fruit, index) => { |
| |
| fruit.y += fruit.speedY; |
| |
| |
| if (fruit.y > canvas.height + fruit.size) { |
| fruits.splice(index, 1); |
| return; |
| } |
| |
| |
| switch (fruit.type) { |
| case 0: |
| ctx.fillStyle = '#FF5252'; |
| break; |
| case 1: |
| ctx.fillStyle = '#FFD740'; |
| break; |
| case 2: |
| ctx.fillStyle = '#9C27B0'; |
| break; |
| } |
| |
| |
| ctx.beginPath(); |
| ctx.arc(fruit.x + fruit.size / 2, fruit.y + fruit.size / 2, fruit.size / 2, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| ctx.fillStyle = '#8BC34A'; |
| ctx.beginPath(); |
| ctx.moveTo(fruit.x + fruit.size / 2 - fruit.size * 0.2, fruit.y + fruit.size * 0.2); |
| ctx.lineTo(fruit.x + fruit.size / 2 + fruit.size * 0.1, fruit.y + fruit.size * 0.1); |
| ctx.lineTo(fruit.x + fruit.size / 2, fruit.y); |
| ctx.fill(); |
| }); |
| } |
| |
| |
| function drawObstacles() { |
| obstacles.forEach((obstacle, index) => { |
| |
| obstacle.x += obstacle.speedX; |
| obstacle.y += Math.sin(Date.now() / 300) * 0.5; |
| obstacle.rotation += obstacle.rotationSpeed; |
| |
| |
| if ((obstacle.speedX > 0 && obstacle.x > canvas.width) || |
| (obstacle.speedX < 0 && obstacle.x + obstacle.width < 0)) { |
| obstacles.splice(index, 1); |
| return; |
| } |
| |
| ctx.save(); |
| ctx.translate(obstacle.x + obstacle.width / 2, obstacle.y + obstacle.height / 2); |
| ctx.rotate(obstacle.rotation); |
| |
| |
| ctx.fillStyle = '#795548'; |
| ctx.beginPath(); |
| ctx.moveTo(-obstacle.width / 2, -obstacle.height / 2); |
| ctx.lineTo(obstacle.width / 2, -obstacle.height / 2 - 10); |
| ctx.lineTo(obstacle.width / 2 + 10, obstacle.height / 2); |
| ctx.lineTo(-obstacle.width / 2 + 10, obstacle.height / 2); |
| ctx.closePath(); |
| ctx.fill(); |
| |
| |
| ctx.fillStyle = '#5D4037'; |
| ctx.beginPath(); |
| ctx.arc(-obstacle.width / 4, -obstacle.height / 4, 5, 0, Math.PI * 2); |
| ctx.arc(obstacle.width / 4, -obstacle.height / 4, 5, 0, Math.PI * 2); |
| ctx.arc(obstacle.width / 4, obstacle.height / 4, 5, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| ctx.restore(); |
| }); |
| } |
| |
| |
| function checkCollisions() { |
| |
| fruits.forEach((fruit, index) => { |
| const dx = bee.x - (fruit.x + fruit.size / 2); |
| const dy = bee.y - (fruit.y + fruit.size / 2); |
| const distance = Math.sqrt(dx * dx + dy * dy); |
| |
| if (distance < bee.size / 2 + fruit.size / 2) { |
| |
| if (gameState.soundEnabled) { |
| sounds.collect.currentTime = 0; |
| sounds.collect.play().catch(e => {}); |
| } |
| |
| gameState.score += 10; |
| scoreDisplay.textContent = gameState.score; |
| fruits.splice(index, 1); |
| |
| |
| if (gameState.score - gameState.lastDifficultyIncrease >= gameState.difficultyIncreaseInterval) { |
| gameState.gameSpeed += 0.5; |
| gameState.lastDifficultyIncrease = gameState.score; |
| } |
| } |
| }); |
| |
| |
| obstacles.forEach(obstacle => { |
| |
| if (bee.x + bee.size / 2 > obstacle.x && |
| bee.x - bee.size / 2 < obstacle.x + obstacle.width && |
| bee.y + bee.size / 2 > obstacle.y && |
| bee.y - bee.size / 2 < obstacle.y + obstacle.height) { |
| |
| if (gameState.soundEnabled) { |
| sounds.gameOver.currentTime = 0; |
| sounds.gameOver.play().catch(e => {}); |
| } |
| |
| gameState.gameOver = true; |
| gameOverScreen.style.display = 'flex'; |
| finalScoreDisplay.textContent = gameState.score; |
| } |
| }); |
| } |
| |
| |
| function gameLoop() { |
| if (gameState.gameOver) return; |
| |
| |
| ctx.clearRect(0, 0, canvas.width, canvas.height); |
| |
| |
| if (keys.ArrowUp && bee.y > bee.size / 2) { |
| bee.y -= bee.speed; |
| } |
| if (keys.ArrowDown && bee.y < canvas.height - bee.size / 2) { |
| bee.y += bee.speed; |
| } |
| if (keys.ArrowLeft && bee.x > bee.size / 2) { |
| bee.x -= bee.speed; |
| } |
| if (keys.ArrowRight && bee.x < canvas.width - bee.size / 2) { |
| bee.x += bee.speed; |
| } |
| |
| |
| fruitSpawnTimer++; |
| if (fruitSpawnTimer > 60 / gameState.gameSpeed) { |
| spawnFruit(); |
| fruitSpawnTimer = 0; |
| |
| |
| if (Math.random() < 0.3) { |
| for (let i = 0; i < 2; i++) { |
| setTimeout(spawnFruit, i * 300); |
| } |
| } |
| } |
| |
| obstacleSpawnTimer++; |
| if (obstacleSpawnTimer > 100 / gameState.gameSpeed) { |
| spawnObstacle(); |
| obstacleSpawnTimer = 0; |
| } |
| |
| |
| drawFruits(); |
| drawObstacles(); |
| drawBee(); |
| |
| |
| checkCollisions(); |
| |
| |
| requestAnimationFrame(gameLoop); |
| } |
| </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 <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body> |
| </html> |