0
点赞
收藏
分享

微信扫一扫

js的H5的水果忍者

素锦时年_1b00 05-16 09:00 阅读 16

下面是一个简单的H5水果忍者游戏实现,使用HTML5 Canvas绘制,包含基本的切水果、计分和游戏结束机制。游戏具有流畅的动画效果和响应式设计,适合在移动设备上体验。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>水果忍者</title>
    <style>
        body { margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background: #000; }
        .game-container { position: relative; }
        canvas { display: block; background: #fff; }
        .score-board { position: absolute; top: 10px; left: 10px; color: white; font-size: 24px; font-weight: bold; }
        .lives { position: absolute; top: 10px; right: 10px; color: white; font-size: 24px; font-weight: bold; }
        .game-over { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0,0,0,0.7); color: white; padding: 20px; text-align: center; border-radius: 10px; display: none; }
        .start-screen { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.8); display: flex; flex-direction: column; justify-content: center; align-items: center; color: white; }
        button { padding: 10px 20px; font-size: 18px; background: #FF5722; color: white; border: none; border-radius: 5px; cursor: pointer; margin-top: 20px; }
    </style>
</head>
<body>
    <div class="game-container">
        <div class="start-screen" id="startScreen">
            <h1>水果忍者</h1>
            <p>滑动屏幕切开水果,避开炸弹</p>
            <button id="startButton">开始游戏</button>
        </div>
        
        <div class="score-board">分数: <span id="score">0</span></div>
        <div class="lives">生命: <span id="lives">3</span></div>
        
        <div class="game-over" id="gameOver">
            <h2>游戏结束</h2>
            <p>最终得分: <span id="finalScore">0</span></p>
            <button id="restartButton">重新开始</button>
        </div>
        
        <canvas id="gameCanvas"></canvas>
    </div>

    <script>
        // 游戏配置
        const config = {
            canvasWidth: 800,
            canvasHeight: 600,
            fruitSpeed: 5,
            fruitSize: 60,
            fruitTypes: ['🍎', '🍌', '🍊', '🍇', '🍓', '🍉', '🍑', '🍒', '🍈', '🍋'],
            bombType: '💣',
            spawnRate: 1500, // 毫秒
            spawnRateVariance: 500,
            gravity: 0.2,
            sliceForce: 8,
            maxLives: 3
        };

        // 游戏状态
        const gameState = {
            canvas: null,
            ctx: null,
            score: 0,
            lives: config.maxLives,
            isPlaying: false,
            isGameOver: false,
            lastSpawnTime: 0,
            objects: [],
            sliceTrail: [],
            mouseDown: false,
            lastMousePos: { x: 0, y: 0 }
        };

        // 获取DOM元素
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');
        const scoreElement = document.getElementById('score');
        const livesElement = document.getElementById('lives');
        const gameOverElement = document.getElementById('gameOver');
        const finalScoreElement = document.getElementById('finalScore');
        const startButton = document.getElementById('startButton');
        const restartButton = document.getElementById('restartButton');
        const startScreen = document.getElementById('startScreen');

        // 设置Canvas尺寸
        canvas.width = config.canvasWidth;
        canvas.height = config.canvasHeight;

        // 初始化游戏
        function initGame() {
            gameState.canvas = canvas;
            gameState.ctx = ctx;
            gameState.score = 0;
            gameState.lives = config.maxLives;
            gameState.isPlaying = false;
            gameState.isGameOver = false;
            gameState.objects = [];
            gameState.sliceTrail = [];
            
            // 更新UI
            scoreElement.textContent = gameState.score;
            livesElement.textContent = gameState.lives;
            gameOverElement.style.display = 'none';
            startScreen.style.display = 'block';
            
            // 绑定事件
            canvas.addEventListener('mousedown', handleMouseDown);
            canvas.addEventListener('mousemove', handleMouseMove);
            canvas.addEventListener('mouseup', handleMouseUp);
            canvas.addEventListener('touchstart', handleTouchStart);
            canvas.addEventListener('touchmove', handleTouchMove);
            canvas.addEventListener('touchend', handleTouchEnd);
            startButton.addEventListener('click', startGame);
            restartButton.addEventListener('click', restartGame);
        }

        // 开始游戏
        function startGame() {
            gameState.isPlaying = true;
            gameState.lastSpawnTime = Date.now();
            startScreen.style.display = 'none';
            gameLoop();
        }

        // 重新开始游戏
        function restartGame() {
            initGame();
            startGame();
        }

        // 游戏主循环
        function gameLoop() {
            if (!gameState.isPlaying) return;
            
            // 清空画布
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // 生成水果/炸弹
            const currentTime = Date.now();
            if (currentTime - gameState.lastSpawnTime > config.spawnRate + Math.random() * config.spawnRateVariance) {
                spawnObject();
                gameState.lastSpawnTime = currentTime;
            }
            
            // 更新和绘制所有物体
            updateObjects();
            drawObjects();
            
            // 绘制切痕轨迹
            drawSliceTrail();
            
            // 检查游戏是否结束
            if (gameState.lives <= 0 && !gameState.isGameOver) {
                endGame();
            }
            
            // 继续游戏循环
            requestAnimationFrame(gameLoop);
        }

        // 生成水果或炸弹
        function spawnObject() {
            const isBomb = Math.random() < 0.15; // 15%的概率生成炸弹
            
            // 随机位置和速度
            const x = Math.random() * (canvas.width - config.fruitSize);
            const y = canvas.height + config.fruitSize;
            const speedX = (Math.random() - 0.5) * 8;
            const speedY = -(Math.random() * 12 + 10);
            
            const obj = {
                x,
                y,
                speedX,
                speedY,
                type: isBomb ? config.bombType : config.fruitTypes[Math.floor(Math.random() * config.fruitTypes.length)],
                isBomb,
                isSliced: false,
                slicePart1: null,
                slicePart2: null,
                gravity: config.gravity,
                size: config.fruitSize,
                rotation: 0,
                rotationSpeed: (Math.random() - 0.5) * 0.1
            };
            
            gameState.objects.push(obj);
        }

        // 更新所有物体
        function updateObjects() {
            for (let i = gameState.objects.length - 1; i >= 0; i--) {
                const obj = gameState.objects[i];
                
                // 更新位置
                obj.x += obj.speedX;
                obj.y += obj.speedY;
                obj.speedY += obj.gravity;
                obj.rotation += obj.rotationSpeed;
                
                // 如果物体离开画布,移除它
                if (obj.y > canvas.height + config.fruitSize || 
                    obj.x < -config.fruitSize || 
                    obj.x > canvas.width + config.fruitSize) {
                    
                    // 如果是未切的水果,减少生命值
                    if (!obj.isBomb && !obj.isSliced) {
                        gameState.lives--;
                        livesElement.textContent = gameState.lives;
                    }
                    
                    gameState.objects.splice(i, 1);
                }
                
                // 检查是否被切开
                if (!obj.isSliced && obj.isBomb === false) {
                    for (let j = 0; j < gameState.sliceTrail.length; j++) {
                        const trailPoint = gameState.sliceTrail[j];
                        const distance = Math.hypot(obj.x + obj.size/2 - trailPoint.x, obj.y + obj.size/2 - trailPoint.y);
                        
                        if (distance < obj.size/2) {
                            sliceFruit(obj);
                            break;
                        }
                    }
                }
                
                // 检查炸弹是否被点击
                if (!obj.isSliced && obj.isBomb === true) {
                    for (let j = 0; j < gameState.sliceTrail.length; j++) {
                        const trailPoint = gameState.sliceTrail[j];
                        const distance = Math.hypot(obj.x + obj.size/2 - trailPoint.x, obj.y + obj.size/2 - trailPoint.y);
                        
                        if (distance < obj.size/2) {
                            explodeBomb(obj);
                            break;
                        }
                    }
                }
            }
            
            // 更新切痕轨迹
            gameState.sliceTrail = gameState.sliceTrail.filter(point => point.life > 0);
            gameState.sliceTrail.forEach(point => point.life--);
        }

        // 绘制所有物体
        function drawObjects() {
            gameState.objects.forEach(obj => {
                ctx.save();
                ctx.translate(obj.x + obj.size/2, obj.y + obj.size/2);
                ctx.rotate(obj.rotation);
                
                if (obj.isSliced && !obj.isBomb) {
                    // 绘制切开的水果
                    if (obj.slicePart1) {
                        ctx.save();
                        ctx.translate(obj.slicePart1.offsetX, obj.slicePart1.offsetY);
                        ctx.font = `${obj.size}px Arial`;
                        ctx.fillText(obj.slicePart1.char, -obj.size/2, obj.size/4);
                        ctx.restore();
                    }
                    
                    if (obj.slicePart2) {
                        ctx.save();
                        ctx.translate(obj.slicePart2.offsetX, obj.slicePart2.offsetY);
                        ctx.font = `${obj.size}px Arial`;
                        ctx.fillText(obj.slicePart2.char, -obj.size/2, obj.size/4);
                        ctx.restore();
                    }
                } else if (obj.isSliced && obj.isBomb) {
                    // 绘制爆炸效果
                    ctx.fillStyle = 'red';
                    ctx.beginPath();
                    ctx.arc(0, 0, obj.size * 1.5, 0, Math.PI * 2);
                    ctx.fill();
                } else {
                    // 绘制完整的水果或炸弹
                    ctx.font = `${obj.size}px Arial`;
                    ctx.fillText(obj.type, -obj.size/2, obj.size/4);
                }
                
                ctx.restore();
            });
        }

        // 绘制切痕轨迹
        function drawSliceTrail() {
            if (gameState.sliceTrail.length < 2) return;
            
            ctx.beginPath();
            ctx.moveTo(gameState.sliceTrail[0].x, gameState.sliceTrail[0].y);
            
            for (let i = 1; i < gameState.sliceTrail.length; i++) {
                ctx.lineTo(gameState.sliceTrail[i].x, gameState.sliceTrail[i].y);
            }
            
            ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)';
            ctx.lineWidth = 8;
            ctx.lineCap = 'round';
            ctx.stroke();
        }

        // 切开水果
        function sliceFruit(obj) {
            obj.isSliced = true;
            
            // 增加分数
            gameState.score += 10;
            scoreElement.textContent = gameState.score;
            
            // 创建水果切片
            const char = obj.type;
            const slice1 = {
                char: char,
                offsetX: -obj.size/4,
                offsetY: 0,
                speedX: obj.speedX - config.sliceForce,
                speedY: obj.speedY + (Math.random() - 0.5) * 2
            };
            
            const slice2 = {
                char: char,
                offsetX: obj.size/4,
                offsetY: 0,
                speedX: obj.speedX + config.sliceForce,
                speedY: obj.speedY + (Math.random() - 0.5) * 2
            };
            
            obj.slicePart1 = slice1;
            obj.slicePart2 = slice2;
            
            // 添加分数动画
            addScoreAnimation(obj.x + obj.size/2, obj.y + obj.size/2);
        }

        // 炸弹爆炸
        function explodeBomb(obj) {
            obj.isSliced = true;
            
            // 减少生命值
            gameState.lives = 0;
            livesElement.textContent = gameState.lives;
        }

        // 添加分数动画
        function addScoreAnimation(x, y) {
            const scoreAnim = {
                x,
                y,
                text: "+10",
                opacity: 1,
                ySpeed: -2
            };
            
            // 显示分数动画
            setTimeout(() => {
                const scoreEl = document.createElement('div');
                scoreEl.style.position = 'absolute';
                scoreEl.style.left = `${x}px`;
                scoreEl.style.top = `${y}px`;
                scoreEl.style.color = 'white';
                scoreEl.style.fontSize = '20px';
                scoreEl.style.fontWeight = 'bold';
                scoreEl.style.textShadow = '0 0 5px rgba(0,0,0,0.5)';
                scoreEl.style.opacity = '1';
                scoreEl.style.transition = 'all 1s';
                scoreEl.textContent = scoreAnim.text;
                
                document.body.appendChild(scoreEl);
                
                // 动画效果
                setTimeout(() => {
                    scoreEl.style.opacity = '0';
                    scoreEl.style.transform = 'translateY(-20px)';
                    
                    setTimeout(() => {
                        document.body.removeChild(scoreEl);
                    }, 1000);
                }, 10);
            }, 0);
        }

        // 结束游戏
        function endGame() {
            gameState.isGameOver = true;
            gameState.isPlaying = false;
            finalScoreElement.textContent = gameState.score;
            gameOverElement.style.display = 'block';
        }

        // 鼠标/触摸事件处理
        function handleMouseDown(event) {
            if (!gameState.isPlaying) return;
            
            gameState.mouseDown = true;
            const rect = canvas.getBoundingClientRect();
            gameState.lastMousePos = {
                x: event.clientX - rect.left,
                y: event.clientY - rect.top
            };
            
            // 添加初始切痕点
            gameState.sliceTrail.push({
                x: gameState.lastMousePos.x,
                y: gameState.lastMousePos.y,
                life: 10
            });
        }

        function handleMouseMove(event) {
            if (!gameState.isPlaying || !gameState.mouseDown) return;
            
            const rect = canvas.getBoundingClientRect();
            const currentPos = {
                x: event.clientX - rect.left,
                y: event.clientY - rect.top
            };
            
            // 计算移动距离
            const distance = Math.hypot(
                currentPos.x - gameState.lastMousePos.x,
                currentPos.y - gameState.lastMousePos.y
            );
            
            // 如果移动距离足够大,添加切痕点
            if (distance > 5) {
                // 添加多个点以确保连续性
                const steps = Math.ceil(distance / 5);
                for (let i = 1; i <= steps; i++) {
                    const t = i / steps;
                    gameState.sliceTrail.push({
                        x: gameState.lastMousePos.x + (currentPos.x - gameState.lastMousePos.x) * t,
                        y: gameState.lastMousePos.y + (currentPos.y - gameState.lastMousePos.y) * t,
                        life: 10
                    });
                }
                
                gameState.lastMousePos = currentPos;
            }
        }

        function handleMouseUp() {
            gameState.mouseDown = false;
        }

        function handleTouchStart(event) {
            if (!gameState.isPlaying) return;
            event.preventDefault();
            
            const touch = event.touches[0];
            const rect = canvas.getBoundingClientRect();
            gameState.mouseDown = true;
            gameState.lastMousePos = {
                x: touch.clientX - rect.left,
                y: touch.clientY - rect.top
            };
            
            // 添加初始切痕点
            gameState.sliceTrail.push({
                x: gameState.lastMousePos.x,
                y: gameState.lastMousePos.y,
                life: 10
            });
        }

        function handleTouchMove(event) {
            if (!gameState.isPlaying || !gameState.mouseDown) return;
            event.preventDefault();
            
            const touch = event.touches[0];
            const rect = canvas.getBoundingClientRect();
            const currentPos = {
                x: touch.clientX - rect.left,
                y: touch.clientY - rect.top
            };
            
            // 计算移动距离
            const distance = Math.hypot(
                currentPos.x - gameState.lastMousePos.x,
                currentPos.y - gameState.lastMousePos.y
            );
            
            // 如果移动距离足够大,添加切痕点
            if (distance > 5) {
                // 添加多个点以确保连续性
                const steps = Math.ceil(distance / 5);
                for (let i = 1; i <= steps; i++) {
                    const t = i / steps;
                    gameState.sliceTrail.push({
                        x: gameState.lastMousePos.x + (currentPos.x - gameState.lastMousePos.x) * t,
                        y: gameState.lastMousePos.y + (currentPos.y - gameState.lastMousePos.y) * t,
                        life: 10
                    });
                }
                
                gameState.lastMousePos = currentPos;
            }
        }

        function handleTouchEnd() {
            gameState.mouseDown = false;
        }

        // 启动游戏
        initGame();
    </script>
</body>
</html>    

这个水果忍者游戏具有以下特点:

  1. 基本玩法:滑动屏幕切开飞来的水果,避开炸弹
  2. 视觉效果:使用表情符号表示水果和炸弹,切水果时有飞溅效果
  3. 物理系统:水果和炸弹有抛物线运动轨迹和重力效果
  4. 游戏机制
  • 成功切水果获得分数
  • 漏掉水果减少生命值
  • 切到炸弹游戏结束
  1. 交互体验:支持鼠标和触摸操作,滑动时有切痕效果

游戏操作流程:

  1. 点击"开始游戏"按钮
  2. 用鼠标或手指在屏幕上滑动切开飞来的水果
  3. 避开炸弹,否则游戏立即结束
  4. 漏掉三个水果后游戏结束
  5. 游戏结束后可点击"重新开始"按钮再次游戏

你可以直接运行代码开始游戏,或根据需要进一步扩展功能,如添加不同难度级别、特殊水果或背景音乐等。

举报

相关推荐

0 条评论