文件名:game4.html

消消乐网页游戏,运行的效果不太好,直接po图和代码。

<!DOCTYPE html>
<html lang="zh-CN">
<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(135deg, #1a1a2e, #16213e);
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            font-family: "Microsoft YaHei", sans-serif;
            color: #fff;
            user-select: none;
        }
        h1 {
            font-size: 36px;
            color: #ffd700;
            margin-bottom: 6px;
            text-shadow: 0 0 20px rgba(255,215,0,0.3);
        }
        #info {
            display: flex;
            gap: 40px;
            margin-bottom: 12px;
            font-size: 20px;
        }
        #info span { color: #ffd700; font-weight: bold; }
        #game-container {
            position: relative;
            border-radius: 16px;
            overflow: hidden;
            box-shadow: 0 20px 60px rgba(0,0,0,0.5);
        }
        canvas { display: block; background: #0d1117; }
        #controls {
            margin-top: 15px;
            display: flex;
            gap: 12px;
        }
        .btn {
            padding: 10px 28px;
            font-size: 16px;
            font-weight: bold;
            color: #fff;
            background: linear-gradient(135deg, #667eea, #764ba2);
            border: none;
            border-radius: 8px;
            cursor: pointer;
            transition: transform 0.15s;
        }
        .btn:hover { transform: scale(1.06); }
        .btn.gold { background: linear-gradient(135deg, #ffd700, #ffb300); color: #4a3010; }
        #overlay {
            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;
            z-index: 10;
        }
        #overlay h2 { font-size: 38px; color: #ffd700; margin-bottom: 12px; }
        #overlay p { font-size: 20px; margin-bottom: 6px; color: #ccc; }
        #overlay .final-score { color: #4fc3f7; font-size: 28px; margin: 10px 0 25px; }
        #overlay button {
            padding: 12px 40px;
            font-size: 20px;
            font-weight: bold;
            color: #4a3010;
            background: linear-gradient(135deg, #ffd700, #ffb300);
            border: none;
            border-radius: 30px;
            cursor: pointer;
            box-shadow: 0 4px 15px rgba(255,215,0,0.4);
            transition: transform 0.2s;
        }
        #overlay button:hover { transform: scale(1.08); }
        #hint {
            margin-top: 10px;
            font-size: 14px;
            color: rgba(255,255,255,0.4);
        }
        #progress-bar {
            width: 480px;
            height: 8px;
            background: rgba(255,255,255,0.1);
            border-radius: 4px;
            margin-bottom: 12px;
            overflow: hidden;
        }
        #progress-fill {
            height: 100%;
            background: linear-gradient(90deg, #4caf50, #ffd700);
            border-radius: 4px;
            transition: width 0.3s;
        }
    </style>
</head>
<body>
    <h1>消消乐</h1>
    <div id="info">
        <div>分数: <span id="score">0</span></div>
        <div>步数: <span id="moves">20</span></div>
        <div>目标: <span id="target">1000</span></div>
    </div>
    <div id="progress-bar"><div id="progress-fill" style="width:0%"></div></div>
    <div id="game-container">
        <canvas id="game" width="480" height="480"></canvas>
        <div id="overlay">
            <h2 id="ov-title">消消乐</h2>
            <p id="ov-text">交换相邻方块,3个或以上同色即可消除</p>
            <p class="final-score" id="ov-score"></p>
            <button onclick="startGame()">开始游戏</button>
        </div>
    </div>
    <div id="controls">
        <button class="btn gold" onclick="startGame()">重新开始</button>
    </div>
    <div id="hint">点击选中方块,再点相邻方块交换 · 3个以上同色消除</div>

<script>
var canvas = document.getElementById('game');
var ctx = canvas.getContext('2d');
var GRID = 8;
var CELL = 60;
var COLORS = ['#ff5252', '#ffd700', '#4caf50', '#42a5f5', '#ab47bc', '#ff80ab'];
var COLOR_NAMES = ['红', '黄', '绿', '蓝', '紫', '粉'];

var board = [];
var selected = null;
var score = 0;
var moves = 20;
var targetScore = 1000;
var state = 'menu'; // menu, playing, animating, won, lost
var animations = [];
var floatTexts = [];
var particles = [];

function init() {
    board = [];
    score = 0;
    moves = 20;
    targetScore = 1000;
    selected = null;
    animations = [];
    floatTexts = [];
    particles = [];
    state = 'playing';

    // 生成无初始消除的棋盘
    for (var y = 0; y < GRID; y++) {
        board.push([]);
        for (var x = 0; x < GRID; x++) {
            var color;
            do {
                color = Math.floor(Math.random() * COLORS.length);
            } while (
                (x >= 2 && board[y][x-1] === color && board[y][x-2] === color) ||
                (y >= 2 && board[y-1][x] === color && board[y-2][x] === color)
            );
            board[y].push(color);
        }
    }
    updateUI();
    draw();
}

function startGame() {
    hideOverlay();
    init();
}

// 检测匹配
function findMatches() {
    var matches = [];
    var matched = [];
    for (var y = 0; y < GRID; y++) {
        matched.push([]);
        for (var x = 0; x < GRID; x++) matched[y].push(false);
    }

    // 横向
    for (var y2 = 0; y2 < GRID; y2++) {
        var runStart = 0;
        for (var x2 = 1; x2 <= GRID; x2++) {
            if (x2 === GRID || board[y2][x2] !== board[y2][runStart] || board[y2][runStart] === -1) {
                if (x2 - runStart >= 3) {
                    for (var k = runStart; k < x2; k++) {
                        matched[y2][k] = true;
                    }
                }
                runStart = x2;
            }
        }
    }

    // 纵向
    for (var x3 = 0; x3 < GRID; x3++) {
        var runStart2 = 0;
        for (var y3 = 1; y3 <= GRID; y3++) {
            if (y3 === GRID || board[y3][x3] !== board[runStart2][x3] || board[runStart2][x3] === -1) {
                if (y3 - runStart2 >= 3) {
                    for (var k2 = runStart2; k2 < y3; k2++) {
                        matched[k2][x3] = true;
                    }
                }
                runStart2 = y3;
            }
        }
    }

    for (var y4 = 0; y4 < GRID; y4++) {
        for (var x4 = 0; x4 < GRID; x4++) {
            if (matched[y4][x4]) matches.push({ x: x4, y: y4 });
        }
    }

    return matches;
}

// 交换
function swap(x1, y1, x2, y2) {
    var tmp = board[y1][x1];
    board[y1][x1] = board[y2][x2];
    board[y2][x2] = tmp;
}

function isAdjacent(x1, y1, x2, y2) {
    return (Math.abs(x1 - x2) === 1 && y1 === y2) || (Math.abs(y1 - y2) === 1 && x1 === x2);
}

function handleClick(px, py) {
    if (state !== 'playing') return;
    var x = Math.floor(px / CELL);
    var y = Math.floor(py / CELL);
    if (x < 0 || x >= GRID || y < 0 || y >= GRID) return;

    if (selected === null) {
        selected = { x: x, y: y };
    } else {
        if (selected.x === x && selected.y === y) {
            selected = null;
        } else if (isAdjacent(selected.x, selected.y, x, y)) {
            trySwap(selected.x, selected.y, x, y);
            selected = null;
        } else {
            selected = { x: x, y: y };
        }
    }
    draw();
}

function trySwap(x1, y1, x2, y2) {
    swap(x1, y1, x2, y2);
    var matches = findMatches();
    if (matches.length === 0) {
        // 无匹配,换回去
        swap(x1, y1, x2, y2);
        floatTexts.push({ text: '无效', x: x2 * CELL + CELL/2, y: y2 * CELL + CELL/2, color: '#f44336', life: 40 });
    } else {
        moves--;
        state = 'animating';
        processMatches();
    }
    updateUI();
}

function processMatches() {
    var matches = findMatches();
    if (matches.length === 0) {
        state = 'playing';
        checkEndGame();
        return;
    }

    // 计分
    var points = matches.length * 30;
    if (matches.length >= 5) points = matches.length * 50;
    score += points;

    // 中心位置飘字
    var avgX = 0, avgY = 0;
    for (var i = 0; i < matches.length; i++) {
        avgX += matches[i].x;
        avgY += matches[i].y;
    }
    avgX = (avgX / matches.length) * CELL + CELL/2;
    avgY = (avgY / matches.length) * CELL + CELL/2;
    floatTexts.push({ text: '+' + points, x: avgX, y: avgY, color: '#ffd700', life: 60 });

    // 粒子效果
    for (var j = 0; j < matches.length; j++) {
        var m = matches[j];
        var cx = m.x * CELL + CELL/2;
        var cy = m.y * CELL + CELL/2;
        spawnParticles(cx, cy, COLORS[board[m.y][m.x]]);
        board[m.y][m.x] = -1; // 标记消除
    }

    updateUI();
    draw();

    // 0.3秒后下落
    setTimeout(function() {
        dropAndFill();
    }, 300);
}

function dropAndFill() {
    // 下落
    for (var x = 0; x < GRID; x++) {
        var writeY = GRID - 1;
        for (var y = GRID - 1; y >= 0; y--) {
            if (board[y][x] !== -1) {
                board[writeY][x] = board[y][x];
                if (writeY !== y) board[y][x] = -1;
                writeY--;
            }
        }
        // 填充新方块
        for (var y2 = writeY; y2 >= 0; y2--) {
            board[y2][x] = Math.floor(Math.random() * COLORS.length);
        }
    }

    draw();

    // 检查连锁
    setTimeout(function() {
        processMatches();
    }, 200);
}

function checkEndGame() {
    if (score >= targetScore) {
        state = 'won';
        showOverlay('过关!', '分数: ' + score, '', '再来一局');
    } else if (moves <= 0) {
        state = 'lost';
        showOverlay('游戏结束', '步数用完了', '分数: ' + score, '重新开始');
    }
}

function showOverlay(title, text, scoreText, btn) {
    document.getElementById('ov-title').textContent = title;
    document.getElementById('ov-text').textContent = text;
    document.getElementById('ov-score').textContent = scoreText;
    var ovBtn = document.querySelector('#overlay button');
    ovBtn.textContent = btn;
    document.getElementById('overlay').style.display = 'flex';
}

function hideOverlay() {
    document.getElementById('overlay').style.display = 'none';
}

function updateUI() {
    document.getElementById('score').textContent = score;
    document.getElementById('moves').textContent = moves;
    document.getElementById('target').textContent = targetScore;
    var pct = Math.min(100, (score / targetScore) * 100);
    document.getElementById('progress-fill').style.width = pct + '%';
}

function spawnParticles(x, y, color) {
    for (var i = 0; i < 10; i++) {
        var a = Math.random() * Math.PI * 2;
        var s = 2 + Math.random() * 5;
        particles.push({
            x: x, y: y,
            vx: Math.cos(a) * s,
            vy: Math.sin(a) * s - 3,
            color: color,
            life: 30,
            size: 3 + Math.random() * 4
        });
    }
}

// ============ 渲染 ============

function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 棋盘背景
    ctx.fillStyle = '#0d1117';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // 网格线
    ctx.strokeStyle = 'rgba(255,255,255,0.04)';
    ctx.lineWidth = 1;
    for (var i = 0; i <= GRID; i++) {
        ctx.beginPath();
        ctx.moveTo(i * CELL, 0);
        ctx.lineTo(i * CELL, canvas.height);
        ctx.stroke();
        ctx.beginPath();
        ctx.moveTo(0, i * CELL);
        ctx.lineTo(canvas.width, i * CELL);
        ctx.stroke();
    }

    // 方块
    for (var y = 0; y < GRID; y++) {
        for (var x = 0; x < GRID; x++) {
            if (board[y][x] === -1) continue;
            drawGem(x, y, board[y][x]);
        }
    }

    // 选中高亮
    if (selected) {
        var sx = selected.x * CELL;
        var sy = selected.y * CELL;
        ctx.strokeStyle = '#fff';
        ctx.lineWidth = 3;
        ctx.strokeRect(sx + 2, sy + 2, CELL - 4, CELL - 4);
        ctx.strokeStyle = 'rgba(255,255,255,0.3)';
        ctx.lineWidth = 1;
        ctx.strokeRect(sx - 1, sy - 1, CELL + 2, CELL + 2);
    }

    // 粒子
    for (var p = particles.length - 1; p >= 0; p--) {
        var pt = particles[p];
        ctx.globalAlpha = pt.life / 30;
        ctx.fillStyle = pt.color;
        ctx.beginPath();
        ctx.arc(pt.x, pt.y, pt.size, 0, Math.PI * 2);
        ctx.fill();
        pt.x += pt.vx;
        pt.y += pt.vy;
        pt.vy += 0.2;
        pt.life--;
        if (pt.life <= 0) particles.splice(p, 1);
    }
    ctx.globalAlpha = 1;

    // 飘字
    for (var f = floatTexts.length - 1; f >= 0; f--) {
        var ft = floatTexts[f];
        ctx.globalAlpha = ft.life / 60;
        ctx.font = 'bold 22px "Microsoft YaHei"';
        ctx.fillStyle = ft.color;
        ctx.textAlign = 'center';
        ctx.fillText(ft.text, ft.x, ft.y);
        ft.y -= 1.5;
        ft.life--;
        if (ft.life <= 0) floatTexts.splice(f, 1);
    }
    ctx.globalAlpha = 1;
}

function drawGem(x, y, colorIdx) {
    var cx = x * CELL + CELL / 2;
    var cy = y * CELL + CELL / 2;
    var r = CELL / 2 - 5;
    var color = COLORS[colorIdx];

    var shapes = ['circle', 'square', 'diamond', 'triangle', 'star', 'hexagon'];
    var shape = shapes[colorIdx];

    // 光晕
    ctx.shadowColor = color;
    ctx.shadowBlur = 8;

    var grad = ctx.createRadialGradient(cx - r * 0.3, cy - r * 0.3, 0, cx, cy, r);
    grad.addColorStop(0, lighten(color, 0.3));
    grad.addColorStop(0.6, color);
    grad.addColorStop(1, darken(color, 0.2));
    ctx.fillStyle = grad;

    ctx.beginPath();
    switch(shape) {
        case 'circle':
            ctx.arc(cx, cy, r * 0.85, 0, Math.PI * 2);
            break;
        case 'square':
            ctx.roundRect(cx - r * 0.75, cy - r * 0.75, r * 1.5, r * 1.5, 8);
            break;
        case 'diamond':
            ctx.moveTo(cx, cy - r * 0.9);
            ctx.lineTo(cx + r * 0.8, cy);
            ctx.lineTo(cx, cy + r * 0.9);
            ctx.lineTo(cx - r * 0.8, cy);
            ctx.closePath();
            break;
        case 'triangle':
            ctx.moveTo(cx, cy - r * 0.85);
            ctx.lineTo(cx + r * 0.8, cy + r * 0.65);
            ctx.lineTo(cx - r * 0.8, cy + r * 0.65);
            ctx.closePath();
            break;
        case 'star':
            for (var i = 0; i < 5; i++) {
                var a1 = -Math.PI / 2 + i * Math.PI * 2 / 5;
                var a2 = a1 + Math.PI / 5;
                ctx.lineTo(cx + Math.cos(a1) * r * 0.9, cy + Math.sin(a1) * r * 0.9);
                ctx.lineTo(cx + Math.cos(a2) * r * 0.4, cy + Math.sin(a2) * r * 0.4);
            }
            ctx.closePath();
            break;
        case 'hexagon':
            for (var j = 0; j < 6; j++) {
                var a = j * Math.PI / 3;
                if (j === 0) ctx.moveTo(cx + Math.cos(a) * r * 0.85, cy + Math.sin(a) * r * 0.85);
                else ctx.lineTo(cx + Math.cos(a) * r * 0.85, cy + Math.sin(a) * r * 0.85);
            }
            ctx.closePath();
            break;
    }
    ctx.fill();

    ctx.shadowBlur = 0;

    // 高光
    ctx.fillStyle = 'rgba(255,255,255,0.35)';
    ctx.beginPath();
    ctx.ellipse(cx - r * 0.3, cy - r * 0.35, r * 0.25, r * 0.12, -0.5, 0, Math.PI * 2);
    ctx.fill();
}

function lighten(hex, amt) {
    var r = parseInt(hex.substr(1,2), 16);
    var g = parseInt(hex.substr(3,2), 16);
    var b = parseInt(hex.substr(5,2), 16);
    r = Math.min(255, r + (255 - r) * amt);
    g = Math.min(255, g + (255 - g) * amt);
    b = Math.min(255, b + (255 - b) * amt);
    return 'rgb(' + Math.round(r) + ',' + Math.round(g) + ',' + Math.round(b) + ')';
}

function darken(hex, amt) {
    var r = parseInt(hex.substr(1,2), 16);
    var g = parseInt(hex.substr(3,2), 16);
    var b = parseInt(hex.substr(5,2), 16);
    r = Math.round(r * (1 - amt));
    g = Math.round(g * (1 - amt));
    b = Math.round(b * (1 - amt));
    return 'rgb(' + r + ',' + g + ',' + b + ')';
}

// roundRect polyfill
if (!CanvasRenderingContext2D.prototype.roundRect) {
    CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
        this.beginPath();
        this.moveTo(x + r, y);
        this.lineTo(x + w - r, y);
        this.quadraticCurveTo(x + w, y, x + w, y + r);
        this.lineTo(x + w, y + h - r);
        this.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
        this.lineTo(x + r, y + h);
        this.quadraticCurveTo(x, y + h, x, y + h - r);
        this.lineTo(x, y + r);
        this.quadraticCurveTo(x, y, x + r, y);
        this.closePath();
    };
}

// ============ 输入 ============

canvas.addEventListener('click', function(e) {
    var rect = canvas.getBoundingClientRect();
    var px = (e.clientX - rect.left) * (canvas.width / rect.width);
    var py = (e.clientY - rect.top) * (canvas.height / rect.height);
    handleClick(px, py);
});

// 渲染循环
function loop() {
    if (particles.length > 0 || floatTexts.length > 0) {
        draw();
    }
    requestAnimationFrame(loop);
}

// 初始画面
draw();
loop();
</script>
</body>
</html>

Logo

AtomGit AI 社区提供模型库、数据集、Agent、Token等资源

更多推荐