スクリーンショット_2019-08-16_7

#xhack勉強会 レポ 「 JavaScriptの基礎構文だけでゲームを作れることを知ってもらう勉強会(2019年7月30) 」

先日参加した #xhack勉強会 の内容をまとめてみました。 

以前参加した際のレポ↓


今回イベントのconnpassページのリンクです↓↓↓

■今回の勉強会のゴール

モグラたたきゲームをJavaScriptだけで書きます!

https://www.youtube.com/watch?v=zIT_a9_i62g&feature=youtu.be


JavaScriptっていろいろできますね〜 使いこなせるようになりたい!

モグラたたきゲームを動画で見てみると、
①表示されたモグラ画像が、ハンマーで叩かれると痛い顔して消える。
②しばらくすると再び出現する。

 みたいな動きですね!

これをプログロミングで実装するとどうなるか、これを勉強していきます!


実際に講義で使ったものと同じ素材を使いたい方はこちらから↓↓↓↓


 ■画像を表示する

<img src="images/モグ2.png”>

基本中の基本!


■画像リソースを切り替える


ハンマーで叩くとモグラが痛い顔に変わる部分!

 // htmlを修正
<img id="mogura" src="images/モグ2.png">

// jsを修正
<script>
 let mogura = document.getElementById("mogura");
 mogura.src = "images/モグ1.png";
</script>

imgタグにid="mogura"を与えて、getElementById("mogura")でimgタグのDOMを取得し、中身の画像パスを書き換える

という感じ。


■関数宣言

この日は松田さんが、
「今日は関数宣言の説明に時間を割きます!!!」と、宣言されました。

function changeImage()
{
 let mogura = document.getElementById("mogura");
 mogura.src = "images/モグ1.png";
}

まあ、こんな感じですよね

関数宣言くらいさすがにわかるぜ〜!
と思ってたら、甘かった。


知らない書き方を2つも教えていただきました笑


よく見る書き方だと、

function plus(a, b){
 return a + b;
}

plus(1,2);    // 3


こんな感じですね。

これを、即時実行する書き方がこちら↓

知らなかった書き方①(即時実行)

((a,b) =>{
 return a + b; 
})(5,10);  //15

無名関数なので、外から呼びだすことはできず即時実行のみ可能

知らなかった書き方②

var myFunc = new Function('return "hello"'); //引数1つ

var myFunc = new Function('a','return a ** a'); //引数2つ

var myFunc = new Function('a','b','return a + b'); //引数3つ

new 演算子を使って Function オブジェクトを作成する

しかし、MDNによれば
「Functionコンストラクターによる関数の生成は推奨されません
とのこと。

推奨されない理由が書いてあるけど素人にはようわかりません。
推奨されていないのであればとにかく使わないということでよいでしょう!

ただ、他人がこの書き方で書いたコードに出会うかもなので、知れてよかった!


余談ですが、8/2(金)に、#xhack勉強会で「MDNを読もう!」という別の勉強会が開催されます。私も参加予定です。

公式リファレンスを見る癖をつけないといけないとわかりつつ、正直それを読むのが難しいよ!と思っていたところにこの勉強会の開催なので、とっても楽しみです。

https://twitter.com/XHACK20/status/1154930170949586944


本題に戻ります。
次は、これをどうやって実行するか。

■onclickイベントで実行!


書き方は3種類!(他にあれば教えてください!)

①タグの中に書き込む

<img id="mogura" onclick="changeImage()" src="images/モグ2.png" />


②タグの中には書かない addEventListenerでイベント登録

mogura.addEventListener('click',changeImage);


③タグの中には書かない addEventListenerも使わない(呼び方わからん笑)

mogura.onclick = changeImage;


前回のレポ↓ でも触れましたが、


changeImage関数に () つけるか つけないか問題 また出ました。

①タグの中に埋め込む パターンでは、()つけないと実行されません

②と③パターンでは、
 ()なしで正常に動きます
 ()をつけると、クリックしなくてもchangeImage関数が実行されちゃいます。(リロードした瞬間にすでに関数実行後の状態になってます)

()をつけると、関数の実行結果を返す
()なしだと、関数そのものをオブジェクトとして扱うことができる

でした!
ちゃんと理解していなくても、上手く動かなかったら()をつけたり外したりすれば上手くいくかも!という選択肢を持っているだけで全然違いますね。(よい子はちゃんと理解しましょう)


■全モグラの情報を取得する

クラス名に"mogura"を付与し、getElementsByClassNameで取得する

<h1>もぐらたたきゲーム</h1>
<div class="container">
 <div class="wrapper">
   <div><img class="mogura" src="images/ana.png"></div>
   <div><img class="mogura" src="images/ana.png"></div>
   <div><img class="mogura" src="images/ana.png"></div>
 </div>
 <div class="wrapper">
   <div><img class="mogura" src="images/ana.png"></div>
   <div><img class="mogura" src="images/ana.png"></div>
   <div><img class="mogura" src="images/ana.png"></div>
 </div>
 <div class="wrapper">
   <div><img class="mogura" src="images/ana.png"></div>
   <div><img class="mogura" src="images/ana.png"></div>
   <div><img class="mogura" src="images/ana.png"></div>
 </div>
</div>
var moguras = document.getElementsByClassName("mogura");



■ランダムで一匹モグラを取得する

var random = Math.floor(Math.random() * moguras.length);

Math.floor() ・・・少数を切り捨てて整数にする
Math.rondom()・・・0〜1の中でランダムな数値を返す

var random は、
もぐらの数(0〜8)のうちどれかを返す
ということですね


■setInterval と setTimeout で一定間隔でもぐらを出す/引っ込める


function moguraDasu()
{
   var random = Math.floor(Math.random()*moguras.length); // ランダム
   let masu = moguras[random];
   masu.src = "images/mogumogu.png";
   setTimeout(function(){moguraKakusu(masu);}, 800);
}
setInterval(moguraDasu, 1000); // 1000ミリ秒に一回実行する


■完成形

今までの内容に、
・SCOREを画面に表示する
・開始から一定時間たったらゲーム終了にする

など、少々加工を加えた完成形のコードが↓↓↓


<!DOCTYPE html>
<html>
<head>
 <meta charset="utf-8">
 <title>もぐらたたきゲーム</title>
 <style media="screen">
   #game {
     cursor: url(./images/hummer.cur) 40 40, auto;
   }
   h1 {
     text-align: center;
     margin: auto 0px;
     background: rgba(140, 248, 155, 0.7);
   }
   h1>img {
     width: 320px;
   }
   body {
     margin: 0px auto;
     background-image: url(./images/草-noitem.png);
     background-repeat: no-repeat;
     background-attachment: fixed;
     background-position: center;
     background-size: cover;
   }
   .container {
     width: 520px;
     height: 160px;
     margin: 20px auto;
     text-align: center;
     display: -webkit-flex;
     display: -moz-flex;
     display: -ms-flex;
     display: -o-flex;
     display: flex;
   }
   .container img {
     margin: 20px;
     width: 140px;
   }
   .wrapper {
     margin: 0px auto;
     display: flex;
   }
   img {
     width: 213px;
   }
   .hit-mogura {
     cursor: url(./images/hummer2.cur) 20 20, auto;
   }
   .uwagaki {
     position: absolute;
     top: 0;
     left: 0;
   }
   .relative {
     position: relative;
   }
   .field {
     margin: 40px;
   }
   
   .score {
     font: xx-large;
     color: aliceblue;
   }
 </style>
</head>
<body id="game">
 <div class="field">
   <h1>
     <img src="images/title.png" alt="">
     <a id="game_end" style="display: none">ゲーム終了</a>
   </h1>
   <div class="score">
     <label>SCORE:</label><a id="score"></a>
   </div>
   <div class="container">
     <div class="relative">
       <img class="ana" src="images/穴.png">
       <img class="mogura uwagaki" src="images/モグ2.png">
     </div>
     <div class="relative">
       <img class="ana" src="images/穴.png">
       <img class="mogura uwagaki" src="images/モグ2.png">
     </div>
     <div class="relative">
       <img class="ana" src="images/穴.png">
       <img class="mogura uwagaki" src="images/モグ2.png">
     </div>
   </div>
   <div class="container">
     <div class="relative">
       <img class="ana" src="images/穴.png">
       <img class="mogura uwagaki" src="images/モグ2.png">
     </div>
     <div class="relative">
       <img class="ana" src="images/穴.png">
       <img class="mogura uwagaki" src="images/モグ2.png">
     </div>
     <div class="relative">
       <img class="ana" src="images/穴.png">
       <img class="mogura uwagaki" src="images/モグ2.png">
     </div>
   </div>
   <div class="container">
     <div class="relative">
       <img class="ana" src="images/穴.png">
       <img class="mogura uwagaki" src="images/モグ2.png">
     </div>
     <div class="relative">
       <img class="ana" src="images/穴.png">
       <img class="mogura uwagaki" src="images/モグ2.png">
     </div>
     <div class="relative">
       <img class="ana" src="images/穴.png">
       <img class="mogura uwagaki" src="images/モグ2.png">
     </div>
   </div>
 </div>
 <script>
   let score = 0;
   let scoreText = document.getElementById("score");
   let mogura_itai   = "./images/モグ1.png"
   let mogura_normal = "./images/モグ2.png"
   // ここにJavaScriptを書く
   let moguras = document.getElementsByClassName('mogura');
   for (let index = 0; index < moguras.length; index++) {
     const element = moguras[index];
     element.addEventListener('click', () => {
       element.src = mogura_itai;
       // 500ミリ秒後に消える
       setTimeout(() => {
         score++;
         scoreText.innerHTML = score;
         element.src = "";
       }, 500);
     })
   }
   // 1000msごとに呼ばれる
   let moguraGenerator = setInterval(() => {
     let num = Math.floor( Math.random() * moguras.length );
     moguras[num].src = mogura_normal;
   }, 600); 
   setTimeout(() => {
     let game_end = document.getElementById('game_end');
     game_end.style = "";
     clearInterval(moguraGenerator);
   }, 10000)
 </script>
</body>
</html>


js単体だと、30行くらいしかない!


■まとめ

このもぐらゲームで結局何をしているかというと、

①全モグラを取得し、(同一class名を与え、getElementsByClassNameでDOM取得)
②それに対しonclickイベントで、画像srcの切り替え(クリックされたら画像切り替え→消す)
③消えたモグラを、ランダムで再出現させる( Math.floor(Math.random()*moguras.length) で モグラをランダム取得し、画像srcの切り替え。それをsetIntervalで一定間隔で実行。)

こんな感じですよね。

言葉で書くとすごく簡単に見えるし、ひとつひとつは本当に基礎的な内容でそれの積み重ねでしかないんだけど、「はい、やって!」と言われたらググりながらでもすっと書くのは難しいですね。


こういうアイデアの引き出し(jsだけでゲームが作れる)が増えるので、xhack勉強会はおもしろいなーと思います!

せっかくなので、今回学んだことを踏まえたアウトプットを何かしら作ってみようと思います。


前回のレポを丁寧に書きすぎて今回手抜き感ありますが、これくらいをスタンダートにしないともたないので、このへんで。笑




この記事が気に入ったらサポートをしてみませんか?