キャンバスの上下左右の部分をクリックすることで、前進、右向き、左向き、後退りの動作になります。
キー操作の場合は↑→←↓
いま偶然発見しましたが、キーで操作していると画面が動いてしまう場合は、シフトキーを押しながら方向キーを押すと画面が動かず操作できることがわかりました。(そうはならないブラウザもあるみたい)
コードは以下のとおりです。
let mpos = []; // 区画の座標とブロックの有無(0 or 1) let rc = 21; // 区画の一辺の数(奇数が望ましい) let bs = 20; // 区画一辺の大きさ let cl; // 区画の色 let pos; let directions = []; let nemn; let emn = 1; // 初期の球の区画番号 let ed = 2; // 向きの初期値 let centerX, centerZ; // 注視点の座標 let eh = 150; // eyeの高さ let dis = 50; //視線の距離 let border1 = 100, border2 = 300; function setup() { createCanvas(400, 400, WEBGL); // すべての区画の座標を格納、yには初期値の0 for (let i = 0; i < rc * rc; i++) { let x = bs / 2 + (i % rc) * bs; let z = bs / 2 + Math.floor(i / rc) * bs; mpos.push(createVector(x, 0, z)); } // 周りの区画の y を 1(ブロックを置く) for (let i = 0; i < rc * rc; i++) { let row = Math.floor(i / rc); //行 let col = i % rc; //列 if (row == 0 || row == rc - 1 || col == 0 || col == rc - 1) { mpos[i].y = 1; } } // 1区画おきにブロックを置いて棒倒しで迷路作成 for (let i = 2; i < rc - 2; i += 2) { for (let j = 2; j < rc - 2; j += 2) { let num = i + j * rc; mpos[num].y = 1; createMaze(num); } } mpos[1].y = 0; // スタート位置はブロック無し mpos[rc * rc - 2].y = 0; // ゴール位置にも無し } function draw() { background(0); // 環境光(全体を少し明るくする) ambientLight(200, 200, 200); // ライトの設定(白色のライトを少し右斜め上から照射) directionalLight(100, 100, 100, 1, -1, -1); pos = mpos[emn]; getCenter(emn, ed); camera(pos.x, -eh, pos.z, centerX, 0, centerZ, 0, 1, 0); // 迷路のブロックを描画 for (let i = 0; i < rc * rc; i++) { pos = mpos[i]; if (pos.y == 1) { if ( i % 4 == 0) { cl = color(0, 100, 0); } else { cl = color(192, 192, 160); } stroke(0); strokeWeight(0.5); createBox(pos.x, 0, pos.z, bs, bs, bs, cl); } } pos = mpos[rc * rc -3]; cl = color(255,255,0); createBox(pos.x, -bs, pos.z, bs, bs, bs, cl); pos = mpos[rc * rc - 1]; cl = color(255,0,255); createBox(pos.x, -bs, pos.z, bs, bs, bs, cl); checkGoal(); } // 棒倒し法によりブロックを設置して迷路作成 function createMaze(num) { directions = []; // 配列を毎回リセット let north = num - rc; let east = num + 1; let south = num + rc; let west = num - 1; // 3行目のみ東西南北の4方向を調べる if (Math.floor(num / rc) == 2) { if (north >= 0 && mpos[north].y == 0) directions.push(north); if (east < rc * rc && mpos[east].y == 0) directions.push(east); if (south < rc * rc && mpos[south].y == 0) directions.push(south); if (west >= 0 && mpos[west].y == 0) directions.push(west); } else { // 4行目以下は北は選択肢から外す if (east < rc * rc && mpos[east].y == 0) directions.push(east); if (south < rc * rc && mpos[south].y == 0) directions.push(south); if (west >= 0 && mpos[west].y == 0) directions.push(west); } // リストからランダムに選択して壁を作る if (directions.length > 0) { let idx = floor(random(directions.length)); let chosen = directions[idx]; mpos[chosen].y = 1; } } // ボックスを作成する関数 function createBox(x, y, z, w, h, d, cl) { //fill(cl); push(); translate(x, y, z); ambientMaterial(cl); // 環境光の影響を受けるようにする box(w, h, d); pop(); } // 注視点の座標を取得 function getCenter(emn, ed) { pos = mpos[emn]; switch (ed) { case 0: // 北を向いているとき centerX = pos.x; centerZ = pos.z - dis; break; case 1: // 東を向いているとき centerX = pos.x + dis; centerZ = pos.z; break; case 2: // 南を向いているとき centerX = pos.x; centerZ = pos.z + dis; break; case 3: // 西を向いているとき centerX = pos.x - dis; centerZ = pos.z; break; } } function keyPressed() { switch (keyCode) { case UP_ARROW: // 前進 nextPosition(); getCenter(emn, ed); break; case RIGHT_ARROW: // 右に向く ed = (ed + 1) % 4; getCenter(emn, ed); break; case LEFT_ARROW: // 左に向く ed = (ed + 3) % 4; getCenter(emn, ed); break; case DOWN_ARROW: // 後退り backPosition(); getCenter(emn, ed); break; } } function mousePressed() { let clickedX = mouseX; let clickedY = mouseY; if (clickedY < border1 && clickedX > border1 && clickedX < border2) { // 上部をクリック nextPosition(); getCenter(emn, ed); } if (clickedX > border2 && clickedY > border1 && clickedY < border2) { // 右部をクリック ed = (ed + 1) % 4; // 右を向く getCenter(emn, ed); } if (clickedX < border1 && clickedY > border1 && clickedY < border2) { // 左部分をクリック ed = (ed + 3) % 4; // 左を向く getCenter(emn, ed); } if (clickedY > border2 && clickedX > border1 && clickedX < border2) { // 下部をクリック backPosition(); getCenter(emn, ed); } } // 進行方向の次の区画番号を取得 function nextPosition() { nemn = emn; // デフォルトは変更なし(動かない) switch (ed) { case 0: // 上向き if (emn >= rc) { // 上端でないとき nemn = emn - rc; } break; case 1: // 右向き if (emn % rc < rc - 1) { // 右端でないとき nemn = emn + 1; } break; case 2: // 下向き if (emn < rc * rc - rc) { // 下端でないとき nemn = emn + rc; } break; case 3: // 左向き if (emn % rc > 0) { // 左端でないとき nemn = emn - 1; } break; } // 移動先が壁でない場合のみ更新(移動) if (mpos[nemn].y == 0) { emn = nemn; } } function backPosition() { nemn = emn; // デフォルトは変更なし(動かない) switch (ed) { case 0: // 上向きのとき if (emn < rc * rc - rc) { // 下端でないとき nemn = emn + rc; } break; case 1: // 右向きのとき if (emn % rc > 0) { // 左端でないとき nemn = emn - 1; } break; case 2: // 下向きのとき if (emn >= rc) { // 上端でないとき nemn = emn - rc; } break; case 3: // 左向きのとき if (emn % rc < rc - 1) { // 右端でないとき nemn = emn + 1; } break; } // 移動先が壁でない場合のみ更新(移動) if (mpos[nemn].y == 0) { emn = nemn; } } // ゴールしたときの処理 function checkGoal() { if (emn == rc * rc - 2) { camera(pos.x -700, -eh, pos.z - 700, 0, 0, 0, 0, 1, 0); strokeWeight(10); stroke(255,0,0); line(0, 0, 0, 300, 0, 0); stroke(0,255,0); line(0, 0, 0, 0, 300, 0); stroke(0, 0, 255); line(0, 0, 0, 0, 0, 300); stroke(255,255,0); fill(0,0,0,50); strokeWeight(2); rotateY(frameCount * 0.02); sphere(100); } }