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>
