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();
}
}
}