// forked from tkinjo's パーティクルテスト // forked from tkinjo's パーリンノイズの生成とフレームスキップ package { import flash.display.*; import flash.events.*; import flash.geom.*; import flash.utils.*; import net.hires.debug.Stats; [SWF(width="465", height="465", backgroundColor="0x0", frameRate="30")] /** * カラーパーティクル * * 色はパーティクルにかかる力の方向で決定 * * @author tkinjo */ public class Main extends Sprite { // farame skip setting private const FRAME_SKIP_MARGIN:uint = 0; // force map setting private const FORCE_MAP_CREATE_BY_ENTER_FRAME:Boolean = true; private const FORCE_MAP_SCALE:uint = 3; private const OFFSET_SPEED:Number = 1; private const NUM_OCTAVES:uint = 4; // particle setting private const PARTICLE_NUM:uint = 1000; private const FRAME_SKIP_DRAW_PARTICLE:Boolean = false; // view setting private const FADE_OUT_COLOR_TRANSFORM:ColorTransform = new ColorTransform( 1, 1, 1, 1, -8, -8, -8); private var stageWidth:Number = stage.stageWidth; private var stageHeight:Number = stage.stageHeight; private var frameSkipper:FrameSkipper; private var forceMapBitmap:Bitmap; private var forceMapBitmapData:BitmapData; private var offsets:Array; private var seed:uint; private var particles:Particle; private var viewBitmapData:BitmapData; private var viewBitmap:Bitmap; public function Main() { frameSkipper = new FrameSkipper( stage.frameRate, FRAME_SKIP_MARGIN ); forceMapBitmapData = new BitmapData( stageWidth >> FORCE_MAP_SCALE, stageHeight >> FORCE_MAP_SCALE ); offsets = new Array(); for ( var i:uint = 0; i < NUM_OCTAVES; i++ ) offsets.push( new Point() ); seed = Math.random() * 0xffff; // create particle var previousParticle:Particle = particles = new Particle( Math.random() * 465, Math.random() * 465 ); var particle:Particle; for ( i = 0; i < PARTICLE_NUM; i++) { previousParticle.next = particle = new Particle( Math.random() * 465, Math.random() * 465 ); previousParticle = particle; } // create view bitmap viewBitmapData = new BitmapData( stageWidth, stageHeight, false, 0x0 ); viewBitmap = new Bitmap( viewBitmapData ); addChild( viewBitmap ); addEventListener( Event.ENTER_FRAME, enterFrameHandler ); forceMapBitmap = new Bitmap( forceMapBitmapData ); forceMapBitmap.scaleX = 100 / forceMapBitmap.width forceMapBitmap.scaleY = 100 / forceMapBitmap.height; forceMapBitmap.x = stageWidth - forceMapBitmap.width; addChild( forceMapBitmap ); var hueCircle:HueCircle = new HueCircle( 50, 30 ); hueCircle.x = hueCircle.radius + stageWidth - hueCircle.width; hueCircle.y = hueCircle.radius + forceMapBitmap.height; hueCircle.graphics.beginFill( 0x0 ); hueCircle.graphics.drawRect( -hueCircle.radius, -hueCircle.radius, hueCircle.width, hueCircle.height ); hueCircle.graphics.endFill(); addChild( hueCircle ); if( !FORCE_MAP_CREATE_BY_ENTER_FRAME ) forceMapBitmapData.perlinNoise( stageHeight >> ( FORCE_MAP_SCALE + 1 ), stageHeight >> ( FORCE_MAP_SCALE + 1 ), NUM_OCTAVES, seed, true, true, 3, false, offsets ); addChild(new Stats); } private function enterFrameHandler( event:Event ):void { frameSkipper.update(); if ( FORCE_MAP_CREATE_BY_ENTER_FRAME && !frameSkipper.skip ) { var offset:Number = ( frameSkipper.skipped + 1 ) * ( OFFSET_SPEED / FORCE_MAP_SCALE ); for ( var i:uint = 0; i < NUM_OCTAVES; i++ ) { switch( i % 4 ) { case 0: offsets[i].x += offset; break; case 1: offsets[i].x -= offset; break; case 2: offsets[i].y += offset; break; case 3: offsets[i].y -= offset; break; } } forceMapBitmapData.lock(); forceMapBitmapData.perlinNoise( stageHeight >> ( FORCE_MAP_SCALE + 1 ), stageHeight >> ( FORCE_MAP_SCALE + 1 ), NUM_OCTAVES, seed, true, true, 3, false, offsets ); forceMapBitmapData.unlock(); } var particle:Particle = particles; var color:uint; var centerPoint:Point = new Point(); var force:Point = new Point(); viewBitmapData.lock(); if ( !FRAME_SKIP_DRAW_PARTICLE || ( FRAME_SKIP_DRAW_PARTICLE && !frameSkipper.skip ) ) viewBitmapData.colorTransform( viewBitmapData.rect, FADE_OUT_COLOR_TRANSFORM ); while ( ( particle = particle.next ) != null ) { color = forceMapBitmapData.getPixel( particle.x >> FORCE_MAP_SCALE, particle.y >> FORCE_MAP_SCALE ); particle.x += ( particle.vx = particle.vx * 0.98 + ( ( color >> 16 & 0xff ) - 128 ) * 0.005 ); particle.y += ( particle.vy = particle.vy * 0.98 + ( ( color >> 8 & 0xff ) - 128 ) * 0.005 ) ; if (particle.x < 0) particle.x += stageWidth; else if ( particle.x >= stageWidth ) particle.x -= stageWidth; if ( particle.y < 0 ) particle.y += stageHeight; else if ( particle.y >= stageHeight ) particle.y -= stageHeight; force.x = ( color >> 16 & 0xff ) - 128; force.y = ( color >> 8 & 0xff ) - 128; if ( !FRAME_SKIP_DRAW_PARTICLE || ( FRAME_SKIP_DRAW_PARTICLE && !frameSkipper.skip ) ) viewBitmapData.setPixel( particle.x, particle.y, getHueCircleColor( centerPoint, force ) ); } viewBitmapData.unlock(); } } } import flash.display.*; import flash.events.*; import flash.utils.*; import flash.geom.*; class Particle { public var vx:Number = 0; public var vy:Number = 0; public var x:int; public var y:int; public var next: Particle; function Particle( x:int, y:int ) { this.x = x; this.y = y; } } class FrameSkipper { /* ================================================== * property * ================================================== */ /** * スキップされたフレーム数 */ public function get skipped():uint { return _skipped; } private var _skipped:uint = 0; /** * スキップするべきときなら true、そうでないなら false * * --- 例 --- * if( !frameSkipper.skip ) * // フレームスキップしない時の処理(描画などの重い処理) * else * // フレームスキップするときの処理(描画をせずにデータ地だけ変更するなどの軽い処理) */ public function get skip():Boolean { return skipCounter >= 0; } /** * 保ちたい fps 値 */ public var expectedFPS:uint; /** * */ public var margin:uint; /* ================================================== * field * ================================================== */ private var previousTime:int = 0; private var currentTime:int = 0; private var skipCounter:int = 0; private var fps:uint; /* ================================================== * method * ================================================== */ public function FrameSkipper( expectedFPS:uint = 60, margin:uint = 0 ):void { this.expectedFPS = expectedFPS; this.margin = margin; } public function update():void { currentTime = getTimer(); fps = 1000 / ( currentTime - previousTime ); if ( !skip && fps < expectedFPS ) { _skipped = expectedFPS / fps + margin; skipCounter = skipped; } else skipCounter--; previousTime = currentTime; } } class HueCircle extends Sprite { public function get radius():Number { return _radius; } public function set radius(value:Number):void { _radius = value; draw(); } private var _radius:Number; public function get innerRadius():Number { return _innerRadius; } public function set innerRadius(value:Number):void { _innerRadius = value; draw(); } private var _innerRadius:Number; private var bitmap:Bitmap; private var circleMask:Sprite; public function HueCircle( radius:Number, innerRadius:Number = 0 ) { _radius = radius; _innerRadius = innerRadius; var diameter:Number = radius * 2; if ( hueCircleBitmapData == null ) resizeHueCircleBitmapData( diameter ); bitmap = new Bitmap( hueCircleBitmapData ); bitmap.cacheAsBitmap = true; addChild( bitmap ); circleMask = new Sprite(); circleMask.cacheAsBitmap = true; addChild( circleMask ); bitmap.mask = circleMask; draw(); } private function draw():void { circleMask.graphics.clear(); circleMask.graphics.beginFill( 0xffffff ); circleMask.graphics.drawCircle( radius, radius, radius); if( innerRadius != 0 ) circleMask.graphics.drawCircle( radius, radius, innerRadius ); circleMask.graphics.endFill(); var largeRadius:Number = ( ( radius > innerRadius ) ? radius : innerRadius ); bitmap.x = -largeRadius; bitmap.y = -largeRadius; bitmap.scaleX = largeRadius / ( hueCircleBitmapData.width / 2 ); bitmap.scaleY = largeRadius / ( hueCircleBitmapData.width / 2 ); circleMask.x = -radius; circleMask.y = -radius; } private static var hueCircleBitmapData:BitmapData; public static function resizeHueCircleBitmapData( width:Number ):void { var centerPoint:Point = new Point( width / 2, width / 2 ); var tempPoint:Point = new Point(); hueCircleBitmapData = new BitmapData( width, width, false ); hueCircleBitmapData.lock(); for ( var i:int = 0; i < width; i++ ) { for ( var j:int = 0; j < width; j++ ) { tempPoint.x = i; tempPoint.y = j; hueCircleBitmapData.setPixel( i, j, getHueCircleColor( centerPoint, tempPoint ) ); } } hueCircleBitmapData.unlock(); } } function getHueCircleColor( centerPoint:Point, point:Point ):uint { var pointFromCenterPoint:Point = point.subtract( centerPoint ); var pointBearingFromCenterPoint:Number = Math.atan2( pointFromCenterPoint.y, pointFromCenterPoint.x ) * 180 / Math.PI; if ( pointBearingFromCenterPoint < 0 ) pointBearingFromCenterPoint = pointBearingFromCenterPoint + 360; return HSVtoRGB( pointBearingFromCenterPoint, 1, 1 ); } function HSVtoRGB( h:Number, s:Number, v:Number ):uint { var Hi:uint = ( h / 60 ) % 6; var f:Number = h / 60 - Hi; var p:Number = v * ( 1 - s ); var q:Number = v * ( 1 - f * s ); var t:Number = v * ( 1 - ( 1 - f ) * s ); switch( Hi ) { case 0: return toRGB255( v, t, p ); case 1: return toRGB255( q, v, p ); case 2: return toRGB255( p, v, t ); case 3: return toRGB255( p, q, v ); case 4: return toRGB255( t, p, v ); case 5: return toRGB255( v, p, q ); } return 0; } function toRGB255( r:Number, g:Number, b:Number ):uint { return ( r * 255 << 16 ) + ( g * 255 << 8 ) + b * 255; } カラーパーティクル