※現在、「wonderfl build flash online」求人コンテンツ制作に関してのアンケートを実施中です!みなさまのお力添えを頂いて、続々とアンケート結果が集まっていますが、まだまだ募集しております。ご協力のほど、どうぞよろしくお願いいたします!

wonderfl運営事務局
→アンケートページ(※ログインしてからお答えいただけるようになっています。)

 notice: Flash editor updated! Join the development! Thanks to MiniBuilder


FORKED
  1. /* ------------------------------------------------------------------
  2.  * [更新内容]
  3.  * ・パフォーマンスを犠牲して、パーティクルを奇麗にしてみた
  4.  * ・パーティクルの軌跡を、滑らかに改善(+経路を塞ぐとフワフワ)
  5.  * ・パーティクルが複数のゴール候補から最短のゴールに向かって進むようになった
  6.  * ・微妙にコードの整理、微妙なバグを修正
  7.  * ------------------------------------------------------------------
  8.  * [いじりどころ]
  9.  * 下の方にあるParameterクラスに簡単にいじれる値をまとめてあります。
  10.  * 是非、Forkしていろいろ試してみてください!
  11.  * ------------------------------------------------------------------
  12.  * エディタ出来ました。
  13.  * Layout Editor (for Dijkstra Particle Streams (Ver.1.1))
  14.  * http://wonderfl.net/code/425545196427ae8659973a8bfb13d54cfdeaf425
  15.  */
  16. /* ------------------------------------------------------------------
  17.  * 最短経路を進むパーティクル
  18.  * 
  19.  * [inspired by]
  20.  * Desktop Tower Defense
  21.  * http://www.handdrawngames.com/DesktopTD/
  22.  * Dijkstra Visualization
  23.  * http://wonderfl.net/code/6faaab5234abf034417a8e753f6309de0b9560f0
  24.  * and 神の書とwonderflのパーティクル作品群
  25.  * ------------------------------------------------------------------
  26.  * [操作方法]
  27.  * マウスのみ。
  28.  * クリックすると壁を設置・除去できます。
  29.  * ------------------------------------------------------------------
  30.  * [簡単な説明]
  31.  * main : 全体の初期化と更新処理
  32.  * Node : マス目の情報
  33.  * Wall : 壁の情報
  34.  * Particle : パーティクルの情報
  35.  * Start : パーティクルを出現させる場所です
  36.  * Goal : パーティクルの目的地(ここで経路探索処理してます)
  37.  * Parameter : ここをいじるといろいろ変えられます
  38.  * ------------------------------------------------------------------
  39.  */
  40. package {
  41.     import flash.display.Bitmap;
  42.     import flash.display.BitmapData;
  43.     import flash.display.Shape;
  44.     import flash.display.Sprite;
  45.     import flash.events.Event;
  46.     import flash.events.MouseEvent;
  47.     import flash.geom.Point;
  48.     
  49.     [SWF(frameRate=30)]
  50.     public class main extends Sprite {        
  51.         public static const MAP_SIZE:int = 33;
  52.         public static const NODE_SIZE:int = 15;
  53.         
  54.         // ノード、パーティクル、スタート、ゴールのコレクション
  55.         public static var nodes:Array;
  56.         public static var particles:Array;
  57.         private var _starts:Array;
  58.         private var _goals:Array;
  59.         
  60.         // パーティクル、壁、カーソルの表示オブジェクト
  61.         private var _particleCanvas:BitmapData;
  62.         private var _wallLayer:Sprite;
  63.         private var _cursor:Shape;
  64.         
  65.         public function main() {
  66.             // ノード、パーティクル、壁、ゴール、スタート、カーソルの順に初期化する
  67.             initializeNodes();
  68.             initializeParticles();
  69.             initializeWalls();
  70.             initializeStreams();    
  71.             initializeCursor();
  72.             
  73.             addEventListener(Event.ENTER_FRAME, update);
  74.         }
  75.         
  76.         // ノードの初期化
  77.         private function initializeNodes():void {
  78.             main.nodes = [];
  79.             for (var row:int = 0; row < main.MAP_SIZE; row++) {
  80.                 main.nodes[row] = [];
  81.                 for (var col:int = 0; col < main.MAP_SIZE; col++) {
  82.                     main.nodes[row][col] = new Node(col, row);
  83.                 }
  84.             }
  85.         }
  86.         
  87.         // パーティクルの初期化
  88.         private function initializeParticles():void {
  89.             Particle.createImages();
  90.             
  91.             particles = new Array(Parameter.PARTICLE_NUM);
  92.             for (var i:int = 0; i < Parameter.PARTICLE_NUM; i++){
  93.                 particles[i]= new Particle();
  94.             }
  95.             
  96.             var particleLayer:Bitmap = new Bitmap();
  97.             _particleCanvas = new BitmapData(465465false, 0x000000);
  98.             particleLayer.bitmapData = _particleCanvas;
  99.             addChild(particleLayer);
  100.         }
  101.         
  102.         // 壁の初期化
  103.         private function initializeWalls():void {
  104.             _wallLayer = new Sprite();
  105.             var walls:Array = Parameter.getWalls();
  106.             var len:int = walls.length;
  107.             for (var i:int = 0; i < len; i++) {
  108.                 _wallLayer.addChild(walls[i]);
  109.             }
  110.             addChild(_wallLayer);
  111.         }
  112.         
  113.         // スタートノード、ゴールノードの初期化
  114.         private function initializeStreams():void {
  115.             _goals = Parameter.getGoals();
  116.             _starts = Parameter.getStarts(_goals);
  117.             
  118.             // パーティクルの出現をばらつかせるためにシャッフルする
  119.             shuffle(_starts);
  120.         }
  121.         
  122.         // 配列をシャッフルする(Fisher-Yatesアルゴリズム)
  123.         private function shuffle(arr:Array):void {
  124.             var i:int = arr.length;
  125.             var j:int;
  126.             var tmp:*;
  127.             
  128.             while (i) {
  129.                 j = int(i * Math.random());
  130.                 
  131.                 tmp = arr[--i];
  132.                 arr[i] = arr[j];
  133.                 arr[j] = tmp;
  134.             }
  135.         }
  136.         
  137.         // カーソルの初期化
  138.         private function initializeCursor():void {
  139.             _cursor = new Shape();
  140.             _cursor.graphics.lineStyle(1, 0xffff00);
  141.             _cursor.graphics.drawRect(00, main.NODE_SIZE * 2 - 0.5, main.NODE_SIZE * 2 - 0.5);
  142.             addChild(_cursor);
  143.             
  144.             stage.addEventListener(MouseEvent.MOUSE_MOVE, moveCursor);
  145.             stage.addEventListener(MouseEvent.CLICK, clickMap);
  146.         }
  147.         
  148.         // カーソルの移動
  149.         private function moveCursor(e:MouseEvent):void {
  150.             _cursor.x = e.stageX - (e.stageX % main.NODE_SIZE);
  151.             _cursor.y = e.stageY - (e.stageY % main.NODE_SIZE);
  152.         }
  153.         
  154.         // マップクリック時の挙動
  155.         private function clickMap(e:MouseEvent):void {
  156.             // クリックした場所が除去できる壁なら除去する
  157.             if (e.target is Wall) {
  158.                 if (e.target.removable) {
  159.                     e.target.remove();
  160.                     reSearchGoals();
  161.                 }
  162.             // クリックした場所に壁が設置できるなら設置する
  163.             }else {
  164.                 var tile:Point = Node.tileFromPos(new Point(e.stageX, e.stageY));
  165.                 if (Wall.buildable(tile.x, tile.y)) {
  166.                     _wallLayer.addChild(new Wall(tile.x, tile.y));
  167.                     reSearchGoals();
  168.                 }
  169.             }
  170.         }
  171.         
  172.         // 全てのゴールノードを更新(経路の最探索)をする
  173.         private function reSearchGoals():void {
  174.             var i:int;
  175.             
  176.             var len:int = _goals.length;
  177.             for (i = 0; i < len; i++) {
  178.                 _goals[i].search();
  179.             }
  180.             
  181.             // 各パーティクルの最短で到達できるゴールノードを更新する
  182.             for (i = 0; i < Parameter.PARTICLE_NUM; i++) {
  183.                 if (main.particles[i].exists) {
  184.                     main.particles[i].updateNearestGoal();
  185.                 }
  186.             }
  187.         }
  188.         
  189.         // 毎フレームの更新処理
  190.         private function update(e:Event):void {
  191.             spawnParticles();    
  192.             updateParticles();
  193.         }
  194.         
  195.         // スタートノードからパーティクルを出現させる
  196.         private function spawnParticles():void {
  197.             var len:int = _starts.length;
  198.             for (var i:int = 0; i < len; i++) {
  199.                 _starts[i].update();
  200.             }
  201.         }
  202.         
  203.         // パーティクルを更新と描画を行う
  204.         private function updateParticles():void {
  205.             _particleCanvas.lock();
  206.             for (var i:int = 0; i < Parameter.PARTICLE_NUM; i++) {
  207.                 // パーティクルが画面内に存在していたら更新
  208.                 if(main.particles[i].exists){
  209.                     main.particles[i].update();
  210.                     main.particles[i].draw(_particleCanvas);
  211.                 }
  212.             }
  213.             _particleCanvas.colorTransform(_particleCanvas.rect, Parameter.CANVAS_COLOR_TRANSFORM);
  214.             _particleCanvas.applyFilter(_particleCanvas, _particleCanvas.rect, _particleCanvas.rect.topLeft, Parameter.CANVAS_FILTER);
  215.             _particleCanvas.unlock();
  216.         }
  217.     }
  218. }
  219. import flash.geom.Point;
  220. class Node {
  221.     private var _tilex:int;
  222.     private var _tiley:int;
  223.     private var _centerx:Number;     // 中心のX座標
  224.     private var _centery:Number;     // 中心のY座標
  225.     private var _passable:Boolean;    // (パーティクルが)通行可能かどうか
  226.     
  227.     public function get tileX():int { return _tilex; }
  228.     public function get tileY():int { return _tiley; }
  229.     public function get centerX():Number { return _centerx; }
  230.     public function get centerY():Number { return _centery; }
  231.     public function get passable():Boolean { return _passable; }
  232.     public function set passable(arg:Boolean):void { _passable = arg; }
  233.     
  234.     public function Node(tilex:int, tiley:int) {
  235.         _tilex = tilex;
  236.         _tiley = tiley;
  237.         var pos:Point = Node.posFromTile(new Point(_tilex, _tiley));
  238.         _centerx = pos.x + (main.NODE_SIZE / 2);
  239.         _centery = pos.y + (main.NODE_SIZE / 2);
  240.         _passable = true;
  241.     }
  242.     
  243.     // タイル縦横値から(有効な)XY座標値を求める
  244.     public static function posFromTile(tile:Point):Point {
  245.         var pos:Point = new Point();
  246.         
  247.         pos.x = (tile.x * main.NODE_SIZE) - main.NODE_SIZE;
  248.         if (pos.x < -main.NODE_SIZE) {
  249.             pos.x = -main.NODE_SIZE;
  250.         }else if (pos.x > (main.NODE_SIZE * (main.MAP_SIZE - 1))) {
  251.             pos.x = (main.NODE_SIZE * (main.MAP_SIZE - 1));
  252.         }
  253.         
  254.         pos.y = (tile.y * main.NODE_SIZE) - main.NODE_SIZE;
  255.         if (pos.y < -main.NODE_SIZE) {
  256.             pos.y = -main.NODE_SIZE;
  257.         }else if (pos.y > (main.NODE_SIZE * (main.MAP_SIZE - 1))) {
  258.             pos.y = (main.NODE_SIZE * (main.MAP_SIZE - 1));
  259.         }
  260.         
  261.         return pos;
  262.     }
  263.     
  264.     // XY座標値から(有効な)タイル縦横値を求める
  265.     public static function tileFromPos(pos:Point):Point {
  266.         var tile:Point = new Point();
  267.         
  268.         tile.x = Math.floor(pos.x / main.NODE_SIZE) + 1;
  269.         if (tile.x < 0) {
  270.             tile.x = 0;
  271.         }else if (tile.x > main.MAP_SIZE - 1) {
  272.             tile.x = main.MAP_SIZE - 1;
  273.         }
  274.         
  275.         tile.y = Math.floor(pos.y / main.NODE_SIZE) + 1;
  276.         if (tile.y < 0) {
  277.             tile.y = 0;
  278.         }else if (tile.y > main.MAP_SIZE - 1) {
  279.             tile.y = main.MAP_SIZE - 1;
  280.         }
  281.         
  282.         return tile;
  283.     }
  284. }
  285. import flash.display.Sprite;
  286. import flash.geom.Point;
  287. class Wall extends Sprite {
  288.     private var _tilex:int;
  289.     private var _tiley:int;
  290.     private var _removable:Boolean;    // (クリックで)除去できるかどうか
  291.     
  292.     public function get removable():Boolean { return _removable; }
  293.     
  294.     public function Wall(tilex:int, tiley:int, removable:Boolean = true) {
  295.         _tilex = tilex;
  296.         _tiley = tiley;
  297.         var pos:Point = Node.posFromTile(new Point(_tilex, _tiley));
  298.         this.x = pos.x;
  299.         this.y = pos.y;
  300.         _removable = removable;
  301.         
  302.         setNodePassablity(false);
  303.         draw();
  304.     }
  305.     
  306.     // 壁を設置した部分のノードの通行可能性を変更する
  307.     private function setNodePassablity(passable:Boolean):void {
  308.         main.nodes[_tiley][_tilex].passable = passable;
  309.         main.nodes[_tiley][_tilex + 1].passable = passable;
  310.         main.nodes[_tiley + 1][_tilex].passable = passable;
  311.         main.nodes[_tiley + 1][_tilex + 1].passable = passable;
  312.     }
  313.     
  314.     // 壁の画像を描画する
  315.     private function draw():void {
  316.         var rectSize:Number = main.NODE_SIZE * 2;
  317.         
  318.         if (_removable) {
  319.             rectSize -= 0.5;
  320.             graphics.lineStyle(1, Parameter.WALL_BORDER_COLOR);
  321.         }
  322.         
  323.         graphics.beginFill(Parameter.WALL_BASE_COLOR);
  324.         graphics.drawRect(00, rectSize, rectSize);
  325.         graphics.endFill();
  326.     }
  327.     
  328.     // 壁を取り除く際に呼ぶ関数
  329.     public function remove():void {
  330.         setNodePassablity(true);
  331.         parent.removeChild(this);
  332.     }
  333.     
  334.     // 指定した位置に壁が設置できるか
  335.     public static function buildable(tilex:int, tiley:int):Boolean {
  336.         return (main.nodes[tiley][tilex].passable &&
  337.         main.nodes[tiley][tilex + 1].passable &&
  338.         main.nodes[tiley + 1][tilex].passable &&
  339.         main.nodes[tiley + 1][tilex + 1].passable);
  340.     }
  341. }
  342. import flash.display.BitmapData;
  343. import flash.display.BlendMode;
  344. import flash.display.Shape;
  345. import flash.geom.Matrix;
  346. import flash.geom.Point;
  347. class Particle {
  348.     private var _posx:Number;        // X座標の位置
  349.     private var _posy:Number;        // Y座標の位置
  350.     private var _vx:Number;            // X方向の速度(ベクトル)
  351.     private var _vy:Number;            // Y方向の速度(ベクトル)
  352.     
  353.     private var _speed:Number;        // 速さ(スカラー)
  354.     private var _exists:Boolean;    // 画面上に存在するかどうか
  355.     private var _start:Start;        // 出現したスタートノード
  356.     private var _nearestGoal:Goal;    // 最短で到達できるゴールノード
  357.     private var _nextNode:Node;        // 次に進むべきノード
  358.     
  359.     private static var _images:Array;
  360.     private var _imageIndex:int;
  361.     
  362.     public function get exists():Boolean { return _exists; }
  363.     
  364.     public function Particle() {
  365.         _posx = _posy = _vx = _vy = 0;
  366.         _speed = ((Parameter.PARTICLE_MAXSPEED - 1) * Math.random()) + 1;
  367.         _exists = false;
  368.         _start = null;
  369.         _nearestGoal = null;
  370.         _nextNode = null;
  371.         _imageIndex = 0;
  372.     }
  373.     
  374.     // スタートノードから出現させる
  375.     public function spawn(start:Start):void {
  376.         _posx = start.node.centerX;
  377.         _posy = start.node.centerY;
  378.         _vx = _vy = 0;
  379.         _exists = true;
  380.         _start = start;
  381.         _nearestGoal = _start.getNearestGoalFrom(start.node.tileX, start.node.tileY);
  382.         _nextNode = _nearestGoal.getNext(start.node.tileX, start.node.tileY);
  383.         _imageIndex = int(Parameter.PARTICLE_COLORS.length * Math.random());
  384.     }
  385.     
  386.     public function update():void {
  387.         _posx += _vx;
  388.         _posy += _vy;
  389.         
  390.         var tile:Point = Node.tileFromPos(new Point(_posx, _posy));
  391.         // ゴールに着いたか、移動不可能(かべのなかにいる)なら消滅する
  392.         if (arrivedGoal(tile.x, tile.y) || !isMovable(tile.x, tile.y)) {
  393.             _exists = false;
  394.             return;
  395.         }
  396.         
  397.         //次に進むべきノードへ到着していたら、次に進むべきノードを更新する
  398.         if (arrivedNextNode(tile.x, tile.y)) {
  399.             updateNextNode(tile.x, tile.y);
  400.         }
  401.         
  402.         // ゴールノードへの経路が存在しないならフワフワする
  403.         if (_nextNode == main.nodes[tile.y][tile.x]) {
  404.             _vx = (_vx * 0.5) + (Math.random() - 0.5);
  405.             _vy = (_vy * 0.5) + (Math.random() - 0.5);
  406.         }else {
  407.             var radians:Number = Math.atan2(_nextNode.centerY - _posy, _nextNode.centerX - _posx);
  408.             _vx = (_vx + _speed * Math.cos(radians)) * 0.5;
  409.             _vy = (_vy + _speed * Math.sin(radians)) * 0.5;
  410.         }
  411.     }
  412.     
  413.     // 最短で到達できるゴールノードを更新する
  414.     public function updateNearestGoal():void {
  415.         var tile:Point = Node.tileFromPos(new Point(_posx, _posy));
  416.         _nearestGoal = _start.getNearestGoalFrom(tile.x, tile.y);
  417.         
  418.         updateNextNode(tile.x, tile.y);
  419.     }
  420.     
  421.     // 次に進むべきノードを更新する
  422.     private function updateNextNode(tilex:int, tiley:int):void {
  423.         _nextNode = _nearestGoal.getNext(tilex, tiley);
  424.     }
  425.     
  426.     // ゴールノードへ到着しているかどうか
  427.     private function arrivedGoal(tilex:int, tiley:int):Boolean {
  428.         return (tilex == _nearestGoal.node.tileX) && (tiley == _nearestGoal.node.tileY);
  429.     }
  430.     
  431.     // パーティクルが移動できるかどうか
  432.     private function isMovable(tilex:int, tiley:int):Boolean {
  433.         return main.nodes[tiley][tilex].passable;
  434.     }
  435.     
  436.     // 次に進むべきノードへ到着しているかどうか
  437.     private function arrivedNextNode(tilex:int, tiley:int):Boolean {
  438.         return  (_nextNode.tileX == tilex) && (_nextNode.tileY == tiley);
  439.     }
  440.     
  441.     // canvasにパーティクルの画像を描画する
  442.     public function draw(canvas:BitmapData):void {
  443.         var matrix:Matrix = new Matrix();
  444.         matrix.translate(_posx - Parameter.PARTICLE_RADIUS, _posy - Parameter.PARTICLE_RADIUS);
  445.         canvas.draw(_images[_imageIndex], matrix, null, BlendMode.ADD);
  446.     }
  447.     
  448.     // パーティクルの画像を予め作成しておく関数
  449.     public static function createImages():void {
  450.         _images = [];
  451.         for (var i:int = 0; i < Parameter.PARTICLE_COLORS.length; i++) {
  452.             var bitmapData:BitmapData = new BitmapData(Math.ceil(Parameter.PARTICLE_RADIUS * 2), Math.ceil(Parameter.PARTICLE_RADIUS * 2), true, 0x00ffffff);
  453.             var shape:Shape = new Shape();
  454.             shape.graphics.beginFill(Parameter.PARTICLE_COLORS[i]);
  455.             shape.graphics.drawCircle(Parameter.PARTICLE_RADIUS, Parameter.PARTICLE_RADIUS, Parameter.PARTICLE_RADIUS);
  456.             shape.graphics.endFill();
  457.             bitmapData.draw(shape);
  458.             _images.push(bitmapData);
  459.         }
  460.     }
  461. }
  462. class Start {
  463.     private var _node:Node;
  464.     private var _goalGroup:Array;    // ゴールノードのグループ
  465.     private var _spawnCount:int;    // パーティクルを出現させる為のカウント
  466.     
  467.     public function get node():Node { return _node; }
  468.     public function getNearestGoalFrom(tilex:int, tiley:int):Goal {
  469.         var nearest:Goal = _goalGroup[0];
  470.         var len:int = _goalGroup.length;
  471.         for (var i:int = 1; i < len; i++) {
  472.             if (_goalGroup[i].getCost(tilex, tiley) < nearest.getCost(tilex, tiley)) {
  473.                 nearest = _goalGroup[i];
  474.             }
  475.         }
  476.         return nearest;
  477.     }
  478.     
  479.     public function Start(node:Node, goalGroup:Array) {
  480.         _node = node;
  481.         _goalGroup = goalGroup;
  482.         _spawnCount = int(Parameter.SPAWN_INTERVAL * Math.random());
  483.     }
  484.     
  485.     public function update():void {
  486.         // ゴールノードへの経路が存在しない場合は何もしない
  487.         if (!hasPathToGoal()) { return; }
  488.         
  489.         // 一定の間隔でパーティクルの出現を試みる
  490.         if (++_spawnCount >= Parameter.SPAWN_INTERVAL) {
  491.             // 画面上に存在しないパーティクルがあれば、それをここから出現させる
  492.             var len:int = main.particles.length;
  493.             for (var i:int = 0; i < len; i++) {
  494.                 if (!main.particles[i].exists) {
  495.                     main.particles[i].spawn(this);
  496.                     _spawnCount = 0;
  497.                     break;
  498.                 }
  499.             }
  500.         }
  501.     }
  502.     
  503.     // 最低一つのゴールノードへの経路が存在するかどうか
  504.     private function hasPathToGoal():Boolean {
  505.         var len:int = _goalGroup.length;
  506.         for (var i:int = 0; i < len; i++) {
  507.             if (_goalGroup[i].accessibleFrom(_node)) {
  508.                 return true;
  509.             }
  510.         }
  511.         return false;
  512.     }
  513. }
  514. class Goal {
  515.     private static const DX:Array = [0, -110, -11, -11];
  516.     private static const DY:Array = [ -1001, -1, -111];
  517.     private static const DCOST:Array = [1111, Math.SQRT2, Math.SQRT2, Math.SQRT2, Math.SQRT2];
  518.     
  519.     private var _groupID:int;        // 所属するグループのID
  520.     private var _node:Node;            // ゴールとする対象のノード
  521.     
  522.     private var _openNodes:Array;    // 保留ノードリスト
  523.     private var _nodeCost:Array;    // 各ノードの移動コスト
  524.     private var _nodeNext:Array;    // 各ノードの次の経路となるノード
  525.     
  526.     public function get groupID():int { return _groupID; }
  527.     public function get node():Node { return _node; }
  528.     public function getCost(tilex:int, tiley:int):Number { return _nodeCost[tiley][tilex]; }
  529.     public function getNext(tilex:int, tiley:int):Node { return _nodeNext[tiley][tilex]; }
  530.     
  531.     public function Goal(groupID:int, node:Node) {
  532.         _groupID = groupID;
  533.         _node = node;
  534.         
  535.         _openNodes = [];
  536.         _nodeCost = [];
  537.         _nodeNext = [];
  538.         for (var row:int = 0; row < main.MAP_SIZE; row++) {
  539.             _nodeCost[row] = [];
  540.             _nodeNext[row] = [];
  541.         }
  542.         
  543.         search();
  544.     }
  545.     
  546.     // ダイクストラ法による経路探索
  547.     public function search():void {
  548.         initialize();
  549.         
  550.         while (_openNodes.length > 0) {
  551.             var subject:Node = _openNodes.pop() as Node;
  552.             
  553.             // 周囲8方向のノードを訪問する
  554.             for (var i:int = 0; i < 8; i++) {
  555.                 // 画面外の存在しないノードを指すなら次の周囲ノードへ進む
  556.                 if (!isValid(subject.tileX + Goal.DX[i]) || !isValid(subject.tileY + Goal.DY[i])) { continue; }
  557.                 // 壁が設置されているノード、計算済み(確定)ノード、直進することができないノードなら次の周囲ノードへ進む
  558.                 var test:Node = main.nodes[subject.tileY + Goal.DY[i]][subject.tileX + Goal.DX[i]];
  559.                 if (isWall(test) || isCalculatedNode(test) || !canGoStraightTo(subject, test)) { continue; }
  560.                 
  561.                 // 移動コストを計算する
  562.                 _nodeCost[test.tileY][test.tileX] = _nodeCost[subject.tileY][subject.tileX] + Goal.DCOST[i];
  563.                 // 次の経路ノードをsubjectノードに設定する
  564.                 _nodeNext[test.tileY][test.tileX] = subject;
  565.                 // 保留ノードリストに追加する
  566.                 insertToOpenNodes(test);
  567.             }
  568.         }
  569.     }
  570.     
  571.     private function initialize():void {
  572.         for (var row:int = 0; row < main.MAP_SIZE; row++) {
  573.             for (var col:int = 0; col < main.MAP_SIZE; col++) {
  574.                 _nodeCost[row][col] = int.MAX_VALUE;
  575.                 _nodeNext[row][col] = main.nodes[row][col];
  576.             }
  577.         }
  578.         
  579.         // Goalのノードを経路探索のスタートノードとする
  580.         _nodeCost[_node.tileY][_node.tileX] = 0;
  581.         _openNodes.push(main.nodes[_node.tileY][_node.tileX]);
  582.     }
  583.     
  584.     // indexの値が有効な値かどうか
  585.     private function isValid(index:int):Boolean {
  586.         return ((index >= 0) && (index < main.MAP_SIZE));
  587.     }
  588.     
  589.     // 既にコストを計算済みのノードかどうか
  590.     private function isCalculatedNode(node:Node):Boolean {
  591.         return _nodeCost[node.tileY][node.tileX] != int.MAX_VALUE;
  592.     }
  593.     
  594.     // 壁が設置されたノードかどうか
  595.     private function isWall(node:Node):Boolean {
  596.         return !node.passable;
  597.     }
  598.     
  599.     // subjectノードからtestノードへ直進できるかどうか
  600.     private function canGoStraightTo(subject:Node, test:Node):Boolean {
  601.         return (main.nodes[subject.tileY][test.tileX].passable && main.nodes[test.tileY][subject.tileX].passable);
  602.     }
  603.     
  604.     // nodeを保留ノードリストの適切な場所に挿入する
  605.     private function insertToOpenNodes(node:Node):void {
  606.         var insertIndex:int;
  607.         var len:int = _openNodes.length;
  608.         var nodeCost:Number = _nodeCost[node.tileY][node.tileX];
  609.         
  610.         for (insertIndex = 0; insertIndex < len; insertIndex++) {
  611.             var openNode:Node = _openNodes[insertIndex];
  612.             if (nodeCost > _nodeCost[openNode.tileY][openNode.tileX]) { break; }
  613.         }
  614.         _openNodes.splice(insertIndex, 0, node);
  615.     }
  616.     
  617.     // nodeからこのゴールノードに到達することができる(経路が存在する)かどうか
  618.     public function accessibleFrom(node:Node):Boolean {
  619.         return _nodeCost[node.tileY][node.tileX] != int.MAX_VALUE;
  620.     }
  621. }
  622. import flash.filters.BitmapFilter;
  623. import flash.filters.BlurFilter;
  624. import flash.geom.ColorTransform;
  625. import flash.geom.Point;
  626. class Parameter {
  627.     // パーティクルの最大数
  628.     public static const PARTICLE_NUM:int = 1000;
  629.     /// パーティクルの半径
  630.     public static const PARTICLE_RADIUS:Number = 4.0;
  631.     // パーティクルの最大速度
  632.     public static const PARTICLE_MAXSPEED:Number = 7.5;
  633.     // パーティクルの色(種類)
  634.     public static const PARTICLE_COLORS:Array = [0x224488];
  635.     // パーティクルの軌跡の色の変化
  636.     public static const CANVAS_COLOR_TRANSFORM:ColorTransform = new ColorTransform(0.90.920.95);
  637.     // パーティクルを描画するキャンバスに適用するフィルタ
  638.     public static const CANVAS_FILTER:BitmapFilter = new BlurFilter(221);
  639.     // パーティクルの出現間隔
  640.     public static const SPAWN_INTERVAL:int = 10;
  641.     // 壁の色
  642.     public static const WALL_BASE_COLOR:uint = 0x202020;
  643.     // (除去可能な)壁の枠線の色
  644.     public static const WALL_BORDER_COLOR:uint = 0x404040;
  645.     
  646.     private static const data:XML =
  647.     <root>
  648.         <walls>
  649.             <wall x="0" y="0" rem="f"/><wall x="2" y="0" rem="f"/>
  650.             <wall x="4" y="0" rem="f"/><wall x="6" y="0" rem="f"/>
  651.             <wall x="8" y="0" rem="f"/><wall x="10" y="0" rem="f"/>
  652.             <wall x="21" y="0" rem="f"/><wall x="23" y="0" rem="f"/>
  653.             <wall x="25" y="0" rem="f"/><wall x="27" y="0" rem="f"/>
  654.             <wall x="29" y="0" rem="f"/><wall x="31" y="0" rem="f"/>
  655.             
  656.             <wall x="0" y="31" rem="f"/><wall x="2" y="31" rem="f"/>
  657.             <wall x="4" y="31" rem="f"/><wall x="6" y="31" rem="f"/>
  658.             <wall x="8" y="31" rem="f"/><wall x="10" y="31" rem="f"/>
  659.             <wall x="21" y="31" rem="f"/><wall x="23" y="31" rem="f"/>
  660.             <wall x="25" y="31" rem="f"/><wall x="27" y="31" rem="f"/>
  661.             <wall x="29" y="31" rem="f"/><wall x="31" y="31" rem="f"/>
  662.             
  663.             <wall x="0" y="2" rem="f"/><wall x="0" y="4" rem="f"/>
  664.             <wall x="0" y="6" rem="f"/><wall x="0" y="8" rem="f"/>
  665.             <wall x="0" y="10" rem="f"/><wall x="0" y="21" rem="f"/>
  666.             <wall x="0" y="23" rem="f"/><wall x="0" y="25" rem="f"/>
  667.             <wall x="0" y="27" rem="f"/><wall x="0" y="29" rem="f"/>
  668.             
  669.             <wall x="31" y="2" rem="f"/><wall x="31" y="4" rem="f"/>
  670.             <wall x="31" y="6" rem="f"/><wall x="31" y="8" rem="f"/>
  671.             <wall x="31" y="10" rem="f"/><wall x="31" y="21" rem="f"/>
  672.             <wall x="31" y="23" rem="f"/><wall x="31" y="25" rem="f"/>
  673.             <wall x="31" y="27" rem="f"/><wall x="31" y="29" rem="f"/>
  674.         </walls>
  675.         <starts>
  676.             <start goal="0" x="13" y="0"/><start goal="0" x="14" y="0"/>
  677.             <start goal="0" x="15" y="0"/><start goal="0" x="16" y="0"/><start goal="0" x="17" y="0"/>
  678.             <start goal="0" x="18" y="0"/><start goal="0" x="19" y="0"/>
  679.             
  680.             <start goal="1" x="0" y="13"/><start goal="1" x="0" y="14"/>
  681.             <start goal="1" x="0" y="15"/><start goal="1" x="0" y="16"/><start goal="1" x="0" y="17"/>
  682.             <start goal="1" x="0" y="18"/><start goal="1" x="0" y="19"/>
  683.         </starts>
  684.         <goals>
  685.             <goal id="0" x="13" y="32"/><goal id="0" x="14" y="32"/>
  686.             <goal id="0" x="15" y="32"/><goal id="0" x="16" y="32"/><goal id="0" x="17" y="32"/>
  687.             <goal id="0" x="18" y="32"/><goal id="0" x="19" y="32"/>
  688.             
  689.             <goal id="1" x="32" y="13"/><goal id="1" x="32" y="14"/>
  690.             <goal id="1" x="32" y="15"/><goal id="1" x="32" y="16"/><goal id="1" x="32" y="17"/>
  691.             <goal id="1" x="32" y="18"/><goal id="1" x="32" y="19"/>
  692.         </goals>
  693.     </root>;
  694.     
  695.     public static function getWalls():Array {
  696.         var walls:Array = [];
  697.         for each(var w:XML in Parameter.data.walls.*) {
  698.             var removable:Boolean = ((w.@rem == "t") ? true : false);
  699.             var wall:Wall = new Wall(int(w.@x), int(w.@y), removable);
  700.             walls.push(wall);
  701.         }
  702.         return walls;
  703.     }
  704.     
  705.     public static function getStarts(goals:Array):Array {
  706.         var starts:Array = [];
  707.         var len:int = goals.length;
  708.         
  709.         for each(var s:XML in Parameter.data.starts.*) {
  710.             var groupID:int = int(s.@goal);
  711.             var goalGroup:Array = [];
  712.             for (var i:int = 0; i < len; i++) {
  713.                 if (goals[i].groupID == groupID) {
  714.                     goalGroup.push(goals[i]);
  715.                 }
  716.             }
  717.             starts.push(new Start(main.nodes[int(s.@y)][int(s.@x)], goalGroup));
  718.         }
  719.         return starts;
  720.     }
  721.     
  722.     public static function getGoals():Array {
  723.         var goals:Array = [];
  724.         for each(var g:XML in Parameter.data.goals.*) {
  725.             var node:Node = main.nodes[int(g.@y)][int(g.@x)];
  726.             var goal:Goal = new Goal(int(g.@id), node);
  727.             goals.push(goal);
  728.         }
  729.         return goals;
  730.     }
  731. }
noswf
  1. package {
  2.     import flash.display.Bitmap;
  3.     import flash.display.BitmapData;
  4.     import flash.display.Shape;
  5.     import flash.display.Sprite;
  6.     import flash.events.Event;
  7.     import flash.events.MouseEvent;
  8.     import flash.geom.ColorTransform;
  9.     import flash.geom.Point;
  10.     import flash.geom.Rectangle;
  11.     
  12.     [SWF(frameRate=30)]
  13.     public class main extends Sprite {        
  14.         public static const MAP_SIZE:int = 33;
  15.         public static const NODE_SIZE:int = 15;
  16.         
  17.         
  18.         public static var nodes:Array;
  19.         public static var particles:Array;
  20.         private var _starts:Array;
  21.         private var _goals:Array;
  22.         
  23.         
  24.         private var _particleCanvas:BitmapData;
  25.         private var _canvasRect:Rectangle;
  26.         private var _colorTransform:ColorTransform;
  27.         private var _wallLayer:Sprite;
  28.         private var _cursor:Shape;
  29.         
  30.         public function main() {
  31.             // ノード、パーティクル、壁、ゴール、スタートの順で初期化
  32.             initializeNodes();
  33.             initializeParticles();
  34.             initializeStreams();    
  35.             
  36.             addEventListener(Event.ENTER_FRAME, update);
  37.             
  38.             
  39.         }
  40.         
  41.         // ノードの初期化
  42.         private function initializeNodes():void {
  43.             main.nodes = [];
  44.             for (var row:int = 0; row < main.MAP_SIZE; row++) {
  45.                 main.nodes[row] = [];
  46.                 for (var col:int = 0; col < main.MAP_SIZE; col++) {
  47.                     main.nodes[row][col] = new Node(col, row);
  48.                 }
  49.             }
  50.         }
  51.         
  52.         // パーティクルの初期化
  53.         private function initializeParticles():void {
  54.             Particle.createImages();
  55.             
  56.             var numParticles:int = Parameter.numParticles;
  57.             particles = new Array(numParticles);
  58.             for (var i:int = 0; i < numParticles; i++){
  59.                 particles[i]= new Particle();
  60.             }
  61.             
  62.             var particleLayer:Bitmap = new Bitmap();
  63.             _particleCanvas = new BitmapData(500400false, 0x000000);
  64.         
  65.             
  66.             particleLayer.bitmapData = _particleCanvas;
  67.             addChild(particleLayer);
  68.             
  69.             _canvasRect = new Rectangle(00500400);
  70.             _colorTransform = new ColorTransform(0.9,0.9,0.9)
  71.         }
  72.         
  73.         // 壁の初期化
  74.         
  75.         // スタートノード、ゴールノードの初期化
  76.         private function initializeStreams():void {
  77.             _goals = Parameter.getGoals();
  78.             _starts = Parameter.getStarts(_goals);
  79.             Start.setSpawnInterval();
  80.             
  81.             // パーティクルの出現をばらつかせるためにシャッフルする
  82.             shuffle(_starts);
  83.         }
  84.         
  85.         // 配列をシャッフルする
  86.         private function shuffle(arr:Array):void {
  87.             var i:int = arr.length;
  88.             while (i) {
  89.                 var j:int = Math.floor(i * Math.random());
  90.                 var tmp:* = arr[--i];
  91.                 arr[i] = arr[j];
  92.                 arr[j] = tmp;
  93.             }
  94.         }
  95.         
  96.     
  97.         // 毎フレームの更新処理
  98.         private function update(e:Event):void {
  99.             spawnParticles();    
  100.             updateParticles();
  101.         }
  102.         
  103.         // スタートノードからパーティクルを出現させる
  104.         private function spawnParticles():void {
  105.             var numStarts:int = _starts.length;
  106.             for (var i:int = 0; i < numStarts; i++) {
  107.                 _starts[i].update();
  108.             }
  109.         }
  110.         
  111.         // パーティクルの更新を行う
  112.         private function updateParticles():void {
  113.             _particleCanvas.lock();
  114.             for (var i:int = 0; i < Parameter.numParticles; i++) {
  115.                 // パーティクルが画面内に存在していたら更新
  116.                 if(main.particles[i].exists){
  117.                     main.particles[i].update();
  118.                     main.particles[i].draw(_particleCanvas);
  119.                 }
  120.             }
  121.             _particleCanvas.colorTransform(_canvasRect, _colorTransform);
  122.             _particleCanvas.unlock();
  123.         }
  124.     }
  125. }
  126. import flash.display.Shape;
  127. import flash.geom.Point;
  128. class Node {
  129.     private var _tilex:int;
  130.     private var _tiley:int;
  131.     private var _centerx:Number;
  132.     private var _centery:Number;
  133.     private var _passable:Boolean;
  134.     
  135.     public function get tileX():int { return _tilex; }
  136.     public function get tileY():int { return _tiley; }
  137.     public function get centerX():Number { return _centerx; }
  138.     public function get centerY():Number { return _centery; }
  139.     public function get passable():Boolean { return _passable; }
  140.     public function set passable(arg:Boolean):void { _passable = arg; }
  141.     
  142.     public function Node(tilex:int, tiley:int) {
  143.         _tilex = tilex;
  144.         _tiley = tiley;
  145.         var pos:Point = Node.posFromTile(new Point(_tilex, _tiley));
  146.         _centerx = pos.x + (main.NODE_SIZE / 2);
  147.         _centery = pos.y + (main.NODE_SIZE / 2);
  148.         _passable = true;
  149.     }
  150.     
  151.     // タイル縦横値からXY座標値を求める
  152.     public static function posFromTile(tile:Point):Point {
  153.         var pos:Point = new Point();
  154.         
  155.         pos.x = (tile.x * main.NODE_SIZE) - main.NODE_SIZE;
  156.         if (pos.x < -main.NODE_SIZE) {
  157.             pos.x = -main.NODE_SIZE;
  158.         }else if (pos.x > (main.NODE_SIZE * (main.MAP_SIZE - 1))) {
  159.             pos.x = (main.NODE_SIZE * (main.MAP_SIZE - 1));
  160.         }
  161.         
  162.         pos.y = (tile.y * main.NODE_SIZE) - main.NODE_SIZE;
  163.         if (pos.y < -main.NODE_SIZE) {
  164.             pos.y = -main.NODE_SIZE;
  165.         }else if (pos.y > (main.NODE_SIZE * (main.MAP_SIZE - 1))) {
  166.             pos.y = (main.NODE_SIZE * (main.MAP_SIZE - 1));
  167.         }
  168.         
  169.         return pos;
  170.     }
  171.     
  172.     // XY座標値からタイル縦横値を求める
  173.     public static function tileFormPos(pos:Point):Point {
  174.         var tile:Point = new Point();
  175.         
  176.         tile.x = Math.floor(pos.x / main.NODE_SIZE) + 1;
  177.         if (tile.x < 0) {
  178.             tile.x = 0;
  179.         }else if (tile.x > main.MAP_SIZE - 1) {
  180.             tile.x = main.MAP_SIZE - 1;
  181.         }
  182.         
  183.         tile.y = Math.floor(pos.y / main.NODE_SIZE) + 1;
  184.         if (tile.y < 0) {
  185.             tile.y = 0;
  186.         }else if (tile.y > main.MAP_SIZE - 1) {
  187.             tile.y = main.MAP_SIZE - 1;
  188.         }
  189.         
  190.         return tile;
  191.     }
  192. }
  193. import flash.display.Sprite;
  194. import flash.geom.Point;
  195. class Wall extends Sprite {
  196.     private var _tilex:int;
  197.     private var _tiley:int;
  198.     private var _removable:Boolean;
  199.     
  200.     public function get removable():Boolean { return _removable; }
  201.     
  202.     public function Wall(tilex:int, tiley:int, removable:Boolean = true) {
  203.         _tilex = tilex;
  204.         _tiley = tiley;
  205.         var pos:Point = Node.posFromTile(new Point(_tilex, _tiley));
  206.         this.x = pos.x;
  207.         this.y = pos.y;
  208.         _removable = removable;
  209.         
  210.         setNodePassablity(false);
  211.         draw();
  212.     }
  213.     
  214.     // 壁を設置した部分のノードの通行可能性を変更する
  215.     private function setNodePassablity(passable:Boolean):void {
  216.         main.nodes[_tiley][_tilex].passable = passable;
  217.         main.nodes[_tiley][_tilex + 1].passable = passable;
  218.         main.nodes[_tiley + 1][_tilex].passable = passable;
  219.         main.nodes[_tiley + 1][_tilex + 1].passable = passable;
  220.     }
  221.     
  222.     // 壁の画像を描画する
  223.     private function draw():void {
  224.         var rectSize:Number = main.NODE_SIZE * 2;
  225.         
  226.         if (_removable) {
  227.             rectSize -= 0.5;
  228.             graphics.lineStyle(1, 0x222222);
  229.         }
  230.         
  231.         graphics.beginFill(0x111111);
  232.         graphics.drawRect(00, rectSize, rectSize);
  233.         graphics.endFill();
  234.     }
  235.     
  236.     // 壁を取り除く際に呼ぶ関数
  237.     public function remove():void {
  238.         setNodePassablity(true);
  239.         parent.removeChild(this);
  240.     }
  241. }
  242. import flash.display.BitmapData;
  243. import flash.display.BlendMode;
  244. import flash.display.Shape;
  245. import flash.geom.Matrix;
  246. import flash.geom.Point;
  247. import flash.geom.Rectangle;
  248. class Particle {
  249.     private static const IMAGE_COLORS:Array =
  250.     [0x0000ff, 0x3333ff, 0x6666ff, 0x9999ff, 0xccccff, 0xffffff,
  251.      0x0033ff, 0x0066ff, 0x0099ff, 0x00ccff, 0x00ffff,
  252.      0x33ffff, 0x66ffff, 0x99ffff, 0xccffff];
  253.     private static var IMAGE_RADIUS:int;
  254.     
  255.     private var _posx:Number;
  256.     private var _posy:Number;
  257.     private var _vx:Number;
  258.     private var _vy:Number;
  259.     
  260.     private var _speed:Number;
  261.     private var _exists:Boolean;
  262.     private var _start:Start;
  263.     
  264.     private static var _images:Array;
  265.     private var _imageIndex:int;
  266.     private static var _sourceRect:Rectangle;
  267.     
  268.     public function get exists():Boolean { return _exists; }
  269.     
  270.     public function Particle() {
  271.         _posx = _posy = _vx = _vy = 0;
  272.         _speed = ((Parameter.particleMaxSpeed - 1) * Math.random()) + 1;
  273.         _exists = false;
  274.         _start = null;
  275.         _imageIndex = 0;
  276.     }
  277.     
  278.     // スタートノードから出現させる
  279.     public function spawn(start:Start):void {
  280.         _posx = start.node.centerX;
  281.         _posy = start.node.centerY;
  282.         _exists = true;
  283.         _start = start;
  284.         _imageIndex = Math.floor(Particle.IMAGE_COLORS.length * Math.random());
  285.     }
  286.     
  287.     public function update():void {
  288.         _posx += _vx;
  289.         _posy += _vy;
  290.         
  291.         var tile:Point = Node.tileFormPos( new Point(_posx, _posy));
  292.         // ゴールに着いたか、移動不可能(かべのなかにいる)なら消滅する
  293.         if (arrivedGoal(tile.x, tile.y) || !isMovable(tile.x, tile.y)) {
  294.             _exists = false;
  295.             return;
  296.         }
  297.         
  298.         var nextNode:Node = _start.destination.getNext(tile.x, tile.y);
  299.         // ゴールノードへの経路が存在しないなら止まる
  300.         if (nextNode == main.nodes[tile.y][tile.x]) {
  301.             _vx = 0;
  302.             _vy = 0;
  303.         }else {
  304.             var radians:Number = Math.atan2(nextNode.centerY - _posy, nextNode.centerX - _posx);
  305.             _vx = _speed * Math.cos(radians);
  306.             _vy = _speed * Math.sin(radians);
  307.         }
  308.     }
  309.     
  310.     // ゴールノードへ到着しているかどうか
  311.     private function arrivedGoal(tilex:int, tiley:int):Boolean {
  312.         var goalNode:Node = _start.destination.node;
  313.         
  314.         return (tilex == goalNode.tileX) && (tiley == goalNode.tileY);
  315.     }
  316.     
  317.     // パーティクルが移動できるかどうか
  318.     private function isMovable(tilex:int, tiley:int):Boolean {
  319.         return main.nodes[tiley][tilex].passable;
  320.     }
  321.     
  322.     public function draw(canvas:BitmapData):void {
  323.         var destPoint:Point = new Point(_posx - Particle.IMAGE_RADIUS, _posy - Particle.IMAGE_RADIUS);
  324.         canvas.copyPixels(_images[_imageIndex], _sourceRect, destPoint);
  325.     }
  326.     
  327.     // パーティクルの画像を予め作成しておく関数
  328.     public static function createImages():void {
  329.         Particle.IMAGE_RADIUS = Parameter.particleRadius;
  330.         _images = [];
  331.         _sourceRect = new Rectangle(00, Particle.IMAGE_RADIUS * 2, Particle.IMAGE_RADIUS * 2);
  332.         for (var i:int = 0; i < Particle.IMAGE_COLORS.length; i++) {
  333.             var bitmapData:BitmapData = new BitmapData(Particle.IMAGE_RADIUS * 2, Particle.IMAGE_RADIUS * 2
  334. true, 0x00ffffff);
  335.             var shape:Shape = new Shape();
  336.             shape.graphics.beginFill(Particle.IMAGE_COLORS[i]);
  337.             shape.graphics.drawCircle(Particle.IMAGE_RADIUS, Particle.IMAGE_RADIUS, Particle.IMAGE_RADIUS);
  338.             shape.graphics.endFill();
  339.             bitmapData.draw(shape);
  340.             _images.push(bitmapData);
  341.         }
  342.     }
  343. }
  344. class Start {
  345.     private static var SPAWN_INTERVAL:int;
  346.     
  347.     private var _node:Node;
  348.     private var _destination:Goal;
  349.     private var _spawnCount:int;
  350.     
  351.     public function get node():Node { return _node; }
  352.     public function get destination():Goal { return _destination; }
  353.     
  354.     public function Start(node:Node, dest:Goal) {
  355.         _node = node;
  356.         _destination = dest;
  357.         _spawnCount = 0;
  358.     }
  359.     
  360.     public function update():void {
  361.         // ゴールノードへの経路が存在しない場合は何もしない
  362.         if (_destination.getCost(node.tileX, node.tileY) == int.MAX_VALUE) { return; }
  363.         
  364.         // 一定の間隔でパーティクルの出現を試みる
  365.         if (++_spawnCount >= Start.SPAWN_INTERVAL) {
  366.             // 画面上に存在しないパーティクルがあればそれをここから出現させる
  367.             var len:int = main.particles.length;
  368.             for (var i:int = 0; i < len; i++) {
  369.                 if (!main.particles[i].exists) {
  370.                     main.particles[i].spawn(this);
  371.                     _spawnCount = 0;
  372.                     break;
  373.                 }
  374.             }
  375.         }
  376.     }
  377.     
  378.     public static function setSpawnInterval():void {
  379.         Start.SPAWN_INTERVAL = Parameter.spawnInterval;
  380.     }
  381. }
  382. class Goal {
  383.     private static const DX:Array = [ -101, -11, -101];
  384.     private static const DY:Array = [ -1, -1, -100111];
  385.     private static const DCOST:Array = [Math.SQRT2, 1, Math.SQRT2, 11, Math.SQRT2, 1, Math.SQRT2];
  386.     
  387.     private var _ID:int;
  388.     private var _node:Node;
  389.     
  390.     private var _openNodes:Array;    // 保留ノードリスト
  391.     private var _closedNodes:Array;    // 確定ノードリスト
  392.     private var _nodeCost:Array;    // 各ノードの移動コスト
  393.     private var _nodeNext:Array;    // 各ノードの次の経路となるノード
  394.     
  395.     public function get ID():int { return _ID; }
  396.     public function get node():Node { return _node; }
  397.     public function getCost(tilex:int, tiley:int):int { return _nodeCost[tiley][tilex]; }
  398.     public function getNext(tilex:int, tiley:int):Node { return _nodeNext[tiley][tilex]; }
  399.     
  400.     public function Goal(ID:int, node:Node) {
  401.         _ID = ID;
  402.         _node = node;
  403.         
  404.         _openNodes = [];
  405.         _closedNodes = [];
  406.         _nodeCost = [];
  407.         _nodeNext = [];
  408.         for (var row:int = 0; row < main.MAP_SIZE; row++) {
  409.             _closedNodes[row] = [];
  410.             _nodeCost[row] = [];
  411.             _nodeNext[row] = [];
  412.         }
  413.         
  414.         search();
  415.     }
  416.     
  417.     private function initialize():void {
  418.         for (var row:int = 0; row < main.MAP_SIZE; row++) {
  419.             for (var col:int = 0; col < main.MAP_SIZE; col++) {
  420.                 _closedNodes[row][col] = false;
  421.                 _nodeCost[row][col] = int.MAX_VALUE;
  422.                 _nodeNext[row][col] = main.nodes[row][col];
  423.             }
  424.         }
  425.         
  426.         // Goalのノードを経路探索のスタートノードとする
  427.         _nodeCost[_node.tileY][_node.tileX] = 0;
  428.         _openNodes.push(main.nodes[_node.tileY][_node.tileX]);
  429.     }
  430.     
  431.     // ダイクストラ法による経路探索
  432.     public function search():void {
  433.         initialize();
  434.         
  435.         while (_openNodes.length > 0) {
  436.             var subject:Node = _openNodes.pop() as Node;
  437.             _closedNodes[subject.tileY][subject.tileX] = true;
  438.             
  439.             // 周囲8方向のノードを訪問する
  440.             for (var i:int = 0; i < 8; i++) {
  441.                 // 画面外の存在しないノードを指すなら次の周囲ノードへ進む
  442.                 if (!isValid(subject.tileX + Goal.DX[i]) || !isValid(subject.tileY + Goal.DY[i])) { 
  443. continue; }
  444.                 
  445.                 // 壁が設置されているノード、確定ノード、直進することができないノードなら次の周囲ノードへ進む
  446.                 var test:Node = main.nodes[subject.tileY + Goal.DY[i]][subject.tileX + Goal.DX[i]];
  447.                 if (isWall(test) || isClosedNode(test) || !canGoStraightTo(subject, test)) { continue; }
  448.                 
  449.                 // 既存の移動コストより小さかったら更新する
  450.                 if (_nodeCost[test.tileY][test.tileX] > _nodeCost[subject.tileY][subject.tileX] + 
  451. Goal.DCOST[i]) {
  452.                     _nodeCost[test.tileY][test.tileX] = _nodeCost[subject.tileY][subject.tileX] + 
  453. Goal.DCOST[i];
  454.                     // 次の経路ノードをsubjectノードに設定する
  455.                     _nodeNext[test.tileY][test.tileX] = subject;
  456.                     // 保留ノードリストに追加する
  457.                     insertToOpenNodes(test);
  458.                 }
  459.             }
  460.         }
  461.     }
  462.     
  463.     // indexの値が有効な値かどうか
  464.     private function isValid(index:int):Boolean {
  465.         return ((index >= 0) && (index < main.MAP_SIZE));
  466.     }
  467.     
  468.     private function isClosedNode(node:Node):Boolean {
  469.         return _closedNodes[node.tileY][node.tileX];
  470.     }
  471.     
  472.     // 壁が設置されたノードかどうか
  473.     private function isWall(node:Node):Boolean {
  474.         return !node.passable;
  475.     }
  476.     
  477.     // subjectノードからtestノードへ直進できるかどうか
  478.     private function canGoStraightTo(subject:Node, test:Node):Boolean {
  479.         return (main.nodes[subject.tileY][test.tileX].passable && main.nodes[test.tileY][subject.tileX].passable);
  480.     }
  481.     
  482.     // nodeを保留ノードリストの適切な場所に挿入する
  483.     private function insertToOpenNodes(node:Node):void {
  484.         var insertIndex:int;
  485.         var len:int = _openNodes.length;
  486.         var nodeCost:int = _nodeCost[node.tileY][node.tileX];
  487.         
  488.         for (insertIndex = 0; insertIndex < len; insertIndex++) {
  489.             var openNode:Node = _openNodes[insertIndex];
  490.             if (nodeCost > _nodeCost[openNode.tileY][openNode.tileX]) { break; }
  491.         }
  492.         _openNodes.splice(insertIndex, 0, node);
  493.     }
  494. }
  495. import flash.geom.Point;
  496. class Parameter {
  497.     private static const data:XML =
  498.     <root>
  499.         <particles num="1000" radius="2" maxspeed="8" />
  500.         <starts interval="10">
  501.             <start dest="0" x="12" y="0"/>
  502.             <start dest="0" x="13" y="0"/>
  503.             <start dest="1" x="14" y="0"/>
  504.             <start dest="1" x="15" y="0"/>
  505.             <start dest="2" x="16" y="0"/>
  506.             <start dest="3" x="17" y="0"/>
  507.             <start dest="3" x="18" y="0"/>
  508.             <start dest="4" x="19" y="0"/>
  509.             <start dest="4" x="20" y="0"/>
  510.             
  511.         </starts>
  512.         <goals>
  513.             <goal id="0" x="14" y="32"/>
  514.             <goal id="1" x="15" y="32"/>
  515.             <goal id="2" x="16" y="32"/>
  516.             <goal id="3" x="17" y="32"/>
  517.             <goal id="4" x="18" y="32"/>
  518.             
  519.         </goals>
  520.     </root>;
  521.     
  522.     public static function get numParticles():int {
  523.         return int(Parameter.data.particles.@num);
  524.     }
  525.     
  526.     public static function get particleRadius():int {
  527.         return int(Parameter.data.particles.@radius);
  528.     }
  529.     
  530.     public static function get particleMaxSpeed():Number {
  531.         return Number(Parameter.data.particles.@maxspeed);
  532.     }
  533.     
  534.     public static function get spawnInterval():int {
  535.         return int(Parameter.data.starts.@interval);
  536.     }
  537.     
  538.     public static function getWalls():Array {
  539.         var walls:Array = [];
  540.         for each(var w:XML in Parameter.data.walls.*) {
  541.             var removable:Boolean = ((w.@rem == "t") ? true : false);
  542.             var wall:Wall = new Wall(int(w.@x), int(w.@y), removable);
  543.             walls.push(wall);
  544.         }
  545.         return walls;
  546.     }
  547.     
  548.     public static function getStarts(goals:Array):Array {
  549.         var starts:Array = [];
  550.         var len:int = goals.length;
  551.         
  552.         for each(var s:XML in Parameter.data.starts.*) {
  553.             var destID:int = int(s.@dest);
  554.             var i:int;
  555.             for (i = 0; i < len; i++) {
  556.                 if (destID == goals[i].ID) { break; }
  557.             }
  558.             if (i >= len) { i = 0; }
  559.             
  560.             var node:Node = main.nodes[int(s.@y)][int(s.@x)];
  561.             var dest:Goal = goals[i];
  562.             var start:Start = new Start(node, dest);
  563.             starts.push(start);
  564.         }
  565.         return starts;
  566.     }
  567.     
  568.     public static function getGoals():Array {
  569.         var goals:Array = [];
  570.         for each(var g:XML in Parameter.data.goals.*) {
  571.             var node:Node = main.nodes[int(g.@y)][int(g.@x)];
  572.             var goal:Goal = new Goal(int(g.@id), node);
  573.             goals.push(goal);
  574.         }
  575.         return goals;
  576.     }
  577. }
noswf
Get Adobe Flash Player