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

forked from: パーティクル祭りの原点「Liquid10000」を初級者が解説してみる。

パーティクル祭りの原点「Liquid10000」を解説してみる。
[冒頭のコメントに追記あり](Download後の閲覧推奨)

前回のPerlinNoiseはこれの伏線だったのだよ!(偶然ですけど)
今回はPerlinNoiseの復習と、ビット演算子の勉強になります。

前々回
http://wonderfl.kayac.com/code/03190641c00b157abc0dbe7d6a513fa80ec987db
前回
http://wonderfl.kayac.com/code/cafcb78df40b35b0c0d571dc8fd8f1bda791e4b4

フォースマップっていう考え方らしいですが、
PerlinNoiseの色を速度に変換するらしいです。
てっきり難しい物理演算を使ってると思ってたよ・・・。

あと、前々回にやった花火の処理も加わってますので、
前々回・前回の内容を知ってると、にやけることができると思います。

これが分かると、あのsnowとかの作り方もわかるかな・・・?
次回はそれにするかもしれません。しないかもしれません。

パーティクル祭りに関してはclockmaker様のまとめが詳しいです。
この記事を元に、自身で高速化を試してみるのもありだと思います。
http://clockmaker.jp/blog/2009/04/particle/
http://clockmaker.jp/blog/2009/04/particle_fes/

Fork元を製作された方へ。
・Forkを拒否されたい場合は、何かしらのアクションでお伝えください。
削除いたします。(できれば生暖かく見守っていただけると嬉しいです)

中級者・上級者の方へ。
・何かアドバイスや校正箇所があれば、ご指摘お願いします。

初心者・初級者の方へ。
・一緒に勉強しましょう。何か質問があればどうぞ。
自分も初級者なので、答えられるか分かりませんが。(笑

[追記]
朝起きて見てみたらFavoriteの多さにびっくりして目が覚めました。
あくまで解説のみで、自分の作品ではないということで恐縮ではありますが、
こういう試みが支持されてるということは、自分の考えは間違ってなかったんだなと、
確信している次第です。

そしてこういう解説ができるのも、勉強したいと思わせられる素
// forked from Hiiragi's パーティクル祭りの原点「Liquid10000」を初級者が解説してみる。
package {

	import flash.display.Stage;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.display.Sprite;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.geom.ColorTransform;
	import flash.geom.Rectangle;
	import flash.geom.Point;
	import flash.events.Event;
	import flash.events.MouseEvent;

	[SWF(width=465, height=465, backgroundColor=0x000000, frameRate=30)];

	
	/*
	 * パーティクル祭りの原点「Liquid10000」を解説してみる。
	 * [冒頭のコメントに追記あり](Download後の閲覧推奨)
	 * 
	 * 前回のPerlinNoiseはこれの伏線だったのだよ!(偶然ですけど)
	 * 今回はPerlinNoiseの復習と、ビット演算子の勉強になります。
	 * 
	 * 前々回
	 * http://wonderfl.kayac.com/code/03190641c00b157abc0dbe7d6a513fa80ec987db
	 * 前回
	 * http://wonderfl.kayac.com/code/cafcb78df40b35b0c0d571dc8fd8f1bda791e4b4
	 * 
	 * フォースマップっていう考え方らしいですが、
	 * PerlinNoiseの色を速度に変換するらしいです。
	 * てっきり難しい物理演算を使ってると思ってたよ・・・。
	 * 
	 * あと、前々回にやった花火の処理も加わってますので、
	 * 前々回・前回の内容を知ってると、にやけることができると思います。
	 * 
	 * これが分かると、あのsnowとかの作り方もわかるかな・・・?
	 * 次回はそれにするかもしれません。しないかもしれません。
	 * 
	 * パーティクル祭りに関してはclockmaker様のまとめが詳しいです。
	 * この記事を元に、自身で高速化を試してみるのもありだと思います。
	 * http://clockmaker.jp/blog/2009/04/particle/
	 * http://clockmaker.jp/blog/2009/04/particle_fes/
	 * 
	 * Fork元を製作された方へ。
	 * ・Forkを拒否されたい場合は、何かしらのアクションでお伝えください。
	 * 	削除いたします。(できれば生暖かく見守っていただけると嬉しいです)
	 * 
	 * 中級者・上級者の方へ。
	 * ・何かアドバイスや校正箇所があれば、ご指摘お願いします。
	 * 
	 * 初心者・初級者の方へ。
	 * ・一緒に勉強しましょう。何か質問があればどうぞ。
	 *  自分も初級者なので、答えられるか分かりませんが。(笑
	 * 
	 * [追記]
	 * 朝起きて見てみたらFavoriteの多さにびっくりして目が覚めました。
	 * あくまで解説のみで、自分の作品ではないということで恐縮ではありますが、
	 * こういう試みが支持されてるということは、自分の考えは間違ってなかったんだなと、
	 * 確信している次第です。
	 * 
	 * そしてこういう解説ができるのも、勉強したいと思わせられる素敵な作品たちが
	 * このwonderflに溢れているからだと思います。製作者の皆さんに多謝。
	 * 
	 * これからもマイペースでやっていきたいと思いますが、あまりに高度なものは
	 * 初級者の自分では解析不可能なので、こういう解説を中級者・上級者の方も
	 * してくださったら、もっとwonderflのすばらしさというのが共有できるんじゃないかと
	 * 思う次第です。
	 * 
	 * あ、あと、clockmakerさん、
	 * 「extends Progression 拡張機能コンテスト」ダブル受賞おめでとうございますー。
	 * 
	 */
	
	 /*
	  * おおまかな流れ
	  *  1.パーティクルを10000個作る
	  *  2.パーティクル情報を打ち込むためのBitmapを作る
	  *  3.PerlinNoiseを作る
	  *  4.パーティクル情報を打ち込むためのBitmapの色を落とす
	  *  5.PerlinNoiseの情報を元にパーティクルの加速度・速度・位置を求める
	  *  6.求められたパーティクルの情報に従って、Bitmapに色を打ち込む
	  *  7.4・5・6の処理をパーティクル毎に処理する
	  *  8.4・5・6・7の処理を毎フレーム行う
	  */
	public class Liquid10000 extends Sprite {

		private const nums:uint = 10000;
		private var bmpDat:BitmapData;
		private var vectorDat:BitmapData;
		private var randomSeed:uint = Math.floor( Math.random() * 0xFFFF );
		private var bmp:Bitmap;
		private var vectorList:Array;
		private var rect:Rectangle;
		private var cTra:ColorTransform;

		public function Liquid10000() {
			initialize();
		}
		private function initialize():void {

			trace("あきよし");
			
			//stage関連の設定
			//stage.align = StageAlign.TOP_LEFT;
			//stage.scaleMode = StageScaleMode.NO_SCALE;

			//Bitmapを作ってaddChild(描画される、メインのBitmap)
			bmpDat = new BitmapData( 465, 465, false, 0x000000 );
			bmp= new Bitmap( bmpDat );
			addChild( bmp );
			
			//PerlinNoiseを描く(addChildされてないのが肝)
			//PerlinNoiseについては、前回やりましたね。なので解説は省略。
			vectorDat= new BitmapData( 465, 465, false, 0x000000 );
			randomSeed = Math.floor( Math.random() * 0xFFFF );
			vectorDat.perlinNoise( 230, 230, 4, randomSeed, false, true, 1 | 2 | 0 | 0 );
			//どんなPerlinNoiseか見たい人は、下のaddChildのコメントアウトを消して、
			//EnterFrameのaddEventListenerをコメントアウトしてみてください。
			//引数からもわかりますが、赤と緑(RG)しか使われていませんね。
			//これは理由があります。記憶の片隅にとどめておいてください。
			//addChild(new Bitmap(vectorDat));
			
			//stageの大きさの矩形範囲を設定
			//colorTransformでの範囲設定に使われます。
			rect = new Rectangle( 0, 0, 465, 465 );
			
			//時間経過とともにパーティクルの残像を暗くして、軌跡を作るための色設定。
			//花火でもありました。重要なパラメータです。
			cTra= new ColorTransform( 0, .8, .8, .9 );

			//パーティクルを入れるための配列
			vectorList= new Array();

			//パーティクルを1万個作ってvectorListに押し込む
			for (var i:uint = 0; i < nums; i++) {
				
				var particle:Particle = new Particle();
				
				particle.x = Math.random() * 465;
				particle.y = Math.random() * 465;
								
				vectorList.push( particle );
			
			}
			//メインタイムラインのEnterFrameイベントをloop関数に関連付ける
			addEventListener( Event.ENTER_FRAME, loop );
			//stageがクリックされたらresetFunc関数が呼び出されるようにする。
			stage.addEventListener( MouseEvent.CLICK, resetFunc );
		}
		
		
		
		//毎フレーム呼び出される関数
		private function loop( e:Event ):void {
			//描画用のBitmapDataをcolorTransformで暗くする(重要)
			//これにより、過去の色が薄くなるのは、以前解説した花火の描画の時も一緒でしたね。
			//これがないとどうなるのかは、花火のとき同様、コメントアウトしてみてください。
			bmpDat.colorTransform( rect, cTra );
			
			//vectorListのコピーを作成
			var list:Array = vectorList;
			
			//listの長さをlenに代入しておく
			//なんかこうすると、for文の処理が早いらしい?
			var len:uint = list.length;
			
			//このクラスの根幹処理なので超重要!
			//for文でlistの最初のエレメントから走査
			for (var i:uint = 0; i < len; i++) {
				//listのi番目のエレメントをdotsに代入
				var particle:Particle = list[i];
			
				
				
				particle.move( stage.mouseX, stage.mouseY );
				
				
				( particle.x > 465 )?particle.x = 0:
				( particle.x < 0 )?particle.x = 465:0;
				//Y軸も同様に。
				( particle.y > 465 )?particle.y = 0:
				( particle.y < 0 )?particle.y = 465:0;
			
				
				//1*1の四角形をdots.pv.x, dots.pv.yの位置に0xFFFFFFの色で描く
				bmpDat.fillRect( new Rectangle( particle.x, particle.y, 1, 1), 0xFFFFFF );
			}
		}

		
		//クリックで呼び出される関数。
		//これと同じ処理が上でもありましたね。なので、解説はしません。
		//・・・が。同じ処理っていうことは、記述的には無駄かもしれませんね。どうすればその無駄を省けるのか考えてみましょう。
		//ヒント(というか答え):最初のinitialize関数のときにもこれを呼び出せばいいじゃん?でもe:MouseEventが邪魔だなぁ・・・。
		//				この引数がなくても関数が使えるようにできないかなぁ・・・。あ、デフォルトの値を入れておけばいいんじゃね?
		private function resetFunc(e:MouseEvent):void {
			randomSeed= Math.floor( Math.random() * 0xFFFF );
			vectorDat.perlinNoise( 230, 230, 4,randomSeed, false, true, 1|2|0|0 );
			vectorList= new Array();
			
			for (var i:uint = 0; i < nums; i++) {
				
				var particle:Particle = new Particle();
				
				particle.x = Math.random() * 465;
				particle.y = Math.random() * 465;
				
				vectorList.push( particle );
			}
		}
	}
}



//パーティクル用のクラスです。花火のときと似てる感じですね。これも説明は不要ですかね。
import flash.geom.Point;
class Particle {

		public var firstVectorLength:Number = 0; //最初のベクトルの長さ
		public var firstVectorDegree:Number = 0; //最初のベクトルの角度
		
		public var vectorX:Number = 0;//自分の持つベクトル
		public var vectorY:Number = 0;

		public var x:Number;
		public var y:Number;
		
		function Particle() {
			
			
			
		}
		
		public function move( n1:Number, n2:Number ){
		
			var rad:Number; 
			var degree:Number;
		
			var distanceX:Number;
			var distanceY:Number;
			
			var affectVectorDegree:Number; // マウスからの影響のベクトルの長さ
			var affectVectorLength:Number;	// マウスからの影響のベクトルの角度
			
			
			var playerHitArea:Number; // ヒット判定の距離
							
			
			var distance:Number; // マウスとの距離
			
			var mouseX:Number =	n1;		
			var mouseY:Number = n2;
			
			vectorX = vectorX * 0.8;
			vectorY = vectorY * 0.8;
			
			//プレイヤーから見たマウスの角度
			distanceY = mouseY - y;
			distanceX = mouseX - x; 
			
			rad = Math.atan2( distanceY, distanceX ); 
			degree = rad * 180 / Math.PI;
			
						
			//プレイヤーからの影響の角度
			affectVectorDegree = ( degree + 360 ) % 360; // マウスに近づける場合
			//affectVectorDegree = ( degree + 360 ) % 360 - 180; // マウスから遠ざける場合
			
							
			//マウスとプレイヤーの距離
			distance = coordinatesDistance( x, y, mouseX, mouseY ); //座標(ax,ay)、(bx,by)の2点間の距離を計測
				
			// affectVectorLengthが大きくなりすぎないように、distanceの下限をつける
			if ( distance < 1 ) {
				distance = 1;
			}
			
			
			playerHitArea = 70; // 効果の範囲
 			
						
			if( distance <= playerHitArea ){ // 範囲内ならばマウスから、影響を受ける
											
				affectVectorLength = 15 / distance; //距離によって、マウスへ向かうベクトルの長さを変更(影響の強さ)
				
				vectorX += Math.cos( affectVectorDegree * Math.PI/180 ) * affectVectorLength;
				vectorY += Math.sin( affectVectorDegree * Math.PI/180 ) * affectVectorLength;
			
			}
			
			
			if( distance <= 3 ){  // マウスにごく近かったら動きを止める
			
				vectorX = 0;
				vectorY = 0;
			
			}
								
			x += vectorX;
			y += vectorY;
		
		}


		///////////////////////////////////////////////////////////////////////////////////////////////
		// 常用関数  
		///////////////////////////////////////////////////////////////////////////////////////////////

		private function coordinatesDistance(ax, ay, bx, by) { //座標(ax,ay)、(bx,by)の2点間の距離を計測
			var distance = Math.sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by));
			return distance;
		}
		
		
	}