【ゲーム】JavaScript:57 記号タップ順チャレンジ

 「⭐ 記号タップ順チャレンジ ⭐」は、画面に並んだ複数の記号がランダムな順番で光るのを覚え、その順番どおりにタップしていく記憶力&反射神経テストゲームです。クリアするたびに記号の数(レベル)が増え、最大レベル10まで挑戦可能。シンプルながらも繰り返すほど記憶力が鍛えられます。

遊び方・操作方法

  1. タイトル画面で「スタート」をタップするとレベル1がスタート。
  2. 画面に並んだ記号(★ ● ▲ ■ …)が、ランダムな順番で“光る”(色が変わる)ので順番をよく覚えます。
  3. 光った順序どおりに記号をタップ。正解すると次の記号へ、すべて正解するとレベルクリア!
  4. レベルが上がるごとに記号が1つずつ増え、記憶負荷がアップ。
  5. タップを間違えるとゲーム終了画面へ。到達レベルが表示されます。
  6. レベル10をクリアするとゲームクリア

ルール

  • 初期レベルは1。レベルnnでは記号nn個を使う。
  • 記号は重複せずランダム選出。
  • 光る順番もランダム。その順番通りにタップしないと終了。
  • 正答し続けるとレベルが上昇。レベル10達成でエンディング。
  • 途中で間違えると「到達レベル」が表示され、タイトル画面へ戻れます。

🎮ゲームプレイ

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

57 記号タップ順チャレンジ

素材のダウンロード

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

symbol_tap_title.pngsymbol_tap_bg.png

ゲーム画面イメージ

プログラム全文(symbol_tap.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('symbol_tap_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.94);
      border-radius: 22px;
      box-shadow: 0 4px 24px rgba(0,0,0,0.13);
      padding-bottom: 32px;
      min-height: 600px;
      position: relative;
    }
    .title-img {
      display: block;
      margin: 20px auto 18px auto;
      width: 400px;
      max-width: 80%;
      height: auto;
    }
    h1 {
      text-align: center;
      font-size: 2.1em;
      margin: 0.6em 0 0.15em 0;
      color: #e7b200;
      text-shadow: 1px 1px 7px #fff;
      letter-spacing: 0.04em;
    }
    .rule-section {
      background: rgba(255,245,210,0.98);
      border-radius: 14px;
      margin: 28px 32px 14px 32px;
      padding: 16px 24px;
      box-shadow: 0 2px 8px #e7b20033;
    }
    .rule-title {
      text-align: center;
      font-weight: bold;
      font-size: 1.3em;
      margin-bottom: 10px;
      color: #e7b200;
    }
    .rule-text {
      text-align: left;
      font-size: 1.08em;
      line-height: 1.65;
      color: #ad8907;
      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, #ffe259, #e7b200);
      color: #684e00;
      font-weight: bold;
      box-shadow: 0 2px 8px #e7b20055;
      cursor: pointer;
      transition: background 0.2s;
    }
    .btn:hover { background: linear-gradient(90deg, #ffe98a, #ad8907); }
    .game-area {
      width: 700px;
      margin: 30px auto 0 auto;
      background: rgba(250,240,200,0.92);
      border-radius: 18px;
      box-shadow: 0 2px 14px #8884;
      min-height: 280px;
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      justify-content: center;
      padding: 28px 10px 38px 10px;
      position: relative;
      user-select: none;
    }
    .symbol-btn {
      width: 90px; height: 90px;
      margin: 18px;
      border-radius: 20px;
      border: 3px solid #e7b200;
      background: #fffbe5;
      font-size: 2.6em;
      font-weight: bold;
      color: #bfa400;
      box-shadow: 0 3px 12px #e7b20033;
      text-align: center;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: background 0.11s, box-shadow 0.11s, color 0.11s;
      cursor: pointer;
      outline: none;
      position: relative;
    }
    .symbol-btn.active {
      background: #ffe259;
      color: #ff9100;
      box-shadow: 0 0 30px #ffe25988;
    }
    .status-bar {
      text-align: center;
      margin: 14px auto 0 auto;
      font-size: 1.1em;
      font-weight: bold;
      background: rgba(255,255,220,0.96);
      color: #e7b200;
      width: 380px;
      border-radius: 12px;
      box-shadow: 0 1px 7px #e7b20044;
      padding: 10px;
      letter-spacing: 0.05em;
    }
    .message-box {
      text-align: center;
      background: rgba(255,245,210,0.98);
      font-size: 2em;
      color: #e7b200;
      font-weight: bold;
      border-radius: 16px;
      box-shadow: 0 4px 24px #ffe25933;
      width: 80%;
      max-width: 480px;
      margin: 40px auto 0 auto;
      padding: 38px 18px;
      position: relative;
      z-index: 2;
    }
    .center-btn { margin: 24px auto 0 auto; }
    @media (max-width: 900px) {
      .container, .game-area { width: 98vw !important; min-width: 0; }
      .game-area { flex-direction: column; align-items: center;}
      .symbol-btn { margin: 10px;}
    }
  </style>
</head>
<body>
  <div class="container" id="main-container"></div>
  <script>
    // --- ゲーム状態変数 ---
    const SYMBOLS = ["★", "●", "▲", "■", "♥", "◆", "♠", "☀", "☁", "♣"];
    let level = 1;
    let indices = [];
    let seqOrder = [];
    let userStep = 0;
    let acceptingInput = false;
    const MAX_LEVEL = 10;

    // タイトル画面
    function showTitleScreen() {
      document.getElementById('main-container').innerHTML = `
        <h1>⭐ 記号タップ順チャレンジ ⭐</h1>
        <img src="symbol_tap_title.png" class="title-img" alt="記号タップ順チャレンジ タイトル">
        <div class="rule-section">
          <div class="rule-title">🎲 遊び方・ルール 🎲</div>
          <div class="rule-text">
            ・画面にいくつかの記号が並びます。<br>
            ・最初に記号が<span style="color:#e7b200;font-weight:bold;">ランダムな順番</span>で光るので、よく覚えてください。<br>
            ・光った順番どおりに記号をタップしましょう。<br>
            ・正しい順で全部押すとレベルアップ!間違えるとゲーム終了です。<br>
            ・レベルが上がるごとに記号が1つ増えます。<br>
            ・<b>レベル10</b>をクリアするとゲームクリアです!
          </div>
        </div>
        <button class="btn" id="start-btn">スタート</button>
      `;
      document.getElementById('start-btn').onclick = ()=>startGame(1);
    }

    // ゲーム開始
    function startGame(startLevel) {
      level = startLevel;
      startRound();
    }

    // ラウンド開始
    function startRound() {
      indices = [];
      userStep = 0;
      acceptingInput = false;
      // 記号をランダムにlevel数だけ選ぶ(重複なし)
      let used = [];
      while(indices.length < level){
        let idx = Math.floor(Math.random()*SYMBOLS.length);
        if (!used.includes(idx)) {
          indices.push(idx);
          used.push(idx);
        }
      }
      // 光らせる順番もランダム
      seqOrder = [];
      let indicesCopy = indices.slice();
      while(seqOrder.length < indices.length) {
        let i = Math.floor(Math.random() * indicesCopy.length);
        seqOrder.push(indicesCopy[i]);
        indicesCopy.splice(i, 1);
      }
      showGameScreen();
      setTimeout(()=>showSequence(0), 1000);
    }

    // ゲーム画面表示
    function showGameScreen(msg) {
      document.getElementById('main-container').innerHTML = `
        <h1>⭐ 記号タップ順チャレンジ ⭐</h1>
        <div class="game-area" id="game-area"></div>
        <div class="status-bar" id="status-bar">${msg ? msg : `レベル ${level}:覚えてください!`}</div>
      `;
      drawSymbols();
    }

    // 記号を並べる
    function drawSymbols() {
      const area = document.getElementById('game-area');
      area.innerHTML = '';
      // 今回使う記号だけ配置
      for (let i = 0; i < indices.length; i++) {
        const idx = indices[i];
        const btn = document.createElement('div');
        btn.className = 'symbol-btn';
        btn.textContent = SYMBOLS[idx];
        btn.dataset.idx = idx;
        btn.onclick = () => handleUserInput(idx, btn);
        area.appendChild(btn);
      }
    }

    // 記号がランダムな順で光るアニメ
    function showSequence(n) {
      acceptingInput = false;
      const area = document.getElementById('game-area');
      const btns = Array.from(area.getElementsByClassName('symbol-btn'));
      if (n >= seqOrder.length) {
        // 入力受付へ
        acceptingInput = true;
        document.getElementById('status-bar').innerHTML = `順番にタップしてください`;
        return;
      }
      // 光らせる記号を特定
      let idxToFlash = seqOrder[n];
      for (let i = 0; i < btns.length; i++) {
        if (parseInt(btns[i].dataset.idx) === idxToFlash) {
          btns[i].classList.add('active');
          playTone(n);
          setTimeout(()=>btns[i].classList.remove('active'), 450);
        }
      }
      setTimeout(()=>showSequence(n+1), 650);
    }

    // プレイヤーが記号を押した時
    function handleUserInput(idx, btn) {
      if (!acceptingInput) return;
      // 今回押すべきidxか?(光った順と一致するか)
      if (idx === seqOrder[userStep]) {
        // 成功: 点滅
        btn.classList.add('active');
        playTone(userStep);
        setTimeout(()=>btn.classList.remove('active'), 250);
        userStep++;
        if (userStep === seqOrder.length) {
          acceptingInput = false;
          if (level === MAX_LEVEL) {
            showClearScreen();
          } else {
            showGameScreen(`<span style="color:#0a9a2b;">クリア!次はレベル${level+1}に挑戦</span>`);
            setTimeout(()=>{level++;startRound();}, 1500);
          }
        }
      } else {
        // 失敗
        acceptingInput = false;
        btn.style.background = "#ff8a65";
        showEndScreen();
      }
    }

    // ゲームクリア画面
    function showClearScreen() {
      document.getElementById('main-container').innerHTML = `
        <h1>⭐ 記号タップ順チャレンジ ⭐</h1>
        <img src="symbol_tap_title.png" class="title-img" alt="記号タップ順チャレンジ タイトル">
        <div class="message-box">
          🎉おめでとう!ゲームクリア!🎉<br>
          <span style="font-size:1.2em;color:#e7b200;">全${MAX_LEVEL}レベル達成!</span>
        </div>
        <button class="btn center-btn" id="back-title-btn">タイトル画面に戻る</button>
      `;
      document.getElementById('back-title-btn').onclick = showTitleScreen;
    }

    // ゲームオーバー画面
    function showEndScreen() {
      document.getElementById('main-container').innerHTML = `
        <h1>⭐ 記号タップ順チャレンジ ⭐</h1>
        <img src="symbol_tap_title.png" class="title-img" alt="記号タップ順チャレンジ タイトル">
        <div class="message-box">
          ゲーム終了!<br>
          <span style="font-size:1.2em;color:#e7b200;">到達レベル:${level}</span>
        </div>
        <button class="btn center-btn" id="back-title-btn">タイトル画面に戻る</button>
      `;
      document.getElementById('back-title-btn').onclick = showTitleScreen;
    }

    // シンプルな効果音
    function playTone(n) {
      try {
        const ctx = new(window.AudioContext||window.webkitAudioContext)();
        const o = ctx.createOscillator();
        const g = ctx.createGain();
        o.type = "triangle";
        o.frequency.value = 440 + 80 * n;
        g.gain.value = 0.08;
        o.connect(g);
        g.connect(ctx.destination);
        o.start();
        g.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.19);
        o.stop(ctx.currentTime + 0.19);
        setTimeout(()=>ctx.close(), 220);
      } catch(e) {}
    }

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

アルゴリズムの流れ

手順処理内容
1showTitleScreen() でタイトル画面を描画
2「スタート」押下 → startGame(1)startRound() で初期レベルラウンド開始
3startRound() で記号リスト(重複なしランダム)とシーケンス順を生成
4showGameScreen() でゲームエリアとステータスバーを描画
5showSequence(0) で順番どおりに記号を光らせ、終わったら acceptingInput=true
6ユーザーのタップは handleUserInput() で受け、正誤を判定
7正解なら次へ、すべて正解ならレベルアップ or クリア画面
8不正解なら showEndScreen() で到達レベル表示のゲームオーバー

関数の詳細

関数名機能概要詳細説明
showTitleScreen()タイトル画面の描画ルール説明&「スタート」ボタン設置。ゲーム状態はクリア済でもリセット。
startGame(startLevel)ゲーム開始/レベル設定引数で開始レベルを受け取り、startRound() を呼び出す
startRound()ラウンド初期化レベル数だけ異なる記号をランダム選出し、光らせる順序をシャッフルして生成
showGameScreen(msg)ゲーム画面再描画ゲームエリア(記号ボタン)とステータスバー(レベル or メッセージ)を描画
drawSymbols()記号ボタンを動的生成indices 配列に基づき、.symbol-btn 要素を並べてクリックハンドラを付与
showSequence(n)記号の光らせ演出(シーケンス再生)配列 seqOrder の n 番目の記号ボタンに active クラスを付与し、次の再生を再帰的にスケジュール
handleUserInput(idx,btn)プレイヤーのタップ処理正解なら次ステップへ、不正解なら背景色を変更してゲームオーバー画面へ
showClearScreen()全レベルクリア時のエンディング表示クリアメッセージとタイトル戻りボタンを描画
showEndScreen()不正解時のゲームオーバー表示到達レベルを表示するメッセージとタイトル戻りボタンを描画
playTone(n)シーケンス再生・タップフィードバック用簡易効果音Web Audio API で三角波オシレーターを生成し、番号 n に応じたピッチで短時間再生

改造のポイント

  • 記号バリエーション:★●▲■以外の絵文字やカラー記号を追加し、視覚的変化をつける。
  • ヒント機能:光らせる順序の一部だけ小さく順番番号を表示して難易度調整。
  • 難易度選択:記号数だけでなく光るスピードや待ち時間を変化させるモード追加。
  • ランキング機能:到達レベルやクリアタイムをlocalStorageに保存し、ハイスコア一覧を表示。
  • 音楽・演出強化:BGMやヒット時のアニメーションで演出を充実させ、没入感をアップ。

アドバイス
まずは記憶チャレンジのコアルールを安定させたうえで、ランキングや難易度モードを段階的に追加すると、プレイヤーの継続率が高まります。視覚的・聴覚的なフィードバックを強化すれば、リズムゲームのように楽しめる要素が生まれます!