【ゲーム】JavaScript:23 世界の首都クイズ

 「世界の首都クイズ」は、画面に表示される国名をヒントに、その国の首都を4択から当てる知識クイズゲームです。全30か国のデータからランダムに5問を出題し、正答率を競います。正解・不正解に応じた効果音も再生され、臨場感があります。

遊び方・操作方法

  1. タイトル画面の「🎬 スタート」ボタンをクリック
  2. 現在の問題数と国名が表示されるので、その国の首都を4つの選択肢からクリック
  3. 正解なら「✅ 正解です!」+正解音、不正解なら「❌ 不正解… 正解は『××』です。」+不正解音を再生
  4. 「次の問題 ➡️」ボタンが表示されたらクリックして次へ
  5. 5問終了後、正答率をパーセンテージで表示
  6. 「🏠 タイトルに戻る」ボタンで再挑戦

ルール

  • 全30か国のデータをシャッフルし、先頭5問を出題
  • 各問題は一度だけ回答可能
  • 回答後に即フィードバック+効果音
  • 5問回答後に正答率を表示し、再挑戦可

🎮ゲームプレイ

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

23 世界の首都クイズ

素材のダウンロード

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

効果音:魔王魂

capital2_quiz_title.pngcapital2_quiz_bg.png

ゲーム画面イメージ

プログラム全体(capital2_quiz.html)

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>🌐 世界の首都クイズ 🏛️</title>
    <style>
        /* ================================
           全体リセット
           ページ内の余分な余白をなくし、
           ボックスサイズを border-box に統一
           ================================ */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        /* ================================
           背景とフォント設定
           ページ全体に背景画像を設定し、
           テキストを中央揃えに
           ================================ */
        body {
            font-family: 'Arial', sans-serif;                 /* 読みやすいフォント */
            background-image: url('capital2_quiz_bg.png');     /* 背景画像を設定 */
            background-size: cover;                            /* 画面いっぱいに伸縮 */
            background-position: center;                       /* 中央に配置 */
            display: flex;                                     /* Flexbox で中央揃え */
            justify-content: center;
            align-items: center;
            height: 100vh;                                     /* ビューポート全高 */
            color: #fff;                                       /* 白文字 */
            text-align: center;                                /* テキスト中央揃え */
        }

        /* ================================
           クイズコンテナ
           背景を半透明黒にして可読性を確保
           ================================ */
        #quiz-container {
            background-color: rgba(0, 0, 0, 0.7);
            padding: 20px;
            border-radius: 10px;
            width: 90%;
            max-width: 600px;
        }

        /* ================================
           メインタイトル
           ゴールドカラーで強調
           ================================ */
        #quiz-container > h1 {
            font-size: 2.5rem;
            margin-bottom: 20px;
            color: #FFD700;
        }

        /* ================================
           各画面(タイトル/ゲーム/結果)の切り替え
           active クラスで表示・非表示を切り替え
           ================================ */
        #title-screen,
        #game-screen,
        #result-screen {
            display: none;
        }
        #title-screen.active,
        #game-screen.active,
        #result-screen.active {
            display: block;
        }

        /* ================================
           タイトル画面:画像表示
           ================================ */
        #title-screen img {
            display: block;
            margin: 0 auto 20px;
            max-width: 80%;
            height: auto;
        }

        /* ================================
           質問番号表示
           ================================ */
        #question-number {
            font-size: 1.2rem;
            margin-bottom: 10px;
        }

        /* ================================
           質問テキスト表示
           ================================ */
        #question-text {
            font-size: 1.5rem;
            margin-bottom: 20px;
        }

        /* ================================
           選択肢ボタン群
           垂直並び&間隔確保
           ================================ */
        #choices-container {
            display: flex;
            flex-direction: column;
            gap: 10px;
            margin-bottom: 20px;
        }
        #choices-container button {
            padding: 10px;
            font-size: 1.1rem;
            border: none;
            border-radius: 5px;
            background-color: #1E90FF;  /* ボタン背景 */
            color: #fff;                /* ボタン文字 */
            cursor: pointer;            /* ホバーでポインタ */
            transition: background-color 0.3s;
        }
        #choices-container button:hover {
            background-color: #104E8B;  /* ホバー時濃く */
        }

        /* ================================
           フィードバック表示
           背景を半透明で強調
           ================================ */
        #feedback {
            font-size: 1.2rem;
            font-weight: bold;
            background-color: rgba(0, 0, 0, 0.6);
            padding: 10px;
            border-radius: 5px;
            margin-bottom: 20px;
        }

        /* ================================
           共通ボタンスタイル
           スタート/次へ/戻る
           ================================ */
        #start-button,
        #next-button,
        #return-button {
            padding: 10px 20px;
            font-size: 1rem;
            border: none;
            border-radius: 5px;
            background-color: #32CD32; /* 緑でスタート感 */
            color: #fff;
            cursor: pointer;
            transition: background-color 0.3s;
            margin-top: 10px;
        }
        #start-button:hover,
        #next-button:hover,
        #return-button:hover {
            background-color: #228B22;
        }
        /* 次へ、戻るは初期非表示 */
        #next-button,
        #return-button {
            display: none;
        }
    </style>
</head>
<body>
    <div id="quiz-container">
        <h1>🌐 世界の首都クイズ 🏛️</h1>

        <!-- ================================
             タイトル画面
             ================================ -->
        <div id="title-screen" class="active">
            <!-- タイトル画像 -->
            <img src="capital2_quiz_title.png" alt="世界の首都クイズ タイトル">
            <!-- ルール説明 -->
            <p>世界の国名が表示されます。その国の首都を4択から選んでください。</p>
            <p>全5問の正答率を競います。</p>
            <!-- ゲーム開始ボタン -->
            <button id="start-button">🎬 スタート</button>
        </div>

        <!-- ================================
             ゲーム画面
             ================================ -->
        <div id="game-screen">
            <!-- 問題番号 -->
            <div id="question-number"></div>
            <!-- 問いかけテキスト -->
            <div id="question-text"></div>
            <!-- 選択肢ボタン配置用 -->
            <div id="choices-container"></div>
            <!-- 正誤フィードバック -->
            <p id="feedback"></p>
            <!-- 次の問題へ移行ボタン -->
            <button id="next-button">次の問題 ➡️</button>
        </div>

        <!-- ================================
             結果画面
             ================================ -->
        <div id="result-screen">
            <!-- 正答率メッセージ -->
            <p id="result-message"></p>
            <!-- タイトル画面に戻る -->
            <button id="return-button">🏠 タイトルに戻る</button>
        </div>
    </div>

    <!-- ================================
         効果音読み込み
         ================================ -->
    <audio id="correct-sound" src="maoudamashii-capital2_quiz_correct.mp3"></audio>
    <audio id="incorrect-sound" src="maoudamashii-capital2_quiz_incorrect.mp3"></audio>

    <script>
        // ──────────────────────────────────────────
        // 国名と首都のデータリスト(30か国)
        // ──────────────────────────────────────────
        const countries = [
            { country: '日本',             capital: '東京' },
            { country: 'アメリカ合衆国',   capital: 'ワシントンD.C.' },
            { country: 'イギリス',         capital: 'ロンドン' },
            { country: 'フランス',         capital: 'パリ' },
            { country: 'ドイツ',           capital: 'ベルリン' },
            { country: 'イタリア',         capital: 'ローマ' },
            { country: 'スペイン',         capital: 'マドリード' },
            { country: 'カナダ',           capital: 'オタワ' },
            { country: 'オーストラリア',   capital: 'キャンベラ' },
            { country: '中国',             capital: '北京' },
            { country: 'ロシア',           capital: 'モスクワ' },
            { country: 'インド',           capital: 'ニューデリー' },
            { country: 'ブラジル',         capital: 'ブラジリア' },
            { country: 'メキシコ',         capital: 'メキシコシティ' },
            { country: 'アルゼンチン',     capital: 'ブエノスアイレス' },
            { country: '南アフリカ',       capital: 'プレトリア' },
            { country: 'エジプト',         capital: 'カイロ' },
            { country: 'トルコ',           capital: 'アンカラ' },
            { country: 'スウェーデン',     capital: 'ストックホルム' },
            { country: 'ノルウェー',       capital: 'オスロ' },
            { country: 'フィンランド',     capital: 'ヘルシンキ' },
            { country: 'オランダ',         capital: 'アムステルダム' },
            { country: 'ベルギー',         capital: 'ブリュッセル' },
            { country: 'スイス',           capital: 'ベルン' },
            { country: 'ギリシャ',         capital: 'アテネ' },
            { country: 'ポルトガル',       capital: 'リスボン' },
            { country: 'サウジアラビア',   capital: 'リヤド' },
            { country: '韓国',             capital: 'ソウル' },
            { country: 'ニュージーランド', capital: 'ウェリントン' },
            { country: 'インドネシア',     capital: 'ジャカルタ' }
        ];

        // 出題用にシャッフルしたリスト
        let quizList = [];
        // 現在の問題番号(0始まり)
        let currentQuestion = 0;
        // 正解数をカウント
        let correctCount = 0;

        // ── DOM 要素取得 ──
        const titleScreen    = document.getElementById('title-screen');
        const gameScreen     = document.getElementById('game-screen');
        const resultScreen   = document.getElementById('result-screen');
        const startButton    = document.getElementById('start-button');
        const questionNumEl  = document.getElementById('question-number');
        const questionTextEl = document.getElementById('question-text');
        const choicesEl      = document.getElementById('choices-container');
        const feedbackEl     = document.getElementById('feedback');
        const nextButton     = document.getElementById('next-button');
        const resultMsgEl    = document.getElementById('result-message');
        const returnButton   = document.getElementById('return-button');
        const correctSound   = document.getElementById('correct-sound');
        const incorrectSound = document.getElementById('incorrect-sound');

        // ── 配列をシャッフルする関数 (Fisher–Yates アルゴリズム) ──
        function shuffleArray(array) {
            for (let i = array.length - 1; i > 0; i--) {
                const j = Math.floor(Math.random() * (i + 1));
                [array[i], array[j]] = [array[j], array[i]];
            }
        }

        // ── ゲーム開始処理 ──
        function startGame() {
            // 国リストを複製してシャッフル
            quizList = [...countries];
            shuffleArray(quizList);
            // 最初の5問を抽出
            quizList = quizList.slice(0, 5);

            // カウンタ初期化
            currentQuestion = 0;
            correctCount = 0;

            // 画面切り替え:タイトル非表示 → ゲーム画面表示
            titleScreen.classList.remove('active');
            resultScreen.classList.remove('active');
            gameScreen.classList.add('active');

            // 問題を表示
            showQuestion();
        }

        // ── 問題表示処理 ──
        function showQuestion() {
            // 現在の問題データを取得
            const { country, capital } = quizList[currentQuestion];

            // 問題番号表示
            questionNumEl.textContent = `問題 ${currentQuestion + 1} / ${quizList.length}`;
            // 問いかけテキスト表示
            questionTextEl.textContent = `${country}の首都はどこでしょう?`;

            // 選択肢を作成:正解 + ランダムで他3つを追加
            const choices = [capital];
            const others = countries.filter(item => item.capital !== capital);
            shuffleArray(others);
            for (let i = 0; i < 3; i++) {
                choices.push(others[i].capital);
            }
            shuffleArray(choices);

            // 選択肢ボタンを生成して配置
            choicesEl.innerHTML = '';
            choices.forEach(choice => {
                const btn = document.createElement('button');
                btn.textContent = choice;
                // クリック時に selectAnswer を呼び出し
                btn.addEventListener('click', () => selectAnswer(choice, capital));
                choicesEl.appendChild(btn);
            });

            // フィードバック初期化、次へボタン非表示
            feedbackEl.textContent = '';
            nextButton.style.display = 'none';
        }

        // ── 回答選択処理 ──
        function selectAnswer(selected, correct) {
            // 一度回答するとすべてのボタンを無効化
            document.querySelectorAll('#choices-container button').forEach(btn => btn.disabled = true);

            // 正誤判定
            if (selected === correct) {
                feedbackEl.textContent = '✅ 正解です!';
                correctSound.play();  // 正解音再生
                correctCount++;       // 正解数増加
            } else {
                feedbackEl.textContent = `❌ 不正解… 正解は「${correct}」です。`;
                incorrectSound.play();  // 不正解音再生
            }

            // 次の問題へ進むボタンを表示
            nextButton.style.display = 'inline-block';
        }

        // ── 次へボタン押下時の処理 ──
        function nextQuestion() {
            currentQuestion++;
            if (currentQuestion < quizList.length) {
                // 問題が残っていれば次の問題を表示
                showQuestion();
            } else {
                // すべて終了したら結果画面へ
                endGame();
            }
        }

        // ── 終了処理 ──
        function endGame() {
            // ゲーム画面を隠して結果画面を表示
            gameScreen.classList.remove('active');
            resultScreen.classList.add('active');

            // 正答率計算と結果メッセージ表示
            const accuracy = Math.round((correctCount / quizList.length) * 100);
            resultMsgEl.textContent = `🎉 クイズ終了!あなたの正答率は ${accuracy}% です!`;
            // タイトルに戻るボタンを表示
            returnButton.style.display = 'inline-block';
        }

        // ── イベントリスナー登録 ──
        startButton.addEventListener('click', startGame);     // スタート
        nextButton.addEventListener('click', nextQuestion);   // 次へ
        returnButton.addEventListener('click', () => {
            // タイトル画面へ戻る
            resultScreen.classList.remove('active');
            titleScreen.classList.add('active');
        });
    </script>
</body>
</html>

アルゴリズムの流れ

ステップ処理内容主な関数/命令
1. データ準備&抽出countries をシャッフルし、先頭5件を quizList に切り出しshuffleArray()slice(0,5)
2. 問題表示問題番号・問い文・選択肢(正解+ランダム他3つ)を描画showQuestion()
3. 回答判定選択肢と正解を比較し、フィードバック表示+効果音再生selectAnswer()
4. 次へ or 終了判定currentQuestion を進め、次問 or endGame() を実行nextQuestion()
5. 正答率計算&結果表示全問終了後、正答率を計算して結果画面に表示endGame()

関数の詳しい解説

関数名説明
shuffleArray(arr)Fisher–Yates アルゴリズムで配列を in-place シャッフル
startGame()問題リスト初期化、カウンタリセット、画面切り替え、最初の問題表示
showQuestion()現在の問題データから問い文・選択肢を生成し、ボタン設置・UIリセット
selectAnswer()ユーザー選択肢を比較、フィードバック+効果音再生、次へボタン表示
nextQuestion()問題インデックスを進め、showQuestion() または endGame() を呼び分岐
endGame()正答率を計算して結果画面に表示、ゲーム画面を隠す

改造のポイント

  • 出題数変更quizList = quizList.slice(0,5) の「5」を変えると問題数を増減可能
  • 選択肢数変更for (let i=0; i<3; i++) のループ回数を変更すると選択肢を増減できる
  • 画像出題化:国旗や地図画像を <img> タグで表示するよう改修し、視覚学習を強化
  • 制限時間の追加:タイマーを実装し、制限時間内に回答しないと不正解扱いにする
  • ハイスコア機能localStorage に正答率やクリアタイムを保存し、ランキング画面を追加
  • 多言語対応:UIテキストやデータを外部JSON化し、英語版・多言語版を切り替えられるように

アドバイス:効果音やアニメーションをさらに充実させると、ユーザーのモチベーションが向上します。また、出題データを外部ファイル化しておくと新問追加やメンテナンスが楽になります。ゲーム性を高めるために、連続正解ボーナスやタイムアタックモードなども検討してみてください!