Maze Ver.1.01(ゴール後に足跡を表示)

20131209a

迷路をクリアした後の全体表示に、プレーヤーの足跡を表示するようにしました。

右の画像をクリックすると実際の迷路ゲームを表示します。

JavaScriptのコレクション(またはコンテナ)と言うデータを格納する仕組み(高機能な配列)を使用し、プレーヤーの操作を記録してみました。

配列宣言は、maze101.js の21行目。

配列にセットする場合は、push()メソッドを使います。同91行。要素数はプレーヤーの操作により増え、メモリー限界まで記録可能。

ゴール後の、配列の要素数は、.length で取得できました。同193行。

足跡は緑色の四角とし、不透明度を50%にしたので、何度も行き来した部分は色が少しだけ濃くなります。

また、プレイ画面に合わせて、スタート地点には緑色の四角を、ゴール地点には黄色の四角を表示しました。

ゴール後のゲーム再開時(=迷路全体の画像をクリックした時)には、配列内に格納された要素を全て削除してやる必要がありました。配列を初期化する方法がよく解らず、とりあえずsplice()メソッドを使って全要素を削除し、要素数をゼロにしました。同60行。


ソースはこちら。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=594">
		<script type="text/javascript" src="maze101.js"></script>
		<style>
			* {
				margin: 0;
				padding: 0;
				text-align: center;
			}
			h1 {
				background-color: orange;
				font-size: 16px;
				color: white;
				padding: 8px;
			}
			#cvMain {
				width: 594px;
				height: 594px;
			}
		</style>
		<title>Maze</title>
	</head>
	<body>
		<div id="top_title"></div>
		<canvas id="cvMain" width="594" height="594"></canvas>
	</body>
</html>
// maze.js
// --------------------------------------------------
// 変数の初期化
var cvMain;	// キャンバス
var ctx;	// 描画用コンテキスト
var width = 33, height = 33;	// 迷路の広さ
var maze = [];	// 迷路データ
var px, py;	// プレイヤーの座標
var keyDir = {	// 押されたキーの向き
	38:[0,-1],40:[0,1],	// up down
	37:[-1,0],39:[1,0],	// left right
	75:[0,-1],74:[0,1],	// j k
	72:[-1,0],76:[1,0]	// h l
};
// ブロックの色
var blockColor = ["white","black","yellow"];
// タイトル文
var top_msg = "<h1>Maze</h1>";	// 迷路のタイトル
var clear_msg = "<h1>Maze GOAL!!</h1>";	// 迷路 クリア時のタイトル
var clearFlag = false;	// ゲームクリアフラグ
var tx = [] , ty = [];	// 足跡データ
// --------------------------------------------------
// 初期化処理
window.onload = function () {
	cvMain = document.getElementById("cvMain");
	ctx = cvMain.getContext("2d");
	document.onkeydown = keyHandler;
	cvMain.onmousedown = mouseHandler;
	initGame();
};
// 乱数の簡易メソッド
function rnd(n) {
	return Math.floor(Math.random()*n);
}
// ゲームの初期化メソッド
function initGame() {
	document.getElementById('top_title').innerHTML = top_msg;
	maze = makeMaze();
	px = 1; py = 1;
	drawMap();
}
// キーを押したとき
function keyHandler(e) {
	if (clearFlag==false) {	// ゲームクリアしていなければ
		var c = e.keyCode;
		var k = keyDir;
		if (!k) return;
		var x2 = px + keyDir[0];
		var y2 = py + keyDir[1];
		var b = checkMap(x2, y2);
		// ブラウザのイベントをキャンセル
		if (b) e.preventDefault();
	}
}
// マウスをクリックしたとき
function mouseHandler(e) {
	if (clearFlag) {	// ----- ゲームクリア後にマウスをクリックした場合の処理 -----
		clearFlag = false;
		ctx.globalAlpha = 1;	// 不透明度を指定
		tx.splice(0,tx.length); ty.splice(0,ty.length);	// 配列をクリア
		initGame();	// キャンバスをクリックしたら再度ゲームを実行
	}else{	// ----- プレイ中のマウス処理 -----
		e.preventDefault();
		var lx = e.layerX;	// クリック位置
		var ly = e.layerY;
		var ax = lx - (cvMain.width / 2);
		var ay = ly - (cvMain.height / 2);
		if (Math.abs(ax) > Math.abs(ay)) {
			var bx = (ax < 0) ? -1 : 1;
			checkMap(px + bx, py);
		} else {
			var by = (ay < 0) ? -1 : 1;
			checkMap(px, py + by);
		}
	}
}
// イベントのチェック
function checkMap(x2, y2) {
	if (!isInMaze(x2,y2)) return false;
	var c = maze[y2][x2];
	if (c == 1) return false;
	if (c == 2) {
		clearFlag = true;
		document.getElementById('top_title').innerHTML = clear_msg;	// ゲームクリア表示
		ctx.clearRect(0,0,cvMain.width,cvMain.height);	// キャンバスをクリア
		drawMaze(maze);	// 迷路全体を描画
		drawTrace(maze);	// 足跡を描画
		return false;
	}
	px = x2; py = y2;
	tx.push(px); ty.push(py);	//足跡データを記録
	drawMap();
}
// 迷路の作成メソッド
function makeMaze() {
	var maze = [];
	// 迷路の全てを壁にする
	for (var y = 0; y < height; y++) {
		maze[y] = [];
		for (var x = 0; x < width; x++) {
			maze[y][x] = 1;
		}
	}
	// 上下左右方向のリストをシャッフルして返す
	var makeDir4 = function () {
		var r = [[0,-1],[0,1],[-1,0],[1,0]];
		for (var i = 0; i < 4; i++) {
			var j = rnd(4);
			var t = r[i];
			r[i] = r[j];
			r[j] = t;
		}
		return r;
	};
	// 再帰的に迷路を作成する
	var recMake = function (x, y) {
		// ランダムに進む方向を決める
		var dir4 = makeDir4();
		for (var i = 0; i < 4; i++) {
			var dir = dir4[i];
			// 2マス先を調べる
			var x2 = dir[0] * 2 + x;
			var y2 = dir[1] * 2 + y;
			// 既に通路なら何もしない
			if (!isInMaze(x2,y2)) continue;
			if (maze[y2][x2] == 0) continue;
			// 2マス穴を掘る
			maze[y+dir[1]][x+dir[0]] = 0;
			maze[y2][x2] = 0;
			recMake(x2, y2);
		}
	};
	// 穴掘りの開始点を決める
	var cx = rnd((width -2)/2)*2+1;
	var cy = rnd((height -2)/2)*2+1;
	maze[cy][cx] = 0;
	recMake(cx,cy);
	// ゴール地点をセット
	maze [height-2][width-2] = 2;
	return maze;
}
// 座標が迷路の有効範囲にあるかどうか
function isInMaze(x,y) {
	return (0 <= x && x < width) && (0 <= y && y < height);
}
// マップの描画
function drawMap() {
	var cw = cvMain.width;
	var w = Math.floor(cw / 9);
	ctx.clearRect(0,0,w,w);
	// プレーヤーの周りを描画
	for (var y = 0; y < 9; y++) {
		for (var x = 0; x < 9; x++) {
			var dx = px + x - 4;
			var dy = py + y - 4;
			var b = 1;
			if (isInMaze(dx,dy)) {
				b = maze[dy][dx];
			}
			ctx.fillStyle = blockColor[b];
			ctx.fillRect(w*x, w*y, w, w);
		}
	}
	// キャラクターの描画
	ctx.fillStyle = "green";
	ctx.fillRect(w*4+10,w*4+10,w-20,w-20);
}

// 迷路全体を描画
function drawMaze(maze) {
	ctx.fillStyle = "black";
	var bw = cvMain.width / 33;
	for (var y = 0; y < maze.length; y++) {
		for (var x = 0; x < maze[y].length; x++) {
			if (maze[y][x]) {
				ctx.fillRect(x*bw, y*bw, bw, bw);
			}
		}
	}
	ctx.fillStyle = "yellow";	// 黄
	y = maze.length -2;
	x = maze.length -2;
	ctx.fillRect(x*bw, y*bw, bw, bw);	//ゴールを描画
}

// プレーヤーの足跡を描画
function drawTrace(maze) {
	var bw = cvMain.width / 33;
	ctx.fillStyle = 'green'; // 緑
	ctx.fillRect((1*bw)+5, (1*bw)+5, 8, 8);	//起点にも描画
	ctx.globalAlpha = 0.5;	// 不透明度を指定
	ctx.fillStyle = 'green'; // 緑
	for (var i = 0; i < tx.length; i++) {
		x = tx[i]; y = ty[i];
		ctx.fillRect((x*bw)+5, (y*bw)+5, 8, 8);
	}
}