バタフライ効果の実験
バタフライ効果の実験
クリックで一番近い人の位置に干渉
干渉しなかったときの挙動を暗い色で表示
もっと緩やかに崩壊していくものかと思ってたらそうでもなかった・・・
/**
* 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/iLLY
*/
// forked from Nao_u's 群集シミュレーション
//
// バタフライ効果の実験
//
// クリックで一番近い人の位置に干渉
// 干渉しなかったときの挙動を暗い色で表示
//
//
// もっと緩やかに崩壊していくものかと思ってたらそうでもなかった・・・
//
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 FieldAry:Vector.<Field>;
var ResetButton:Button;
var bReset:Boolean = false;
function initialize():void{
Text = new TextField();
Text.text = "----";
Text.autoSize = "left";
Main.addChild(Text);
graphicClear();
createResetButton();
reset();
}
function reset():void{
bReset = false;
FieldAry = new Vector.<Field>;
FieldAry.push( new Field(false) );
FieldAry.push( new Field(true) );
}
function createResetButton():void{
ResetButton = new Button(64, 18, 8, "Reset", 16);
ResetButton.x = 468-74
ResetButton.y = 468-28
ResetButton.addEventListener(MouseEvent.CLICK,
function(event:MouseEvent):void{bReset = true;}
);
Main.addChild(ResetButton);
}
function update(e :Event):void{
var time:int = getTimer();
if( bReset == true ) reset();
graphicClear();
KeyPrev = KeyData;
var dx:Number, dy:Number;
var i:int;
var a0:Agent, a1:Agent;
for( i=0; i<FieldAry[0].AgentAry.length; i++ ){
a0 = FieldAry[0].AgentAry[i];
a1 = FieldAry[1].AgentAry[i];
drawLine( a0.Pos.x, a0.Pos.y, a1.Pos.x, a1.Pos.y, 0.5, 0xffffff-(16 * 65536 + 8*256) );
}
for( i=0; i<FieldAry.length; i++ ){
FieldAry[i].update();
}
var endTime:int = getTimer() - time;
Text.text = "生成時間:" + endTime + "[ms]";
updateKey();
MouseUpdate();
}
var GridSize:int = 16;
var GridLength:int = (SCREEN_W / GridSize)+2;
class Field{
public var AgentAry:Vector.<Agent> = new Vector.<Agent>;
public var Grid:Vector.<Array>;
public var Wall:Vector.<int>;
public var GridVec:Vector.<Point>;
public var GridVecDisp:Boolean = false;
public var bBackGround:Boolean = false;
public function Field( bBack:Boolean ):void{
bBackGround = bBack;
initialize();
}
public function initialize():void{
initWall();
updateGrid();
var num:int = 150;
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; target.y = -SCREEN_H/16; }
else { dir =Math.PI/2; target.x = SCREEN_W+SCREEN_W/16; target.y = SCREEN_H/2;}
var color:int;
if( i < num/2 ){
if( bBackGround ) color = 0xf0e800;
else color = 0x706000;
}else{
if( bBackGround ) color = 0xb0e800;
else color = 0x405000;
}
AgentAry.push( new Agent( this, new Point(0+(i/25)*35, 0+((i%5)*55)), dir, target, color ) );
AgentAry[i].update();
}
}
public function update():void{
drawWall();
updateGrid();
for( var i:int=0; i<AgentAry.length; i++ ){
AgentAry[i].update();
}
if( bBackGround ) updateClick();
if( KeyPrev == 0 && KeyData != 0 ){
if( GridVecDisp == true ) GridVecDisp = false;
else GridVecDisp = true;
}
KeyPrev = KeyData;
drawGridVec();
}
public 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;
*/
var agt:Agent = getNearAgent( mx, my );
agt.Pos.x += 5;
}
}
public function getNearAgent( mx:Number, my:Number ):Agent{
var ret:Agent;
var mp:Point = new Point( mx, my );
var minDist:Number = 999999;
var dist:Number;
for( var i:int=0; i<AgentAry.length; i++ ){
dist = getDist( mp, AgentAry[i].Pos );
if( dist < minDist ) { minDist = dist; ret = AgentAry[i]; }
}
return ret;
}
public 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;
}
public 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 );
}
}
}
}
public 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;
}
public 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 );
}
}
public 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 );
}
}
}
public function addGridVec( agt:Agent ):void{
var idx:int = agt.getIndex();
GridVec[idx].x += agt.PrevVec.x;
GridVec[idx].y += agt.PrevVec.y;
}
public var target:Point = new Point;
public 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 );
}
}
}
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 var Fld:Field
public function Agent( fld:Field, p:Point, r:Number, t:Point, c:int ){
Fld = fld;
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;
Color = c;
}
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;
Fld.addGridVec( this );
}
PrevPos.x = Pos.x;
PrevPos.y = Pos.y;
Fld.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 = Fld.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 < 10 ){
leave( ag, 0.12 );
if( spdDot < 0.5 )
{
if( d > 0.2 ) Spd *= 0.99;
else Spd *= 0.999;
}
}else if( dist < 11 ){
leave( ag, 0.035 );
}else if( dist < 15 ){
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( Fld.Wall[idx] != 0 ){
var wallPos:Point = Fld.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);
}
import flash.filters.ColorMatrixFilter;
import flash.filters.GlowFilter;
class Button extends Sprite{
private static const mono:ColorMatrixFilter = new ColorMatrixFilter([
1 / 3, 1 / 3, 1 / 3, 0, 10,
1 / 3, 1 / 3, 1 / 3, 0, 10,
1 / 3, 1 / 3, 1 / 3, 0, 10,
0, 0, 0, 1, 0
]);
private var _textField:TextField = new TextField();
private var _size:int;
private var _hover:Boolean = false;
public function get hover():Boolean{
return _hover;
}
public function set hover(value:Boolean):void{
if(_hover != value){
_hover = value;
filters = (_hover ? null : [mono]);
}
}
public function Button(W:Number, H:Number, R:Number, label:String = "", size:int = 11){
var matrix:Matrix = new Matrix();
matrix.createGradientBox(W, H, Math.PI / 2);
var bg:Sprite = new Sprite();
bg.graphics.beginGradientFill("linear", [0x8c99a4, 0xc5d4e1, 0xBAD2E8], [1, 1, 1],
[0, 120, 136], matrix);
bg.graphics.drawRoundRect(0, 0, W, H, R, R);
bg.graphics.endFill();
bg.filters = [new GlowFilter(0xFFFFBE, .5, 10, 10, 2, 1, true)];
addChild(bg);
var line:Sprite = new Sprite();
line.graphics.lineStyle(3, 0xBAD2E8);
line.graphics.drawRoundRect(0, 0, W, H, R, R);
addChild(line);
filters = [mono];
buttonMode = true;
mouseChildren = false;
if (label != ""){
_size = size;
_textField.selectable = false;
_textField.autoSize = "left";
_textField.htmlText = <font size={size} color="#4B4349">{label}</font>.toXMLString();
_textField.x = (W - _textField.width) / 2;
_textField.y = (H - _textField.height) / 2;
addChild(_textField);
}
addEventListener("rollOver", buttonRollOver);
addEventListener("rollOut", buttonRollOut);
addEventListener("removed", function(event:Event):void{
removeEventListener("rollOver", buttonRollOver);
removeEventListener("rollOut", buttonRollOut);
removeEventListener("removed", arguments.callee);
});
}
public function setLabel( label:String ):void{
_textField.htmlText = <font size={_size} color="#4B4349">{label}</font>.toXMLString();
}
protected function buttonRollOver(event:Event):void{
hover = true;
}
protected function buttonRollOut(event:Event):void{
hover = false;
}
}