JavaScriptのチュートリアルを通して簡単なゲームを作ってみたので、学んだことを忘れないうちにメモとして残しておこうと思います。
今回作成したゲームは「数当てゲーム」で、1から100までの数字の中からランダムに割り当てられる数字を10回以内に当てるというものです。
(↓以下がデモになります)
数字当てゲーム
1 から 100 までの数字を当ててみて!10 回以内に当てられるでしょうか。選んだ数字が大きいか小さいかを表示します。
内容はMDNのチュートリアルとほぼ同じですが、少しだけアレンジを加えています。
それでは参ります。
数当てゲームの全体像
数当てゲームを作成する前に、まずはゲームの動作およびコードの全体像を見てみましょう。
まずは、ゲームの動作の流れについて以下に記します。
- 1から100までの中からランダムな数字を1つ生成する
- プレイヤーが数字を予想して回答する(フォームに入力)
- プレイヤーが予想を回答した回数を記録する(最初は1回から)
- 予想が入力されたら、プレイヤーが以前の予想を見られるように、画面上に表示(記録)する
- 入力された数字(予想)が正しいかどうか判定する
- 入力された数字が正しい場合→お祝いメッセージを表示、入力フォームを非活性化する、リセットボタンを表示する
- 入力された数字が間違いの場合→間違いメッセージを表示、予想回数に1を加算する
- 入力された数字が間違いで、予想回数の上限に達した場合→ゲームオーバー、入力フォームを非活性化する、リセットボタンを表示して新しいゲームを再開できるようにする
- ゲームが再開したら、画面とロジックがリセットされるようにする(①に戻る)
このような処理をJavaScriptで書いていきます。
今回はindex.html
内に用意した<script>
タグ内にまとめてJavaScriptのコードを書きました。
<h1>数字当てゲーム</h1>
<p>1 から 100 までの数字を当ててみて!10 回以内に当てられるでしょうか。選んだ数字が大きいか小さいかを表示します。</p>
<div class="form">
<label for="guessField">予想を入力してください: </label>
<input type="text" id="guessField" class="guessField">
<input type="submit" value="予想を入力" class="guessSubmit">
</div>
<!-- JavaScriptで返す値を追加するための要素 -->
<div class="resultParas">
<p class="guesses"></p>
<p class="lastResult"></p>
<p class="lowOrHi"></p>
<p class="answer"></p>
</div>
<!-- リセットボタン用のフィールド(ゲームが終了したら表示) -->
<div class="resetField"></div>
<!-- JavaScript -->
<script>
let randomNumber = Math.floor(Math.random()*100) + 1; // 1から100までの中からランダムな数字を1つ生成
// html の各要素を取得する
const guesses = document.querySelector('.guesses');
const lastResult = document.querySelector('.lastResult');
const lowOrHi = document.querySelector('.lowOrHi');
const answer = document.querySelector('.answer');
const guessSubmit = document.querySelector('.guessSubmit'); // 予想を入力ボタンの要素
const guessField = document.querySelector('.guessField'); // 入力フォーム(<input>)の要素
const resetField = document.querySelector('.resetField'); // リセットボタンの要素
let guessCount = 1; // 予想回数(初期値は1)
let resetButton; // リセットボタンを定義
function checkGuess() { // 入力された予想の正誤判定
const userGuess = Number(guessField.value); // ユーザーが入力した値(入力フォームのvalueから取得)
if(guessCount === 1) {
guesses.textContent = '前回の予想: '; // 予想が入力されたら左記テキストを挿入
}
guesses.textContent += userGuess + ' '; // ユーザーが入力した値を挿入
if(userGuess === randomNumber) { // 正解した場合
lastResult.textContent = '正解です!おめでとう!!';
lastResult.style.backgroundColor = 'green';
lastResult.style.color = 'white';
lowOrHi.textContent = '';
setGameOver(); // ゲームオーバー時の処理を行う(後述)
} else if(guessCount === 10) { // 予想回数が10回に達した場合
lastResult.textContent = '!!!ゲームオーバー!!!';
answer.textContent = `正解は ${randomNumber} でした`;
answer.style.color = 'blue';
setGameOver(); // ゲームオーバー時の処理を行う(後述)
} else { // 入力した予想が間違っている、かつ予想回数が10回未満の場合
lastResult.textContent = '間違いです!';
lastResult.style.backgroundColor = 'red';
lastResult.style.color = 'white';
if(userGuess < randomNumber) {
lowOrHi.textContent = 'もっと大きい値です!';
} else if(userGuess > randomNumber) {
lowOrHi.textContent = 'もっと小さい値です!';
}
}
guessCount++; // 予想回数に1を加算する
guessField.value = ''; // 入力フォームを空にする
guessField.focus(); // 入力フォームにフォーカスを設定する
}
guessSubmit.addEventListener('click', checkGuess); // 予想を入力ボタンをクリックするとcheckGuess()関数が発火する
function setGameOver() { // ゲームオーバー時の処理を行う関数
guessField.disabled = true; // 入力フォームを利用不可にする
guessSubmit.disabled = true; // 予想を入力ボタンを利用不可にする
resetButton = document.createElement('button'); // ボタン要素を生成してresetButtonに代入
resetButton.textContent = '新しいゲームを始める'; // resetButtonのテキスト
resetField.appendChild(resetButton); // リセットボタン用のフィールドにresetButtonを表示
resetButton.addEventListener('click', resetGame); // resetButtonをクリックするとresetGame()関数が発火する
}
function resetGame() { // 画面とロジックをリセットするための関数
guessCount = 1; // 予想回数を1に戻す
const resetParas = document.querySelectorAll('.resultParas p');
for (const resetPara of resetParas) { // .resultParas の<p>要素のテキストを全て空にする
resetPara.textContent = '';
}
resetButton.parentNode.removeChild(resetButton); // リセットボタンを削除する
guessField.disabled = false; // 入力フォームを利用できるようにする
guessSubmit.disabled = false; // 予想を入力ボタンを利用できるようにする
guessField.value = ''; // 入力フォームを空にする
guessField.focus(); // 入力フォームにフォーカスを設定する
lastResult.style.backgroundColor = 'white';
randomNumber = Math.floor(Math.random()*100) + 1; // 1から100までの中からランダムな数字を1つ生成して更新
}
</script>
これらの処理をいくつかの手順に分けて実装していきます。
数当てゲームを作る手順
データを保持する定数と変数を追加する
<script>タグ内に以下のように定数const
と変数let
を定義します。
変数の定義にはlet
の他にvar
も使えますが、var
は推奨されていないのでlet
を用いて進めていきます。
詳しくはvarとletの違いを参照。
<script>
let randomNumber = Math.floor(Math.random()*100) + 1; // 1から100までの中からランダムな数字を1つ生成
// html の各要素を取得する
const guesses = document.querySelector('.guesses');
const lastResult = document.querySelector('.lastResult');
const lowOrHi = document.querySelector('.lowOrHi');
const answer = document.querySelector('.answer');
const guessSubmit = document.querySelector('.guessSubmit'); // 予想を入力ボタンの要素
const guessField = document.querySelector('.guessField'); // 入力フォーム(<input>)の要素
const resetField = document.querySelector('.resetField'); // リセットボタンの要素
let guessCount = 1; // 予想回数(初期値は1)
let resetButton; // リセットボタンを定義
</script>
一番最初の変数randomNumber
には、数学的なアルゴリズムにより計算された 1 から 100 までのうちランダムな数字が代入されます。
ここで、Math.random()
は0 以上 1 未満 (0 は含むが、 1 は含まない) の範囲で浮動小数点の擬似乱数を返す関数です。
> Math.random()
> 0.539380900662114
このMath.random()に100を乗算してMath.floor()で与えられた数値以下の最大の整数を返すことで、0〜99の乱数を生成することができます。
> Math.floor(9.999)
> 9
> Math.floor(9.999*100)
> 99
> Math.floor(Math.random()*100) // Math.random() → 0.539380900662114
> 53
ただ、今回は1〜100までの乱数を生成したいので、Math.floor(Math.random()*100)+1
とすることで範囲を1〜100までにすることができます。
また、document.querySelector('セレクタ')
では、指定されたセレクターまたはセレクター群に一致する、文書内の最初のElementを返します。
> document.querySelector('.guesses');
> <p class="guesses"></p>
> document.querySelector('.guessField');
> <input type="text" id="guessField" class="guessField">
後で追加する関数には、これらquerySelector
で取得したElementを用いて、値を代入したり削除したりしていきます。
入力された予想の正誤判定を行う関数 checkGuess() を追加する
最初に、ユーザーが入力した値userGuess
を取得します。
ユーザーがguessField(入力フォーム)で予想を入力すると、guessField.value
で入力した値をJavaScript内に読み込み、Number()
で数値化してuserGuess
に代入されます。
あとは、取得したuserGuessを用いて、入力された予想の正誤判定を行う記述をしていきます。
<script>
・・・
// 追記
function checkGuess() { // 入力された予想の正誤判定
const userGuess = Number(guessField.value); // ユーザーが入力した値(入力フォームのvalueから取得)
if(guessCount === 1) {
guesses.textContent = '前回の予想: '; // 予想が入力されたら左記テキストを挿入
}
guesses.textContent += userGuess + ' '; // ユーザーが入力した値を挿入
if(userGuess === randomNumber) { // 正解した場合
lastResult.textContent = '正解です!おめでとう!!';
lastResult.style.backgroundColor = 'green';
lastResult.style.color = 'white';
lowOrHi.textContent = '';
setGameOver(); // ゲームオーバー時の処理を行う(後述)
} else if(guessCount === 10) { // 予想回数が10回に達した場合
lastResult.textContent = '!!!ゲームオーバー!!!';
answer.textContent = `正解は ${randomNumber} でした`;
answer.style.color = 'blue';
setGameOver(); // ゲームオーバー時の処理を行う(後述)
} else { // 入力した予想が間違っている、かつ予想回数が10回未満の場合
lastResult.textContent = '間違いです!';
lastResult.style.backgroundColor = 'red';
lastResult.style.color = 'white';
if(userGuess < randomNumber) {
lowOrHi.textContent = 'もっと大きい値です!';
} else if(userGuess > randomNumber) {
lowOrHi.textContent = 'もっと小さい値です!';
}
}
guessCount++; // 予想回数に1を加算する
guessField.value = ''; // 入力フォームを空にする
guessField.focus(); // 入力フォームにフォーカスを設定する
}
</script>
このcheckGuess()
関数は1回実行する度に、<div class="resultParas"></div>
の<p>
タグ内には以下ようなテキスト(およびスタイル)が追加されるようになっています。
<div class="resultParas">
<p class="guesses">前回の予想: 50 55 77 88 90 </p>
<p class="lastResult" style="background-color: red; color: white;">間違いです!</p>
<p class="lowOrHi">もっと大きい値です!</p>
</div>
ただ、今のままではcheckGuess()
関数を実行(発火)させることができないので、関数を実行させるためのイベントを作成します。
addEventListener でクリック時に checkGuess() を発火させる
以下のように、checkGuess()
関数の直下にguessSubmit
ボタンに対するイベントリスナーaddEventListener
を追加します。
<script>
・・・
function checkGuess() { // 入力された予想の正誤判定
・・・
}
// 追記
guessSubmit.addEventListener('click', checkGuess);
</script>
addEventListener('click', checkGuess);
とすることで、guessSubmit
ボタン(予想を入力ボタン)クリック時にcheckGuess()
を発火させることができます。
ゲーム終了の処理を行う関数 setGameOver() を追加する
入力した予想が正解した場合、もしくは予想の回数が10回に達した場合はゲームを終了して新しいゲームを開始できるようにします。
ゲーム終了処理の具体的な内容は、
- 入力フォームを利用できないようにする(非活性化)
- 予想を入力ボタンを利用できないようにする(非活性化)
- 「新しいゲームを始める」ボタンを表示させる
- 「新しいゲームを始める」ボタンをクリックすると初期化されるようにする
これらの処理をsetGameOver()
関数に記述していきます。
<script>
・・・
function checkGuess() { // 入力された予想の正誤判定
・・・
}
guessSubmit.addEventListener('click', checkGuess);
// 追記
function setGameOver() {
guessField.disabled = true; // 入力フォームを利用不可にする
guessSubmit.disabled = true; // 予想を入力ボタンを利用不可にする
resetButton = document.createElement('button'); // ボタンタグを生成してresetButtonに代入
resetButton.textContent = '新しいゲームを始める'; // resetButtonのテキスト
resetField.appendChild(resetButton); // リセットボタン用のフィールドにresetButtonを表示
resetButton.addEventListener('click', resetGame); // resetButtonをクリックするとresetGame()関数が発火する
}
</script>
document.createElement('button')
でボタンタグを新たに生成してresetButton
に代入。
そのresetButton
を、appendChild
でresetField
内(<div class=”resetField”></div>タグ内)に追加するようにします。
追加されたresetButton
をクリックすると、resetGame()
関数を実行しゲームを初期状態にリセットして新しいゲームが始まるようにします。
ゲームを初期状態にリセットする関数 resetGame() を追加する
最後に、ゲームをリセットする関数resetGame()
を追加します。
<script>
・・・
function checkGuess() { // 入力された予想の正誤判定
・・・
}
guessSubmit.addEventListener('click', checkGuess);
function setGameOver() { // ゲームオーバー時の処理を行う関数
・・・
}
// 追記
function resetGame() {
guessCount = 1; // 予想回数を1に戻す
const resetParas = document.querySelectorAll('.resultParas p');
for (const resetPara of resetParas) { // .resultParas の<p>要素のテキストを全て空にする
resetPara.textContent = '';
}
resetButton.parentNode.removeChild(resetButton); // リセットボタンを削除する
guessField.disabled = false; // 入力フォームを利用できるようにする
guessSubmit.disabled = false; // 予想を入力ボタンを利用できるようにする
guessField.value = ''; // 入力フォームを空にする
guessField.focus(); // 入力フォームにフォーカスを設定する
lastResult.style.backgroundColor = 'white';
randomNumber = Math.floor(Math.random()*100) + 1; // 1から100までの中からランダムな数字を1つ生成して更新
}
</script>
これで全ての記述が終わりました。
入力した値の大小を判定するだけの簡単なゲームですが、JavaScriptのコードの記述量は思いの外多くて個人的には驚きました。
もっと効率の良い書き方があると思いますが、今はJavaScriptを始めたばかりということもあり、
一つ一つのプログラムの動きをしっかりと理解するためにも、遠回りでも丁寧に書いていく必要があるのかなと思っています。
以上です。
コメント