トップへ(mam-mam.net/)

JavaScript神経衰弱ゲーム|HTML5で簡単実装&コード公開

JavaScriptで神経衰弱ゲームを作ろう!HTML5&Canvasで簡単に実装

JavaScriptとHTML5 Canvasを使って、ブラウザで遊べる神経衰弱ゲームを作成しました。
マウスやタッチ操作でカードをめくるだけ!
初心者でもすぐに試せる、コピペOKのソースコード付きです。
ぜひあなたのWebサイトや学習教材に活用してみてください。

スマホ対応のポイント:メディアクエリでレイアウト調整

以下のようなCSSを使って、カードの幅をスマホサイズでは広めに設定しています。


<style>
.trumpdiv {
  width: 12.5%;
}
@media screen and (max-width: 560px) {
  .trumpdiv {
    width: 25%;
  }
}
</style>

こうすることでPCではカードが8列、スマホではカードが4列に並び操作しやすくなります。
画面の幅に応じて見た目を柔軟に変えるのが「レスポンシブデザイン」の基本です。
たとえば「もっと大きなカードがいい」「縦向きのときは3列にしたい」といった場合も、 この@mediaの中身を調整するだけで対応できます。
ぜひ自分なりのレイアウトを試してみてください!

このようなメディアクエリを活用すれば、ひとつのHTML&CSSファイルでPC・スマホどちらにも対応できます。
読みやすさや操作性を保つのは、ゲームでもWebアプリでも大切なポイント。
さらに工夫するなら、縦持ちと横持ちでレイアウトを切り替える方法や、カードサイズを画面幅に合わせて自動でスケーリングする方法などもあります。
初心者の方は、まずこのコードをベースに少しずつ応用していくのがオススメです。

ソースコード

全てのソースコードは以下です。

<div id="memorymain"></div>

<style>
.trumpdiv{
  position:relative;
  margin:0;
  padding:2px;box-sizing:border-box;
  width:12.5%;
  transform-style: preserve-3d;
  box-sizing:border-box;
}
@media screen and (max-width:560px){
  .trumpdiv{
    width:25%;
  }
}
</style>

<script>
/*
  this.elm //大外のDIVタグ
    this.trump[0~15].div //ひっくり返すDIV
      this.trump[0~15].imgf //表の画像
      this.trump[0~15].imgb //裏の画像
      this.trump[0~15].kind //0~7までの海苔の種類
      this.trump[0~15].status //0:裏 1:表
  this.turnnum //ひっくり返した数 0:ひっくり返してない 1:1枚前 2:2つともひっくり返した
  this.turn[0~1]  //ひっくり返した2つの札
  this.status //0:何もなし 1:1枚目ひっくり返し中 2:2枚目ひっくり返し中
  this.comp //完了時のDIVタグ
  this.counter //2枚を開けた回数
*/

class TMemory{
  init(){
    this.turn=[-1,-1];
    this.turnnum=0;
    this.status=0;
    this.counter=0;
    for(let i=0;i<this.trump.length;i++){
      this.trump[i].kind=-1;
      this.trump[i].status=0;//裏
    }
    for(let i=0;i<imgf.length;i++){
      let r=Math.floor(Math.random()*this.trump.length);
      while(this.trump[r].kind!=-1){
        r=Math.floor(Math.random()*this.trump.length);
      }
      this.trump[r].kind=i;
      r=Math.floor(Math.random()*this.trump.length);
      while(this.trump[r].kind!=-1){
        r=Math.floor(Math.random()*this.trump.length);
      }
      this.trump[r].kind=i;
    }
    for(let i=0;i<this.trump.length;i++){
      this.trump[i].imgf.src=imgf[ this.trump[i].kind ].src;
    }
  }
  constructor(elm){
    this.elm=elm;
    this.elm.style.cssText="display:flex;flex-wrap:wrap;width:100%;max-width:1000px;padding:4px;margin:0;box-sizing:border-box;";
    this.comp=document.createElement("div");
    this.comp.style.cssText="position:fixed;display:none;opacity:0;color:blue;left:5vw;right:5vw;text-align:center;top:calc(50vh - 1em);font-weight:bold;background:#fff;font-size:20px;border:4px solid black;cursor:pointer;";
    this.comp.innerHTML="";
    this.comp.addEventListener("click",function(){
      this.comp.style.display="none";
      this.init();
    }.bind(this));
    document.body.appendChild(this.comp);
    this.trump=[];
    for(let i=0;i<(imgf.length*2);i++){
      this.trump[i]=[];
      this.trump[i].div=document.createElement("div");
      this.trump[i].div.addEventListener("click",this.click.bind(this,i));
      this.trump[i].div.style.cssText="";
      this.trump[i].div.classList.add("trumpdiv");
      this.elm.appendChild(this.trump[i].div);
      this.trump[i].imgb=document.createElement("img");
      this.trump[i].imgb.src=imgb.src;
      this.trump[i].imgb.style="position:relative;width:100%;height:auto;transform;backface-visibility:hidden;";
      this.trump[i].div.appendChild(this.trump[i].imgb);
      this.trump[i].imgf=document.createElement("img");
      this.trump[i].imgf.style="position:absolute;width:calc(100% - 4px);height:auto;left:0;top:0;transform:rotateY(-180deg);backface-visibility: hidden;";
      this.trump[i].div.appendChild(this.trump[i].imgf);
    }
    this.init();
  }
  
  click(trumpid){
    if(this.turnnum<2 && this.trump[trumpid].status==0){
      this.trump[trumpid].status=1;
      this.turn[this.turnnum]=trumpid;
      this.turnnum++;
      this.trump[trumpid].div.animate(
        [ {transform:"rotateY(0deg)"}, {transform:"rotateY(180deg)"} ],
        {duration:500, fill:'both',}
      );
      if(this.turnnum==2){//2枚開けたとき
        this.counter++;
        if( this.trump[this.turn[0]].kind == this.trump[this.turn[1]].kind ){
          //2枚が同じ
          setTimeout(this.judge.bind(this),500);
        }else{
          //2枚が違う
          setTimeout(this.judge.bind(this),2000);
        }
      }
    }
  }
  judge(){
    if( this.trump[this.turn[0]].kind == this.trump[this.turn[1]].kind ){
      //2枚が同じ
      this.turnnum=0;
      let flag=true;
      for(let i=0;i<this.trump.length;i++){
        if(this.trump[i].status==0){
          flag=false;
          break;
        }
      }
      if(flag){
        //全部ひっくり返した
        //完了文字の表示
        this.comp.style.display='block';
        this.comp.innerHTML="おめでとう!<br>"+this.counter+"回でクリアー";
        this.comp.animate(
          [ {opacity:0},{opacity:1}],
          {delay:300,duration:700,fill:'both',}
        );
      }
    }else{
      //2枚が違った
      this.trump[this.turn[0]].status=0;
      this.trump[this.turn[1]].status=0;
      this.trump[this.turn[0]].div.animate(
        [ {transform:"rotateY(180deg)"}, {transform:"rotateY(0deg)"} ],
        {duration:500, fill:'both',}
      );
      this.trump[this.turn[1]].div.animate(
        [ {transform:"rotateY(180deg)"}, {transform:"rotateY(0deg)"} ],
        {duration:500, fill:'both',}
      );
      this.turnnum=0;
    }
  }
  goto(){
    location.href="https://www.shirako-nori.co.jp/";
  }
}
let urls=['./imgs/game_memory/back.png','./imgs/game_memory/0.png','./imgs/game_memory/1.png','./imgs/game_memory/2.png','./imgs/game_memory/3.png','./imgs/game_memory/4.png','./imgs/game_memory/5.png','./imgs/game_memory/6.png','./imgs/game_memory/7.png'];

let imgf=[];
let imgb=null;
let memory=null;
window.addEventListener("load",function(){
  loadImages();
});

function loadImages(){
  Promise.all(
    urls.map(function(url){
      return new Promise(function(resolve,reject){
        let img=new Image();
        img.onload=function(){resolve(img);}
        img.onerror=function(e){reject(e);}
        img.src=url;
      });
    })
  ).then(function(imgs){
      imgs.forEach(function(img,i){
        if(i==0){
          imgb=img;
        }else{
          imgf[i-1]=img;
        }
      });
      memory=new TMemory(document.querySelector("#memorymain"));
  }).catch(function(e){
     //1つでも画像ファイルがロードできなければエラー
      console.log('Error');
  });
}
</script>