「迷路」は英語で「maze(メイズ)」と言うようです。なので、パズルとしての迷路は、迷図(めいず)という当て字が使われるらしいです。
今回も日経ソフトウェア2014年1月号「HTML5でゲームを作ろう」に掲載されたものです。
右の画像をクリックすると実際のゲームを表示します。プレーヤー(■)を移動させるのはマウスでもできますが、上下左右のカーソルキーの方が扱いやすいようです。
そうそう、スタートは左上からで、ゴール地点は右下になっています。
重要な部分は全く手を付けていませんが、部分的に改造しました。
- ゲームクリア後に迷路全体を表示するように変更しました。本当は足跡を表示したかったのですが、理解力が足りないので断念。
- ゲームクリア後のポップアップ(alert)表示「ゴール!」を止め、タイトル部分に「Maze GOAL!!」を表示。画面をクリックするとゲームをリセットし再開します。
ソースはこちら。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=594">
<script type="text/javascript" src="maze.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; // ゲームクリアフラグ
// --------------------------------------------------
// 初期化処理
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;
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.fillStyle = "white"; // キャンバスをクリア
ctx.fillRect(0,0,cvMain.width,cvMain.height);
drawMaze(maze); // 迷路全体を描画
return false;
}
px = x2; py = y2;
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) {
var cvMain = document.getElementById("cvMain");
var ctx = cvMain.getContext("2d");
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);
}
}
}
}