【ゲーム】JavaScript:15 クリックマスター

 「クリックマスター」は、制限時間内にフィールド上にランダムで出現する絵文字をクリックしてスコアを競うシンプルなクリックゲームです。
 プレイヤーはポジティブな絵文字(😍😎😇🤩🥳😜🤪🤑)をクリックすると+1点、ネガティブな絵文字(🤢💀👻)をクリックすると−1点となり、20秒間に出現する1〜2個の絵文字をいかにすばやく正確に処理できるかが勝負となります。

遊び方・操作方法

  1. 「▶️ ゲームを開始する」ボタンをクリック
  2. 制限時間20秒のカウントダウンが始まり、フィールドに絵文字が出現
  3. ポジティブ絵文字をクリック → スコア+1
  4. ネガティブ絵文字をクリック → スコア−1
  5. 制限時間終了後、ランキングが表示されるのでハイスコアを目指して再挑戦!

ルール

  • 制限時間:20秒
  • 出現数:1秒ごとに1〜2個の絵文字
  • ポイント:
    ・ポジティブ絵文字 😍😎😇🤩🥳😜🤪🤑 → +1点
    ・ネガティブ絵文字 🤢💀👻 → −1点
  • クリックを逃したり、ネガティブ絵文字を誤クリックするとスコアが減少
  • ゲーム終了後、ランキング(上位5位)を表示

🎮ゲームプレイ

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

15 クリックマスター

素材のダウンロード

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

click_master_bg.png

ゲーム画面イメージ

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

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>🎯クリックマスター🎯</title>
  <style>
    /* 全体背景に画像を設定 */
    body {
      font-family: Arial, sans-serif;
      text-align: center;
      margin: 0;
      padding: 0;
      background: url('click_master_bg.png') center center fixed no-repeat;
      background-size: cover;
      color: #fff;
    }
    /* メインタイトル */
    h1 {
      margin: 20px 0;
      font-size: 40px;
      text-shadow: 2px 2px 6px rgba(0,0,0,0.7);
    }
    /* 説明文エリア */
    #description {
      background-color: rgba(0,0,0,0.6);
      display: inline-block;
      padding: 20px;
      border-radius: 10px;
      text-align: left;
      color: #fff;
      font-size: 18px;
      line-height: 1.6;
      margin-bottom: 20px;
    }
    /* 「ゲームを開始する」ボタンを中央に */
    #start-game {
      display: block;
      margin: 20px auto 0;
    }
    /* ゲームエリア(フィールド)を大きく */
    #game-area {
      margin: 0 auto;
      width: 360px;
      height: 360px;
      background-color: rgba(255,255,255,0.8);
      position: relative;
      border: 5px solid #00bcd4;
      border-radius: 20px;
      box-shadow: 0 0 20px rgba(0,0,0,0.7);
      display: none;
      overflow: hidden; /* はみ出し防止 */
    }
    /* ターゲット絵文字 */
    .target {
      position: absolute;
      width: 60px;               /* 固定サイズ */
      height: 60px;
      font-size: 48px;           /* 絵文字を大きく */
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      transition: transform 0.2s ease;
      user-select: none;
    }
    .target:hover {
      transform: scale(1.3);
    }
    /* スコアとタイマー表示 */
    #score, #timer {
      font-size: 24px;
      font-weight: bold;
      background-color: rgba(0,0,0,0.6);
      color: #fff;
      display: none;
      padding: 8px 12px;
      margin: 10px;
      border-radius: 6px;
      text-shadow: 1px 1px 3px rgba(0,0,0,0.8);
    }
    /* ランキング表示 */
    #ranking {
      background-color: rgba(0,0,0,0.6);
      display: none;
      padding: 20px;
      border-radius: 10px;
      color: #fff;
      font-size: 18px;
      margin: 20px auto;
      width: 320px;
      text-align: center;        /* センタリング */
    }
    #ranking h2 {
      margin-top: 0;
      font-size: 24px;
    }
    /* ボタン共通スタイル */
    button {
      font-size: 18px;
      padding: 12px 24px;
      margin: 10px;
      background: linear-gradient(45deg, #ff9800, #f44336);
      color: #fff;
      border: none;
      border-radius: 8px;
      cursor: pointer;
      box-shadow: 0 6px rgba(0,0,0,0.3);
      transition: transform 0.1s, box-shadow 0.1s;
    }
    button:hover {
      transform: translateY(-2px);
      box-shadow: 0 8px rgba(0,0,0,0.4);
    }
    button:active {
      transform: translateY(2px);
      box-shadow: 0 4px rgba(0,0,0,0.2);
    }
  </style>
</head>
<body>

  <!-- タイトル -->
  <h1>🎯 クリックマスター 🎯</h1>

  <!-- 説明エリア -->
  <div id="description">
    <p>🔍 <strong>ゲームの説明:</strong></p>
    <ul>
      <li>フィールド上に登場する絵文字をクリックしてスコアを稼ごう!</li>
      <li>😍😎😇🤩🥳😜🤪🤑 が出たら+1点!</li>
      <li>🤢💀👻 が出たら−1点!</li>
      <li>同時に1つか2つ登場。制限時間は20秒!</li>
      <li>終了後、ランキングが表示されるよ。高スコアを目指そう!</li>
    </ul>
    <button id="start-game">▶️ ゲームを開始する</button>
  </div>

  <!-- ゲーム表示エリア -->
  <div id="game-area"></div>

  <!-- スコア&タイマー表示 -->
  <div>
    <span id="score">スコア:0</span>
    <span id="timer">残り時間:20秒</span>
  </div>

  <!-- ランキング表示 -->
  <div id="ranking"></div>

  <!-- 再挑戦ボタン -->
  <button id="restart" style="display:none;">🔄 もう一度遊ぶ</button>

  <script>
    // --- 要素取得 ---
    const gameArea       = document.getElementById("game-area");
    const scoreDisplay   = document.getElementById("score");
    const timerDisplay   = document.getElementById("timer");
    const rankingDisplay = document.getElementById("ranking");
    const restartButton  = document.getElementById("restart");
    const description    = document.getElementById("description");
    const startButton    = document.getElementById("start-game");

    // --- ゲーム状態変数 ---
    let score      = 0;
    let timeLeft   = 20;
    let timerId;
    const rankings = [];

    // --- 絵文字リスト ---
    const goodEmojis = ["😍","😎","😇","🤩","🥳","😜","🤪","🤑"];
    const badEmojis  = ["🤢","💀","👻"];
    const allEmojis  = [...goodEmojis, ...badEmojis];

    /**
     * ターゲットを1つ生成して配置(重ならない位置を試行)
     */
    function createOneTarget() {
      const emoji = allEmojis[Math.floor(Math.random() * allEmojis.length)];
      const target = document.createElement("div");
      target.classList.add("target");
      target.textContent = emoji;

      const size = 60; // targetの幅・高さ
      const existing = Array.from(gameArea.querySelectorAll(".target"));
      let x, y, tries = 0;

      do {
        x = Math.floor(Math.random() * (gameArea.clientWidth  - size));
        y = Math.floor(Math.random() * (gameArea.clientHeight - size));
        tries++;
      } while (
        existing.some(el => {
          const rect = el.getBoundingClientRect();
          const parentRect = gameArea.getBoundingClientRect();
          const ex = rect.left - parentRect.left;
          const ey = rect.top  - parentRect.top;
          return !(x + size < ex || ex + size < x || y + size < ey || ey + size < y);
        }) && tries < 20
      );

      target.style.left = x + "px";
      target.style.top  = y + "px";

      // クリック時の処理
      target.addEventListener("click", () => {
        if (goodEmojis.includes(emoji)) score++;
        else score--;
        scoreDisplay.textContent = `スコア:${score}`;
        if (gameArea.contains(target)) gameArea.removeChild(target);
      });

      gameArea.appendChild(target);

      // 1秒後に自動で消える
      setTimeout(() => {
        if (gameArea.contains(target)) {
          gameArea.removeChild(target);
        }
      }, 1000);
    }

    /**
     * 1秒ごとに1~2個のターゲットを生成
     */
    function generateTargets() {
      const count = Math.random() < 0.5 ? 1 : 2;
      for (let i = 0; i < count; i++) {
        createOneTarget();
      }
    }

    /**
     * タイマー処理:残り時間を減らし、終了判定
     */
    function tick() {
      timeLeft--;
      timerDisplay.textContent = `残り時間:${timeLeft}秒`;
      generateTargets();
      if (timeLeft <= 0) {
        endGame();
      }
    }

    /**
     * ゲーム終了時の処理
     */
    function endGame() {
      clearInterval(timerId);
      gameArea.style.display     = "none";
      timerDisplay.style.display = "none";
      scoreDisplay.style.display = "none";

      rankings.push(score);
      rankings.sort((a,b) => b - a);
      rankingDisplay.innerHTML = `<h2>🏆 ランキング 🏆</h2>`;
      rankings.slice(0,5).forEach((s,i) => {
        rankingDisplay.innerHTML += `<p>${i+1}位:${s}点</p>`;
      });
      rankingDisplay.style.display = "block";
      restartButton.style.display   = "inline-block";
    }

    /**
     * ゲーム開始処理
     */
    function startGame() {
      // 初期化
      score                   = 0;
      timeLeft                = 20;
      scoreDisplay.textContent = `スコア:${score}`;
      timerDisplay.textContent = `残り時間:${timeLeft}秒`;
      rankingDisplay.style.display = "none";
      restartButton.style.display  = "none";
      description.style.display    = "none";

      // 表示
      gameArea.style.display     = "block";
      scoreDisplay.style.display = "inline-block";
      timerDisplay.style.display = "inline-block";

      // タイマー開始
      timerId = setInterval(tick, 1000);
      generateTargets();
    }

    // 開始ボタン
    startButton.addEventListener("click", startGame);
    // 再挑戦ボタン
    restartButton.addEventListener("click", startGame);
  </script>
</body>
</html>

アルゴリズムの流れ

手順関数/処理内容
1startGame()スコアとタイマーを初期化し、画面要素を表示。setInterval(tick,1000) を起動し、即座に generateTargets()
2tick()残り時間を1秒減算し表示更新。generateTargets() を呼び出し、0秒になったら endGame()
3generateTargets()1〜2個の絵文字を生成するループ。各回 createOneTarget()
4createOneTarget()ランダムな絵文字を選択し、重複しない位置に <div> を生成。クリックで得点変動&要素削除。1秒後に自動削除。
5endGame()タイマー停止、ゲームエリア非表示、スコア・タイマー非表示。スコアを rankings 配列に追加・ソートし、上位5位を表示。再挑戦ボタン表示。

関数の解説

関数名説明
startGame()ゲーム開始時の初期化処理。スコア・タイマーのリセット、DOMの表示切替、タイマーの起動を行う。
tick()1秒ごとに呼ばれ、残り時間をデクリメント。ターゲット生成と終了判定を実行。
generateTargets()1秒間に1~2個の絵文字ターゲットを生成する。
createOneTarget()絵文字を表示する要素を生成し、フィールド上の重なりを避ける位置に配置。クリック&タイムアウトで削除。
endGame()ゲーム終了処理。タイマー停止後、ランキング計算・表示、再挑戦ボタンの表示を行う。

改造のポイント

  • 制限時間の変更
    timeLeft の初期値(20秒)を変更すれば、ゲームの長さを調整可能。
  • フィールドサイズの調整
    CSS の #game-areawidthheight を変更し、表示エリアを拡大・縮小。
  • 絵文字リストのカスタマイズ
    goodEmojisbadEmojis 配列を編集し、新たな絵文字やアイコンを追加。
  • スコア加算ルールの拡張
    ネガティブ絵文字をクリックした際に −2点にするなど、得点ロジックを createOneTarget() 内クリック処理で調整。
  • 効果音やアニメーションの追加
    クリック時に音を鳴らす Audio 要素の再生や、絵文字削除時のフェードアウトアニメーションを導入。
  • 難易度設定の導入
    「易しい」「普通」「難しい」といったモードを用意し、generateTargets() の発生頻度や出現数をモード別に変更。
  • マルチプレイヤーモード
    ローカルで順番制の2人プレイを実装し、交互にプレイしたスコアを比較表示。

これらを参考に、用途やターゲットユーザーに合わせたバリエーションを楽しんでみてください。