In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

"LIFE" (「命」)

Layout Editor (for Dijkstra Particle Streams (Ver.1.1)) で作成した例です
* http://wonderfl.net/code/425545196427ae8659973a8bfb13d54cfdeaf425
* ------------------------------------------------------------------
* 生命エネルギーが新たな命を生み出す瞬間を表現しました。
* だから何だ?と言われても、別に何でも無いのです。

------------------------------------------------------------------
* [更新内容]
* ・パフォーマンスを犠牲して、パーティクルを奇麗にしてみた
* ・パーティクルの軌跡を、滑らかに改善(+経路を塞ぐとフワフワ)
* ・パーティクルが複数のゴール候補から最短のゴールに向かって進むようになった
* ・微妙にコードの整理、微妙なバグを修正
* ------------------------------------------------------------------
* [いじりどころ]
* 下の方にあるParameterクラスに簡単にいじれる値をまとめてあります。
* 是非、Forkしていろいろ試してみてください!
* ------------------------------------------------------------------
* 後でレイアウトを編集するエディタを作る予定。

------------------------------------------------------------------
* 最短経路を進むパーティクル
* 
* [inspired by]
* Desktop Tower Defense
* http://www.handdrawngames.com/DesktopTD/
* Dijkstra Visualization
* http://wonderfl.net/code/6faaab5234abf034417a8e753f6309de0b9560f0
* and 神の書とwonderflのパーティクル作品群
* ---
/**
 * Copyright o8que ( http://wonderfl.net/user/o8que )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/mppu
 */

// forked from o8que's Dijkstra Particle Streams (Ver.1.1)
/*
 * Layout Editor (for Dijkstra Particle Streams (Ver.1.1)) で作成した例です
 * http://wonderfl.net/code/425545196427ae8659973a8bfb13d54cfdeaf425
 * ------------------------------------------------------------------
 * 生命エネルギーが新たな命を生み出す瞬間を表現しました。
 * だから何だ?と言われても、別に何でも無いのです。
 */

/* ------------------------------------------------------------------
 * [更新内容]
 * ・パフォーマンスを犠牲して、パーティクルを奇麗にしてみた
 * ・パーティクルの軌跡を、滑らかに改善(+経路を塞ぐとフワフワ)
 * ・パーティクルが複数のゴール候補から最短のゴールに向かって進むようになった
 * ・微妙にコードの整理、微妙なバグを修正
 * ------------------------------------------------------------------
 * [いじりどころ]
 * 下の方にあるParameterクラスに簡単にいじれる値をまとめてあります。
 * 是非、Forkしていろいろ試してみてください!
 * ------------------------------------------------------------------
 * 後でレイアウトを編集するエディタを作る予定。
 */

/* ------------------------------------------------------------------
 * 最短経路を進むパーティクル
 * 
 * [inspired by]
 * Desktop Tower Defense
 * http://www.handdrawngames.com/DesktopTD/
 * Dijkstra Visualization
 * http://wonderfl.net/code/6faaab5234abf034417a8e753f6309de0b9560f0
 * and 神の書とwonderflのパーティクル作品群
 * ------------------------------------------------------------------
 * [操作方法]
 * マウスのみ。
 * クリックすると壁を設置・除去できます。
 * ------------------------------------------------------------------
 * [簡単な説明]
 * main : 全体の初期化と更新処理
 * Node : マス目の情報
 * Wall : 壁の情報
 * Particle : パーティクルの情報
 * Start : パーティクルを出現させる場所です
 * Goal : パーティクルの目的地(ここで経路探索処理してます)
 * Parameter : ここをいじるといろいろ変えられます
 * ------------------------------------------------------------------
 */
package {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	
	[SWF(frameRate=30)]
	public class main extends Sprite {		
		public static const MAP_SIZE:int = 33;
		public static const NODE_SIZE:int = 15;
		
		// ノード、パーティクル、スタート、ゴールのコレクション
		public static var nodes:Array;
		public static var particles:Array;
		private var _starts:Array;
		private var _goals:Array;
		
		// パーティクル、壁、カーソルの表示オブジェクト
		private var _particleCanvas:BitmapData;
		private var _wallLayer:Sprite;
		private var _cursor:Shape;
		
		public function main() {
			Wonderfl.capture_delay(5);
			// ノード、パーティクル、壁、ゴール、スタート、カーソルの順に初期化する
			initializeNodes();
			initializeParticles();
			initializeWalls();
			initializeStreams();	
			initializeCursor();
			
			addEventListener(Event.ENTER_FRAME, update);
		}
		
		// ノードの初期化
		private function initializeNodes():void {
			main.nodes = [];
			for (var row:int = 0; row < main.MAP_SIZE; row++) {
				main.nodes[row] = [];
				for (var col:int = 0; col < main.MAP_SIZE; col++) {
					main.nodes[row][col] = new Node(col, row);
				}
			}
		}
		
		// パーティクルの初期化
		private function initializeParticles():void {
			Particle.createImages();
			
			particles = new Array(Parameter.PARTICLE_NUM);
			for (var i:int = 0; i < Parameter.PARTICLE_NUM; i++){
				particles[i]= new Particle();
			}
			
			var particleLayer:Bitmap = new Bitmap();
			_particleCanvas = new BitmapData(465, 465, false, 0x000000);
			particleLayer.bitmapData = _particleCanvas;
			addChild(particleLayer);
		}
		
		// 壁の初期化
		private function initializeWalls():void {
			_wallLayer = new Sprite();
			var walls:Array = Parameter.getWalls();
			var len:int = walls.length;
			for (var i:int = 0; i < len; i++) {
				_wallLayer.addChild(walls[i]);
			}
			addChild(_wallLayer);
		}
		
		// スタートノード、ゴールノードの初期化
		private function initializeStreams():void {
			_goals = Parameter.getGoals();
			_starts = Parameter.getStarts(_goals);
			
			// パーティクルの出現をばらつかせるためにシャッフルする
			shuffle(_starts);
		}
		
		// 配列をシャッフルする(Fisher-Yatesアルゴリズム)
		private function shuffle(arr:Array):void {
			var i:int = arr.length;
			var j:int;
			var tmp:*;
			
			while (i) {
				j = int(i * Math.random());
				
				tmp = arr[--i];
				arr[i] = arr[j];
				arr[j] = tmp;
			}
		}
		
		// カーソルの初期化
		private function initializeCursor():void {
			_cursor = new Shape();
			_cursor.graphics.lineStyle(1, 0xffff00);
			_cursor.graphics.drawRect(0, 0, main.NODE_SIZE * 2 - 0.5, main.NODE_SIZE * 2 - 0.5);
			addChild(_cursor);
			
			stage.addEventListener(MouseEvent.MOUSE_MOVE, moveCursor);
			stage.addEventListener(MouseEvent.CLICK, clickMap);
		}
		
		// カーソルの移動
		private function moveCursor(e:MouseEvent):void {
			_cursor.x = e.stageX - (e.stageX % main.NODE_SIZE);
			_cursor.y = e.stageY - (e.stageY % main.NODE_SIZE);
		}
		
		// マップクリック時の挙動
		private function clickMap(e:MouseEvent):void {
			// クリックした場所が除去できる壁なら除去する
			if (e.target is Wall) {
				if (e.target.removable) {
					e.target.remove();
					reSearchGoals();
				}
			// クリックした場所に壁が設置できるなら設置する
			}else {
				var tile:Point = Node.tileFromPos(new Point(e.stageX, e.stageY));
				if (Wall.buildable(tile.x, tile.y)) {
					_wallLayer.addChild(new Wall(tile.x, tile.y));
					reSearchGoals();
				}
			}
		}
		
		// 全てのゴールノードを更新(経路の最探索)をする
		private function reSearchGoals():void {
			var i:int;
			
			var len:int = _goals.length;
			for (i = 0; i < len; i++) {
				_goals[i].search();
			}
			
			// 各パーティクルの最短で到達できるゴールノードを更新する
			for (i = 0; i < Parameter.PARTICLE_NUM; i++) {
				if (main.particles[i].exists) {
					main.particles[i].updateNearestGoal();
				}
			}
		}
		
		// 毎フレームの更新処理
		private function update(e:Event):void {
			spawnParticles();	
			updateParticles();
		}
		
		// スタートノードからパーティクルを出現させる
		private function spawnParticles():void {
			var len:int = _starts.length;
			for (var i:int = 0; i < len; i++) {
				_starts[i].update();
			}
		}
		
		// パーティクルを更新と描画を行う
		private function updateParticles():void {
			_particleCanvas.lock();
			for (var i:int = 0; i < Parameter.PARTICLE_NUM; i++) {
				// パーティクルが画面内に存在していたら更新
				if(main.particles[i].exists){
					main.particles[i].update();
					main.particles[i].draw(_particleCanvas);
				}
			}
			_particleCanvas.colorTransform(_particleCanvas.rect, Parameter.CANVAS_COLOR_TRANSFORM);
			_particleCanvas.applyFilter(_particleCanvas, _particleCanvas.rect, _particleCanvas.rect.topLeft, Parameter.CANVAS_FILTER);
			_particleCanvas.unlock();
		}
	}
}

import flash.geom.Point;

class Node {
	private var _tilex:int;
	private var _tiley:int;
	private var _centerx:Number; 	// 中心のX座標
	private var _centery:Number; 	// 中心のY座標
	private var _passable:Boolean;	// (パーティクルが)通行可能かどうか
	
	public function get tileX():int { return _tilex; }
	public function get tileY():int { return _tiley; }
	public function get centerX():Number { return _centerx; }
	public function get centerY():Number { return _centery; }
	public function get passable():Boolean { return _passable; }
	public function set passable(arg:Boolean):void { _passable = arg; }
	
	public function Node(tilex:int, tiley:int) {
		_tilex = tilex;
		_tiley = tiley;
		var pos:Point = Node.posFromTile(new Point(_tilex, _tiley));
		_centerx = pos.x + (main.NODE_SIZE / 2);
		_centery = pos.y + (main.NODE_SIZE / 2);
		_passable = true;
	}
	
	// タイル縦横値から(有効な)XY座標値を求める
	public static function posFromTile(tile:Point):Point {
		var pos:Point = new Point();
		
		pos.x = (tile.x * main.NODE_SIZE) - main.NODE_SIZE;
		if (pos.x < -main.NODE_SIZE) {
			pos.x = -main.NODE_SIZE;
		}else if (pos.x > (main.NODE_SIZE * (main.MAP_SIZE - 1))) {
			pos.x = (main.NODE_SIZE * (main.MAP_SIZE - 1));
		}
		
		pos.y = (tile.y * main.NODE_SIZE) - main.NODE_SIZE;
		if (pos.y < -main.NODE_SIZE) {
			pos.y = -main.NODE_SIZE;
		}else if (pos.y > (main.NODE_SIZE * (main.MAP_SIZE - 1))) {
			pos.y = (main.NODE_SIZE * (main.MAP_SIZE - 1));
		}
		
		return pos;
	}
	
	// XY座標値から(有効な)タイル縦横値を求める
	public static function tileFromPos(pos:Point):Point {
		var tile:Point = new Point();
		
		tile.x = Math.floor(pos.x / main.NODE_SIZE) + 1;
		if (tile.x < 0) {
			tile.x = 0;
		}else if (tile.x > main.MAP_SIZE - 1) {
			tile.x = main.MAP_SIZE - 1;
		}
		
		tile.y = Math.floor(pos.y / main.NODE_SIZE) + 1;
		if (tile.y < 0) {
			tile.y = 0;
		}else if (tile.y > main.MAP_SIZE - 1) {
			tile.y = main.MAP_SIZE - 1;
		}
		
		return tile;
	}
}

import flash.display.Sprite;
import flash.geom.Point;

class Wall extends Sprite {
	private var _tilex:int;
	private var _tiley:int;
	private var _removable:Boolean;	// (クリックで)除去できるかどうか
	
	public function get removable():Boolean { return _removable; }
	
	public function Wall(tilex:int, tiley:int, removable:Boolean = true) {
		_tilex = tilex;
		_tiley = tiley;
		var pos:Point = Node.posFromTile(new Point(_tilex, _tiley));
		this.x = pos.x;
		this.y = pos.y;
		_removable = removable;
		
		setNodePassablity(false);
		draw();
	}
	
	// 壁を設置した部分のノードの通行可能性を変更する
	private function setNodePassablity(passable:Boolean):void {
		main.nodes[_tiley][_tilex].passable = passable;
		main.nodes[_tiley][_tilex + 1].passable = passable;
		main.nodes[_tiley + 1][_tilex].passable = passable;
		main.nodes[_tiley + 1][_tilex + 1].passable = passable;
	}
	
	// 壁の画像を描画する
	private function draw():void {
		var rectSize:Number = main.NODE_SIZE * 2;
		
		if (_removable) {
			rectSize -= 0.5;
			graphics.lineStyle(1, Parameter.WALL_BORDER_COLOR);
		}
		
		graphics.beginFill(Parameter.WALL_BASE_COLOR);
		graphics.drawRect(0, 0, rectSize, rectSize);
		graphics.endFill();
	}
	
	// 壁を取り除く際に呼ぶ関数
	public function remove():void {
		setNodePassablity(true);
		parent.removeChild(this);
	}
	
	// 指定した位置に壁が設置できるか
	public static function buildable(tilex:int, tiley:int):Boolean {
		return (main.nodes[tiley][tilex].passable &&
		main.nodes[tiley][tilex + 1].passable &&
		main.nodes[tiley + 1][tilex].passable &&
		main.nodes[tiley + 1][tilex + 1].passable);
	}
}

import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Shape;
import flash.geom.Matrix;
import flash.geom.Point;

class Particle {
	private var _posx:Number;		// X座標の位置
	private var _posy:Number;		// Y座標の位置
	private var _vx:Number;			// X方向の速度(ベクトル)
	private var _vy:Number;			// Y方向の速度(ベクトル)
	
	private var _speed:Number;		// 速さ(スカラー)
	private var _exists:Boolean;	// 画面上に存在するかどうか
	private var _start:Start;		// 出現したスタートノード
	private var _nearestGoal:Goal;	// 最短で到達できるゴールノード
	private var _nextNode:Node;		// 次に進むべきノード
	
	private static var _images:Array;
	private var _imageIndex:int;
	
	public function get exists():Boolean { return _exists; }
	
	public function Particle() {
		_posx = _posy = _vx = _vy = 0;
		_speed = ((Parameter.PARTICLE_MAXSPEED - 1) * Math.random()) + 1;
		_exists = false;
		_start = null;
		_nearestGoal = null;
		_nextNode = null;
		_imageIndex = 0;
	}
	
	// スタートノードから出現させる
	public function spawn(start:Start):void {
		_posx = start.node.centerX;
		_posy = start.node.centerY;
		_vx = _vy = 0;
		_exists = true;
		_start = start;
		_nearestGoal = _start.getNearestGoalFrom(start.node.tileX, start.node.tileY);
		_nextNode = _nearestGoal.getNext(start.node.tileX, start.node.tileY);
		_imageIndex = int(Parameter.PARTICLE_COLORS.length * Math.random());
	}
	
	public function update():void {
		_posx += _vx;
		_posy += _vy;
		
		var tile:Point = Node.tileFromPos(new Point(_posx, _posy));
		// ゴールに着いたか、移動不可能(かべのなかにいる)なら消滅する
		if (arrivedGoal(tile.x, tile.y) || !isMovable(tile.x, tile.y)) {
			_exists = false;
			return;
		}
		
		//次に進むべきノードへ到着していたら、次に進むべきノードを更新する
		if (arrivedNextNode(tile.x, tile.y)) {
			updateNextNode(tile.x, tile.y);
		}
		
		// ゴールノードへの経路が存在しないならフワフワする
		if (_nextNode == main.nodes[tile.y][tile.x]) {
			_vx = (_vx * 0.5) + (Math.random() - 0.5);
			_vy = (_vy * 0.5) + (Math.random() - 0.5);
		}else {
			var radians:Number = Math.atan2(_nextNode.centerY - _posy, _nextNode.centerX - _posx);
			_vx = (_vx + _speed * Math.cos(radians)) * 0.5;
			_vy = (_vy + _speed * Math.sin(radians)) * 0.5;
		}
	}
	
	// 最短で到達できるゴールノードを更新する
	public function updateNearestGoal():void {
		var tile:Point = Node.tileFromPos(new Point(_posx, _posy));
		_nearestGoal = _start.getNearestGoalFrom(tile.x, tile.y);
		
		updateNextNode(tile.x, tile.y);
	}
	
	// 次に進むべきノードを更新する
	private function updateNextNode(tilex:int, tiley:int):void {
		_nextNode = _nearestGoal.getNext(tilex, tiley);
	}
	
	// ゴールノードへ到着しているかどうか
	private function arrivedGoal(tilex:int, tiley:int):Boolean {
		return (tilex == _nearestGoal.node.tileX) && (tiley == _nearestGoal.node.tileY);
	}
	
	// パーティクルが移動できるかどうか
	private function isMovable(tilex:int, tiley:int):Boolean {
		return main.nodes[tiley][tilex].passable;
	}
	
	// 次に進むべきノードへ到着しているかどうか
	private function arrivedNextNode(tilex:int, tiley:int):Boolean {
		return  (_nextNode.tileX == tilex) && (_nextNode.tileY == tiley);
	}
	
	// canvasにパーティクルの画像を描画する
	public function draw(canvas:BitmapData):void {
		var matrix:Matrix = new Matrix();
		matrix.translate(_posx - Parameter.PARTICLE_RADIUS, _posy - Parameter.PARTICLE_RADIUS);
		canvas.draw(_images[_imageIndex], matrix, null, BlendMode.ADD);
	}
	
	// パーティクルの画像を予め作成しておく関数
	public static function createImages():void {
		_images = [];
		for (var i:int = 0; i < Parameter.PARTICLE_COLORS.length; i++) {
			var bitmapData:BitmapData = new BitmapData(Math.ceil(Parameter.PARTICLE_RADIUS * 2), Math.ceil(Parameter.PARTICLE_RADIUS * 2), true, 0x00ffffff);
			var shape:Shape = new Shape();
			shape.graphics.beginFill(Parameter.PARTICLE_COLORS[i]);
			shape.graphics.drawCircle(Parameter.PARTICLE_RADIUS, Parameter.PARTICLE_RADIUS, Parameter.PARTICLE_RADIUS);
			shape.graphics.endFill();
			bitmapData.draw(shape);
			_images.push(bitmapData);
		}
	}
}

class Start {
	private var _node:Node;
	private var _goalGroup:Array;	// ゴールノードのグループ
	private var _spawnCount:int;	// パーティクルを出現させる為のカウント
	
	public function get node():Node { return _node; }
	public function getNearestGoalFrom(tilex:int, tiley:int):Goal {
		var nearest:Goal = _goalGroup[0];
		var len:int = _goalGroup.length;
		for (var i:int = 1; i < len; i++) {
			if (_goalGroup[i].getCost(tilex, tiley) < nearest.getCost(tilex, tiley)) {
				nearest = _goalGroup[i];
			}
		}
		return nearest;
	}
	
	public function Start(node:Node, goalGroup:Array) {
		_node = node;
		_goalGroup = goalGroup;
		_spawnCount = int(Parameter.SPAWN_INTERVAL * Math.random());
	}
	
	public function update():void {
		// ゴールノードへの経路が存在しない場合は何もしない
		if (!hasPathToGoal()) { return; }
		
		// 一定の間隔でパーティクルの出現を試みる
		if (++_spawnCount >= Parameter.SPAWN_INTERVAL) {
			// 画面上に存在しないパーティクルがあれば、それをここから出現させる
			var len:int = main.particles.length;
			for (var i:int = 0; i < len; i++) {
				if (!main.particles[i].exists) {
					main.particles[i].spawn(this);
					_spawnCount = 0;
					break;
				}
			}
		}
	}
	
	// 最低一つのゴールノードへの経路が存在するかどうか
	private function hasPathToGoal():Boolean {
		var len:int = _goalGroup.length;
		for (var i:int = 0; i < len; i++) {
			if (_goalGroup[i].accessibleFrom(_node)) {
				return true;
			}
		}
		return false;
	}
}

class Goal {
	private static const DX:Array = [0, -1, 1, 0, -1, 1, -1, 1];
	private static const DY:Array = [ -1, 0, 0, 1, -1, -1, 1, 1];
	private static const DCOST:Array = [1, 1, 1, 1, Math.SQRT2, Math.SQRT2, Math.SQRT2, Math.SQRT2];
	
	private var _groupID:int;		// 所属するグループのID
	private var _node:Node;			// ゴールとする対象のノード
	
	private var _openNodes:Array;	// 保留ノードリスト
	private var _nodeCost:Array;	// 各ノードの移動コスト
	private var _nodeNext:Array;	// 各ノードの次の経路となるノード
	
	public function get groupID():int { return _groupID; }
	public function get node():Node { return _node; }
	public function getCost(tilex:int, tiley:int):Number { return _nodeCost[tiley][tilex]; }
	public function getNext(tilex:int, tiley:int):Node { return _nodeNext[tiley][tilex]; }
	
	public function Goal(groupID:int, node:Node) {
		_groupID = groupID;
		_node = node;
		
		_openNodes = [];
		_nodeCost = [];
		_nodeNext = [];
		for (var row:int = 0; row < main.MAP_SIZE; row++) {
			_nodeCost[row] = [];
			_nodeNext[row] = [];
		}
		
		search();
	}
	
	// ダイクストラ法による経路探索
	public function search():void {
		initialize();
		
		while (_openNodes.length > 0) {
			var subject:Node = _openNodes.pop() as Node;
			
			// 周囲8方向のノードを訪問する
			for (var i:int = 0; i < 8; i++) {
				// 画面外の存在しないノードを指すなら次の周囲ノードへ進む
				if (!isValid(subject.tileX + Goal.DX[i]) || !isValid(subject.tileY + Goal.DY[i])) { continue; }
				// 壁が設置されているノード、計算済み(確定)ノード、直進することができないノードなら次の周囲ノードへ進む
				var test:Node = main.nodes[subject.tileY + Goal.DY[i]][subject.tileX + Goal.DX[i]];
				if (isWall(test) || isCalculatedNode(test) || !canGoStraightTo(subject, test)) { continue; }
				
				// 移動コストを計算する
				_nodeCost[test.tileY][test.tileX] = _nodeCost[subject.tileY][subject.tileX] + Goal.DCOST[i];
				// 次の経路ノードをsubjectノードに設定する
				_nodeNext[test.tileY][test.tileX] = subject;
				// 保留ノードリストに追加する
				insertToOpenNodes(test);
			}
		}
	}
	
	private function initialize():void {
		for (var row:int = 0; row < main.MAP_SIZE; row++) {
			for (var col:int = 0; col < main.MAP_SIZE; col++) {
				_nodeCost[row][col] = int.MAX_VALUE;
				_nodeNext[row][col] = main.nodes[row][col];
			}
		}
		
		// Goalのノードを経路探索のスタートノードとする
		_nodeCost[_node.tileY][_node.tileX] = 0;
		_openNodes.push(main.nodes[_node.tileY][_node.tileX]);
	}
	
	// indexの値が有効な値かどうか
	private function isValid(index:int):Boolean {
		return ((index >= 0) && (index < main.MAP_SIZE));
	}
	
	// 既にコストを計算済みのノードかどうか
	private function isCalculatedNode(node:Node):Boolean {
		return _nodeCost[node.tileY][node.tileX] != int.MAX_VALUE;
	}
	
	// 壁が設置されたノードかどうか
	private function isWall(node:Node):Boolean {
		return !node.passable;
	}
	
	// subjectノードからtestノードへ直進できるかどうか
	private function canGoStraightTo(subject:Node, test:Node):Boolean {
		return (main.nodes[subject.tileY][test.tileX].passable && main.nodes[test.tileY][subject.tileX].passable);
	}
	
	// nodeを保留ノードリストの適切な場所に挿入する
	private function insertToOpenNodes(node:Node):void {
		var insertIndex:int;
		var len:int = _openNodes.length;
		var nodeCost:Number = _nodeCost[node.tileY][node.tileX];
		
		for (insertIndex = 0; insertIndex < len; insertIndex++) {
			var openNode:Node = _openNodes[insertIndex];
			if (nodeCost > _nodeCost[openNode.tileY][openNode.tileX]) { break; }
		}
		_openNodes.splice(insertIndex, 0, node);
	}
	
	// nodeからこのゴールノードに到達することができる(経路が存在する)かどうか
	public function accessibleFrom(node:Node):Boolean {
		return _nodeCost[node.tileY][node.tileX] != int.MAX_VALUE;
	}
}

import flash.filters.BitmapFilter;
import flash.filters.BlurFilter;
import flash.geom.ColorTransform;
import flash.geom.Point;

class Parameter {
	// パーティクルの最大数
	public static const PARTICLE_NUM:int = 400;
	/// パーティクルの半径
	public static const PARTICLE_RADIUS:Number = 12.0;
	// パーティクルの最大速度
	public static const PARTICLE_MAXSPEED:Number = 4.0;
	// パーティクルの色(種類)
	public static const PARTICLE_COLORS:Array = [0x048804];
	// パーティクルの軌跡の色の変化
	public static const CANVAS_COLOR_TRANSFORM:ColorTransform = new ColorTransform(0.9, 0.9, 0.9);
	// パーティクルを描画するキャンバスに適用するフィルタ
	public static const CANVAS_FILTER:BitmapFilter = new BlurFilter(2, 2, 1);
	// パーティクルの出現間隔
	public static const SPAWN_INTERVAL:int = 2;
	// 壁の色
	public static const WALL_BASE_COLOR:uint = 0x202020;
	// (除去可能な)壁の枠線の色
	public static const WALL_BORDER_COLOR:uint = 0x404040;
	
	private static const data:XML =
	<root>
	  <walls/>
	  <starts>
	    <start goal="0" x="16" y="3"/>
	    <start goal="1" x="16" y="3"/>
	    <start goal="2" x="10" y="13"/>
	    <start goal="3" x="9" y="18"/>
	    <start goal="4" x="9" y="18"/>
	    <start goal="5" x="14" y="18"/>
	    <start goal="6" x="18" y="18"/>
	    <start goal="7" x="18" y="18"/>
	    <start goal="8" x="24" y="18"/>
	    <start goal="5" x="9" y="23"/>
	    <start goal="9" x="24" y="25"/>
	  </starts>
	  <goals>
	    <goal id="2" x="22" y="13"/>
	    <goal id="0" x="4" y="15"/>
	    <goal id="1" x="28" y="15"/>
	    <goal id="4" x="14" y="18"/>
	    <goal id="7" x="24" y="18"/>
	    <goal id="3" x="9" y="23"/>
	    <goal id="5" x="14" y="23"/>
	    <goal id="9" x="22" y="23"/>
	    <goal id="8" x="24" y="25"/>
	    <goal id="6" x="18" y="29"/>
	  </goals>
	</root>;
	
	public static function getWalls():Array {
		var walls:Array = [];
		for each(var w:XML in Parameter.data.walls.*) {
			var removable:Boolean = ((w.@rem == "t") ? true : false);
			var wall:Wall = new Wall(int(w.@x), int(w.@y), removable);
			walls.push(wall);
		}
		return walls;
	}
	
	public static function getStarts(goals:Array):Array {
		var starts:Array = [];
		var len:int = goals.length;
		
		for each(var s:XML in Parameter.data.starts.*) {
			var groupID:int = int(s.@goal);
			var goalGroup:Array = [];
			for (var i:int = 0; i < len; i++) {
				if (goals[i].groupID == groupID) {
					goalGroup.push(goals[i]);
				}
			}
			starts.push(new Start(main.nodes[int(s.@y)][int(s.@x)], goalGroup));
		}
		return starts;
	}
	
	public static function getGoals():Array {
		var goals:Array = [];
		for each(var g:XML in Parameter.data.goals.*) {
			var node:Node = main.nodes[int(g.@y)][int(g.@x)];
			var goal:Goal = new Goal(int(g.@id), node);
			goals.push(goal);
		}
		return goals;
	}
}