用HTML5+CSS3+JavaScript实现龙舟游戏

发布于:2025-06-19 ⋅ 阅读:(15) ⋅ 点赞:(0)

用HTML5+CSS3+JavaScript实现龙舟游戏

使用说明

    使用左右方向键控制龙舟移动

    当龙舟到达右边界时,会从左侧重新出现

    当龙舟到达左边界时,会从右侧重新出现

    使用空格键进行冲刺加速

    避开障碍物,尽量保持生命值

当龙舟与障碍物(岩石、原木和波浪)发生碰撞时,会减少一条生命值,成功避开障碍物会获得分。

初始生命值:3 条,生命值归零时游戏结束。

界面如下

源码如下:


<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>龙舟游戏改进版</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#E63946',
                        secondary: '#FFB703',
                        water: '#1D3557',
                        boat: '#A8DADC',
                        accent: '#F1FAEE',
                        danger: '#E63946',
                    },
                    fontFamily: {
                        game: ['"Ma Shan Zheng"', 'cursive', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .text-shadow {
                text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
            }
            .water-animation {
                background-image: linear-gradient(to bottom, #1D3557 0%, #457B9D 100%);
                animation: water-move 2s linear infinite;
            }
            .paddle-animation {
                animation: paddle 0.5s ease-in-out infinite alternate;
            }
            .boat-bounce {
                animation: boat-bounce 1s ease-in-out infinite alternate;
            }
            .obstacle-move {
                animation: obstacle-move 3s linear infinite;
            }
            .btn-hover {
                transition: all 0.3s ease;
            }
            .btn-hover:hover {
                transform: translateY(-5px);
                box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
            }
            .portal-effect {
                position: absolute;
                width: 100px;
                height: 100px;
                border-radius: 50%;
                background: radial-gradient(circle, #FFB703, transparent);
                opacity: 0;
                pointer-events: none;
                z-index: 5;
            }
        }

        @keyframes water-move {
            0% { background-position: 0 0; }
            100% { background-position: 0 50px; }
        }

        @keyframes paddle {
            0% { transform: rotate(-15deg); }
            100% { transform: rotate(15deg); }
        }

        @keyframes boat-bounce {
            0% { transform: translateY(0); }
            100% { transform: translateY(-10px); }
        }

        @keyframes obstacle-move {
            0% { transform: translateX(100vw); }
            100% { transform: translateX(-100px); }
        }
        
        @keyframes portal {
            0% { transform: scale(0.2); opacity: 0.8; }
            100% { transform: scale(1.5); opacity: 0; }
        }
    </style>
    <link href="https://fonts.googleapis.com/css2?family=Ma+Shan+Zheng&display=swap" rel="stylesheet">
</head>
<body class="bg-gradient-to-b from-blue-900 to-blue-700 min-h-screen overflow-hidden font-game">
    <!-- 游戏容器 -->
    <div id="game-container" class="relative w-full h-screen">
        <!-- 开始屏幕 -->
        <div id="start-screen" class="absolute inset-0 flex flex-col items-center justify-center bg-water/90 z-50 transition-opacity duration-500">
            <h1 class="text-[clamp(2.5rem,8vw,5rem)] text-secondary font-bold text-shadow mb-8 animate-bounce">龙舟游戏</h1>
            <p class="text-accent text-[clamp(1rem,3vw,1.5rem)] mb-10 text-center max-w-md px-4">
                划动龙舟,避开障碍物,冲向终点!<br>
                左右方向键控制龙舟并划船,空格键快速冲刺<br>
                <span class="text-secondary">新功能:龙舟可穿越边界循环移动!</span>
            </p>
            <button id="start-btn" class="bg-primary hover:bg-primary/80 text-white font-bold py-4 px-10 rounded-full text-2xl shadow-lg btn-hover">
                <i class="fa fa-play mr-2"></i>开始游戏
            </button>
            <div class="mt-10 flex gap-4">
                <div class="flex items-center gap-2 text-accent">
                    <i class="fa fa-trophy text-secondary"></i>
                    <span>最高分: <span id="high-score" class="font-bold">0</span></span>
                </div>
                <div class="flex items-center gap-2 text-accent">
                    <i class="fa fa-users text-secondary"></i>
                    <span>龙舟手: <span id="crew-count" class="font-bold">10</span></span>
                </div>
            </div>
        </div>

        <!-- 游戏界面 -->
        <div id="game-screen" class="absolute inset-0 hidden">
            <!-- 背景 -->
            <div class="water-animation absolute inset-0 opacity-70"></div>
            <div class="absolute inset-0 bg-gradient-to-b from-blue-900/50 to-blue-700/30"></div>
            
            <!-- 水面波浪装饰 -->
            <div class="absolute bottom-0 left-0 right-0 h-20 bg-blue-500/50"></div>
            <div class="absolute bottom-10 left-0 right-0 h-10 bg-blue-400/30 rounded-t-[50%]"></div>
            <div class="absolute bottom-16 left-0 right-0 h-6 bg-blue-300/20 rounded-t-[50%]"></div>
            
            <!-- 游戏元素 -->
            <canvas id="game-canvas" class="absolute inset-0 w-full h-full"></canvas>
            
            <!-- 游戏UI -->
            <div class="absolute top-4 left-4 right-4 flex justify-between items-center z-10">
                <div class="bg-black/40 backdrop-blur-sm text-white py-2 px-4 rounded-full flex items-center gap-2">
                    <i class="fa fa-trophy text-secondary"></i>
                    <span>得分: <span id="score" class="font-bold">0</span></span>
                </div>
                <div class="bg-black/40 backdrop-blur-sm text-white py-2 px-4 rounded-full flex items-center gap-2">
                    <i class="fa fa-heart text-danger"></i>
                    <span>生命: <span id="lives" class="font-bold">3</span></span>
                </div>
                <button id="pause-btn" class="bg-black/40 backdrop-blur-sm text-white py-2 px-4 rounded-full hover:bg-black/60 transition-all">
                    <i class="fa fa-pause"></i>
                </button>
            </div>
            
            <!-- 倒计时 -->
            <div id="countdown" class="absolute inset-0 flex items-center justify-center z-20 text-[clamp(3rem,15vw,10rem)] font-bold text-white text-shadow hidden">
                3
            </div>
            
            <!-- 暂停菜单 -->
            <div id="pause-menu" class="absolute inset-0 flex flex-col items-center justify-center bg-black/70 z-30 hidden">
                <h2 class="text-[clamp(2rem,6vw,4rem)] text-secondary font-bold text-shadow mb-10">游戏暂停</h2>
                <button id="resume-btn" class="bg-primary hover:bg-primary/80 text-white font-bold py-3 px-8 rounded-full text-xl shadow-lg mb-4 btn-hover">
                    <i class="fa fa-play mr-2"></i>继续游戏
                </button>
                <button id="restart-btn" class="bg-secondary hover:bg-secondary/80 text-white font-bold py-3 px-8 rounded-full text-xl shadow-lg mb-4 btn-hover">
                    <i class="fa fa-refresh mr-2"></i>重新开始
                </button>
                <button id="exit-btn" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-3 px-8 rounded-full text-xl shadow-lg btn-hover">
                    <i class="fa fa-home mr-2"></i>退出游戏
                </button>
            </div>
            
            <!-- 游戏结束 -->
            <div id="game-over" class="absolute inset-0 flex flex-col items-center justify-center bg-black/70 z-30 hidden">
                <h2 class="text-[clamp(2rem,6vw,4rem)] text-danger font-bold text-shadow mb-4">游戏结束</h2>
                <p class="text-white text-[clamp(1.2rem,3vw,2rem)] mb-2">你的得分: <span id="final-score" class="font-bold">0</span></p>
                <p class="text-white text-[clamp(1rem,2vw,1.5rem)] mb-8">最高分: <span id="game-over-high-score" class="font-bold">0</span></p>
                <button id="play-again-btn" class="bg-primary hover:bg-primary/80 text-white font-bold py-3 px-8 rounded-full text-xl shadow-lg mb-4 btn-hover">
                    <i class="fa fa-refresh mr-2"></i>再玩一次
                </button>
                <button id="back-to-menu-btn" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-3 px-8 rounded-full text-xl shadow-lg btn-hover">
                    <i class="fa fa-home mr-2"></i>返回主菜单
                </button>
            </div>
            
            <!-- 操作提示 -->
            <div class="absolute bottom-4 left-4 right-4 flex justify-center z-10">
                <div class="bg-black/40 backdrop-blur-sm text-white py-2 px-4 rounded-full text-sm md:text-base">
                    <span class="mr-4"><i class="fa fa-arrow-left mr-1"></i><i class="fa fa-arrow-right mr-1"></i>左右移动并划船</span>
                    <span class="mr-4"><i class="fa fa-space-shuttle mr-1"></i>空格键快速冲刺</span>
                    <span><i class="fa fa-pause mr-1"></i>暂停游戏</span>
                </div>
            </div>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            // 获取DOM元素
            const startScreen = document.getElementById('start-screen');
            const gameScreen = document.getElementById('game-screen');
            const countdown = document.getElementById('countdown');
            const pauseMenu = document.getElementById('pause-menu');
            const gameOver = document.getElementById('game-over');
            const startBtn = document.getElementById('start-btn');
            const pauseBtn = document.getElementById('pause-btn');
            const resumeBtn = document.getElementById('resume-btn');
            const restartBtn = document.getElementById('restart-btn');
            const exitBtn = document.getElementById('exit-btn');
            const playAgainBtn = document.getElementById('play-again-btn');
            const backToMenuBtn = document.getElementById('back-to-menu-btn');
            const scoreElement = document.getElementById('score');
            const livesElement = document.getElementById('lives');
            const finalScoreElement = document.getElementById('final-score');
            const highScoreElement = document.getElementById('high-score');
            const gameOverHighScoreElement = document.getElementById('game-over-high-score');
            const crewCountElement = document.getElementById('crew-count');
            const canvas = document.getElementById('game-canvas');
            const ctx = canvas.getContext('2d');
            
            // 设置Canvas尺寸
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
            
            // 游戏状态
            let gameState = 'start'; // start, countdown, playing, paused, gameOver
            let score = 0;
            let lives = 3;
            let highScore = localStorage.getItem('dragonBoatHighScore') || 0;
            let crewCount = 10; // 龙舟手数量,影响速度
            let animationId;
            let lastTime = 0;
            let obstacleTimer = 0;
            let obstacleInterval = 2000; // 毫秒
            let isPaddling = false;
            let paddlePower = 0;
            let keys = {};
            let lastDirection = 0; // 0: 静止, 1: 右, -1: 左
            let portalEffects = []; // 存储边界穿越特效
            
            // 更新高分显示
            highScoreElement.textContent = highScore;
            gameOverHighScoreElement.textContent = highScore;
            
            // 游戏对象
            const boat = {
                x: canvas.width / 2 - 60,
                y: canvas.height - 150,
                width: 120,
                height: 60,
                speed: 5,
                dx: 0,
                draw() {
                    // 龙舟主体
                    ctx.fillStyle = '#E63946';
                    ctx.beginPath();
                    ctx.moveTo(this.x, this.y + this.height / 2);
                    ctx.lineTo(this.x + this.width, this.y + this.height / 2);
                    ctx.lineTo(this.x + this.width - 10, this.y);
                    ctx.lineTo(this.x + 10, this.y);
                    ctx.closePath();
                    ctx.fill();
                    
                    // 龙舟龙头
                    ctx.fillStyle = '#FFB703';
                    ctx.beginPath();
                    ctx.moveTo(this.x + this.width, this.y + this.height / 2);
                    ctx.quadraticCurveTo(this.x + this.width + 30, this.y + this.height / 2 - 10, this.x + this.width + 20, this.y - 10);
                    ctx.quadraticCurveTo(this.x + this.width + 10, this.y - 20, this.x + this.width - 10, this.y);
                    ctx.closePath();
                    ctx.fill();
                    
                    // 龙眼
                    ctx.fillStyle = '#000';
                    ctx.beginPath();
                    ctx.arc(this.x + this.width + 15, this.y - 15, 3, 0, Math.PI * 2);
                    ctx.fill();
                    
                    // 龙舟桨手
                    const paddleCount = crewCount;
                    const paddleSpacing = this.width / (paddleCount + 1);
                    for (let i = 1; i <= paddleCount; i++) {
                        const paddleX = this.x + paddleSpacing * i;
                        const paddleY = this.y - 5;
                        
                        // 桨手
                        ctx.fillStyle = '#1D3557';
                        ctx.beginPath();
                        ctx.arc(paddleX, paddleY, 5, 0, Math.PI * 2);
                        ctx.fill();
                        
                        // 桨
                        ctx.strokeStyle = '#A8DADC';
                        ctx.lineWidth = 2;
                        ctx.beginPath();
                        
                        // 根据方向和划桨状态设置桨的位置
                        if (isPaddling) {
                            if (lastDirection > 0) {
                                // 向右划
                                if (i % 2 === 0) {
                                    ctx.moveTo(paddleX, paddleY);
                                    ctx.lineTo(paddleX - 15, paddleY + 10);
                                } else {
                                    ctx.moveTo(paddleX, paddleY);
                                    ctx.lineTo(paddleX + 15, paddleY + 10);
                                }
                            } else {
                                // 向左划
                                if (i % 2 === 0) {
                                    ctx.moveTo(paddleX, paddleY);
                                    ctx.lineTo(paddleX + 15, paddleY + 10);
                                } else {
                                    ctx.moveTo(paddleX, paddleY);
                                    ctx.lineTo(paddleX - 15, paddleY + 10);
                                }
                            }
                        } else {
                            // 静止状态
                            ctx.moveTo(paddleX, paddleY);
                            ctx.lineTo(paddleX, paddleY + 15);
                        }
                        ctx.stroke();
                    }
                    
                    // 船尾浪花效果 - 增强版
                    if (isPaddling) {
                        ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
                        for (let i = 0; i < 5 + Math.floor(paddlePower / 2); i++) {
                            const splashX = this.x + Math.random() * this.width;
                            const splashY = this.y + this.height / 2 + Math.random() * 10;
                            const splashSize = 2 + Math.random() * 3;
                            ctx.beginPath();
                            ctx.arc(splashX, splashY, splashSize, 0, Math.PI * 2);
                            ctx.fill();
                        }
                        
                        // 船尾浪花
                        ctx.fillStyle = 'rgba(255, 255, 255, 0.6)';
                        for (let i = 0; i < 10 + paddlePower; i++) {
                            const tailX = this.x + Math.random() * this.width;
                            const tailY = this.y + this.height + Math.random() * 20;
                            const tailSize = 3 + Math.random() * 4;
                            ctx.beginPath();
                            ctx.arc(tailX, tailY, tailSize, 0, Math.PI * 2);
                            ctx.fill();
                        }
                    }
                },
                update(deltaTime) {
                    // 处理按键输入
                    if (keys.ArrowLeft) {
                        this.dx = -this.speed;
                        isPaddling = true;
                        lastDirection = -1;
                    } else if (keys.ArrowRight) {
                        this.dx = this.speed;
                        isPaddling = true;
                        lastDirection = 1;
                    } else {
                        this.dx = 0;
                        // 当没有按键时,逐渐停止划桨动画
                        if (paddlePower <= 0) {
                            isPaddling = false;
                        }
                    }
                    
                    // 空格键冲刺
                    if (keys[' ']) {
                        isPaddling = true;
                        paddlePower = Math.min(paddlePower + 1, 15);
                    } else {
                        paddlePower = Math.max(paddlePower - 0.3, 0);
                    }
                    
                    // 更新位置
                    this.x += this.dx;
                    
                    // 循环边界检查(新增)
                    if (this.x + this.width < 0) {
                        // 龙舟完全离开左侧边界,从右侧出现
                        this.x = canvas.width;
                        createPortalEffect(canvas.width, this.y + this.height/2, 'right');
                    } else if (this.x > canvas.width) {
                        // 龙舟完全离开右侧边界,从左侧出现
                        this.x = -this.width;
                        createPortalEffect(0, this.y + this.height/2, 'left');
                    }
                    
                    // 绘制龙舟
                    this.draw();
                }
            };
            
            // 创建边界穿越特效(新增)
            function createPortalEffect(x, y, direction) {
                portalEffects.push({
                    x: x,
                    y: y,
                    direction: direction,
                    size: 0,
                    opacity: 1,
                    createdAt: Date.now()
                });
            }
            
            // 绘制边界穿越特效(新增)
            function drawPortalEffects() {
                const now = Date.now();
                for (let i = portalEffects.length - 1; i >= 0; i--) {
                    const effect = portalEffects[i];
                    const elapsed = now - effect.createdAt;
                    
                    if (elapsed > 1000) {
                        portalEffects.splice(i, 1);
                        continue;
                    }
                    
                    const progress = elapsed / 1000;
                    const size = 50 + progress * 100;
                    const opacity = 1 - progress;
                    
                    ctx.globalAlpha = opacity;
                    
                    // 绘制圆形传送门
                    ctx.beginPath();
                    ctx.arc(effect.x, effect.y, size, 0, Math.PI * 2);
                    ctx.strokeStyle = '#FFB703';
                    ctx.lineWidth = 3;
                    ctx.stroke();
                    
                    // 绘制内部粒子
                    for (let j = 0; j < 20; j++) {
                        const angle = Math.random() * Math.PI * 2;
                        const radius = Math.random() * size;
                        const particleX = effect.x + Math.cos(angle) * radius;
                        const particleY = effect.y + Math.sin(angle) * radius;
                        const particleSize = 2 + Math.random() * 3;
                        
                        ctx.fillStyle = j % 2 === 0 ? '#FFB703' : '#E63946';
                        ctx.beginPath();
                        ctx.arc(particleX, particleY, particleSize, 0, Math.PI * 2);
                        ctx.fill();
                    }
                    
                    ctx.globalAlpha = 1;
                }
            }
            
            // 障碍物数组
            let obstacles = [];
            
            // 障碍物类型
            const obstacleTypes = [
                { type: 'rock', width: 40, height: 30, color: '#5C4033' },
                { type: 'log', width: 60, height: 20, color: '#8B4513' },
                { type: 'wave', width: 80, height: 40, color: '#457B9D' }
            ];
            
            // 背景元素
            let clouds = [];
            let birds = [];
            
            // 初始化背景元素
            function initBackground() {
                clouds = [];
                birds = [];
                
                // 创建云朵
                for (let i = 0; i < 5; i++) {
                    clouds.push({
                        x: Math.random() * canvas.width,
                        y: Math.random() * 150,
                        width: 100 + Math.random() * 100,
                        height: 60 + Math.random() * 40,
                        speed: 0.5 + Math.random() * 0.5
                    });
                }
                
                // 创建飞鸟
                for (let i = 0; i < 3; i++) {
                    birds.push({
                        x: Math.random() * canvas.width,
                        y: 50 + Math.random() * 100,
                        width: 30,
                        height: 20,
                        speed: 2 + Math.random() * 2,
                        direction: Math.random() > 0.5 ? 1 : -1
                    });
                }
            }
            
            // 绘制背景
            function drawBackground() {
                // 绘制云朵
                clouds.forEach(cloud => {
                    ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
                    
                    // 绘制云朵形状
                    ctx.beginPath();
                    ctx.arc(cloud.x, cloud.y, cloud.height / 2, 0, Math.PI * 2);
                    ctx.arc(cloud.x + cloud.width / 3, cloud.y - cloud.height / 4, cloud.height / 2 + 10, 0, Math.PI * 2);
                    ctx.arc(cloud.x + cloud.width * 2/3, cloud.y, cloud.height / 2, 0, Math.PI * 2);
                    ctx.fill();
                    
                    // 更新云朵位置
                    cloud.x += cloud.speed;
                    if (cloud.x > canvas.width) cloud.x = -cloud.width;
                });
                
                // 绘制飞鸟
                birds.forEach(bird => {
                    ctx.strokeStyle = '#000';
                    ctx.lineWidth = 2;
                    
                    // 绘制飞鸟形状
                    ctx.beginPath();
                    ctx.moveTo(bird.x, bird.y);
                    ctx.quadraticCurveTo(
                        bird.x + bird.width / 3 * bird.direction, 
                        bird.y - bird.height / 2, 
                        bird.x + bird.width * 2/3 * bird.direction, 
                        bird.y
                    );
                    ctx.quadraticCurveTo(
                        bird.x + bird.width / 3 * bird.direction, 
                        bird.y + bird.height / 2, 
                        bird.x, 
                        bird.y
                    );
                    ctx.stroke();
                    
                    // 更新飞鸟位置
                    bird.x += bird.speed * bird.direction;
                    if (bird.direction > 0 && bird.x > canvas.width) bird.x = -bird.width;
                    if (bird.direction < 0 && bird.x < -bird.width) bird.x = canvas.width;
                });
            }
            
            // 创建障碍物
            function createObstacle() {
                const type = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
                const x = Math.random() * (canvas.width - type.width);
                const y = -type.height;
                const speed = 3 + Math.random() * 2;
                
                obstacles.push({
                    ...type,
                    x,
                    y,
                    speed
                });
                
                // 随时间增加难度
                if (obstacleInterval > 800) {
                    obstacleInterval -= 0.5;
                }
            }
            
            // 更新障碍物
            function updateObstacles(deltaTime) {
                // 创建新障碍物
                obstacleTimer += deltaTime;
                if (obstacleTimer > obstacleInterval) {
                    createObstacle();
                    obstacleTimer = 0;
                }
                
                // 更新和绘制现有障碍物
                for (let i = obstacles.length - 1; i >= 0; i--) {
                    const obstacle = obstacles[i];
                    obstacle.y += obstacle.speed + (isPaddling ? paddlePower * 0.1 : 0);
                    
                    // 绘制障碍物
                    ctx.fillStyle = obstacle.color;
                    
                    if (obstacle.type === 'rock') {
                        // 岩石
                        ctx.beginPath();
                        ctx.ellipse(
                            obstacle.x + obstacle.width / 2, 
                            obstacle.y + obstacle.height, 
                            obstacle.width / 2, 
                            obstacle.height / 2, 
                            0, 
                            0, 
                            Math.PI * 2
                        );
                        ctx.fill();
                    } else if (obstacle.type === 'log') {
                        // 原木
                        ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
                        ctx.fillStyle = '#6B3A10';
                        ctx.beginPath();
                        ctx.arc(obstacle.x + obstacle.width / 4, obstacle.y + obstacle.height / 2, 3, 0, Math.PI * 2);
                        ctx.arc(obstacle.x + obstacle.width * 3/4, obstacle.y + obstacle.height / 2, 3, 0, Math.PI * 2);
                        ctx.fill();
                    } else if (obstacle.type === 'wave') {
                        // 波浪
                        ctx.fillStyle = 'rgba(69, 123, 157, 0.7)';
                        ctx.beginPath();
                        ctx.moveTo(obstacle.x, obstacle.y + obstacle.height);
                        for (let j = 0; j < 4; j++) {
                            const curveX = obstacle.x + j * (obstacle.width / 4);
                            const curveY = obstacle.y + (j % 2 === 0 ? 0 : obstacle.height / 2);
                            const nextCurveX = obstacle.x + (j + 1) * (obstacle.width / 4);
                            ctx.quadraticCurveTo(curveX, curveY, nextCurveX, obstacle.y + obstacle.height);
                        }
                        ctx.fill();
                    }
                    
                    // 检测碰撞
                    if (
                        boat.x < obstacle.x + obstacle.width &&
                        boat.x + boat.width > obstacle.x &&
                        boat.y < obstacle.y + obstacle.height &&
                        boat.y + boat.height > obstacle.y
                    ) {
                        // 发生碰撞
                        obstacles.splice(i, 1);
                        lives--;
                        livesElement.textContent = lives;
                        
                        // 显示碰撞效果
                        ctx.fillStyle = 'rgba(230, 57, 70, 0.5)';
                        ctx.fillRect(0, 0, canvas.width, canvas.height);
                        
                        if (lives <= 0) {
                            gameOverHandler();
                        }
                    }
                    
                    // 移除离开画布的障碍物
                    if (obstacle.y > canvas.height) {
                        obstacles.splice(i, 1);
                        
                        // 得分增加
                        score += 10 + Math.floor(paddlePower / 2);
                        scoreElement.textContent = score;
                    }
                }
            }
            
            // 绘制终点线
            function drawFinishLine() {
                const finishLineY = 100;
                ctx.strokeStyle = '#FFB703';
                ctx.lineWidth = 5;
                ctx.setLineDash([20, 10]);
                ctx.beginPath();
                ctx.moveTo(0, finishLineY);
                ctx.lineTo(canvas.width, finishLineY);
                ctx.stroke();
                ctx.setLineDash([]);
                
                // 终点旗帜
                const flagSpacing = canvas.width / 10;
                for (let i = 0; i <= 10; i++) {
                    const flagX = i * flagSpacing;
                    
                    // 旗杆
                    ctx.strokeStyle = '#A8DADC';
                    ctx.lineWidth = 2;
                    ctx.beginPath();
                    ctx.moveTo(flagX, finishLineY);
                    ctx.lineTo(flagX, finishLineY - 40);
                    ctx.stroke();
                    
                    // 旗帜
                    ctx.fillStyle = i % 2 === 0 ? '#E63946' : '#FFB703';
                    ctx.beginPath();
                    ctx.moveTo(flagX, finishLineY - 40);
                    ctx.lineTo(flagX + 20, finishLineY - 30);
                    ctx.lineTo(flagX, finishLineY - 20);
                    ctx.closePath();
                    ctx.fill();
                }
                
                // 检测是否到达终点
                if (boat.y < finishLineY + 30) {
                    // 加分
                    score += 500 + crewCount * 20 + Math.floor(paddlePower * 10);
                    scoreElement.textContent = score;
                    
                    // 显示胜利消息
                    gameOverHandler(true);
                }
            }
            
            // 游戏结束处理
            function gameOverHandler(isWin = false) {
                cancelAnimationFrame(animationId);
                gameState = 'gameOver';
                gameOver.classList.remove('hidden');
                
                // 更新最终得分
                finalScoreElement.textContent = score;
                
                // 更新最高分
                if (score > highScore) {
                    highScore = score;
                    localStorage.setItem('dragonBoatHighScore', highScore);
                    gameOverHighScoreElement.textContent = highScore;
                    highScoreElement.textContent = highScore;
                    
                    // 显示新纪录消息
                    const newRecord = document.createElement('div');
                    newRecord.className = 'text-secondary text-2xl mb-4 font-bold';
                    newRecord.textContent = '新纪录!';
                    gameOver.querySelector('h2').after(newRecord);
                    
                    // 5秒后移除新纪录消息
                    setTimeout(() => {
                        if (newRecord.parentNode) {
                            newRecord.parentNode.removeChild(newRecord);
                        }
                    }, 5000);
                }
                
                // 如果是胜利,显示特殊消息
                if (isWin) {
                    gameOver.querySelector('h2').textContent = '恭喜获胜!';
                    gameOver.querySelector('h2').className = 'text-[clamp(2rem,6vw,4rem)] text-secondary font-bold text-shadow mb-4';
                    
                    // 添加烟花效果
                    createFireworks();
                }
            }
            
            // 创建烟花效果
            function createFireworks() {
                const fireworks = [];
                
                // 创建5个烟花
                for (let i = 0; i < 5; i++) {
                    setTimeout(() => {
                        const x = Math.random() * canvas.width;
                        const y = canvas.height;
                        const targetY = Math.random() * canvas.height / 2;
                        
                        fireworks.push({
                            x,
                            y,
                            targetY,
                            speed: 5 + Math.random() * 5,
                            exploded: false,
                            particles: []
                        });
                    }, i * 500);
                }
                
                // 渲染烟花
                function renderFireworks() {
                    ctx.clearRect(0, 0, canvas.width, canvas.height);
                    
                    for (let i = fireworks.length - 1; i >= 0; i--) {
                        const firework = fireworks[i];
                        
                        if (!firework.exploded) {
                            // 烟花上升
                            firework.y -= firework.speed;
                            
                            // 绘制烟花
                            ctx.fillStyle = '#FFB703';
                            ctx.beginPath();
                            ctx.arc(firework.x, firework.y, 5, 0, Math.PI * 2);
                            ctx.fill();
                            
                            // 到达目标位置,爆炸
                            if (firework.y <= firework.targetY) {
                                firework.exploded = true;
                                
                                // 创建爆炸粒子
                                for (let j = 0; j < 50; j++) {
                                    const angle = Math.random() * Math.PI * 2;
                                    const speed = 1 + Math.random() * 3;
                                    const color = `hsl(${Math.random() * 360}, 100%, 70%)`;
                                    
                                    firework.particles.push({
                                        x: firework.x,
                                        y: firework.y,
                                        dx: Math.cos(angle) * speed,
                                        dy: Math.sin(angle) * speed,
                                        radius: 2 + Math.random() * 2,
                                        color,
                                        alpha: 1,
                                        gravity: 0.05
                                    });
                                }
                            }
                        } else {
                            // 绘制爆炸粒子
                            for (let j = firework.particles.length - 1; j >= 0; j--) {
                                const particle = firework.particles[j];
                                
                                // 更新粒子位置
                                particle.x += particle.dx;
                                particle.y += particle.dy;
                                particle.dy += particle.gravity;
                                particle.alpha -= 0.02;
                                
                                // 绘制粒子
                                ctx.fillStyle = `rgba(${parseInt(particle.color.substr(4, 3))}, ${parseInt(particle.color.substr(9, 3))}, ${parseInt(particle.color.substr(14, 3))}, ${particle.alpha})`;
                                ctx.beginPath();
                                ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2);
                                ctx.fill();
                                
                                // 移除消失的粒子
                                if (particle.alpha <= 0) {
                                    firework.particles.splice(j, 1);
                                }
                            }
                            
                            // 移除所有粒子都消失的烟花
                            if (firework.particles.length === 0) {
                                fireworks.splice(i, 1);
                            }
                        }
                    }
                    
                    // 如果还有烟花,继续渲染
                    if (fireworks.length > 0) {
                        requestAnimationFrame(renderFireworks);
                    }
                }
                
                renderFireworks();
            }
            
            // 游戏主循环
            function gameLoop(timestamp) {
                const deltaTime = timestamp - lastTime || 0;
                lastTime = timestamp;
                
                // 清除画布
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                
                // 绘制背景
                drawBackground();
                
                // 更新和绘制龙舟
                boat.update(deltaTime);
                
                // 更新和绘制障碍物
                updateObstacles(deltaTime);
                
                // 绘制终点线
                drawFinishLine();
                
                // 绘制边界穿越特效(新增)
                drawPortalEffects();
                
                // 继续游戏循环
                if (gameState === 'playing') {
                    animationId = requestAnimationFrame(gameLoop);
                }
            }
            
            // 开始游戏
            function startGame() {
                // 重置游戏状态
                score = 0;
                lives = 3;
                livesElement.textContent = lives;
                scoreElement.textContent = score;
                obstacles = [];
                portalEffects = [];
                obstacleInterval = 2000;
                boat.x = canvas.width / 2 - 60;
                boat.y = canvas.height - 150;
                isPaddling = false;
                paddlePower = 0;
                lastDirection = 0;
                
                // 初始化背景
                initBackground();
                
                // 隐藏开始屏幕,显示游戏屏幕
                startScreen.classList.add('hidden');
                gameScreen.classList.remove('hidden');
                
                // 显示倒计时
                gameState = 'countdown';
                countdown.classList.remove('hidden');
                
                let count = 3;
                countdown.textContent = count;
                
                const countdownInterval = setInterval(() => {
                    count--;
                    if (count > 0) {
                        countdown.textContent = count;
                    } else {
                        clearInterval(countdownInterval);
                        countdown.classList.add('hidden');
                        gameState = 'playing';
                        lastTime = 0;
                        animationId = requestAnimationFrame(gameLoop);
                    }
                }, 1000);
            }
            
            // 暂停游戏
            function pauseGame() {
                if (gameState === 'playing') {
                    gameState = 'paused';
                    pauseMenu.classList.remove('hidden');
                    cancelAnimationFrame(animationId);
                }
            }
            
            // 恢复游戏
            function resumeGame() {
                if (gameState === 'paused') {
                    gameState = 'playing';
                    pauseMenu.classList.add('hidden');
                    animationId = requestAnimationFrame(gameLoop);
                }
            }
            
            // 重新开始游戏
            function restartGame() {
                pauseMenu.classList.add('hidden');
                gameOver.classList.add('hidden');
                startGame();
            }
            
            // 返回主菜单
            function backToMenu() {
                gameState = 'start';
                gameScreen.classList.add('hidden');
                pauseMenu.classList.add('hidden');
                gameOver.classList.add('hidden');
                startScreen.classList.remove('hidden');
                cancelAnimationFrame(animationId);
            }
            
            // 事件监听
            startBtn.addEventListener('click', startGame);
            pauseBtn.addEventListener('click', pauseGame);
            resumeBtn.addEventListener('click', resumeGame);
            restartBtn.addEventListener('click', restartGame);
            exitBtn.addEventListener('click', backToMenu);
            playAgainBtn.addEventListener('click', restartGame);
            backToMenuBtn.addEventListener('click', backToMenu);
            
            // 键盘事件
            window.addEventListener('keydown', (e) => {
                keys[e.key] = true;
                
                // P键暂停/恢复游戏
                if (e.key === 'p' || e.key === 'P') {
                    if (gameState === 'playing') {
                        pauseGame();
                    } else if (gameState === 'paused') {
                        resumeGame();
                    }
                }
            });
            
            window.addEventListener('keyup', (e) => {
                keys[e.key] = false;
            });
            
            // 鼠标点击/触摸事件
            canvas.addEventListener('mousedown', () => {
                if (gameState === 'playing') {
                    keys[' '] = true;
                }
            });
            
            canvas.addEventListener('mouseup', () => {
                keys[' '] = false;
            });
            
            canvas.addEventListener('touchstart', (e) => {
                if (gameState === 'playing') {
                    e.preventDefault();
                    keys[' '] = true;
                }
            });
            
            canvas.addEventListener('touchend', (e) => {
                e.preventDefault();
                keys[' '] = false;
            });
            
            // 窗口调整大小事件
            window.addEventListener('resize', () => {
                canvas.width = window.innerWidth;
                canvas.height = window.innerHeight;
            });
            
            // 调整龙舟手数量
            crewCountElement.addEventListener('click', () => {
                crewCount = (crewCount % 15) + 5; // 5-15人
                crewCountElement.textContent = crewCount;
            });
        });
    </script>
</body>
</html>


网站公告

今日签到

点亮在社区的每一天
去签到