🎮ArcadeLab

6×6 地宫打地鼠 · 吱吱声

by PrismBuilder59
491 lines14.4 KB
▶ Play
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
  <title>6×6 地宫打地鼠 · 吱吱声</title>
  <style>
    * {
      box-sizing: border-box;
      user-select: none;
    }
    body {
      min-height: 100vh;
      margin: 0;
      background: linear-gradient(145deg, #1e3c2c, #2a5a3a);
      display: flex;
      justify-content: center;
      align-items: center;
      font-family: 'Segoe UI', Roboto, system-ui, sans-serif;
    }
    .game-container {
      background: #3d2e1b;
      padding: 1.8rem 2rem 2.5rem;
      border-radius: 4rem 4rem 3rem 3rem;
      box-shadow: 0 20px 30px rgba(0,0,0,0.7), inset 0 -4px 8px #1f140a;
      border-bottom: 6px solid #5b3f24;
    }
    .board {
      display: grid;
      grid-template-columns: repeat(6, 1fr);
      gap: 12px;
      background: #5a4029;
      padding: 20px;
      border-radius: 32px;
      box-shadow: inset 0 0 0 3px #2f1f10, inset 0 8px 12px rgba(0,0,0,0.5);
      margin-bottom: 20px;
    }
    .cell {
      width: 70px;
      height: 70px;
      background: #7d5d3c;
      border-radius: 30% 30% 40% 40%;
      box-shadow: inset 0 -8px 0 #3d2a17, inset 0 4px 8px #b48b5a, 0 6px 10px rgba(0,0,0,0.4);
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      transition: 0.08s linear;
      position: relative;
    }
    .cell:active {
      transform: scale(0.92);
      box-shadow: inset 0 -2px 0 #3d2a17, inset 0 4px 6px #b48b5a;
    }
    /* 地鼠洞 */
    .mole {
      width: 90%;
      height: 90%;
      background: #c97d3d;
      border-radius: 50% 50% 45% 45%;
      box-shadow: inset 0 -4px 0 #5e3a1c, inset 0 6px 10px #f5c48a, 0 4px 6px rgba(0,0,0,0.3);
      transform: translateY(6px);
      transition: 0.1s ease;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 2.8rem;
      color: #2d1d0c;
      text-shadow: 0 2px 4px #eeb576;
      line-height: 1;
      font-weight: bold;
      position: relative;
    }
    .mole::after {
      content: "🐭";
      filter: drop-shadow(0 3px 2px rgba(0,0,0,0.3));
      font-size: 2.6rem;
    }
    /* 空坑 */
    .empty-hole {
      width: 70%;
      height: 30%;
      background: #3b2819;
      border-radius: 50%;
      box-shadow: inset 0 4px 6px #1f130a, 0 2px 4px #664a2b;
      transform: translateY(10px);
    }
    .status-bar {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 0 12px;
      color: #f7e9c3;
      text-shadow: 0 3px 0 #1f140a;
      font-weight: 600;
      font-size: 1.2rem;
      letter-spacing: 1px;
    }
    .score-box {
      background: #281e12;
      padding: 8px 22px;
      border-radius: 60px;
      box-shadow: inset 0 3px 6px #0f0a05, 0 6px 0 #3d2a17;
      color: #f7d98c;
      font-size: 1.8rem;
      font-weight: 700;
      line-height: 1.4;
    }
    .reset-btn {
      background: #b17d4b;
      border: none;
      color: #21170c;
      font-size: 1.4rem;
      font-weight: bold;
      padding: 6px 20px;
      border-radius: 40px;
      cursor: pointer;
      box-shadow: inset 0 -6px 0 #5f401f, 0 6px 0 #2d1d0c;
      transition: 0.06s linear;
      display: flex;
      align-items: center;
      gap: 6px;
      font-family: inherit;
      letter-spacing: 1px;
    }
    .reset-btn:active {
      transform: translateY(4px);
      box-shadow: inset 0 -2px 0 #5f401f, 0 2px 0 #2d1d0c;
    }
    .hint {
      color: #d4c09d;
      font-size: 0.9rem;
      margin-top: 8px;
      text-align: center;
      letter-spacing: 1px;
      opacity: 0.8;
    }
    @media (max-width: 580px) {
      .game-container { padding: 1.2rem; border-radius: 2.5rem; }
      .board { gap: 8px; padding: 12px; }
      .cell { width: 54px; height: 54px; }
      .mole { font-size: 2rem; }
      .mole::after { font-size: 2rem; }
      .score-box { font-size: 1.4rem; padding: 4px 14px; }
      .reset-btn { font-size: 1rem; padding: 4px 14px; }
    }
    @media (max-width: 430px) {
      .cell { width: 44px; height: 44px; }
      .board { gap: 6px; padding: 8px; }
      .mole::after { font-size: 1.6rem; }
    }
  </style>
</head>
<body>
<div class="game-container">
  <div class="board" id="board"></div>
  <div class="status-bar">
    <span>🏆 地宫</span>
    <span class="score-box" id="scoreDisplay">0</span>
    <button class="reset-btn" id="resetBtn">🔄 重来</button>
  </div>
  <div class="hint">👆 点击地鼠 · 吱吱作响</div>
</div>
<script>
  (function() {
    const BOARD_SIZE = 6;
    const TOTAL_CELLS = BOARD_SIZE * BOARD_SIZE;

    // 游戏状态
    let score = 0;
    let activeMoleIndex = -1;          // -1 表示没有地鼠
    let gameInterval = null;
    let timeoutId = null;              // 额外延迟控制

    const boardEl = document.getElementById('board');
    const scoreDisplay = document.getElementById('scoreDisplay');

    // 音效上下文 (Web Audio)
    let audioCtx = null;

    // 确保 AudioContext 在用户手势后初始化
    function getAudioContext() {
      if (!audioCtx) {
        audioCtx = new (window.AudioContext || window.webkitAudioContext)();
      }
      if (audioCtx.state === 'suspended') {
        audioCtx.resume();
      }
      return audioCtx;
    }

    // 播放 "吱吱" 声 (使用合成音效, 像老鼠叫)
    function playSqueak() {
      try {
        const ctx = getAudioContext();
        // 创建一个短促的、音调较高的吱吱声
        const oscillator = ctx.createOscillator();
        const gainNode = ctx.createGain();

        oscillator.type = 'sawtooth';   // 锯齿波带点毛刺感
        oscillator.frequency.setValueAtTime(1800, ctx.currentTime);
        oscillator.frequency.exponentialRampToValueAtTime(800, ctx.currentTime + 0.08);

        gainNode.gain.setValueAtTime(0.25, ctx.currentTime);
        gainNode.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.12);

        oscillator.connect(gainNode);
        gainNode.connect(ctx.destination);

        oscillator.start(ctx.currentTime);
        oscillator.stop(ctx.currentTime + 0.13);

        // 再加一个小“吱”的泛音 让声音更丰富
        const osc2 = ctx.createOscillator();
        const gain2 = ctx.createGain();
        osc2.type = 'square';
        osc2.frequency.setValueAtTime(2400, ctx.currentTime);
        osc2.frequency.exponentialRampToValueAtTime(1200, ctx.currentTime + 0.06);
        gain2.gain.setValueAtTime(0.12, ctx.currentTime);
        gain2.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.09);
        osc2.connect(gain2);
        gain2.connect(ctx.destination);
        osc2.start(ctx.currentTime);
        osc2.stop(ctx.currentTime + 0.1);

        // 快速衰减的噪声 (模拟“吱”的摩擦感)
        const bufferSize = ctx.sampleRate * 0.04;
        const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
        const data = buffer.getChannelData(0);
        for (let i = 0; i < bufferSize; i++) {
          data[i] = (Math.random() * 2 - 1) * Math.exp(-i / (bufferSize * 0.3));
        }
        const noise = ctx.createBufferSource();
        noise.buffer = buffer;
        const gainNoise = ctx.createGain();
        gainNoise.gain.setValueAtTime(0.15, ctx.currentTime);
        gainNoise.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.04);
        noise.connect(gainNoise);
        gainNoise.connect(ctx.destination);
        noise.start(ctx.currentTime);
        noise.stop(ctx.currentTime + 0.05);

      } catch (e) {
        // 某些浏览器可能不支持,静默降级
        console.warn('吱吱声播放失败:', e);
      }
    }

    // 重置游戏 (清空地鼠, 重置分数)
    function resetGame() {
      // 清除所有定时器
      if (gameInterval) {
        clearInterval(gameInterval);
        gameInterval = null;
      }
      if (timeoutId) {
        clearTimeout(timeoutId);
        timeoutId = null;
      }

      // 重置状态
      score = 0;
      updateScore();
      // 移除当前地鼠
      if (activeMoleIndex !== -1) {
        const prevCell = document.querySelector(`[data-index="${activeMoleIndex}"]`);
        if (prevCell) {
          const hole = prevCell.querySelector('.empty-hole');
          const mole = prevCell.querySelector('.mole');
          if (mole) {
            mole.remove();
            prevCell.appendChild(hole || createEmptyHole());
          }
        }
        activeMoleIndex = -1;
      }

      // 重新开始循环 (延迟一点点让重置更干净)
      startMoleCycle();
    }

    // 创建空洞元素 (复用)
    function createEmptyHole() {
      const hole = document.createElement('div');
      hole.className = 'empty-hole';
      return hole;
    }

    // 更新分数显示
    function updateScore() {
      scoreDisplay.textContent = score;
    }

    // 在地洞中生成地鼠 (指定索引)
    function showMole(index) {
      // 边界检查
      if (index < 0 || index >= TOTAL_CELLS) return false;

      // 如果当前已经有地鼠,先移除
      if (activeMoleIndex !== -1) {
        const oldCell = document.querySelector(`[data-index="${activeMoleIndex}"]`);
        if (oldCell) {
          const oldMole = oldCell.querySelector('.mole');
          if (oldMole) {
            oldMole.remove();
            oldCell.appendChild(createEmptyHole());
          }
        }
        activeMoleIndex = -1;
      }

      const cell = document.querySelector(`[data-index="${index}"]`);
      if (!cell) return false;

      // 移除现有空洞 (如果有)
      const hole = cell.querySelector('.empty-hole');
      if (hole) hole.remove();

      // 创建地鼠
      const mole = document.createElement('div');
      mole.className = 'mole';
      cell.appendChild(mole);

      activeMoleIndex = index;
      return true;
    }

    // 隐藏地鼠 (不播放声音)
    function hideMole() {
      if (activeMoleIndex !== -1) {
        const cell = document.querySelector(`[data-index="${activeMoleIndex}"]`);
        if (cell) {
          const mole = cell.querySelector('.mole');
          if (mole) {
            mole.remove();
            cell.appendChild(createEmptyHole());
          }
        }
        activeMoleIndex = -1;
      }
    }

    // 处理打地鼠 (点击事件)
    function handleCellClick(e) {
      const cell = e.currentTarget;
      const index = parseInt(cell.dataset.index, 10);

      // 如果当前地鼠存在且点击的是地鼠所在格子
      if (activeMoleIndex === index) {
        // 加分
        score += 1;
        updateScore();

        // 播放吱吱声
        playSqueak();

        // 移除地鼠 (并更新洞)
        const mole = cell.querySelector('.mole');
        if (mole) {
          mole.remove();
          cell.appendChild(createEmptyHole());
        }
        activeMoleIndex = -1;

        // 重置计时器:打中后立刻重新安排下一次地鼠出现 (避免同一只停留)
        if (gameInterval) {
          clearInterval(gameInterval);
          gameInterval = null;
        }
        if (timeoutId) {
          clearTimeout(timeoutId);
          timeoutId = null;
        }
        // 快速启动下一轮 (延迟0.2s 让画面不突兀)
        timeoutId = setTimeout(() => {
          timeoutId = null;
          startMoleCycle();
        }, 180);
      } else {
        // 点空处轻微反馈 (但无音效, 保持干净)
        // 可以加一点触感: 只是纯反馈
      }
    }

    // 开始地鼠出现循环 (使用setInterval随机出现)
    function startMoleCycle() {
      // 清除旧循环
      if (gameInterval) {
        clearInterval(gameInterval);
        gameInterval = null;
      }
      if (timeoutId) {
        clearTimeout(timeoutId);
        timeoutId = null;
      }

      // 立即生成一只地鼠 (延迟一点点让初始化同步)
      timeoutId = setTimeout(() => {
        timeoutId = null;
        // 如果有地鼠先隐藏 (安全)
        if (activeMoleIndex !== -1) {
          hideMole();
        }
        // 随机选一个洞
        const randomIndex = Math.floor(Math.random() * TOTAL_CELLS);
        showMole(randomIndex);

        // 设置定时器,每隔1.8~2.6秒移动地鼠(换位置)
        gameInterval = setInterval(() => {
          // 如果当前有地鼠,先隐藏
          if (activeMoleIndex !== -1) {
            hideMole();
          }
          // 随机生成新地鼠 (不与上次相同? 但无所谓,概率低)
          let newIndex;
          do {
            newIndex = Math.floor(Math.random() * TOTAL_CELLS);
          } while (newIndex === activeMoleIndex && TOTAL_CELLS > 1); // 避免完全重复,但无伤大雅
          showMole(newIndex);
        }, 1900 + Math.random() * 900); // 1.9~2.8秒变化
      }, 300);
    }

    // 初始化棋盘 (6x6)
    function buildBoard() {
      boardEl.innerHTML = '';
      for (let i = 0; i < TOTAL_CELLS; i++) {
        const cell = document.createElement('div');
        cell.className = 'cell';
        cell.dataset.index = i;
        // 初始为空洞
        const hole = createEmptyHole();
        cell.appendChild(hole);
        cell.addEventListener('click', handleCellClick);
        boardEl.appendChild(cell);
      }
    }

    // 重新启动 (重置 + 构建)
    function initGame() {
      // 如果已有循环清除
      if (gameInterval) {
        clearInterval(gameInterval);
        gameInterval = null;
      }
      if (timeoutId) {
        clearTimeout(timeoutId);
        timeoutId = null;
      }
      // 重置地鼠
      activeMoleIndex = -1;
      score = 0;
      updateScore();
      // 重建棋盘 (保证干净)
      buildBoard();
      // 开始循环
      startMoleCycle();
    }

    // 绑定重置按钮
    document.getElementById('resetBtn').addEventListener('click', function() {
      // 重置并重新开始
      initGame();
      // 轻触反馈 (并尝试恢复音频上下文)
      try {
        getAudioContext();
      } catch (e) {}
    });

    // 页面加载启动
    window.addEventListener('load', () => {
      initGame();
      // 预创建音频上下文(在用户第一次点击时自动激活)
      document.addEventListener('click', function wakeAudio() {
        try {
          getAudioContext();
        } catch (e) {}
        document.removeEventListener('click', wakeAudio);
      }, { once: true });
    });

    // 在页面关闭前清除定时器
    window.addEventListener('beforeunload', function() {
      if (gameInterval) clearInterval(gameInterval);
      if (timeoutId) clearTimeout(timeoutId);
    });

  })();
</script>
</body>
</html>

Game Source: 6×6 地宫打地鼠 · 吱吱声

Creator: PrismBuilder59

Libraries: none

Complexity: complex (491 lines, 14.4 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: 6-6-prismbuilder59" to link back to the original. Then publish at arcadelab.ai/publish.