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;
}
}