JavaScriptで作るミニゲーム「HIT & BLOW」

「Hit & Blow」をプレイしてみる。

まず今回の動画で制作した「Hit & Blow」をプレイしてみようという方は、コチラからどうぞ。

スマホでのプレイに対応するためのコードを追加していますが、基本的な流れは動画と同じですm(_ _)m

JavaScriptで簡単なゲームを。

JavaScriptの入門講座も第8回になりました。
今回は「HIT & BLOW」という数あてゲームをプログラミングしています。

正直、ちょちょいと簡単に終わると思っていたんだけれど、ちょっと数字入力の仕組みなんかにこだわった結果、尺も長いしその上どうにも・・・なんだかスッキリとまとまってないなぁ・・・という感じになってしまいました。

そんなこともあって、このブログ記事で少しフォローの解説を入れることにいたしました。

その動画本編はコチラ

ポイントごとにコードとその解説を書いていきます。また、全コードは下の方に掲載しておりますm(_ _)m

HTMLとCSSで大体の体裁を整える

<div style=”font-size:40px; color:blue;”>
  Hit & Blow !
  <button id=”startBtn” onclick=”newGame()”>
    START
  </button>

</div>

40ピクセルの青地で「Hit & Blow !」を表示。タイトルの横には「START」ボタンを設置しています。ボタンにはonClick属性でnewGame()関数を紐づけています。

<div id=”choiceNumber”></div>

「choiceNumber」というidで作ったdiv要素。
ここに、ユーザーが選択した数字を表示します。

<div>決定:ENTER / クリア:DEL</div>

idなしのdivタグ。
ゲームの操作方法を表示しておきます。

<div id=”field”></div>

「field」というidで作ったdiv要素。
ここには、ヒットとブローの判定結果を表示します。

これが<body></body>タグ内に書いたHTMLの部品の全体像です。

一部の装飾は、<head></head>内に<style></style>要素を作って記述しています。
それが下記。

<style>
#choiceNumber{
  font-size:60px;
  width:300px;
  height:80px;
  background-color:lavender;
  text-align:center;
  border:solid 3px blueviolet;
}
#field{
  background-color:burlywood;
  font-size:20px;
}
</style>

関数newGame()

STARTボタンがクリックされたときに呼び出される関数です。

function newGame(){
  ansNum = [“*”,”*”,”*”,”*”];
  turn = 1;
  disNum = document.getElementById(“choiceNumber”);
  disNum.innerText = ansNum[0]+ansNum[1]+ansNum[2]+ansNum[3];
  document.getElementById(“startBtn”).style.visibility = “hidden”;
  document.addEventListener(“keydown”, keyPush);
  selectNumber();
}

まずは disNum = document.getElementById(“choiceNumber”);でエレメントをオブジェクトとして取得して、
disNum.innerText = ansNum[0]+ansNum[1]+ansNum[2]+ansNum[3]; でinnerTextを設定して表示します。

document.getElementById(“startBtn”).style.visibility = “hidden”;
ここで、スタートボタンを「hidden」つまり非表示にしています。

document.addEventListener(“keydown”, keyPush);
ここで、キーを押したときに起動するイベントリスナーを設定。

関数selectNumber()

出題する4つの数字をランダムに生成する関数です。

function selectNumber(){
  do{
    let numList = [“0″,”1″,”2″,”3″,”4″,”5″,”6″,”7″,”8″,”9”];
    for(let i=0; i<4; i++){
      let ran = Math.floor(Math.random()*numList.length);
      queNum[i] = numList[ran];
      numList.splice(ran,1);
    }
  }
  while(queNum[0]==0);
  console.log(queNum);
}

今回は、0~9までの数字を格納した配列からランダムに選んでいきます。一度選んだ数字はqueNumに代入後、もとの配列から削除するので、重複しません。do~while文で先頭の数字が0だった場合は再度選びなおします。

最後のconsole.logは、確認用です。

関数keyPush()

addEventListener()で設定した関数です。
キーボードのキーが押されたときに呼び出されます。

function keyPush(e){
  if (e.key==”Enter”){judge();}
  if (e.key==”Delete” || e.key==” “){
    ansNum = [“*”,”*”,”*”,”*”];
    disNum.innerText = ansNum[0]+ansNum[1]+ansNum[2]+ansNum[3];
    return;
  }
  if (Number.isInteger(Number(e.key))==false){return;}
  if (ansNum.indexOf(“*”)!=-1){
    ansNum[ansNum.indexOf(“*”)] = e.key;
    disNum.innerText = ansNum[0]+ansNum[1]+ansNum[2]+ansNum[3];
  }
}

keyPush(e)
この「e」は、発生したイベント自体を表すオブジェクトです。
.keyプロパティはオブジェクトが持っている値を表しているので、e.keyでどのキーが押されたのか判断できます。

Number.isInteger(Number(e.key))はちょっと複雑に見えるかもしれません。
Number(e.key)は、もしe.keyが「1」「2」などの数字の文字列だったなら、その値を数値に変換します。
そしてNumber.isInteger(Number(e.key))は、引数のNumber(e.key)が数値なら「true」を、数値でないなら「false」を返します。

この処理で、e.keyが数値(正確には数字の文字列)の場合に限り、次の処理に移ります。

.indexOf(“*”)は、「*」がどの位置にあるかを返して、ansNum[ansNum.indexOf(“*”)] = e.key;で、その「*」を数字(の文字列)に置き換えます。

関数judge()

これが最後の関数。
ヒットとブローの判定をします。

function judge(){
  if (ansNum[3]==”*”){return;}
  hit = 0;
  blow = 0;
  for (let i=0; i<4; i++){
    if (ansNum[i]==queNum[i]){hit += 1;}

ここでヒットの判定。

  else if (queNum.indexOf(ansNum[i])!=-1){blow += 1;}

そしてコッチでブローの判定をしています。

ここからは、判定後の表示を。

let str = ansNum[0]+ansNum[1]+ansNum[2]+ansNum[3];
let result = document.createElement(“div”);
result.innerText = turn + “回目のチャレンジ[ ” + str + ” ] ” + hit + ” HIT / ” + blow + ” BLOW”;
field.appendChild(result);

ここからが、ゲームクリアの表示ですね。

if (hit==4){
  window.alert(“おめでとうございます。\n” + turn + “回目で正解しました。”);
  document.getElementById(“startBtn”).style.visibility = “visible”;
  field.innerText = “”;
  ansNum = [“”, “”, “”, “”];
  disNum.innerText = ansNum[0]+ansNum[1]+ansNum[2]+ansNum[3];
  document.removeEventListener(“keydown”, keyPush);
}

まだクリアでないときは、ここで回数を+1、数字選択をリセットして再びキーイベントを待ちます。

else{
  turn += 1;
  ansNum = [“*”,”*”,”*”,”*”];
  disNum.innerText = ansNum[0]+ansNum[1]+ansNum[2]+ansNum[3];
  return;
}

最後に全コードをまとめて掲載してきます。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hit & Blow</title>
<style>
    #choiceNumber{
        font-size:60px;
        width:300px;
        height:80px;
        background-color:lavender;
        text-align:center;
        border:solid 3px blueviolet;    }
    #field{
        background-color:burlywood;
        font-size:20px;     }
</style>
</head>
<body>
    <div style="font-size:40px; color:blue;">
        Hit & Blow !
        <button id="startBtn" onclick="newGame()">START</button>
    </div>
    <div id="choiceNumber"></div>
    <div>決定:ENTER / クリア:DEL</div>
    <div id="enter"></div>
    <div id="field"></div>
<script>
    let arrayChoice = ["0","1","2","3","4","5","6","7","8","9","Delete","Enter"];
    for (let i=0; i<12; i++){								
            let newBtn = document.createElement("button");		
            newBtn.textContent = arrayChoice[i];				
            newBtn.onclick = btnClick;							
            enter.appendChild(newBtn);		}

    let ansNum = [], queNum = [], disNum = [], turn, hit, blow;

    function newGame(){
        ansNum = ["*","*","*","*"];
        turn = 1;
        disNum = document.getElementById("choiceNumber");
        disNum.innerText = ansNum[0]+ansNum[1]+ansNum[2]+ansNum[3];
        document.getElementById("startBtn").style.visibility = "hidden";
        document.addEventListener("keydown", keyPush);
        selectNumber();         }

    function selectNumber(){
        do{
            let numList = ["0","1","2","3","4","5","6","7","8","9"];
            for(let i=0; i<4; i++){
                let ran = Math.floor(Math.random()*numList.length);
                queNum[i] = numList[ran];
                numList.splice(ran,1);
            }
        }while(queNum[0]==0);
        console.log(queNum);        }

    function keyPush(e){
        if (e.key=="Enter"){judge();}
        if (e.key=="Delete" || e.key==" "){
            ansNum = ["*","*","*","*"];
            disNum.innerText = ansNum[0]+ansNum[1]+ansNum[2]+ansNum[3];
            return;             }
        if (Number.isInteger(Number(e.key))==false){return;}
        if (ansNum.indexOf("*")!=-1){
            ansNum[ansNum.indexOf("*")] = e.key;
            disNum.innerText = ansNum[0]+ansNum[1]+ansNum[2]+ansNum[3];     }       }

    function btnClick(e){
        let pushNum = e.target.textContent;
        if (pushNum=="Enter"){judge();}
        if (pushNum=="Delete" || pushNum==" "){
            ansNum = ["*","*","*","*"];
            disNum.innerText = ansNum[0]+ansNum[1]+ansNum[2]+ansNum[3];
            return;         }
        if (Number.isInteger(Number(pushNum))==false){return;}
        if (ansNum.indexOf("*")!=-1){
            ansNum[ansNum.indexOf("*")] = pushNum;
            disNum.innerText = ansNum[0]+ansNum[1]+ansNum[2]+ansNum[3];     }           }

    function judge(){
        if (ansNum[3]=="*"){return;}
        hit = 0;
        blow = 0;
        for (let i=0; i<4; i++){
            if (ansNum[i]==queNum[i]){hit += 1;}
            else if (queNum.indexOf(ansNum[i])!=-1){blow += 1;}     }
        let str = ansNum[0]+ansNum[1]+ansNum[2]+ansNum[3];
        let result = document.createElement("div");
        result.innerText = turn + "回目のチャレンジ[ " + str + " ] " + hit + " HIT / " + blow + " BLOW";
        field.appendChild(result);
        if (hit==4){
            window.alert("おめでとうございます。\n" + turn + "回目で正解しました。");
            document.getElementById("startBtn").style.visibility = "visible";
            field.innerText = "";
            ansNum = ["", "", "", ""];
            disNum.innerText = ansNum[0]+ansNum[1]+ansNum[2]+ansNum[3];
            document.removeEventListener("keydown", keyPush);       }
        else{
            turn += 1;
            ansNum = ["*","*","*","*"];
            disNum.innerText = ansNum[0]+ansNum[1]+ansNum[2]+ansNum[3];
            return;             }           }
</script>
</body>
</html>