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 = [ [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], [1,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,1], [1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,1,1], [1,1,0,0,0,0,1,1,1,1,1,1,0,1,1,1,1,0,1,1], [1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,0,1,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1], [1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1], [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] ]; this.lenX = 20; this.lenY = 10; } 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 = []; } } let game; // 幅優先探索でdirMapを計算する // 使用例 スタート地点を 4,2 ゴールを 4,5 としたい場合は // calcDirMap(4, 2, 4, 5, game.level) let calcDirMap = (startX, startY, goalX, goalY, level) => { let dirMap = new Level(); 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 vec = [pos[0]-nextPos[0], pos[1]-nextPos[1]]; dirMap.setTile(...nextPos, vec); } } // dirMapをコンソールに出してみる(デバッグ用) let str = ''; for(let y=0; y wayToStr(...vec)).join(' '); str += '\n'; } console.log(str); return dirMap; }; function setup() { game = new Game(); let player = new Actor(Kind.Player, 4,2,'🧙‍♀️'); let enemy1 = new Actor(Kind.Enemy, 2,1,'👻'); let enemy2 = new Actor(Kind.Enemy, 1,1,'🎃'); game.player = player; game.actors = [player, enemy1, enemy2]; createCanvas(480, 480); } function draw() { let w = 60; //////////////////////////////////////////////////////// // プレイヤーの入力を受け付ける 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.commands.push(new ComMove(game.player, dxy[0], dxy[1])); // 敵を動かす for(let enemy1 of game.actors.filter(act => act.kind === Kind.Enemy)) { // 左手の方向 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]; } game.commands.push(new ComMove(enemy1, enemy1.wx, enemy1.wy)); } } } // コマンドをすべて1フレーム分実行する for(let c of game.commands) { c.exec(); } // 実行し終わったコマンドを消す game.commands = game.commands.filter(c => !c.done); //////////////////////////////////////////////////////// let p = game.player; let c = game.camera; c.x = p.x - 7/2; c.y = p.y - 7/2; let cx = w * c.x; let cy = w * c.y; background('Bisque'); textAlign(LEFT, TOP); textSize(w * 7/8); for(let y=0; y<10; y++){ for(let x=0; x<20; x++){ let t = game.level.tileAt(x,y); if(t === 1){ text('🟫', w*x-cx, w*y-cy); } } } for(let a of game.actors) { text(a.image, w*a.x-cx, w*a.y-cy); text(wayToStr(a.wx, a.wy), w*a.x-cx, w*a.y-cy); } } /*-------------------------------------------------- 11/06 enemyの動作アルゴリズム 進行方向の左手に壁がないとき、反時計回りに向きをかえる 進行方向に壁があるとき、時計回りに向きをかえる 反時計回りは、関数turnLeft 時計回りは、関数turnLeftを3回繰り返す --------------------------------------------------*/