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

群集シミュレーション

群集シミュレーション
黄色い人は画面下から上へ、緑の人は左から右へ移動
マウスクリックで壁を設置
カーソルキーで速度ベクトル表示のON/OFF切り替え
解説など:http://game.g.hatena.ne.jp/Nao_u/20091223
/**
 * Copyright Nao_u ( http://wonderfl.net/user/Nao_u )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/m29Y
 */

//
// 群集シミュレーション
// 
// 黄色い人は画面下から上へ、緑の人は左から右へ移動
//
// マウスクリックで壁を設置
// カーソルキーで速度ベクトル表示のON/OFF切り替え
//
// 解説など:http://game.g.hatena.ne.jp/Nao_u/20091223
//
package {     
    import flash.display.Sprite;     
    import flash.events.*;     
    [SWF(width="465", height="465", backgroundColor="0xFFFFFF", frameRate="60")]      
        
    public class FlashTest extends Sprite {     
        public function FlashTest() {     
            Main = this;     
            initialize();     
            stage.addEventListener(Event.ENTER_FRAME,update);      
            stage.addEventListener(KeyboardEvent.KEY_UP,   keyCheckUp);      
            stage.addEventListener(KeyboardEvent.KEY_DOWN, keyCheckDown);      
            stage.addEventListener(MouseEvent.MOUSE_UP,    MouseCheckUp);      
            stage.addEventListener(MouseEvent.MOUSE_DOWN,  MouseCheckDown);      
        }     
    }     
}             
    
import flash.display.Sprite;      
import flash.events.Event;     
import flash.events.KeyboardEvent;      
import flash.events.MouseEvent;       
import flash.ui.Keyboard;      
import flash.text.TextField;     
import flash.geom.*;  
import flash.utils.getTimer; 
import frocessing.color.ColorHSV; 

var Main:Sprite;     
var SCREEN_W:Number = 465;    
var SCREEN_H:Number = 465;    
var Text:TextField    

var T:Number = 0.5; 
var Mode:int = 0; 
var AgentAry:Vector.<Agent> = new Vector.<Agent>; 
var Grid:Vector.<Array>; 
var GridSize:int = 16;
var GridLength:int = (SCREEN_W / GridSize)+2;
var Wall:Vector.<int>; 
var GridVec:Vector.<Point>; 
var GridVecDisp:Boolean = false;

function initialize():void{     
    Text = new TextField();     
    Text.text = "----";   
    Text.autoSize = "left";   
    Main.addChild(Text);      
       
    graphicClear();   

    initWall();
    updateGrid();
    var num:int = 380;
    for( var i:int=0; i<num; i++ ){
        var dir:Number;
        var target:Point = new Point;
        if( i < num/2 )  { dir =Math.PI;   target.x = SCREEN_W/2-15; target.y = -SCREEN_H/4; }
        else             { dir =Math.PI/2; target.x = SCREEN_W*1.25; target.y = SCREEN_H/2+15;}   
        //dir =Math.PI*0.5;
        AgentAry.push( new Agent( new Point(0+(i/25)*35, 0+((i%5)*55)), dir, target ) ); 
       AgentAry[i].update();
    }
}     

function update(e :Event):void{     

    var time:int = getTimer();  

    graphicClear();   

    drawWall();
    updateGrid();
    for( var i:int=0; i<AgentAry.length; i++ ){
       AgentAry[i].update();
    }
    updateClick();

    if( KeyPrev == 0 && KeyData != 0 ){
        if( GridVecDisp == true ) GridVecDisp = false;
        else                      GridVecDisp = true;
    }
    KeyPrev = KeyData;
    drawGridVec();

    var endTime:int = getTimer() - time; 
    Text.text = "生成時間:" + endTime + "[ms]";    

    updateKey(); 
    MouseUpdate();

}  


function updateClick():void{
    var mx:Number = Main.stage.mouseX;     
    var my:Number = Main.stage.mouseY;     
    if( MouseData & MOUSE_LEFT_TRG ){
        var idx:int = int(mx/GridLength)+1+ (int(my/GridLength)+1)*GridSize;
        if( Wall[idx] == 0 ) Wall[idx] = 1;
        else                 Wall[idx] = 0;   
    }
}

function initWall():void{
    GridVec = new Vector.<Point>
    Wall = new Vector.<int>
    var idx:int;
    for( var x:int=0; x<GridSize+2; x++ ){
        for( var y:int=0; y<GridSize+2; y++ ){
            idx = x + y*GridSize;
            Wall.push( 0 );
            GridVec.push( new Point() );
        }
   }
//    Wall[117] = 1;
    Wall[118] = 1;
    Wall[119] = 1;
    Wall[120] = 1;
//    Wall[121] = 1;
    Wall[123] = 1;
    Wall[123+16] = 1;
    Wall[123+32] = 1;
}

function drawWall():void{
    var idx:int;
    for( var x:int=1; x<GridSize; x++ ){
        for( var y:int=1; y<GridSize; y++ ){
            idx = x + y*GridSize;
            if( Wall[idx] == 1 ){
                var sx:Number = (x-1)*GridLength;
                var sy:Number = (y-1)*GridLength;
                var ex:Number = GridLength;
                var ey:Number = GridLength;
                drawRect( sx, sy, ex, ey, 0x808080 );                
            }
        }
   }
}

function getWallCenter( pos:Point ):Point{
    var ret:Point = new Point;
    ret.x = (int(pos.x/GridLength))*GridLength + GridLength/2;
    ret.y = (int(pos.y/GridLength))*GridLength + GridLength/2;
    return ret;
}

function updateGrid():void{
    Grid = new Vector.<Array>
    var idx:int;
    for( var x:int=0; x<GridSize+2; x++ ){
        for( var y:int=0; y<GridSize+2; y++ ){
            idx = x + y*GridSize;
            Grid.push( new Array() );
            GridVec[idx].x *= 0.5;
            GridVec[idx].y *= 0.5;
        }
   }

   var agt:Agent;
   for( var i:int=0; i<AgentAry.length; i++ ){
      agt = AgentAry[i];
      idx = agt.getIndex(); 
      Grid[idx].push( agt );
   }
}

function addGridVec( agt:Agent ):void{
    var idx:int = agt.getIndex(); 
    GridVec[idx].x += agt.PrevVec.x;
    GridVec[idx].y += agt.PrevVec.y;
}

var target:Point = new Point;
function appryGridVec( agt:Agent ):void{
    var idx:int = agt.getIndex(); 
    target.x = GridVec[idx].x + agt.Pos.x;
    target.y = GridVec[idx].y + agt.Pos.y;
    if( Math.abs(GridVec[idx].x) > 0.01 || Math.abs(GridVec[idx].y) > 0.01 ){
        agt.rotToTarget( target, 0.6 );
    }
}

function drawGridVec():void{
    if( GridVecDisp == false ) return;

    var idx:int;
    for( var x:int=1; x<GridSize; x++ ){
        for( var y:int=1; y<GridSize; y++ ){
            idx = x + y*GridSize;
            var sx:Number = (x-1)*GridLength+GridLength*0.5;
            var sy:Number = (y-1)*GridLength+GridLength*0.5;
            var ex:Number = sx+GridVec[idx].x * 2.5;
            var ey:Number = sy+GridVec[idx].y * 2.5;
            drawLine( sx, sy, ex, ey, 1.4, 0xff0000 );                
        }
   }
}

class Agent{  
    public var Sp:Sprite;  
    public var isEnable:Boolean = false; 
    public var Pos:Point = new Point; 
    public var PrevPos:Point = new Point; 
    public var PrevVec:Point = new Point; 
    public var SpdVec:Point = new Point; 
    public var MoveVec:Point = new Point; 
    public var NearVel:Point = new Point; 
    public var TargetPos:Point = new Point; 
    public var WallTargetPos:Point = new Point; 
    public var DirVec:Point = new Point; 
    public var Spd:Number; 
    public var Rot:Number; 
    public var Color:int;
    public var WallHitCnt:int;
    public function Agent( p:Point, r:Number, t:Point ){  
        Sp=new Sprite();    
        Pos = p;  
        PrevPos.x = Pos.x;
        PrevPos.y = Pos.y;
        Sp.x = Pos.x;  
        Sp.y = Pos.y; 
        Main.stage.addChild(Sp);   
        Rot = r;
        Spd = 1;
        TargetPos = t;
        
        while( Rot > Math.PI*2 ) Rot -= Math.PI*2;
        //if( Rot > Math.PI/2 && Rot < Math.PI/2*3 )  
        if( Rot > Math.PI/2 )  
                             Color = 0xf0e800;//new ColorHSV( (Rot % (2*Math.PI))*36, 0.5, 1).value32;
        else                 Color = 0xb0e800;
    }  

    public function update( ):void{
        var reset:Boolean = false;
        if( Spd < 1.5 ) Spd += 0.004;
        
        SpdVec.x = Spd * Math.sin( Rot );
        SpdVec.y = Spd * Math.cos( Rot );

        if( Pos.x < -15 ) { Pos.x += SCREEN_W+30; reset = true; }
        if( Pos.y < -15 ) { Pos.y += SCREEN_H+30; Pos.x = Pos.x * 0.25 + 160; reset = true; }
        if( Pos.x > SCREEN_W+15 ) { Pos.x -= SCREEN_W+30; Pos.y = Pos.y * 0.25 + 200; reset = true; }
        if( Pos.y > SCREEN_H+15 ) { Pos.y -= SCREEN_H+30; reset = true; }
        Sp.x = Pos.x;  
        Sp.y = Pos.y; 
        
        checkWall();
        checkNear();

        var dirVec:Point = new Point;
        if( !(NearVel.x == 0.0 && NearVel.y == 0.0 ) ) normalize( NearVel );
        dirVec.x = SpdVec.x;
        dirVec.y = SpdVec.y;
        normalize( dirVec );
        var c:Number = cross( dirVec, NearVel );
        Rot -= c*0.6;
      
        Pos.x += SpdVec.x + MoveVec.x*3;                      
        Pos.y += SpdVec.y + MoveVec.y*3;                      

        drawCircle( Pos.x, Pos.y, 5, Color );
        var vec:Point = new Point;
        vec.x = SpdVec.x + MoveVec.x*0.05;
        vec.y = SpdVec.y + MoveVec.y*0.05;
        normalize( vec );
        DirVec.x += (vec.x - DirVec.x) * 0.10;
        DirVec.y += (vec.y - DirVec.y) * 0.10;
        drawCircle( Pos.x+DirVec.x*5, Pos.y+DirVec.y*5, 1.4, 0x0 );
        
        if( WallHitCnt > 0 ) {
            WallHitCnt--;
            rotToTarget( WallTargetPos, 0.6 );
        }else{
            rotToTarget( TargetPos, 0.6 );
        }
        MoveVec.x = 0;
        MoveVec.y = 0;
        checkWall();
        Pos.x += MoveVec.x*3;                      
        Pos.y += MoveVec.y*3;                      

        if( reset == false ){
            PrevVec.x = (Pos.x - PrevPos.x) + MoveVec.x*3;
            PrevVec.y = (Pos.y - PrevPos.y) + MoveVec.y*3;
            addGridVec( this );
        }
        PrevPos.x = Pos.x;
        PrevPos.y = Pos.y;

        appryGridVec( this );
   }

    public function checkNear():void{     
        var idx:int;
        var lp:int;
        var ar:Array;
        var ag:Agent;
        NearVel.x = 0;
        NearVel.y = 0;
        for( var x:int=-1; x<2; x++ ){
            for( var y:int=-1; y<2; y++ ){
                 idx = getIndexOfs(x,y);
                ar = Grid[idx];
                lp = ar.length;
                for( var i:int=0; i<lp; i++ ){
                    ag = ar[i];
                    if( ag != this ){
                        var dist:Number = getDist( this.Pos, ag.Pos );

                        if( dist < 50 ){
                            if( dist < 25 ){
                                NearVel.x += ag.SpdVec.x;
                                NearVel.y += ag.SpdVec.y;
                            }
                            var dirVec:Point = new Point;
                            var dirVecAg:Point = new Point;
                            var targetVec:Point = new Point;
                            targetVec.x = ag.Pos.x - this.Pos.x;
                            targetVec.y = ag.Pos.y - this.Pos.y;
                            dirVec.x = SpdVec.x;
                            dirVec.y = SpdVec.y;
                            dirVecAg.x = ag.SpdVec.x;
                            dirVecAg.y = ag.SpdVec.y;

                            normalize( targetVec );
                            normalize( dirVec );
                            normalize( dirVecAg );

                            var spdDot:Number = dot( dirVec, dirVecAg );
                            var d:Number = dot( targetVec, dirVec );
                            if( d > 0 ){
                                var c:Number = cross( targetVec, dirVec );
                                Rot -= c*0.030;
                                if( dist < 18 ){
                                    Rot -= c*0.005;
                                    if( d > 0.40 ){
                                        if( spdDot < 0.4 ) Spd *= 0.90;
                                        Rot -= c*0.5+0.001;
                                    }
                                }
                            }
                            
                            if( dist < 9 ){
                                leave( ag, 0.25 );
                                if( spdDot < 0.95 )
                                {
                                    if( d > 0.2 ) Spd *= 0.98;
                                    else        Spd *= 0.99;
                                }
                            }else if( dist < 13 ){
                                leave( ag, 0.12 );
                                if( spdDot < 0.5 )
                                {
                                    if( d > 0.2 ) Spd *= 0.99;
                                    else        Spd *= 0.999;
                                }
                            }else if( dist < 20 ){
                                leave( ag, 0.035 );
                            }else if( dist < 30 ){
                                leave( ag, 0.01 );
                            }
                        }
                    }
                }
            }
        }
    }

    public function checkWall():void{     
         checkWallPos( Pos.x, Pos.y );
         checkWallPos( Pos.x, Pos.y-6 );
         checkWallPos( Pos.x, Pos.y+6 );
         checkWallPos( Pos.x-6, Pos.y );
         checkWallPos( Pos.x+6, Pos.y );
         checkWallPos( Pos.x-6, Pos.y-6 );
         checkWallPos( Pos.x-6, Pos.y+6 );
         checkWallPos( Pos.x+6, Pos.y-6 );
         checkWallPos( Pos.x+6, Pos.y+6 );
    }

    public function checkWallPos( px:Number, py:Number ):void{     
        var idx:int = getIndexPos(px, py);
        if( Wall[idx] != 0 ){
            var wallPos:Point = getWallCenter( new Point(px,py) );
            var vec:Point = new Point;
            vec.x = wallPos.x - px;
            vec.y = wallPos.y - py;
            normalize( vec );
            this.MoveVec.x -= 0.125 * vec.x;
            this.MoveVec.y -= 0.125 * vec.y;
            WallHitCnt = 50;
            
            WallTargetPos.x = Pos.x - vec.x * 100; 
            WallTargetPos.y = Pos.y - vec.y * 100; 
        }
    }

    public function leave( ag:Agent, pow:Number ):void{
        var vec:Point = new Point;
        vec.x = ag.Pos.x - this.Pos.x;
        vec.y = ag.Pos.y - this.Pos.y;
        normalize( vec );
        this.MoveVec.x -= pow * vec.x;
        this.MoveVec.y -= pow * vec.y;
        ag.MoveVec.x += pow * vec.x;
        ag.MoveVec.y += pow * vec.y;
    }

    private var dirVec:Point = new Point;
    private var targetVec:Point = new Point;
    public function rotToTarget( target:Point, pow:Number ):void{     
        dirVec.x = SpdVec.x;
        dirVec.y = SpdVec.y;
        normalize( dirVec );
        targetVec.x = target.x - Pos.x;
        targetVec.y = target.y - Pos.y;
        normalize( targetVec );
        var c:Number = cross( dirVec, targetVec );
        Rot -= c*pow;
    }

    public function getIndex():int{     
        var idx:int = int(Pos.x/GridLength)+1+ (int(Pos.y/GridLength)+1)*GridSize;
//        if( idx < 0 || idx > (GridSize+1)*(GridSize+1) ) Text.text = "ERR " + idx +"x:"+Pos.x +" y:"+Pos.y ;
        return idx;
    }
    public function getIndexOfs( ofsX:int, ofsY:int ):int{     
        var idx:int = int(Pos.x/GridLength)+1+ofsX + (int(Pos.y/GridLength)+1+ofsY)*GridSize;
//        if( idx < 0 || idx > (GridSize+1)*(GridSize+1) ) Text.text = "ERR " + idx +"x:"+Pos.x +" y:"+Pos.y ;
        return idx;
    }

    public function getIndexPos( px:Number, py:Number ):int{     
        var idx:int = int(px/GridLength)+1+ (int(py/GridLength)+1)*GridSize;
//        if( idx < 0 || idx > (GridSize+1)*(GridSize+1) ) Text.text = "ERR " + idx +"x:"+Pos.x +" y:"+Pos.y ;
        return idx;
    }

    public function setEnable( flg:Boolean ):void{     
        if( flg == true && isEnable == false ){ 
            Sp.graphics.clear(); 
            Sp.graphics.lineStyle(1.4,0x000000);        
            Sp.graphics.beginFill(0xe0d000,1);    
            Sp.graphics.drawCircle(0,0,8.0);    
            Sp.graphics.endFill();    
        }else if( flg == false && isEnable == true ){ 
            Sp.graphics.clear(); 
        } 
        isEnable = flg; 
    } 
}  


function graphicClear():void{   
    Main.graphics.clear();    
    Main.graphics.lineStyle(1.2,0xb0b040);        
    Main.graphics.moveTo( SCREEN_W/2,          0 );        
    Main.graphics.lineTo( SCREEN_W/2,   SCREEN_H );           
    Main.graphics.moveTo(          0, SCREEN_H/2 );        
    Main.graphics.lineTo(   SCREEN_W, SCREEN_H/2 );           
}   

function drawRect(sx:Number, sy:Number, ex:Number, ey:Number, col:int ):void{     
    Main.graphics.lineStyle( 0, col );
    Main.graphics.beginFill( col );
    Main.graphics.drawRect( sx, sy, ex, ey );
    Main.graphics.endFill();
}
function drawLine( sx:Number, sy:Number, ex:Number, ey:Number, size:Number, col:int ):void{     
    Main.graphics.lineStyle(size,col);        
    Main.graphics.moveTo( sx, sy );        
    Main.graphics.lineTo( ex, ey );           
}    

function drawCircle( x:Number, y:Number, size:Number, col:int ):void{     
    Main.graphics.lineStyle(1.0,0x000000);        
    Main.graphics.beginFill( col, 1 );     
    Main.graphics.drawCircle( x, y, size );     
    Main.graphics.endFill();     
}    

function drawCircleLine( x:Number, y:Number, size:Number, col:int ):void{     
    Main.graphics.lineStyle(1.0,0x000000);        
//    Main.graphics.beginFill( col, 1 );     
    Main.graphics.drawCircle( x, y, size );     
//    Main.graphics.endFill();     
}  
var KEY_UP:int    = 0x01;     
var KEY_DOWN:int  = 0x02;     
var KEY_LEFT:int  = 0x04;     
var KEY_RIGHT:int = 0x08;     
var KEY_UP_TRG:int    = 0x10;     
var KEY_DOWN_TRG:int  = 0x20;     
var KEY_LEFT_TRG:int  = 0x40;     
var KEY_RIGHT_TRG:int = 0x80;     
var KeyData:int;     
var KeyPrev:int;     
function keyCheckDown(event:KeyboardEvent):void {      
    switch (event.keyCode){      
        case Keyboard.UP:      KeyData |= KEY_UP|KEY_UP_TRG; break;     
        case Keyboard.DOWN:    KeyData |= KEY_DOWN|KEY_DOWN_TRG; break;     
        case Keyboard.LEFT:    KeyData |= KEY_LEFT|KEY_LEFT_TRG; break;     
        case Keyboard.RIGHT:   KeyData |= KEY_RIGHT|KEY_RIGHT_TRG; break;     
    }     
} 

function keyCheckUp(event:KeyboardEvent):void {      
    switch (event.keyCode){      
        case Keyboard.UP:      KeyData &= ~KEY_UP; break;     
        case Keyboard.DOWN:    KeyData &= ~KEY_DOWN; break;     
        case Keyboard.LEFT:    KeyData &= ~KEY_LEFT; break;     
        case Keyboard.RIGHT:   KeyData &= ~KEY_RIGHT; break;     
    }     
}      

function updateKey():void{     
    KeyData &= ~(KEY_UP_TRG|KEY_DOWN_TRG|KEY_LEFT_TRG|KEY_RIGHT_TRG); 
}              

var MOUSE_LEFT:int = 0x01;     
var MOUSE_LEFT_TRG:int = 0x02;     
var MouseData:int;     
function MouseCheckDown(event:MouseEvent):void{     
    MouseData |= MOUSE_LEFT;     
    MouseData |= MOUSE_LEFT_TRG;     
}              

function MouseCheckUp(event:MouseEvent):void{     
    MouseData &= ~MOUSE_LEFT;     
}              

function MouseUpdate():void{     
    MouseData &= ~MOUSE_LEFT_TRG;     
}   

function normalize( a:Point ):void { 
    var dist:Number = Math.sqrt( a.x * a.x + a.y*a.y );
    a.x /= dist;
    a.y /= dist;
}

// 2点間の距離を求める 
function getDist( a:Point, b:Point ):Number { 
    var dx:Number = a.x-b.x; 
    var dy:Number = a.y-b.y; 
  return Math.sqrt(dx*dx + dy*dy); 
} 

// 内積 (dot product) : a⋅b = |a||b|cosθ 
function dot( a:Point, b:Point ):Number { 
  return (a.x * b.x + a.y * b.y); 
} 

// 外積 (cross product) : a×b = |a||b|sinθ 
function cross( a:Point, b:Point ):Number { 
  return (a.x * b.y - a.y * b.x); 
}