let wayToStr = (x,y) => ( 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*this.lenX + x]; } } let Kind = { None: 0, Player: 1, Enemy: 2, } class Actor { constructor(kind, x,y,image) { this.kind = kind; this.x = x; this.y = y; this.wx = 1; this.wy = 0; this.image = image; } } class Camera { constructor(x,y) { this.x = x; this.y = y; } } //////////////////////////////////////////////////////// class ComMove { /** * @param {Actor} actor 移動させたいアクター * @param {number} dx 何マス移動するか * @param {number} dy 何マス移動するか */ constructor(actor, dx, dy) { let t = this; t.actor = actor; t.dx = dx; t.dy = dy; t.beginX = -1; t.beginY = -1; t.endX = -1; t.endY = -1; /** 実行したフレーム数 */ t.f = 0; } /** * コマンドを1フレーム実行する */ exec() { let t = this; if (t.done) return t.done; //終了しているコマンドは実行しない t.f++; if (t.f === 1) { // 開始地点と終了地点の座標を計算 t.beginX = t.actor.x; t.beginY = t.actor.y; t.endX = t.actor.x + t.dx; t.endY = t.actor.y + t.dy; } if (game.level.tileAt(t.endX, t.endY) === 1) { t.f = 20; return t.done; } // ↑で計算した座標の間を移動する t.actor.x = t.beginX + t.f*t.dx/20; t.actor.y = t.beginY + t.f*t.dy/20; return t.done; } /** * @returns {boolean} コマンドが終了していればtrue, 実行中ならfalse */ get done() { return this.f >= 20; } } //////////////////////////////////////////////////////// class Game { constructor() { this.level = new Level(); this.player = null; this.actors = []; this.camera = new Camera(0,0); this.commands = []; } } let game; function setup() { game = new Game(); let player = new Actor(Kind.Player, 4,2,'🧙‍♀️'); let enemy = new Actor(Kind.Enemy, 2,1,'👻'); game.player = player; game.actors = [player, enemy]; 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 enemy of game.actors.filter(act => act.kind === Kind.Enemy)) { let ways = [[1,0], [0,1], [-1,0], [0,-1]]; // 壁に向かうベクトルを除外 ways = ways.filter(w => game.level.tileAt(enemy.x+w[0], enemy.y+w[1]) === 0); // 進行方向の逆のベクトルを除外 ways = ways.filter(w => !(w[0] === -enemy.wx && w[1] === -enemy.wy)); // 左手の方向 let left = leftOne(enemy.wx, enemy.wy); // 左手にあるタイル番号(0は通路、1は壁) let leftTileNum = game.level.tileAt(enemy.x+left[0], enemy.y+left[1]); console.log(...ways); game.commands.push(new ComMove(game.actors[1], enemy.wx, enemy.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); } } /*-------------------------------------------------- 10/23 enemyの動作アルゴリズム 壁に当たった場合(tileNum === 1) 進める方向見つかるまで、左回転 壁に当たらない場合(tileNum === 0) rランダム(0,1,2,3) tNU、tND、tNR、tNLの値取得(0,1) ❶左右移動中(enemy.wy === 0)  ①tNU === 0 || tND === 0 --> r0とr1はそのまま、r2は^、r3はv  ②tNU === 1 || tND === 0 --> r0とr1はそのまま、r2とr3はv  ③tNU === 0 || tND === 1 --> r0とr1はそのまま、r2とr3は^  ④tNU === 1 || tND === 1 --> そのまま ❷上下移動中(enemy.wx === 0)  ①tNR === 0 || tNL === 0 --> r0とr1はそのまま、r2は>、r3は<  ②tNR === 1 || tNL === 0 --> r0とr1はそのまま、r2とr3は<  ③tNR === 0 || tNL === 1 --> r0とr1はそのまま、r2とr3は>  ④tNR === 1 || tNL === 1 --> そのまま --------------------------------------------------*/