package { /* * [勉強]perlinNoiseを動かす(の解説を初級者がしてみる) * * Forkするかどうかについて考えた結果、「Forkしないでくれ」って * いう意見があったら削除する方向で行きたいと思います。 * * 毎度思うんですけど、コメントの幅がどれくらいあればいいのか * 気になりますね。 * * 今回は今の今まで避けていた「PerlinNoiseを動かす」という、 * 多分、初心者・初級者がやりたいと思いつつ、難しそうで手を * 出しづらいコンテンツの製作方法を勉強したいと思います。 * * なお、「PerlinNoiseって何?」っていうのはとりあえず * 作品を見てもらって、後に書いてある参考URLをみて、 * 「あー、こんな感じなんだー」っていうのだけつかんでもらえれば。 * これが扱えるようになると、別のフィルターを駆使して * 雲とか火とか海とか作れるらしいですよ。 * Bitmap系フィルタの魅力の一つだと思うので、これを習得すると * Bitmapの面白さが見つかるかもしれません。 * 僕はまだそこのレベルまで達していませんが。 * * 初心者・初級者の方々、一緒に勉強しましょう。 * また、中級者・上級者の方、何か指摘・補足があればよろしくお願いします。 * * なお、今回はPerlinNoiseの引数の解説が非常に長くなっています。 * しかし逆にこれさえわかってしまえば、後はちょっとした応用です。 * でも、あんまり構える必要もないな、と、調べながら思いました。 * * Fork元の作品は綺麗ですっきりしてますので、そちらを見て、わからないところがあったら * こちらを参考してみるというのも手だと思います。 * * 今回もDownload後の閲覧推奨です。 */ import flash.display.Sprite; import flash.display.Stage; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BitmapDataChannel; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Point; [SWF(width="200", height="200", frameRate="60", backgroundColor="#ffffff")] /* * 先に大まかな流れを。 * 1.PerlinNoiseの引数用変数の設定をする * 2.PerlinNoiseのそれぞれのレイヤーをどう動かすかを決める(X軸・Y軸の速度) * 3.PerlinNoiseのレイヤーを別々に動かす。 * * これだけです。「それぞれのレイヤー」っていうのが鍵です。 */ public class PerlinNoiseTest extends Sprite { // -------------------- プロパティ -------------------- private var bmd : BitmapData; //wonderflって465*465より小さいやつは拡大するんですね。 //小さくしてステージを拡大させてるのはやはり、負荷を軽くするためなのでしょうか? private var w : uint = 200; //幅 private var h : uint = 200; //高さ //// perlinNoise 用変数 ついでに引数を確認。細かく知りたい人はヘルプか下のURL参照。 //// とにかく多い。けど、それぞれ重要なので一つ一つじっくりと確認していきましょう。 //// 特に今回の最大目標である「PerlinNoiseを動かす」というのは最後の引数が重要になります。 //// 最初のURLのサンプルを弄りながら確認すると吉。 //// 参考 //// http://www.project-nya.jp/modules/weblog/details.php?blog_id=478 //// http://www.fumiononaka.com/TechNotes/Flash/FN0510001.html // ・第1引数 BaseX 第2引数 BaseY 周波数。 // PerlinNoiseっぽくするならステージの大きさに数値をあわせる。 // この作品ではPerlinNoise生成時にwとhを設定されています。 // ・第3引数 numOctaves (重要) // きめ細やかさを決める。あんまり多すぎても違いが大きくはなさげ。 // 設定した数だけレイヤーを重ねていって、大きくなるにつれて細かくなるみたい。 // 作品にもよるだろうけど、まずは10以下くらいで考えるといいかもしれんです。 // 大きくすればするほど、その分処理時間もかかります。 private var octaves : uint = 6; // ・第4引数 randomSeed ランダムのパターン番号みたいな感じ // この作品みたいに綺麗に流れるようなものを作りたいならパターンは統一したほうがいい? private var seed : Number; // ・第5引数 stitch 同じビットマップをタイル上に複数並べたときに // シームレスにつながるように処理をするらしいです。 // HTMLでBackgroundに小さなタイルの画像をおいて、それを並べてもつなぎ目がないようにする // っていうとわかりやすいでしょうか・・・。 // この作品ではPerlinNoise生成時にfalseを設定されています // ・第6引数 fractalNoise trueだったらフラクタルノイズを生成らしいです。 // falseだったら乱流らしいです。よくわかりません。 // trueだと全体のエッジがスムーズになる感はありますね。 // falseだと逆にシャープになるから、他のフィルターと組み合わせて // 炎や海の波のようなものを作れるそうです。 // この作品ではPerlinNoise生成時にtrueを設定されています // ・第7引数 channelOptions どの色を使って色をつけますか、みたいな感じでしょうか。 // defaultは7で、RGB全部。つまり、このサンプルの定義はデフォルトをあらわしてるみたいです。正直若干色合いが気持ち悪い。(笑 // 数値はBitmapDataChannelクラスで定数として入ってます。 // BitmapDataChannel.RED = 1 // BitmapDataChannel.GREEN = 2 // BitmapDataChannel.BLUE = 4 // で、その和だから7っていうことらしいです。BitmapDataChannel.ALPHAは8らしいですね。 // 演算に使用されている、縦線の|っていうのは論理和っていうらしいです。OR演算子とも。 // 一応参考URLの2番目のページに少しだけ解説されてます。 // ここらへんは+を使わないところから特別な計算法だということはわかりますが、使う理由がよくわからないですね。 // 多分2進数で考えると、REDは1、GREENは10、BLUEは100で、足すと111で、それは10進数に直すと7っていうことなんでしょうけど。 // でも別に普通に10進数でプラスすればいいんじゃって思ったんですけど、なんかその演算を使いなさいってことらしいです。 // 実際普通にプラスするだけでもいけるみたいですが、メリット・デメリットがわからないですね。 // 詳しい方は補足してくだされば・・・。 // // ちなみに、別に定数を使わなくても、数値で入力してもOKです。 // private var channels : uint = 7 もOKってことですね。 // ただ、多分可読性的にはこの作品のようにしたほうがいいかもしれません。 private var channels : uint = BitmapDataChannel.RED | BitmapDataChannel.GREEN | BitmapDataChannel.BLUE; // ・第8引数 grayScale 全体をグレースケールにするかしないかです。 // これはまんまなので説明のしようがないですね。 // 第7引数のchannelOptionsで設定したRGB色も、これをtrueにすることで上書きされて無意味になるようです。 // AlphaChannelだけは別のようですが。 // この作品ではPerlinNoise生成時にfalseを設定されています // ・第9引数 offsets(超重要!) // PerlinNoiseを動かすのはここが肝のようです。 // どうやら、これを使ってnumOctavesで設定した各オクターブ(レイヤー)を動かすみたい。 // 作品を見てみると、確かに各レイヤーがそれぞれの方向に一定速度で動いてるようにみえますね。 // 煙っぽく見えるのはこれが原因だったようです。 // Array型ではありますが、中に入るエレメントはPoint型なのに注意。 private var offset : Array = new Array(); //ここまでくれば半分終わったも同然です。がんばりましょう。 //上で説明したoffsetsを動かすための変数です。詳細はsetup関数で。 //それぞれのオクターブを動かすためのX・Y軸の速度を格納する配列。 private var offxy : Array = new Array(); //速度を決めるための単位速度。 private var dist : uint = 6; //1クリック目で移動をとめて、2クリック目でPerlinNoiseを再設定するためのフラグ //clickHandler関数で使用されます。 private var flg : Boolean = false; // -------------------- メソッド -------------------- // コンストラクタ public function PerlinNoiseTest() { if ( this.stage != null ) { init( this.stage ); } } public function init( s:Stage ):void { // create BitmapData and Bitmap //BitmapDataを作って、ごにょごにょでaddChildします。幅200、高さ200,透明サポートは無し、色は#cccccc //色とかは結局PerlinNoiseで全体を塗りつぶされるので、細かく考える必要はないかも。 //でもPerlinNoiseを描画完了する前はこの色で塗りつぶされるので色を変えるときはお気をつけて。 bmd = new BitmapData( w , h , false , 0x00cccccc ); var bm:Bitmap = new Bitmap( bmd ); addChild( bm ); // setup value for perlinNoise //PerlinNoise生成のための値を設定します。 setup(); // handler // クリックしたときの関数と毎フレームに実行される関数を結び付けます。 stage.addEventListener( MouseEvent.CLICK , clickHandler ); addEventListener( Event.ENTER_FRAME , onEnterFrameHandler ); } // setup value for perlinNoise private function setup():void { //ランダムパターンを決めます seed = Math.floor( Math.random() * 0xffff ); //各オクターブ(レイヤー)の初期位置と、動く向きと速度を決めます。(超重要!) for ( var i:int = 0; i < octaves; i++ ) { //i番目のレイヤーの初期位置 offset[i] = new Point( Math.random() * w , Math.random() * h ); //i番目のレイヤーを動かすための向きと速度を設定 //単位速度を元にランダムに決めます。 offxy[i] = new Array(2); //X軸の速度 //計算の意味としては、プラスだけでなくマイナスの方向にも動かしたいから、っていうことですね。 //distが6でMath.random() * distで1が算出された場合、1-6*0.5で-2。 //つまり、毎フレームX軸方向に-2動くっていうことになります。 offxy[i][0] = Math.random() * dist - dist * 0.5; //Y軸の速度。X軸と同じ計算です。 offxy[i][1] = Math.random()*dist - dist*0.5; } } // handler private function onEnterFrameHandler( e:Event ):void { //Noiseを動かす歯車の部分ですねー。PerlinNoiseの引数の解説が長かった分拍子抜けかもしれません。 //でも、PerlinNoise最後の引数を知らないとこれ見ても意味がないのです。あと少しですよ。 for ( var i:int = 0; i < octaves; i++ ) { //setup関数で設定した初期位置に、同じくsetup関数で設定した速度をXY双方にプラスしていきます。 offset[i].x += offxy[i][0]; //offxy[i][0]はX軸の速度でしたね offset[i].y += offxy[i][1]; //offxy[i][1]はY軸の速度でした } //これでそれぞれのオクターブ(レイヤー)の新しい場所が求められました。新しくPerlinNoiseを描画しましょう。 //毎フレーム変わるのはoffsetのみです。 bmd.perlinNoise( w , h , octaves , seed , false , true , channels , false , offset ); //これの関数の繰り返しで動いてるように見えるようですね。 //ひとつ曖昧なままなのは、PerlinNoiseって、イメージとしてはステージ(というか設定した範囲)外にも広がってる感じなのでしょうか。 //PerlinNoiseを動かしたら、動かした部分だけ空白ができるんじゃないかと思ってましたが、 //ちゃんとPerlinNoiseは続きを描画されていました。そこらへんは、オフセットされた分だけ計算して描画するように作られてるんでしょうかね。 //こういう作品を見てると、そういう仕組みってどうなってるだろうと思っただけに、 //若干拍子抜けというか、のれんに腕押しというか。逆に言うと、そこらへん考えなくていいんだなと。 //たぶんここら辺が、「PerlinNoiseを動かす」というものを考えた上で初心者・初級者が難しく考えてしまう原因な気がします。 } //最後に、クリックしたときの挙動です。これはおまけ的なものなので解説を最後に移動しました。 private function clickHandler( e:MouseEvent ):void { //!を付けると、trueとfalseが反転するらしいです。こういうフラグの切り替えもあるのですねぇ。勉強になります。 //グローバル変数で初期値はfalseだったので、起動して1回目のクリックは!falseでtrueになりますね。 flg = !flg; //ということは、1回目はまずtrueのため、trueのときの処理が行われますね。 //2回目はfalseになるのでfalseの処理が。3回目はまたtrueに戻るのでtrueの処理が行われます。 if ( flg ) { //初期値を変えて、違うランダムシードと速度を使ってPerlinNoiseを描画して動かしますよー、ということですね。 setup(); //一応、同じターゲットの同じイベントに同じ関数を登録しようとしても重複しないようになってるとは思いますが、 //hasEventListenerで登録されているかどうかを判断して、登録されてなかったときにのみ //addEventListenerしたほうがいいかもしれないですね。と、昔講師の人に指摘された気がします。 //hasEventListenerに関してはヘルプ参照でー。 addEventListener( Event.ENTER_FRAME , onEnterFrameHandler ); } else { //removeするということは、この場合は「とまる」っていうことですね。 removeEventListener( Event.ENTER_FRAME , onEnterFrameHandler ); } } } } [勉強]perlinNoiseを動かす(の解説を初級者がしてみる)