神経衰弱ゲーム

Screenshot from 2014-06-27 18:00:25

日経ソフトウェア2014年8月号「HTML5でゲームを作ろう(第9回)」に掲載された「神経衰弱」です。

今回もプログラムソースには手を加えていません。

2015年7月26日追記・・・「sinsui.js」ファイル修正。
ゲームをクリアした後、2回目以降のプレイから、カウントダウンのスピードが早くなっていました。そうならないように改修しました。
・19行目・・・変数「timer」を追加。
・129行目・・・タイマーをクリアする関数コール。
・157行目・・・タイマーをクリアする関数コール。
・162行目・・・タイマーを変数に取り込み。

右の画像をクリックすると実際のゲームを表示します。
(プレイ中は音が出ますので御注意下さい。)

通常のトランプ(52枚)を使った神経衰弱とは違い、カードは2枚組×10種類で全20枚だけです。

ランダムに配られたカードの絵柄と位置と記憶して、全てのカードを、制限時間(60秒)以内に、ペアで表示できればクリアです。

・・・記憶力を鍛えるのには良いかも・・・。

resource

ゲームで使用している画像(resource.png)はこちら。

カードの絵(イラスト)は、昔入手した素材集「具満タン」から。GIMPでサイズを縮小し、適当に描いたカード枠に統合しました。カード裏側の葉の写真はGIMP標準添付の塗りつぶしパターンを利用。

プレイ中に再生される効果音「ピポンピポン」(正解時)と「ブー」(不正解時)も「具満タン」のものです。wav形式のファイルだったので、サウンド編集ソフト「Audacity」で読み込み、不要部分をカットし、mp3形式とogg形式に変換保存しました。



ソースはこちら。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=600">
		<title>タイム神経衰弱</title>
		<script type="text/javascript" src="sinsui.js"></script>
		<link rel="stylesheet" type="text/css" href="sinsui.css">
	</head>
	<body>
		<h1>神経衰弱</h1>
		<div>
			<span id="score">SCORE: 0</span>
			<span id="time">準備中です。少々お待ちください。</span>
		</div>
		<div>
			<canvas id="mainCanvas" width="400" height="480"></canvas>
		</div>
		<!-- 音声再生用 -->
		<audio id="nice">
			<source src="nice.mp3">
			<source src="nice.ogg">
		</audio>
		<audio id="ng">
			<source src="ng.mp3">
			<source src="ng.ogg">
		</audio>
	</body>
</html>
* {
	margin: 0;
	padding: 0;
	text-align: center;
}
h1 {
	font-size: 2em;
	background-color: yellow;
	color: blue;
}
#score {
	font-size: 1em;
	padding: 10px;
}
// 神経衰弱
// ----------------------------------------
// 定数
var RESOURCE_FILE = "resource.png";
var ROWS = 4;		// ステージの行数
var COLS = 5;		// ステージの列数
var CARD_H = 120;	// カードの高さ
var CARD_W = 80;	// カードの幅
var DEFAULT_TIME = 60;	// 残り時間
// 変数
var cards = [];	// カードの番号を記録
var opened = [];	// 開いたかどうかを記録
var resImage;
var ctx;
var selIndex;	// プレーヤーの選択した値
var score;		// スコア
var time;		// 残り時間
var lock;		// 連続リクック防止用
var timer;		// タイマー

// 初期化処理
window.onload = function () {
	// 描画コンテキストの取得
	var canvas = $("mainCanvas");
	ctx = canvas.getContext("2d");
	canvas.onmousedown = canvasMDHandler;
	// 画像ファイルの読み込み
	resImage = loadImage(RESOURCE_FILE, function () {
		initGame();
	});
};

// ゲームの初期化
function initGame() {
	selIndex = -1;	// 未選択
	score = 0;
	time = DEFAULT_TIME;
	$("score").innerHTML = "SCORE: O";
	initCards();
	drawStage();
	countTime();
}

// カードを初期化してシャッフルする
function initCards() {
	// 10ペアのカード20枚を配列に代入
	for (var i = 0; i < ROWS * COLS; i++) {
		cards[i] = 1 + Math.floor(i / 2);
	}
	for (var i = 0; i < cards.length; i++) {
		opened[i] = false;
	}
	// シャッフルする
	for (var i = 0; i < cards.length; i++) {
		var r = rand(cards.length);
		var tmp = cards[i];
		cards[i] = cards[r];
		cards[r] = tmp;
	}
}

// ステージを描画する
function drawStage() {
	// カードを一枚ずつ描画する
	for (var i = 0; i < cards.length; i++) {
		var no = cards[i];
		if (opened[i] == false && selIndex != i) {
			no = 0;
		}
		var row = Math.floor(i / COLS);
		var col = i % COLS;
		var y = CARD_H * row;
		var x = CARD_W * col;
		ctx.drawImage(resImage,
			no * CARD_W, 0 , CARD_W, CARD_H,
			x, y, CARD_W, CARD_H);
		// プレイヤーが選択中なら色をつける
		if (selIndex == i) {
			ctx.strokeSty1e = "rgba(255,100,100,0.5)";
			ctx.lineWidth = 2;
			ctx.strokeRect(x+2, y+2, CARD_W-4, CARD_H-4);
		}
	}
}

// マウスでカードを選んだ時のイベント
function canvasMDHandler(e) {
	// クリックした位置を得る
	var x = e.clientX;
	var y = e.clientY;
	var r = e.target.getBoundingClientRect();
	x -= r.left;
	y -= r.top;
	// どの位置のカードをクリックしたか判定
	var col = Math.floor(x / CARD_W);
	var row = Math.floor(y / CARD_H);
	pos = col + row *COLS;
	console.log("click=" + pos);
	clickCard(pos);
}

// プレイヤーがカードを選んだときの処理
function clickCard(pos) {
	// 既にオープンしたカードなら何もしない
	if (opened[pos]) return;
	if (lock) return;
	// 1枚目の選択か
	if (selIndex < 0) {
		selIndex = pos;
		drawStage();
		return;
	}
	// 2枚目の選択
	if (pos == selIndex) return;
	// 選択した2枚が合致するか?
	var c1 = cards[selIndex];
	var c2 = cards[pos];
	if (c1 == c2) {
		opened[selIndex] = true;
		opened[pos] = true;
		selIndex = -1;
		score += 2;
		$("score").innerHTML = "SCORE: " + score;
		drawStage();
		// クリア判定
		if (score >= cards.length) {
			setTimeout(function () {
				alert("GAME CLEAR!");
				clearTimeout(timer);
				initGame();
			},1);
		}
		$("nice").play();
	} else {
		// 間違い!1秒だけカードをプレイヤーに見せる
		opened[pos] = true;	// posのカードを表向きにセット
		drawStage();	// ステージを描画
		lock = true;
		setTimeout(function () {
			opened[pos] = false;	// カードを裏向きに戻す
			selIndex = -1;		// 1枚目のカードを未選択に
			drawStage();	// ステージを描画
			lock = false;
		},1000);
		$("ng").play();
	}
}

// タイマーのカウントダウン
function countTime() {
	if (score >= ROWS * COLS) return;
	time--;
	$("time").innerHTML = "TIME: " + time;
	// タイムアップ
	if (time <= 0) {
		alert("TIME UP...GAME OVER");
		clearTimeout(timer);
		initGame();
		return;
	}
	// 次回タイマーの設定
	timer = setTimeout(countTime, 1000);
}

// 画像を読み込む関数
function loadImage(fname, onload) {
	var image = new Image();
	image.src = fname;
	image.onload = onload;
	return image;
}
		
// 乱数生成用関数
function rand(n) {
	return Math.floor(Math.random() * n);
}		

// DOM要素を返す
function $(id) {
	return document.getElementById(id);
}