【ゲーム】JavaScript:68 ユニーク絵文字クリックゲーム

 「ユニーク絵文字クリックゲーム」は、16個の絵文字パネルから “重複しないもの”(一度だけ出現)“重複するもの”(同じ絵文字が2つ出現) が混在する中で、

  • 重複しない絵文字は 必ずタップ
  • 重複する絵文字は どちらか片方だけタップ
    というルールで、制限時間内に正しくすべてのパネルを消すミニゲームです。

正しくクリアできれば「クリア!」、重複絵文字を両方消すと「ゲームオーバー」、時間切れは「タイムアップ」となります。

遊び方・操作方法

  1. スタート ボタンをクリックしてゲーム開始。
  2. 16個のパネルが表示されるので、
    ・一度しかない絵文字は タップ
    ・同じ絵文字が2個並んでいるものは、どちらか 片方だけタップ
  3. タップすると該当パネルがグレーアウト&無効化。
  4. 重複絵文字を両方消す と即ゲームオーバー。
  5. 制限時間(30秒)内に全正解パネルを消せばクリア。

ルール

  • パネルは 8種類のユニーク絵文字 ×1、4種類の絵文字 ×2、合計16枚。
  • 重複絵文字は「2つ」のうち どちらか1つだけ を消す。
  • 同じ重複絵文字を両方タップすると 即ゲームオーバー
  • 制限時間は 30秒
  • すべてルール通りに消せば「クリア」、時間切れは「タイムアップ」です。

🎮ゲームプレイ

以下のリンク先から実際にプレイできます。

68 ユニーク絵文字クリックゲーム

素材のダウンロード

以下のリンクから使用する素材をダウンロードできます。

unique_emoji_clicking_game_title.pngunique_emoji_clicking_game_bg.png

ゲーム画面イメージ

プログラム全文(unique_emoji_clicking_game.html)

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>✨ ユニーク絵文字クリックゲーム ✨</title>
  <style>
    html, body {
      margin: 0; padding: 0;
      width: 100vw; height: 100vh;
      font-family: 'Yu Gothic', 'Meiryo', sans-serif;
      background: url('unique_emoji_clicking_game_bg.png') no-repeat center center fixed;
      background-size: cover;
    }
    body {
      width: 100vw; height: 100vh;
      overflow: auto;
    }
    .container {
      width: 800px;
      margin: 40px auto;
      background: rgba(255,255,255,0.96);
      border-radius: 22px;
      box-shadow: 0 4px 24px rgba(0,0,0,0.13);
      padding-bottom: 32px;
      min-height: 520px;
      position: relative;
    }
    .title-img {
      display: block;
      margin: 20px auto 18px auto;
      width: 340px;
      max-width: 80%;
      height: auto;
    }
    h1 {
      text-align: center;
      font-size: 2.1em;
      margin: 0.6em 0 0.15em 0;
      color: #1976d2;
      text-shadow: 1px 1px 7px #fff;
      letter-spacing: 0.04em;
    }
    .rule-section {
      background: rgba(235,245,255,0.98);
      border-radius: 14px;
      margin: 28px 32px 14px 32px;
      padding: 16px 24px;
      box-shadow: 0 2px 8px #4d90fe22;
    }
    .rule-title {
      text-align: center;
      font-weight: bold;
      font-size: 1.3em;
      margin-bottom: 10px;
      color: #1976d2;
    }
    .rule-text {
      text-align: left;
      font-size: 1.09em;
      line-height: 1.65;
      color: #174078;
      letter-spacing: 0.01em;
    }
    .btn {
      display: block;
      margin: 28px auto 0 auto;
      padding: 14px 50px;
      font-size: 1.2em;
      border: none;
      border-radius: 28px;
      background: linear-gradient(90deg, #87c9fa, #1565c0);
      color: #fff;
      font-weight: bold;
      box-shadow: 0 2px 8px #1976d266;
      cursor: pointer;
      transition: background 0.2s;
    }
    .btn:hover { background: linear-gradient(90deg, #b7e4fe, #1976d2); }
    .game-area {
      width: 740px;
      margin: 36px auto 0 auto;
      background: rgba(240,248,255,0.96);
      border-radius: 16px;
      box-shadow: 0 1px 10px #1565c055;
      display: flex;
      flex-direction: column;
      align-items: center;
      min-height: 320px;
      padding: 16px 0 20px 0;
      user-select: none;
    }
    .info {
      text-align: center;
      color: #1976d2;
      font-size: 1.19em;
      margin: 5px 0 0 0;
    }
    .emoji-list {
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      gap: 22px 30px;
      margin: 36px 0 12px 0;
      min-height: 120px;
    }
    .emoji-btn {
      font-size: 2.8em;
      width: 66px; height: 66px;
      border-radius: 50%;
      border: 2.5px solid #1976d2;
      background: #fff;
      box-shadow: 0 2px 8px #b3e5fc99;
      cursor: pointer;
      transition: background 0.12s, box-shadow 0.14s;
      user-select: none;
      margin-bottom: 10px;
      position: relative;
      outline: none;
    }
    .emoji-btn.used {
      background: #b0bec5;
      color: #fff;
      cursor: default;
      border: 2.5px solid #b0bec5;
      opacity: 0.55;
    }
    .timer-bar {
      text-align: center;
      font-size: 1.18em;
      margin: 0 0 12px 0;
      color: #388e3c;
      font-weight: bold;
      letter-spacing: 0.07em;
    }
    .message-box {
      text-align: center;
      background: rgba(235,245,255,0.98);
      font-size: 1.7em;
      color: #1976d2;
      font-weight: bold;
      border-radius: 16px;
      box-shadow: 0 4px 24px #1976d233;
      width: 80%;
      max-width: 480px;
      margin: 34px auto 0 auto;
      padding: 32px 18px;
      position: relative;
      z-index: 2;
    }
    .center-btn { margin: 22px auto 0 auto; }
  </style>
</head>
<body>
  <div class="container" id="main-container"></div>
  <script>
    // ==========================================
    // ✨ ユニーク絵文字クリックゲーム(重複は1つだけ消せばOK・両方消すとゲームオーバー)
    // ==========================================

    const EMOJI_POOL = [
      "🍎", "🍌", "🍇", "🍉", "🍊", "🍓",
      "🍒", "🥝", "🍍", "🥑", "🥕", "🥦",
      "🍆", "🍋", "🍈", "🍑", "🥔", "🍐"
    ];
    const GAME_TIME = 30;    // 制限時間(秒)
    const PANEL_COUNT = 16;  // 表示パネル数

    let gameEmojis = [];
    let clickedIndices = [];
    let alreadyClickedEmojis = {};
    let emojiCountMap = {}; // 出現回数
    let duplicateClickedMap = {}; // 重複絵文字で既に消したか
    let remaining = GAME_TIME;
    let timer = null;
    let playing = false;

    // タイトル画面
    function showTitleScreen() {
      clearInterval(timer);
      playing = false;
      document.getElementById('main-container').innerHTML = `
        <h1>✨ ユニーク絵文字クリックゲーム ✨</h1>
        <img src="unique_emoji_clicking_game_title.png" class="title-img" alt="ユニーク絵文字クリック タイトル">
        <div class="rule-section">
          <div class="rule-title">✨ 遊び方・ルール ✨</div>
          <div class="rule-text">
            ・パネルに出てくる<strong>重複しない</strong>絵文字(1つしか出てこない絵文字)は押して消そう!<br>
            ・<strong>重複する</strong>絵文字(同じものが2個出る場合)は<strong>どちらか一つだけ</strong>押して消そう!<br>
            ・同じ重複絵文字を2個とも消すとゲームオーバー。<br>
            ・制限時間は${GAME_TIME}秒、正しく全て消すとクリア!
          </div>
        </div>
        <button class="btn" id="start-btn">スタート</button>
      `;
      document.getElementById('start-btn').onclick = startGame;
    }

    // ゲーム開始
    function startGame() {
      // 8個ユニーク, 4個ダブル=16個
      let pool = EMOJI_POOL.slice();
      let uniqueEmojis = [];
      let duplicateEmojis = [];
      for (let i = 0; i < 8; i++) {
        let idx = Math.floor(Math.random() * pool.length);
        uniqueEmojis.push(pool.splice(idx, 1)[0]);
      }
      for (let i = 0; i < 4; i++) {
        let idx = Math.floor(Math.random() * pool.length);
        duplicateEmojis.push(pool.splice(idx, 1)[0]);
      }
      let chosen = [...uniqueEmojis, ...duplicateEmojis, ...duplicateEmojis];
      // シャッフル
      for (let i = chosen.length - 1; i > 0; i--) {
        let j = Math.floor(Math.random() * (i + 1));
        [chosen[i], chosen[j]] = [chosen[j], chosen[i]];
      }
      gameEmojis = chosen;
      clickedIndices = Array(PANEL_COUNT).fill(false);
      alreadyClickedEmojis = {};
      emojiCountMap = {};
      duplicateClickedMap = {};
      for (const emoji of gameEmojis) {
        emojiCountMap[emoji] = (emojiCountMap[emoji] || 0) + 1;
      }
      for (const emoji in emojiCountMap) {
        if (emojiCountMap[emoji] === 2) duplicateClickedMap[emoji] = false;
      }
      remaining = GAME_TIME;
      playing = true;
      renderGameScreen();

      timer = setInterval(() => {
        remaining -= 0.1;
        if (remaining <= 0) {
          remaining = 0;
          clearInterval(timer);
          playing = false;
          setTimeout(() => showEndScreen(false), 500);
        }
        updateTimer();
      }, 100);
    }

    // ゲーム画面
    function renderGameScreen() {
      let emojiHtml = "";
      for (let i = 0; i < gameEmojis.length; i++) {
        emojiHtml += `<button class="emoji-btn${clickedIndices[i] ? ' used' : ''}" 
                        ${clickedIndices[i] ? 'disabled' : ''} 
                        onclick="clickEmoji(${i})">${gameEmojis[i]}</button>`;
      }
      document.getElementById('main-container').innerHTML = `
        <h1>✨ ユニーク絵文字クリックゲーム ✨</h1>
        <div class="game-area">
          <div class="timer-bar" id="timer-bar">残り: ${remaining.toFixed(1)} 秒</div>
          <div class="emoji-list">${emojiHtml}</div>
          <div class="info">重複しない絵文字は押して消し、重複する絵文字はどちらか一つだけ消してください。</div>
        </div>
      `;
    }

    // タイマー更新
    function updateTimer() {
      let t = document.getElementById('timer-bar');
      if (t) t.textContent = `残り: ${remaining.toFixed(1)} 秒`;
    }

    // クリック処理
    window.clickEmoji = function(idx) {
      if (!playing) return;
      if (clickedIndices[idx]) return;
      let emoji = gameEmojis[idx];

      // 重複絵文字なら、すでに片方が消されていたらゲームオーバー
      if (emojiCountMap[emoji] === 2) {
        if (duplicateClickedMap[emoji]) {
          // 2つ目を押してしまった
          clickedIndices[idx] = true;
          renderGameScreen();
          clearInterval(timer);
          playing = false;
          setTimeout(() => showEndScreen(false, true), 400);
          return;
        } else {
          duplicateClickedMap[emoji] = true; // 片方だけ消す
          clickedIndices[idx] = true;
          renderGameScreen();
        }
      } else {
        // ユニーク(1回しか出ない)は普通に消す
        clickedIndices[idx] = true;
        renderGameScreen();
      }
      // クリア判定
      if (isAllCorrectEmojisClicked()) {
        clearInterval(timer);
        playing = false;
        setTimeout(() => showEndScreen(true), 600);
      }
    };

    // 正しい条件で全て押したかチェック
    function isAllCorrectEmojisClicked() {
      let counted = {}; // 重複の1つだけ押したか管理
      for (let i = 0; i < gameEmojis.length; i++) {
        let emoji = gameEmojis[i];
        if (emojiCountMap[emoji] === 1) {
          if (!clickedIndices[i]) return false; // ユニークは全部消す必要あり
        } else if (emojiCountMap[emoji] === 2) {
          // 重複はどちらか片方だけ消せばOK
          if (!(emoji in counted)) counted[emoji] = 0;
          if (clickedIndices[i]) counted[emoji]++;
        }
      }
      for (let emoji in counted) {
        if (counted[emoji] !== 1) return false; // どちらか一つだけ消えている
      }
      return true;
    }

    // 終了画面
    function showEndScreen(isClear, isMiss = false) {
      let message = "";
      if (isClear) {
        message = `🎉 クリア!正しい絵文字をすべて消しました!`;
      } else if (isMiss) {
        message = `❌ 重複する絵文字を2つとも消してしまいました…<br>ゲームオーバー`;
      } else {
        message = `⏰ 時間切れ!もう一度挑戦してみましょう!`;
      }
      document.getElementById('main-container').innerHTML = `
        <h1>✨ ユニーク絵文字クリックゲーム ✨</h1>
        <img src="unique_emoji_clicking_game_title.png" class="title-img" alt="ユニーク絵文字クリック タイトル">
        <div class="message-box">${message}</div>
        <button class="btn center-btn" id="back-title-btn">タイトル画面に戻る</button>
      `;
      document.getElementById('back-title-btn').onclick = showTitleScreen;
    }

    // 初期表示
    showTitleScreen();
  </script>
</body>
</html>

アルゴリズムの流れ

手順処理内容実装箇所/関数
1タイトル画面表示showTitleScreen()
2スタート→パネル選出・シャッフル→初期化startGame()
316個パネルのHTML生成&タイマー開始renderGameScreen()
4パネルクリック判定clickEmoji(idx)
5重複絵文字 → 既に1つ消していた場合GAMEOVERduplicateClickedMap
6全正解判定isAllCorrectClicked()
7クリア or ゲームオーバー or タイムアップshowEndScreen()

関数の詳細

関数名説明
showTitleScreen()タイトル画面のHTMLを生成し、スタートボタンにイベントを登録
startGame()ユニーク8種+重複4種×2 の16絵文字をランダム選出&シャッフル、各種初期化
renderGameScreen()タイマー・絵文字リスト・操作説明をHTML出力
updateTimer()タイマー表示を 0.1秒ごとに更新
clickEmoji(idx)パネルクリック処理。重複判定・ゲームオーバー判定・クリア判定を呼び出し
isAllCorrectClicked()「ユニークは全消し&重複は片方のみ消し」のクリア条件をチェック
showEndScreen(isClear,isMiss)終了時のメッセージを切り替え表示。タイトル画面に戻るボタンも設置

改造のポイント

  • パネル数・重複数 を変えて難易度調整(例:ユニーク10+重複3種×2 → 16枚)。
  • TIME 定数をいじって時間制限を緩めたり厳しく。
  • Emojii pool を増やして種類をバリエーションアップ。
  • サウンド:クリック時/クリア時/ゲームオーバー時に効果音を追加して盛り上げ。
  • UI:モバイル対応にタップ感やアニメーション(消えるときのフェードなど)を追加。
  • スコア保存localStorage やサーバー連携でハイスコアを記録可能。

 これらを活用して、あなた独自のルールや演出を盛り込んだ楽しさあふれるゲームに仕上げてみてください!