Forked from: tepe's forked from: 流体パーティクル(解読用) diff:2 forked from: forked from: 流体パーティクル(解読用) WeiChih_Lin forked:0favorite:0lines:282license : see code comments modified : 2012-04-27 19:52:01 Embed Tweet // forked from tepe's forked from: 流体パーティクル(解読用) // forked from clockmaker's 流体パーティクル(解読用) // forked from miniapp's 流体パーティクル package { import flash.display.*; import flash.events.*; import flash.geom.*; import net.hires.debug.Stats; [SWF(backgroundColor=0x000000, width=465, height=465, frameRate=60)] public class FluidLine extends Sprite { public function FluidLine() { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private var pmouseX:Number; private var pmouseY:Number; private var canvasWidth:int = 465; private var canvasHeight:int = 465; private var mousePressed:Boolean; private var resolution:int = 300;//グリッドサイズ private var penSize:int = 40; //グリッド マトリクス private var numCols:int = canvasWidth / resolution;//x方向 private var numRows:int = canvasHeight / resolution;//y方向 //パーティクル総数 private var numParticles:int = 5000; //グリッドデータ private var gridDatasVectors:Vector.<Vector.<GridData>> = new Vector.<Vector.<GridData>>(); //パーティクルデータ private var particles:Vector.<Particle> = new Vector.<Particle>(numParticles, true); private var pcount:int = 0; private var arrows:Array = []; //----init---- private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; opaqueBackground = 0x0; //マウスイベント stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); stage.quality = StageQuality.LOW; addChild(new Stats()); //パーティクル生成 for (var i:int = 0; i < numParticles; i++) { particles[i] = new Particle(Math.random() * canvasWidth, Math.random() * canvasHeight); } //グリッド生成 for (var col:int=0; col < numCols; ++col) { gridDatasVectors[col] = new Vector.<GridData>(numRows, true); arrows[col] = []; for (var row:int = 0; row < numRows; ++row) { var gridData:GridData = new GridData(col * resolution, row * resolution, resolution); gridData.col = col; gridData.row = row; gridDatasVectors[col][row] = gridData; var arrow:Arrow = new Arrow(); addChild(arrow); arrows[col][row] = arrow; } } //グリッド描画 var gridView:Shape = new Shape(); gridView.graphics.beginFill(0x808080); addChild(gridView); for (col = 0; col <= numCols; ++col) { gridView.graphics.drawRect(0, col * resolution, canvasWidth, 1); } for (row = 0; row <= numRows; ++row) { gridView.graphics.drawRect(row * resolution, 0, 1, canvasHeight); } //隣接するグリッドをセットしていく。 for (col = 0; col < numCols; ++col) { //col:X方向 for (row = 0; row < numRows; ++row) { //row:Y方向 gridData = gridDatasVectors[col][row]; if (row > 0) { var up:GridData = gridDatasVectors[col][row - 1];//上 gridData.up = up; up.low = gridData;//下 } if (col > 0) { var left:GridData = gridDatasVectors[col - 1][row];//左 gridData.left = left; left.right = gridData;//右 } if (row > 0 && col > 0) { var upperLeft:GridData = gridDatasVectors[col - 1][row - 1]; gridData.upperLeft = upperLeft; upperLeft.lowerRight = gridData; } if (row > 0 && col < numCols - 1) { var upperRight:GridData = gridDatasVectors[col + 1][row - 1]; gridData.upperRight = upperRight; upperRight.lowerLeft = gridData; } } } gridDatasVectors.fixed = true; addEventListener(Event.ENTER_FRAME, draw);//フレーム処理 } //----init---- //-------- private function mouseDownHandler(e:Event):void { mousePressed = true; } private function mouseUpHandler(e:Event):void { mousePressed = false; } //-------- //----draw---- private function draw(e:Event):void { var mouseXvel:Number = mouseX - pmouseX; var mouseYvel:Number = mouseY - pmouseY; for each(var gridDatas:Vector.<GridData> in gridDatasVectors) { for each(var gridData:GridData in gridDatas) { if (mousePressed) {//マウスドラッグ操作 updateGridDataVelocity(gridData, mouseXvel, mouseYvel, penSize); } updatePressure(gridData); } } graphics.clear(); graphics.lineStyle(1, 0xFFFFFF); updateParticle(); for each(gridDatas in gridDatasVectors) { for each(gridData in gridDatas) { apdateVelocity(gridData); } } pmouseX = mouseX; pmouseY = mouseY; } //----draw---- //----updateParticle---- public function updateParticle():void { for each(var p:Particle in particles) { if (p.x >= 0 && p.x < canvasWidth && p.y >= 0 && p.y < canvasHeight) { var col:int = int(p.x / resolution);//自身が属しているgridDataを見つける var row:int = int(p.y / resolution); if (col > numCols - 1) col = numCols - 1; if (row > numRows - 1) row = numRows - 1; var gridData:GridData = gridDatasVectors[col][row]; var ax:Number = (p.x % resolution) / resolution; var ay:Number = (p.y % resolution) / resolution; p.xvel += (1 - ax) * gridData.xvel * 0.05; p.yvel += (1 - ay) * gridData.yvel * 0.05; p.xvel += ax * gridData.right.xvel * 0.05; p.yvel += ax * gridData.right.yvel * 0.05; p.xvel += ay * gridData.low.xvel * 0.05; p.yvel += ay * gridData.low.yvel * 0.05; p.x += p.xvel; p.y += p.yvel; var dx:Number = p.px - p.x; var dy:Number = p.py - p.y; var dist:Number = Math.sqrt(dx * dx + dy * dy); var limit:Number = Math.random() * 0.5; if (dist > limit) { graphics.moveTo(p.x, p.y); graphics.lineTo(p.px, p.py); } else { graphics.moveTo(p.x, p.y); graphics.lineTo(p.x + limit, p.y + limit); } p.px = p.x; p.py = p.y; } else { p.x = p.px = Math.random() * canvasWidth; p.y = p.py = Math.random() * canvasHeight; p.xvel = 0; p.yvel = 0; } p.xvel *= 0.5; p.yvel *= 0.5; } } //----updateParticle---- //----updateGridDataVelocity---- /** * マウスドラッグの処理 * @param gridData * @param mvelX * @param mvelY * @param penSize */ public function updateGridDataVelocity(gridData:GridData,//更新するグリッド mvelX:int, mvelY:int,//マウス移動量 penSize:Number):void {//ペン var dx:Number = gridData.x - mouseX; var dy:Number = gridData.y - mouseY; var dist:Number = Math.sqrt(dy * dy + dx * dx); if (dist < penSize) { if (dist < 4) { dist = penSize; } //マウスに近いほど力が強くなるように。 var power:Number = penSize / dist; gridData.xvel += mvelX * power; gridData.yvel += mvelY * power; } } //----updateGridDataVelocity---- //----updatePressure---- private var n1:Number = 0.1;//周囲のグリッドへの影響 private var n2:Number = 0.1;//減衰量 public function updatePressure(gridData:GridData):void { var pressureX:Number = ( gridData.upperLeft.xvel * n1 //左上 + gridData.left.xvel //左 + gridData.lowerLeft.xvel * n1 //左下 - gridData.upperRight.xvel * n1 //右上 - gridData.right.xvel //右 - gridData.lowerRight.xvel * n1 //右下 ); var pressureY:Number = ( gridData.upperLeft.yvel * n1 //左上 + gridData.up.yvel //上 + gridData.upperRight.yvel * n1 //右上 - gridData.lowerLeft.yvel * n1 //左下 - gridData.low.yvel //下 - gridData.lowerRight.yvel * n1 //右下 ); gridData.pressure = (pressureX + pressureY) * n2; //ベクトル表示 var arrow:Arrow = arrows[gridData.col][gridData.row]; arrow.x = gridData.x + resolution / 2 >> 0; arrow.y = gridData.y + resolution / 2 >> 0; arrow.rotation = Math.atan2(gridData.yvel, gridData.xvel) * 180 / Math.PI; arrow.scaleX = arrow.scaleY = Math.sqrt(gridData.yvel * gridData.yvel + gridData.xvel * gridData.xvel) / 100 } //----updatePressure---- //----apdateVelocity---- public function apdateVelocity(gridData:GridData):void { gridData.xvel += ( gridData.upperLeft.pressure * 0.5 //左上 + gridData.left.pressure //左 + gridData.lowerLeft.pressure * 0.5 //左下 - gridData.upperRight.pressure * 0.5 //右上 - gridData.right.pressure //右 - gridData.lowerRight.pressure * 0.5 //右下 ) * 0.25; gridData.yvel += ( gridData.upperLeft.pressure * 0.5 //左上 + gridData.up.pressure //上 + gridData.upperRight.pressure * 0.5 //右上 - gridData.lowerLeft.pressure * 0.5 //左下 - gridData.low.pressure //下 - gridData.lowerRight.pressure * 0.5 //右下 ) * 0.25; gridData.xvel *= 0.99; gridData.yvel *= 0.99; } //----apdateVelocity---- } }//---Main--- //---------------------------- // >>> GridData import flash.geom.Rectangle; class BaseGridData { public var col:int = 0; public var row:int = 0; public var x:int = 0; public var y:int = 0; public var xvel:Number = 0; public var yvel:Number = 0; public var pressure:Number = 0; public var color:Number = 0; public var rgb:uint; public var rectangle:Rectangle; } //---------------------------- class GridData extends BaseGridData{ public function GridData(x:int, y:int, resolution:Number) { this.x = x; this.y = y; rectangle = new Rectangle(x, y, resolution, resolution) } //すべてのグリッドが8方向に隣接したグリッドを持つわけではないので、 //空のデータをセットしておく。 public var upperLeft:BaseGridData = new NullGridData();//左上 public var up:BaseGridData = new NullGridData();//上 public var upperRight:BaseGridData = new NullGridData();//右上 public var left:BaseGridData = new NullGridData();//左 public var right:BaseGridData = new NullGridData();//右 public var lowerLeft:BaseGridData = new NullGridData();//左下 public var low:BaseGridData = new NullGridData();//下 public var lowerRight:BaseGridData = new NullGridData();//右下 } //---------------------------- // >>> GridData class NullGridData extends BaseGridData{ } //---------------------------- class Particle { public function Particle(x:Number, y:Number) { this.x = px = x; this.y = py = y; } public var x:Number; public var y:Number; public var px:Number; public var py:Number; public var xvel:Number = 0; public var yvel:Number = 0; } //---------------------------- import flash.display.*; class Arrow extends Sprite { function Arrow() { var s:Shape = new Shape(); s.graphics.beginFill(0xFF0000, 1); s.graphics.moveTo(2, 4); s.graphics.lineTo(8, 4); s.graphics.lineTo(8, 0); s.graphics.lineTo(20, 7); s.graphics.lineTo(8, 14); s.graphics.lineTo(8, 10); s.graphics.lineTo(2, 10); s.graphics.lineTo(2, 4); s.x = -10; s.y = -7; addChild(s); } } Code Fullscreen Preview Fullscreen mouseY mouseX Shape Math.sqrt addChild drawRect opaqueBackground addEventListener Event.ADDED_TO_STAGE fixed moveTo draw rotation stage StageQuality.LOW StageAlign.TOP_LEFT right lineTo left scaleY