草野球の走塁を学べるゲームを作ってみた(HTML + CSS + JavaScript)
※上の画像をクリックしたら実際のサイトに飛びます
Ⅰ.きっかけ
- 私の所属する草野球チームで、「今シーズンは以下の ”4つのゴー” を走塁で実践しよう!」ということになりました。
- これをチーム全員で楽しく覚えられたらと思い、クイズ形式で学べるゲームにしてみました。
走塁における「4つのゴー」
場面 | ランナーの判断 | |
---|---|---|
「ゴロ」ゴー |
|
全員スタートを切る。内野ゴロが外野に抜けた場合に、2塁ランナーの生還率が高まる。 |
「スイング」ゴー |
|
全員スタートを切る。 |
「ストライク」ゴー |
|
スタートを切る。 |
「軌道」ゴー |
|
スタートを切る。キャッチャーが普通に捕ったら急いで戻る。 |
Ⅱ.作ったもの
- 野球のランナーの判断を3択で出題するゲーム。
- 回答を選んだら正誤判定と解説を表示する。
- すべて回答し終えたらトータルの正解数とコメントを表示する。
- 作成したサイトはこちら。
Ⅲ.使用したもの
- Windows10 Home 64bit
- Visual Studio Code
- Sourcetree
- GitHubアカウント
Ⅳ.大まかな流れ
- HTML、CSS、JavaScript、画像のファイルを作る。
- ファイルをSourcetreeでGitHubにプッシュする。
- GitHub Pagesを使ってWebサイトを公開する。
Ⅴ.詳細な手順
1.必要なファイルの用意
(1)HTMLファイル
- 以下のコードを書いて「index.html」として保存する。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>草野球の走塁クイズ</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="css/styles.css"> </head> <body> <main> <div class="row"> <h1>草野球の走塁クイズ</h1> </div> <div class="container"> <!-- 何問目かを表示する領域 --> <h2 id="question_number"></h2> <!-- 問題を表示する領域 --> <p id="question"></p> <!-- 画像を表示する領域 --> <p id="image"></p> <!-- 選択肢を表示する領域 --> <ul id="choices"></ul> <!-- 解説を表示する領域 --> <p id="answer"></p> <!-- 「次の問題へ」ボタンを表示する領域。回答前はボタンを押せない --> <div id="btn" class="disabled">次の問題へ</div> <!-- 正答数を表示する領域。最初は隠れた状態にする --> <div id="result" class="hidden"> <p id="score"></p> <p id="comment"></p> <a href="">リトライ</a> </div> </div> <script src="js/main.js"></script> </main> <footer> <p>Copyright © shiisuke</p> </footer> </body> </html>
(2)CSSファイル
/* 基本の設定 -------------------------------------------- */ body { background: #e9edef; font-size: 16px; font-family: Verdana, sans-serif; line-height: 2; margin: 0; padding: 0; } div, header, main, footer { display: block; } h1 { font-size: 36px; font-family: Verdana, sans-serif; } h2 { font-size: 24px; font-family: Verdana, sans-serif; } /* メインエリア -------------------------------------------- */ main { width: 100%; max-width: 930px; margin: 0 auto; padding: 10px 0 5px; color: #114046; } main h1 { color: #0061b1; text-align: center; } /* フッター -------------------------------------------- */ footer { background-color: #e9edef; color: #bbcfd4; font-family: "Montserrat",sans-serif; width: 100%; padding: 5px; text-align: center; } /* カラムの設定 -------------------------------------------- */ .row { max-width: 930px; margin: 0 auto 25px; } .container { max-width: 930px; margin: 8px auto; background: #fff; border-radius: 4px; padding: 10px 20px 40px; position: relative; } #image > img{ width: 352px; height: 216px; margin: 8px auto; padding: 0 20px; } /* 問題文のスタイル */ #question { margin-bottom: 16px; } /* 選択肢のスタイル */ #choices { list-style: none; /* リストにデフォルトで付いている黒マルなどを外す */ padding: 0; margin-bottom: 25px; } /* 選択肢一つ一つのスタイル */ #choices > li { border: 1px solid #ccc; border-radius: 4px; padding: 8px; margin-bottom: 17px; cursor: pointer; } #choices > li:hover { background: #f8f8f8; } /* 正解した場合の選択肢のスタイル */ #choices > li.correct { background: #d4edda; border-color: #c3e6cb; color: #155724; font-weight: bold; } /* 正解だったら「〇」と表示する */ #choices > li.correct::after { content: ' 〇'; } /* 間違えた場合の場合の選択肢のスタイル */ #choices > li.wrong { background: #f8d8da; border-color: #f5c6cb; color: #721c24; font-weight: bold; } /* 間違えたら「✖」と表示する */ #choices > li.wrong::after { content: ' ✖'; } #answer { padding: 0; margin-bottom: 25px; color: #721c24; } /* 回答後の「次の問題へ」ボタンと「リトライ」ボタンのスタイル */ #btn, #result > a { background: #3498db; padding: 10px; margin: 50px, 0, 30px; border-radius: 0.40px; cursor: pointer; text-align: center; color: #fff; font-weight: bold; box-shadow: 0 4px 0 #2880b9; } /* 回答前の「次の問題へ」ボタンのスタイル */ #btn.disabled { background:#ccc; box-shadow: 0 4px 0 #bbb; opacity: 0.7; } /* 正答数を表示する領域のスタイル */ #result { position: absolute; width: 300px; background: #f0fff0; padding: 30px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); top: 100px; left: 0; right: 0; margin: 0 auto; border-radius: 4px; text-align: center; transition: 0.4s; } /* 正答数を表示する領域を画面外に置いておく */ #result.hidden { transform: translateY(-500px); } /* 正答数のスタイル */ #score { font-size: 24px; color: #130d4a; font-weight: bold; margin: 15px 0 10px; } /* コメントのスタイル */ #comment { font-size: 34px; color: #e73738; font-weight: bold; margin: 0 0 20px; } /* 「リトライ」ボタンのスタイル */ #result > a { display: block; text-decoration: none; margin-bottom: 25px; } /* ---------------------- 画面の横幅が599px以下の場合に適用(スマホ用) ---------------------- */ @media screen and (max-width: 599px) { body { line-height: 1.5; } h1 { font-size: 24px; } h2 { font-size: 20px; } #image > img { max-width: 352px; max-height: 216px; width: 80vw; padding: 0 15px; } .container { margin-left: 0; width: auto; } .row, #question_number, #question, #choices, #answer, #btn, #btn.disabled, #result, #result.hidden, #score, #comment { padding: 0 8px; } }
(3)jsファイル
- 以下のコードを書いて、jsフォルダに「main.js」として保存する。
'use strict'; { // index.htmlから必要な要素を取得 const question = document.getElementById('question'); const choices = document.getElementById('choices'); const answer = document.getElementById('answer'); const btn = document.getElementById('btn'); const result = document.getElementById('result'); const scoreLabel = document.getElementById('score'); const commentLabel = document.getElementById('comment'); // クイズのデータ const quizSet = shuffle([ // qは問題文の配列、cは選択肢の配列(配列の先頭が正解)、aは解説 {q: '無死2, 3塁でバッターが内野ゴロを打った。2塁ランナーと3塁ランナーはどうする?', c: ['どちらもスタートを切る。', '3塁ランナーはスタートを切り、2塁ランナーは戻る。', 'どちらも戻る。'], a: '<解説>「ゴロ」ゴーの場面。両ランナーとも打った瞬間にスタートを切ることで、打球が外野に抜けた場合に2塁ランナーもホームインできる。ピッチャーゴロの場合は、3塁ランナーが挟まれている間にバッターが2塁へ行く。', x: '<img src="img/scene1.png">'}, {q: '無死2塁でバッターがショートゴロを打った。2塁ランナーはどうする?', c: ['3塁へスタートを切る。', '2塁へ戻る。','2塁と3塁の間で止まる。'], a: '<解説>「ゴロ」ゴーの場面。セオリーでは2塁に戻ることになっているが、草野球レベルではショートが3塁に投げてアウトにするのは難しい。', x: '<img src="img/scene2.png">'}, {q: '2死1, 3塁で2ストライクの場面。1塁ランナーはどのタイミングでスタートを切る?', c: ['バッターがスイングを始めた時。', 'バッターが打った時。', 'ピッチャーが投げた時。'], a: '<解説>「スイング」ゴーの場面。バッターがスイングを始めたらスタートを切る。', x: '<img src="img/scene3.png">'}, {q: '2死2塁で2ストライクの場面。2塁ランナーはどのタイミングでスタートを切る?', c: ['ピッチャーの投球がストライクゾーンに向かった時。', 'バッターが打った時。', 'ピッチャーが投げた時。'], a: '<解説>「ストライク」ゴーの場面。2塁ランナーはピッチャーの投球がよく見えるので、投球がストライクになりそうだったらスタートを切る。', x: '<img src="img/scene4.png">'}, {q: '0死2塁でピッチャーがワンバウンドする軌道の投球をした。2塁ランナーはどうする?', c: ['3塁へスタートを切り、キャッチャーが捕ったら2塁へ戻る。', 'キャッチャーが弾いたら3塁へスタートを切る。', '3塁へスタートを切り、キャッチャーが捕ってもそのまま3塁へ。'], a: '<解説>「軌道」ゴー。ピッチャーの投球がワンバウンドする軌道ならスタートを切り、キャッチャーが捕ったら急いで戻る。', x: '<img src="img/scene5.png">'}, ]); // いま何問目かを管理する変数 let currentNum = 0; // 回答したかを管理する変数 let isAnswered; // 正答数を管理する変数 let score = 0; // 選択肢をシャッフルする関数 function shuffle(arr) { for (let i = arr.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [arr[j], arr[i]] = [arr[i], arr[j]]; } return arr; } // 正誤判定の関数 function checkAnswer(li) { // 回答済みの場合には処理をしない if (isAnswered) { return; } // 状態を「回答済み」にする isAnswered = true; // 選択したリストが選択肢の配列の先頭と一致していれば正解とする if (li.textContent === quizSet[currentNum].c[0]) { // 正解したときの処理 li.classList.add('correct'); score++; } else { // 間違えたときの処理 li.classList.add('wrong'); } // 解説を表示する answer.textContent = quizSet[currentNum].a; // 回答後はボタンの色を青に戻す btn.classList.remove('disabled'); } // 画面描画の処理の関数 function setQuiz() { // 状態を「未回答」にする isAnswered = false; // 何問目かを表示する document.getElementById('question_number').textContent = "第" + (currentNum + 1) + "問 / 全5問"; // 問題文を表示する question.textContent = quizSet[currentNum].q; // 画像を表示する document.getElementById('image').innerHTML = quizSet[currentNum].x; // 前の問題の選択肢を削除する while (choices.firstChild) { choices.removeChild(choices.firstChild); } // 前の問題の解説を削除する answer.textContent = ""; // 選択肢を表示する // シャッフル関数に選択肢の配列のコピーを渡す const shuffledChoices = shuffle([...quizSet[currentNum].c]); // 一つ一つの要素をchoiceとして受け取る shuffledChoices.forEach(choice => { const li = document.createElement('li'); li.textContent = choice; // 選択肢をクリックしたときに正誤判定を行う li.addEventListener('click', () => { checkAnswer(li); }); choices.appendChild(li); }); // 最後の問題だったらボタンのテキストを「スコアを見る」にする if (currentNum === quizSet.length - 1) { btn.textContent = 'スコアを見る'; } } setQuiz(); // ボタンを押したときの処理 btn.addEventListener('click', () => { // 未回答の場合は次の問題に進めないようにする if (btn.classList.contains('disabled')) { return; } // 新しい問題に進んだらボタンの色をグレーに変える btn.classList.add('disabled'); // 最後の問題だったら正答数を表示する if (currentNum === quizSet.length - 1) { scoreLabel.textContent = `スコア: ${score} / ${quizSet.length}`; if (score === 5) { commentLabel.textContent = 'Excellent!'; } else if (score === 4) { commentLabel.textContent = 'Great!'; } else if (score === 3) { commentLabel.textContent = 'Good!'; } else { commentLabel.textContent = ''; } result.classList.remove('hidden'); } else { // 最後の問題でなかったら次の選択肢を表示する currentNum++; setQuiz(); } }); }
(4)画像ファイル
- imgファイルに画像を保存する。
2.ファイルのアップロード
3.GitHub Pagesでの公開
- 青色で表示されるURL(今回は「shiisukeuniform.web.fc2.comhttps://shiisuke1229.github.io/baseball-quiz/」)にアクセスし、サイトが公開されていることを確認する。
回答画面
正解判定画面
スコア表示画面
スマホで見た時の画面
Ⅵ.感想
- この作業を通して、ボタンを押したときの処理の方法や、jsファイルのデータをHTML側に渡す方法などを知ることができました。
- 画面のスマホ対応(レスポンシブ化)は結構大変でしたが、チームメイトに紹介した時に「スマホ対応です!」とドヤ顔で自慢できたり、飲み会などちょっとした場でもゲームを楽しめたりできたので、やっておいて良かったと思います(⌒∇⌒)
- 今回はクイズの問題データをjsファイルに書きましたが、問題数の増加にも対応できるよう、外部のデータベースから取得する方法も調べたいです。
Ⅶ.参考
- ドットインストール「JavaScriptで三択クイズを作ろう」
- ドットインストール「詳解JavaScript DOM編」
- 湊川あい氏『わかばちゃんと学ぶWebサイト制作の基本』(2016年、シーアンドアール研究所)