神経衰弱ゲームとは?

プレイヤーは好きな2枚をその場で表に向ける。2枚が同じ数字であればそれらを得ることができ、もう一度プレイできる。2枚が異なる数の場合、カードを元通りに伏せて次のプレイヤーの順番となる。すべてのカードが取られるまで行い、取ったカードの枚数が多いプレイヤーの勝ちとなる。

引用ーWikipidia

操作方法

MAC、WINDOW : マウスでクリックすることでカードの裏返しができます。
Mobile : 画面をタッチすることでカードの裏返しができます。

始める前に

もし、ソースを参考にしてゲームを作ってみたい方は初めてのWeb基本を最初から一通り見てみるのをお勧めします。

実行結果

(見た目は環境によって違うこともあります。)

ダウンロード

ソースコード

<!DOCTYPE html>
<html>
<head>
	<title>Flip Cards</title>
	<meta charset="utf-8">
	<style>
		td.card {
			width: 100px;
			height: 140px;
			border: 1px solid blue;
			border-radius: 10px;
			text-align: center;
			font-size: 36px;
			box-shadow: rgb(128, 128, 128) 5px 5px;
		}
		td.back {
			background-image: url("card.png");
			background-size: 100px 140px;
		}
	</style>
	<script>
		"use strict";
		//配列シャッフル
		Array.prototype.shuffle = function() {

			var i = this.length;
			while (i) {
				var j = Math.floor(Math.random()*i);
				var t = this[--i];
				this[i] = this[j];
				this[j] = t;
			}
			return this;
		}

		// 広域変数
		var timer = NaN, score = 0, flipTimer, prevCard, startTime;

		//初期化関数
		function init() {
			var table = document.getElementById("table");
			var cards = [];
			for(var i=1; i <= 10; i++) {
				cards.push(i);
				cards.push(i);
			}
			cards.shuffle();

			for(var i=0; i<4; i++) {
				var tr = document.createElement("tr");

				for(var j=0; j<5; j++) {
					var td = document.createElement("td");
					td.className = "card back";
					td.number = cards[i*5 + j];
					td.onclick = flip;
					tr.appendChild(td);
				}
				table.appendChild(tr);
			}

			startTime = new Date();
			timer = setInterval(tick, 1000);
		}

		//経過時間測定用タイマー
		function tick() {
			var now = new Date();
			var elapsed = Math.floor((now.getTime() - startTime.getTime())/1000);
			document.getElementById("time").textContent = elapsed;
		}

		//カード裏返し
		function flip(e) {
			var src = e.srcElement;
			if(flipTimer || src.textContent != "") {
				return;
			} 

			var num = src.number;
			src.className = "card";
			src.textContent = num;

			switch (num) {
				case 1:
					src.style.backgroundImage = 'url("1.png")'
					break;
				case 2:
					src.style.backgroundImage = 'url("2.png")'
					break;
				case 3:
					src.style.backgroundImage = 'url("3.png")'
					break;
				case 4:
					src.style.backgroundImage = 'url("4.png")'
					break;
				case 5:
					src.style.backgroundImage = 'url("5.png")'
					break;
				case 6:
					src.style.backgroundImage = 'url("6.png")'
					break;
				case 7:
					src.style.backgroundImage = 'url("7.png")'
					break;
				case 8:
					src.style.backgroundImage = 'url("8.png")'
					break;
				case 9:
					src.style.backgroundImage = 'url("9.png")'
					break;
				case 10:
					src.style.backgroundImage = 'url("10.png")'
					break;
			}

			// 1枚目
			if(prevCard == null) {
				prevCard = src;
				return;
			}

			// 2枚目
			if(prevCard.number == num) {
				if(++score == 10) {
					clearInterval(timer);
				}
				prevCard = null;
				clearTimeout(flipTimer)
			} else {
				flipTimer = setTimeout(function() {
					src.className = "card back";
					src.textContent = "";
					prevCard.className = "card back"
					prevCard.textContent = "";
					prevCard.style.backgroundImage = 'url("card.png")'
					prevCard = null;
					flipTimer = NaN;
					src.style.backgroundImage = 'url("card.png")'
				}, 1000);
			}
		}
		
	</script>
</head>
<body onload="init()">
	<table id="table"></table>
	<h2>
		経過時間: <span id="time">0</span>
	</h2>
</body>
</html>

ソースコード解説

CSSから見ていきましょう。

  • 7列,16列 – <style>要素ではtd.card(カードの大きさ、フォントサイズなど)とtd.back(裏面)という二つのセレクタを定義しています。JavaScriptでは、これらのスタイルを動的に適用するために以下の様な処理を行っています。
element.className = "card back";
element.className = "card";

ここで”element”はtd要素を参照する変数です。プログラムの中ではtdという変数やsrcという変数が使用されています。

class属性をJavaScriptから設定する時は、classNameプロパティに値を代入します。代入する値を空白で区切ることにより、複数のセレクタを一度に設定していることに注目してください。

  • 17列 – ちなみに、「background-image: url(“card.png”);」は背景画像を設定するCSSスタイルです。
  • 24列 – 配列の要素をランダムに入れ帰るためのメソッドです。
  • 37列 – 使用されてる広域変数は以下の通りです。
変数 説明
timer 1秒ごとにtick()を呼び出すためのタイマー
fliptimer 2枚目にめくったカードをしばらく表示状態にしておくためのタイマー
score 何ペアー一致したか確認
prevCard 1枚目にめくったカード
startTimer 最初にゲームを開始した時間

このゲームでは2つのタイマーを使っていることに注意してください。

timerは経過時間を計測するためのものでsetInterval, clearIntervalで制御されます。一方、flipTimerは1秒後にカードを裏返すためのもので、setTimeout、clearTimeoutで制御されます。混乱しないように注意しましょう。

  • 40列 – 初期化処理init()関数が始まります。
  • 42列 ~ 47列 – 20枚(ペア x 10組)のカードをランダムに並べ替えています。
  • 49列 – そのあと、2重のfor分でカードを並べています。15puzzleと同じ処理を行ってますので、詳しくは15puzzleを参考してください。
  • 67列 ~ 71列 – tick()関数では、経過時間を表示します。
    • 68列 – newDate()で現在時間が取得できます。
    • 69列 – DateオブジェクトのgetTime()メソッドで、1970年1月1日00:00:00UTCからの経過ミリ秒を取得しています。ゲーム開始時の値との差分を取り、1000で割ることにより秒単位の経過時間を求め、画面に表示しています。
  • 74列 ~ 78列 – flipTimerが値を保持している間(2枚が表になってしばらく数字が表示されている間)、もしくはすでに表になったカードがクリックされた場合(「src.textContent != “”」)は、何も行わずreturenで戻ります。
  • 80列 ~ 82列 – ここではクリックされたカードを表にします。
  • 84列 ~ 115列 – カードが表を表示したときに表示する絵を選別します。
  • 118列 ~ 121列 – 「prevCard == null 」が成り立つ場合、すなわち、今クリックされたカードが1枚目だった時は、現在のカードをprevCardに代入し、returnで関数を抜けます。2枚目のクリックを持つためです。
  • 124列 ~ 129列 – 2枚目のクリックされたときに、1枚目と同じカードの時に行われます。10枚目になったときは全部のカードが裏返しになったので経過時間を計測するためのtimerを止めます。2枚のカードが同じだったので、prevCardにnull代入するとともに(=一枚目をクリア)、元に裏返すためのタイマーflipTimerを停止させます。
  • 130列 ~ 140列 – 2枚目のクリックされたときに、1枚目と異なるカードの時に行われます。 1秒後に無名関数が実行されます。つまり、1秒間二つの数字が表示された状態となります。無名関数のなかでは1枚目のカードprevCardと2枚目のカードに対して、classNameでスタイルを適用し、textContentに空文字を代入することで元の 例によって、処理はinit()関数から始まります。

イラストレータ

대해원さんから画像を受け取りました!
誠にありがとうございました!

Webゲーム開発

サイトメイン

プログラミングメイン