let chip = 13 , chipSize = 60; let walls = []; let maze = []; class Wall { constructor(x, y) { this.x = x; this.y = y; } } let wayToStr = (x,y) => ( x === 0 && y === 0 ? '0' : x === 1 && y === 0 ? ">" : x === 0 && y === 1 ? 'v' : x === -1 && y === 0 ? '<' : x === 0 && y === -1 ? '^' : '?' ); let turnLeft = (x,y) => ( x === 1 && y === 0 ? [0, -1] : x === 0 && y === -1 ? [-1, 0] : x === -1 && y === 0 ? [0, 1] : x === 0 && y === 1 ? [1, 0] : [0, 0] ); let leftOne = (x,y) => { if (x === 1 && y === 0) return [0, -1]; if (x === 0 && y === 1) return [1, 0]; if (x === -1 && y === 0) return [0, 1]; if (x === 0 && y === -1) return [-1, 0]; }; class Level { constructor() { this.tiles = []; this.lenX = 0; this.lenY = 0; } tileAt(x, y) { if (x<0 || x>=this.lenX || y<0 || y>=this.lenY) return 1; return this.tiles[y][x]; } setTile(x, y, value) { this.tiles[y][x] = value; } clear(value) { for(let y=0; y= 20; } } //////////////////////////////////////////////////////// class Game { constructor() { this.level = new Level(); this.player = null; this.actors = []; this.camera = new Camera(0,0); this.commands = []; this.dirMapToPlayer = null; } } let game; // 幅優先探索でdirMapを計算する // 使用例 スタート地点を 4,2 ゴールを 4,5 としたい場合は // calcDirMap(4, 2, game.level) let calcDirMap = (startX, startY, level) => { let dirMap = new Level(); // デフォルトの大きさではなく、chipかけるchipにする dirMap.tiles = new Array(chip).fill(0).map(_ => new Array(chip).fill(0)); dirMap.lenX = chip; dirMap.lenY = chip; dirMap.clear([0,0]); //---- 幅優先探索 let posList = [[startX,startY]]; while(posList.length >= 1) { let pos = posList.shift(); let nextPosList = [ [pos[0]+1, pos[1]], [pos[0], pos[1]+1], [pos[0]-1, pos[1]], [pos[0], pos[1]-1] ]; for(let nextPos of nextPosList) { let dirAtNextPos = dirMap.tileAt(...nextPos); if (dirAtNextPos[0] !== 0 || dirAtNextPos[1] !== 0) continue; let tileNumAtNextPos = level.tileAt(...nextPos); if (tileNumAtNextPos !== 0) continue; let vec = [pos[0]-nextPos[0], pos[1]-nextPos[1]]; dirMap.setTile(...nextPos, vec); posList.push(nextPos); } } // dirMapをコンソールに出してみる(デバッグ用) let str = ''; for(let y=0; y wayToStr(...vec)).join(' '); str += '\n'; } console.log(str); return dirMap; }; //////////////////////////////////////////////////////// function setup() { //二次元配列初期化 for (let y = 0; y < chip; y++) { walls[y] = []; maze[y] = []; for (let x = 0; x < chip; x++) { walls[y][x] = 0; maze[y][x] = 0; } } //棒倒し法迷路設定 for (let b = 0; b < chip; b++) { for (let a = 0; a < chip; a++) { if(b === 0 || b === chip - 1 || a === 0 || a === chip - 1){ maze[b][a] = 1; }else{ if(b % 2 === 0 && a % 2 === 0){ maze[b][a] = 1; let c; b === 2 ? c = Math.floor(random(4)) : c = Math.floor(random(3)) c === 0 ? maze[b][a-1] = 1 //left : c === 1 ? maze[b][a+1] = 1 //right : c === 2 ? maze[b+1][a] = 1 //down : maze[b-1][a] = 1 //up } } } } //壁クラス設定 for (let q = 0; q < chip; q++) { for (let p = 0; p < chip; p++) { walls[q][p] = new Wall(p * chipSize, q * chipSize); } } game = new Game(); let player = new Actor(Kind.Player, 1,1,'🧙‍♀️', Behaviour.None); let enemy1 = new Actor(Kind.Enemy, 5,5,'👻', Behaviour.Lefthand); let enemy2 = new Actor(Kind.Enemy, 11,11,'🎃', Behaviour.Stalker); let sword = new Actor(Kind.Sword, 3,3, '🗡️', Behaviour.None); game.player = player; game.actors = [player, enemy1, enemy2, sword]; // 生成した迷路を適用 game.level.tiles = maze; game.level.lenX = chip; game.level.lenY = chip; createCanvas(800, 600); } function draw() { let w = chipSize; //////////////////////////////////////////////////////// // プレイヤーの入力を受け付ける if (keyIsPressed && game.commands.length === 0) { let dxy = {37:[-1,0], 38:[0,-1], 39:[1,0], 40:[0,1]}[keyCode]; if (dxy !== undefined) { game.player.wx = dxy[0]; game.player.wy = dxy[1]; game.commands.push(new ComMove(game.player, dxy[0], dxy[1])); game.dirMapToPlayer = calcDirMap(game.player.x, game.player.y, game.level); // 敵を動かす for(let enemy1 of game.actors.filter(act => act.kind === Kind.Enemy)) { if (enemy1.behaviour === Behaviour.Lefthand) { // 左手の方向 let left = leftOne(enemy1.wx, enemy1.wy); // 左手にあるタイル番号(0は通路、1は壁) let leftTileNum = game.level.tileAt(enemy1.x+left[0], enemy1.y+left[1]); // 進行方向の左手に壁がないとき、反時計回りに向きをかえる if (leftTileNum === 0) { let leftWay = turnLeft(enemy1.wx, enemy1.wy); enemy1.wx = leftWay[0]; enemy1.wy = leftWay[1]; } let frontTileNum = game.level.tileAt(enemy1.x+enemy1.wx, enemy1.y+enemy1.wy); // 進行方向に壁があるとき、時計回りに向きをかえる if (frontTileNum === 1) { // turnRight関数がないので、3回turnLeftして代用 let rightWay = [enemy1.wx, enemy1.wy]; for(let i=0; i<3; i++) { rightWay = turnLeft(...rightWay); } enemy1.wx = rightWay[0]; enemy1.wy = rightWay[1]; } } if (enemy1.behaviour === Behaviour.Stalker) { // 敵を方向マップに応じて(幅優先探索の結果に応じて)動かす let dirAtEnemyPos = game.dirMapToPlayer.tileAt(enemy1.x, enemy1.y); enemy1.wx = dirAtEnemyPos[0]; enemy1.wy = dirAtEnemyPos[1]; } // 敵の行き先 let destX = enemy1.x + enemy1.wx; let destY = enemy1.y + enemy1.wy; // プレイヤーの行き先(衝突を考慮する) let playerX; let playerY; // プレイヤーの行き先(衝突を考慮しない) let playerDestX = game.player.x + game.player.wx; let playerDestY = game.player.y + game.player.wy; if (game.level.tileAt(playerDestX, playerDestY) === 1) { // 壁にぶつかりそうなら元の座標 playerX = game.player.x; playerY = game.player.y; }else{ // 壁にぶつからない場合は、行き先の座標 playerX = playerDestX; playerY = playerDestY; } if (destX === playerX && destY === playerY) { // プレイヤーと衝突してしまいそう // 何もしない }else{ // 衝突しなさそう game.commands.push(new ComMove(enemy1, enemy1.wx, enemy1.wy)); } // game.commands.push(new ComMove(enemy1, enemy1.wx, enemy1.wy)); } } } // コマンドをすべて1フレーム分実行する for(let c of game.commands) { c.exec(); } let nPrevComs = game.commands.length; // 実行し終わったコマンドを消す game.commands = game.commands.filter(c => !c.done); let nCurrentComs = game.commands.length; if (nPrevComs >= 1 && nCurrentComs === 0) { // コマンドが0個になった瞬間に行う処理(アイテム拾得など) // 剣を拾う判定 if (game.commands.length === 0) { let items = game.actors .filter(act => act.kind === Kind.Sword) .filter(act => act.x === game.player.x && act.y === game.player.y); if (items.length === 1) { let item = items[0]; game.player.items.push(Kind.Sword); item.kind = Kind.None; } } } //////////////////////////////////////////////////////// let p = game.player; let c = game.camera; c.x = p.x - 9/2; c.y = p.y - 9/2; let cx = w * c.x; let cy = w * c.y; background(0); textAlign(LEFT, TOP); textSize(w * 7/8); for(let y=0; y