迷路をクリアした後の全体表示に、プレーヤーの足跡を表示するようにしました。
右の画像をクリックすると実際の迷路ゲームを表示します。
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);
}
}