AS3でゲームを作成する その40 | Photoshop CC Tutorials
今回は前回作成した経路探索アルゴリズムのプログラムの壁の配置を
さらに変えて迷路にしてみました。
敵キャラ君はこの迷路を解くことができるのでしょうか。(;^_^ A

できあがりはこちらをクリック

実行結果
$ピック社長のブログ

Display.as
package 
{
/**
* 経路探索アルゴリズム
*/
import flash.display.BitmapData;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.media.Sound;
import flash.net.URLRequest;

public class Display
{
private const MAP_WIDTH:int = 15;/* マップのサイズ */
private const MAP_HEIGHT:int = 15;

private const ROBO_XPOS:int = 0;/* ロボットの位置 */
private const ROBO_YPOS:int = 14;

private const GOAL_XPOS:int = 14;/* ゴールの位置 */
private const GOAL_YPOS:int = 2;

private const ATTR_EMPTY:int = 0;/* マップの1マスぶんの内容 */
private const ATTR_SEARCH:int = 1;
private const ATTR_CLOSE:int = 2;
private const ATTR_WALL:int = 3;
private const ATTR_GOAL:int = 4;
private const ATTR_START:int = 5;
private const ATTR_PATH:int = 6;

/* マップ全体を示すmapfield配列 */
private var mapfield:Array = [MAP_HEIGHT];
/* 探索地点を集めるバッファ */
private const SEARCHQUE_LIMIT:int = 50;
private var searchque:Array = [SEARCHQUE_LIMIT];
private var searchque_head:int = 0;

// 敵キャラの初期位置
public var charX:int = ROBO_XPOS * 32;
public var charY:int = (ROBO_YPOS - 1) * 32;

private var tile:Vector.;
private var tile_char:Vector.;
private var charAnimTime:int = 0;

// 敵キャラのパス
private var enemyPath:Array = new Array();

public var keyState:int = 1; // 敵キャラの向き

public function Display(source_01:BitmapData, source_02:BitmapData)
{
map_init();
root_search(ROBO_XPOS, ROBO_YPOS, GOAL_XPOS, GOAL_YPOS);

// コンピュータが動くために必要なノードをmoveInfo[]に格納する
var nextx:int = mapfield[GOAL_YPOS][GOAL_XPOS].backpos.x;
var nexty:int = mapfield[GOAL_YPOS][GOAL_XPOS].backpos.y;
enemyPath.push(mapfield[nexty][nextx]);
do {
var x:int = nextx;
var y:int = nexty;
mapfield[y][x].attr = ATTR_PATH;
nextx = mapfield[y][x].backpos.x;
nexty = mapfield[y][x].backpos.y;
enemyPath.push(mapfield[nexty][nextx]);
} while(mapfield[nexty][nextx].attr != ATTR_START);

// コストを得る
/*for (i = 0; i < MAP_HEIGHT; i++ ) {
for (j = 0; j < MAP_WIDTH; j++ ) {
var cost:int = poscost(mapfield[i][j].pos, mapfield[GOAL_YPOS][GOAL_XPOS].pos);
trace(cost);
}
}*/

tile = new Vector.;
tile.length = 40;
tile.fixed = true;
tile_char = new Vector.;
tile_char.length = 12;
tile_char.fixed = true;

for (var i:int = 0; i < 40; i++) {
tile[i] = new BitmapData(32, 32);
}

for (var j:int = 0; j < 4; j++){
for (i = 0; i < 10;i++){
tile[j*10+i].copyPixels(source_01, new Rectangle(i*32, j*32, i*32+32, j*32+32), new Point(0, 0));
}
}

// キャラクター
for (i = 0; i < 12; i++) {
tile_char[i] = new BitmapData(32, 64);
}

var k:int = 0;
var l:int = 64;
for (i = 0; i < 12; i += 3) {
tile_char[i + 0].copyPixels(source_02, new Rectangle( 0, k, 32, l), new Point(0, 0));
tile_char[i + 1].copyPixels(source_02, new Rectangle(32, k, 64, l), new Point(0, 0));
tile_char[i + 2].copyPixels(source_02, new Rectangle(64, k, 96, l), new Point(0, 0));

k += 64;
l += 64;
}
}

/* マップの初期化 */
public function map_init():void
{
var j:int, i:int;

for (i = 0; i < MAP_HEIGHT; i++) {
mapfield[i] = new Array(MAP_WIDTH);
for (j = 0; j < MAP_WIDTH; j++) {
mapfield[i][j] = new t_node();
mapfield[i][j].pos.x = j;
mapfield[i][j].pos.y = i;
}
}

/* 障害物の設定 */
/*for (i = 0; i < 4; i++) {
mapfield[i][10].attr = ATTR_WALL;
mapfield[(MAP_HEIGHT - 1) - i][4].attr = ATTR_WALL;
}*/

for (i = 0; i < 15; i++ ) {
for (j = 0; j < 15; j++ ) {
if (i == 0) {
mapfield[i][j].attr = ATTR_WALL;
}else if (i == 1) {
if (j == 0 || j == 10 || j == 12 || j == 14)
mapfield[i][j].attr = ATTR_WALL;
}else if (i == 2) {
if (j == 0 || j >= 2 && j <= 8 || j == 10 || j == 12 || j == 14)
mapfield[i][j].attr = ATTR_WALL;
}else if (i == 3) {
if (j == 0 || j == 8 || j == 10 || j == 12 || j == 14)
mapfield[i][j].attr = ATTR_WALL;
}else if (i == 4) {
if (j == 0|| j >= 2 && j <= 6 || j == 8 || j == 10 || j == 12 || j == 14)
mapfield[i][j].attr = ATTR_WALL;
}else if (i == 5) {
if (j == 0 || j == 6 || j == 8 || j == 10 || j == 12 || j == 14)
mapfield[i][j].attr = ATTR_WALL;
}else if (i == 6) {
if (j == 0 || j == 2 || j == 3 || j == 4 || j == 6 || j == 8 || j == 10 || j == 12 || j == 14)
mapfield[i][j].attr = ATTR_WALL;
}else if (i == 7) {
if (j == 0 || j == 4 || j == 6 || j == 7 || j == 8 || j == 10 || j == 12 || j == 14)
mapfield[i][j].attr = ATTR_WALL;
}else if (i == 8) {
if (j == 0 || j == 1 || j == 2 || j == 4 || j == 6 || j == 10 || j == 14)
mapfield[i][j].attr = ATTR_WALL;
}else if (i == 9) {
if (j == 0 || j == 4 || j == 6 || j >= 8 && j <= 12 || j == 14)
mapfield[i][j].attr = ATTR_WALL;
}else if (i == 10) {
if (j == 0 || j >=2 && j <= 6 || j == 12 || j == 14)
mapfield[i][j].attr = ATTR_WALL;
}else if (i == 11) {
if (j == 0 || j == 5 || j >= 8 && j <= 12 || j == 14)
mapfield[i][j].attr = ATTR_WALL;
}else if (i == 12) {
if (j >= 0 && j <= 3 || j == 5 || j == 8 || j == 14)
mapfield[i][j].attr = ATTR_WALL;
}else if (i == 13) {
if (j == 0 || j == 5 || j == 14)
mapfield[i][j].attr = ATTR_WALL;
}else if (i == 14) {
if(j != 0 && j != 1)
mapfield[i][j].attr = ATTR_WALL;
}
}
}
}

/* -------------------------------------------------------- */
/* 2点間の距離を測る */
public function distance(pos1:t_point, pos2:t_point):int
{
var dx:int, dy:int, d:int;

dx = Math.abs(pos1.x - pos2.x);
dy = Math.abs(pos1.y - pos2.y);

d = ((dx < dy) ? dx : dy) / 2;
return dx + dy - d;
}

/* コストを計る */
public function poscost(pos:t_point, checkpos:t_point):int
{
var cost:int;

/* 2点間の距離をそのままコストに */
cost = distance(pos, checkpos);
return cost;
}

/* -------------------------------------------------------- */
/* キューに探索候補を追加 */
public function que_push(node:t_node):void
{
var tmpnode:t_node;
var i:int, j:int;

if (searchque_head >= SEARCHQUE_LIMIT)
return;
searchque[searchque_head] = node;
searchque_head++;

// コストの低い順でソートしておく
if (searchque_head <= 1)
return;
for (i = 0; i < searchque_head; i++) {
for (j = 1; j < searchque_head - i; j++) {
if (searchque[j - 1].cost < searchque[j].cost) {
tmpnode = searchque[j - 1];
searchque[j - 1] = searchque[j];
searchque[j] = tmpnode;
}
}
}
}

/* キューから探索候補を取り出す */
public function que_pop():t_node
{
if (searchque_head == 0)
return null;
searchque_head--;
return searchque[searchque_head];
}

/* -------------------------------------------------------- */
/* 探索されたパスをたどる */
public function path_check(goaly:int, goalx:int):void
{
var x:int, y:int, nextx:int, nexty:int;

nextx = mapfield[goaly][goalx].backpos.x;
nexty = mapfield[goaly][goalx].backpos.y;
do {
x = nextx;
y = nexty;
mapfield[y][x].attr = ATTR_PATH;
nextx = mapfield[y][x].backpos.x;
nexty = mapfield[y][x].backpos.y;
} while(mapfield[nexty][nextx].attr != ATTR_START);
}


/* -------------------------------------------------------- */
/* -------------------------------------------------------- */
/* 探索本体 */
public function root_search(startx:int, starty:int, goalx:int, goaly:int):void
{
/* 8方向を調べるための相対座標 */
var difpos:Array = [ { x: -1, y: -1 }, { x: 0, y: -1 }, { x:1, y: -1 },
{ x: -1, y: 0 }, { x: 1, y: 0 },
{ x: -1, y: 1 }, { x: 0, y: 1 }, { x:1, y: 1 } ];

var i:int, checkx:int, checky:int, cost:int, checkcount:int;
var checkxCopy:int, checkyCopy:int;
var nowpos:t_node;
searchque_head = 0;

// ゴールとスタートの設定
mapfield[starty][startx].attr = ATTR_START;
mapfield[starty][startx].cost = 200;
mapfield[goaly][goalx].attr = ATTR_GOAL;

// スタート位置をキューに入れる
que_push(mapfield[starty][startx]);

// checknodeがゴールに行くまでループ
do {
// 調べる位置をキューから取り出す
nowpos = que_pop();
if(nowpos == null) {
trace("ゴールに到達できません。\n");
return;
}

// 8方向調べる
checkcount = 0;
for (i = 0; i < 8; i++) {
checkxCopy = nowpos.pos.x + difpos[i].x;
checkyCopy = nowpos.pos.y + difpos[i].y;

// 範囲チェック
if ((checkxCopy < 0) || (checkxCopy >= MAP_WIDTH) || (checkyCopy < 0) || (checkyCopy >= MAP_HEIGHT))
continue;

checkx = checkxCopy;
checky = checkyCopy;

// ゴールだったら抜ける
if (mapfield[checky][checkx].attr == ATTR_GOAL) {
mapfield[checky][checkx].backpos.x = nowpos.pos.x;
mapfield[checky][checkx].backpos.y = nowpos.pos.y;
break;
}

// 壁や調査済みだったらその位置は使わない
if (mapfield[checky][checkx].attr != ATTR_EMPTY)
continue;

// コストを得る
cost = poscost(mapfield[checky][checkx].pos,
mapfield[goaly][goalx].pos);

// コストをそこに置いて次の調査へ
mapfield[checky][checkx].attr = ATTR_SEARCH;
mapfield[checky][checkx].cost = cost;
mapfield[checky][checkx].backpos.x = nowpos.pos.x;
mapfield[checky][checkx].backpos.y = nowpos.pos.y;
que_push(mapfield[checky][checkx]);
checkcount++;
}

// 1回も周囲を調べられなかったら,今後その位置は使わない
if ((!checkcount) && (nowpos.attr == ATTR_SEARCH))
nowpos.attr = ATTR_CLOSE;
if (nowpos.attr == ATTR_EMPTY)
nowpos.attr = ATTR_SEARCH;
}
while(mapfield[checky][checkx].attr != ATTR_GOAL);
path_check(goaly, goalx);
}

private var moveCnt:int = 0;
private var cnt:int = enemyPath.length - 1;
public function enemyMove():void
{
moveCnt++;

if (3 < moveCnt % 5) {
if (cnt < 0) {
charX = ROBO_XPOS * 32;
charY = (ROBO_YPOS - 1) * 32;
cnt = enemyPath.length - 1;
}else {
charX = enemyPath[cnt].pos.x * 32;
charY = (enemyPath[cnt].pos.y - 1) * 32;
cnt--;
}
}
}

public function render(output:BitmapData):void
{
var n:int;
var rect:Rectangle = new Rectangle(0, 0, 32, 32);
var anime:int;
output.lock();
// 画面を塗りつぶす
output.fillRect(new Rectangle(0, 0, 465 , 465), 0xffffff);

for (var i:int = 0; i < MAP_HEIGHT; i++ ) {
for (var j:int = 0; j < MAP_WIDTH; j++) {
if (mapfield[i][j].attr == ATTR_WALL) {
output.copyPixels(tile[1], rect, new Point(j * 32, i * 32));
}else if (mapfield[i][j].attr == ATTR_GOAL) {
output.copyPixels(tile[39], rect, new Point(j * 32, i * 32));
}else {
output.copyPixels(tile[0], rect, new Point(j * 32, i * 32));
}
}
}

// キャラクターのアニメーション
if (charAnimTime++ > 100000) {
charAnimTime = 0;
}
anime = charAnimTime % 40;

switch(this.keyState) {
case 0: // 上
if (anime >= 0 && anime < 10) { output.copyPixels(tile_char[1], new Rectangle(0, 0, 32, 64), new Point(charX, charY), null, null, true); }
else if (anime >= 10 && anime < 20) { output.copyPixels(tile_char[0], new Rectangle(0, 0, 32, 64), new Point(charX, charY), null, null, true); }
else if (anime >= 20 && anime < 30) { output.copyPixels(tile_char[1], new Rectangle(0, 0, 32, 64), new Point(charX, charY), null, null, true); }
else if (anime >= 30 && anime < 40) { output.copyPixels(tile_char[2], new Rectangle(0, 0, 32, 64), new Point(charX, charY), null, null, true); }
break;

case 1: // 右
if (anime >= 0 && anime < 10) { output.copyPixels(tile_char[4], new Rectangle(0, 0, 32, 64), new Point(charX, charY), null, null, true); }
else if (anime >= 10 && anime < 20) { output.copyPixels(tile_char[3], new Rectangle(0, 0, 32, 64), new Point(charX, charY), null, null, true); }
else if (anime >= 20 && anime < 30) { output.copyPixels(tile_char[4], new Rectangle(0, 0, 32, 64), new Point(charX, charY), null, null, true); }
else if (anime >= 30 && anime < 40) { output.copyPixels(tile_char[5], new Rectangle(0, 0, 32, 64), new Point(charX, charY), null, null, true); }
break;

case 2: // 下
if (anime >= 0 && anime < 10) { output.copyPixels(tile_char[7], new Rectangle(0, 0, 32, 64), new Point(charX, charY), null, null, true); }
else if (anime >= 10 && anime < 20) { output.copyPixels(tile_char[6], new Rectangle(0, 0, 32, 64), new Point(charX, charY), null, null, true); }
else if (anime >= 20 && anime < 30) { output.copyPixels(tile_char[7], new Rectangle(0, 0, 32, 64), new Point(charX, charY), null, null, true); }
else if (anime >= 30 && anime < 40) { output.copyPixels(tile_char[8], new Rectangle(0, 0, 32, 64), new Point(charX, charY), null, null, true); }
break;
case 3: // 左
if (anime >= 0 && anime < 10) { output.copyPixels(tile_char[10], new Rectangle(0, 0, 32, 64), new Point(charX, charY), null, null, true); }
else if (anime >= 10 && anime < 20) { output.copyPixels(tile_char[9], new Rectangle(0, 0, 32, 64), new Point(charX, charY), null, null, true); }
else if (anime >= 20 && anime < 30) { output.copyPixels(tile_char[10], new Rectangle(0, 0, 32, 64), new Point(charX, charY), null, null, true); }
else if (anime >= 30 && anime < 40) { output.copyPixels(tile_char[11], new Rectangle(0, 0, 32, 64), new Point(charX, charY), null, null, true); }
break;
}

output.unlock();
}
}

}