不気味な火の玉時計を作ってみたら、呪いのようなバグに悩まされた

  • このエントリーをはてなブックマークに追加
  • 0

TeraFireTeraClockシリーズ第2段と言う事で、時・分・秒の値をRGBとして使った、色と大きさの変わる炎で時計を表現すると言う妙な時計を作ってみた。demoはこちら。当初はもうちょっと違う物を作りたかったような気もするが、気付けば凄く不気味なものが出来てしまった。まぁいいか。
元々のTeraFireの仕様上、毎回色の違う炎を新規作成し、古い炎をStageから取り除き、新しい炎をStageに乗せるという方法をとってみたんだが、取り除いた炎がメモリから消えず、15?20秒で目に見えて動きが遅くなってしまった。
TeraFireのENTER_FRAMEイベントハンドラ関数loopremoveEventListenerしてから取り除いてやった所、メモリ圧迫がなくなったんだが、何?イベントハンドラ関数があるSpriteをStageから削除してそのまま破棄するときは、イベントハンドラ関数を全部removeしないと駄目って事なの?

最終的には、以下の2点の変更をTeraFireに施し、コミットすると言う荒業に出てみたんだ。

  • 炎とゆらぎ用の表示オブジェクトを作っている部分をpublicな外部関数化し、炎の幅・高さ・色を変更可能にした
  • ENTER_FRAMEイベントが他の表示オブジェクト上に乗ってる時以外は発生しないようにした

でもやっぱ思い直してbranchesにしといた。TeraFire本流の変更は全部元に戻しといた。

一応、火の玉時計はちゃんと時・分・秒が分かるようになってる。

  • 炎の幅で今何時かが分かる
  • 炎の高さで今何分かが分かる
  • 炎の色で今何秒かが分かる

ただし、色は時分秒全部から出来てるので、1日中毎回違うと言う状況だが。
と言うか、これ見ても今何時何分何秒かなんて絶対分からんな。

何でも、今TeraClock作品を紹介してくれるキャンペーン的なものをやっているらしいが、到底出せない。

「TeraClock」ユーザの皆さん

14日のイベント「dotFes 2008 Tokyo」の展示スペースにiMac24inchで皆さんの時計作品を紹介させていただきたいのです。趣旨としましては「TeraClock でいろんな人がいろんな時計を表現してくれましたよー!」という感じになればありがたいのです。ネット上で作品公開してくださっている方で、紹介OKよという人は、このエントリのコメント欄やメール等で、紹介OKの旨を11日までにお知らせいただければ本当に助かります。
trick7.com blog: 東京てら子 5 の連絡と、dotfes&WebDesigningでのお願い


では、branches版の説明をしていく。

炎の変更

まず、炎を作ったら二度と変えられないと言う現仕様を変更するため、コンストラクタの一部を別関数changeFireに移してみた。
コンストラクタからはコイツを呼びつけてある。ゆらぎ用BitmapDataを毎回作る必要が無いかもしれないが、そこは要研究だな。本当は、幅と高さだけ書き換えて、上手く再利用できるといいんだが。

ActionScript

  1. public function changeFire(fireWidth:Number = 30,
  2.                            fireHeight:Number = 90,
  3.                            fireColorIn:uint = 0xFFCC00,
  4.                            fireColorOut:uint = 0xE22D09):void {
  5.     fireW = fireWidth;
  6.     fireH = fireHeight;
  7.     var matrix:Matrix = new Matrix();
  8.     matrix.createGradientBox(fireW,fireH,Math.PI/2,-fireW/2,-fireH*(focalPointRatio+1)/2);
  9.     var colors:Array = [fireColorIn, fireColorOut, fireColorOut];
  10.     var alphas:Array = [1,1,0];
  11.     var ratios:Array = [30, 100, 220];
  12.    
  13.     ball.graphics.clear();
  14.     ball.graphics.beginGradientFill(GradientType.RADIAL,colors, alphas, ratios, matrix,"pad","rgb",focalPointRatio);
  15.     ball.graphics.drawEllipse(-fireW/2,-fireH*(focalPointRatio+1)/2,fireW,fireH);
  16.     ball.graphics.endFill();
  17.     //余白確保用透明矩形
  18.     ball.graphics.beginFill(0x000000,0);
  19.     ball.graphics.drawRect(-fireW/2,0,fireW+margin,1);
  20.     ball.graphics.endFill();
  21.  
  22.     //ゆらぎ用のBitmap(ステージに貼付ける必要はないのでBitmapに貼る必要はない)
  23.     if (displaceImage == null ||
  24.         (displaceImage.width != fireW+margin && displaceImage.height != fireH)) {
  25.         //ゆらぎ用のBitmap(ステージに貼付ける必要はないのでBitmapに貼る必要はない)
  26.         displaceImage = new BitmapData(fireW+margin,fireH,false,0xFFFFFFFF);
  27.         //火の芯付近の揺らぎを抑える用のグラデーション
  28.         var matrix2:Matrix = new Matrix();
  29.         matrix2.createGradientBox(fireW+margin,fireH,Math.PI/2,0,0);
  30.         var gradient_mc:Sprite = new Sprite;
  31.         gradient_mc.graphics.beginGradientFill(GradientType.LINEAR,[0x666666,0x666666], [0,1], [120,220], matrix2);
  32.         gradient_mc.graphics.drawRect(0,0,fireW+margin,fireH);//drawのターゲットなので生成位置にこだわる必要はない。
  33.         gradient_mc.graphics.endFill();
  34.         gradientImage = new BitmapData(fireW+margin,fireH,true,0x00FFFFFF);
  35.         gradientImage.draw(gradient_mc);//gradient_mcを消す必要は?
  36.         //同サイズの炎の揺らぎをランダム化
  37.         rdm = Math.floor(Math.random() * 10);
  38.     }
  39. }

イベントハンドラの使い方変更

コンストラクタに以下のコードを追加してみた。ただし、これは1個目の変更を行った事であまり要らなくなった。

ActionScript

  1. //ゆらぎは、他のSprite等にaddChildされた時のみ行うようにする
  2. addEventListener(Event.ADDED, function(e:Event):void {
  3.     addEventListener(Event.ENTER_FRAME,loop);
  4. });
  5. addEventListener(Event.REMOVED, function(e:Event):void {
  6.     removeEventListener(Event.ENTER_FRAME, loop);
  7. });

そもそも、何故メモリを食い潰すのか?

1秒毎にnewして作っていたdemoでは、不要になり、参照も外したTeraFireインスタンスが消去されずに残った上、多分全オブジェクトのENTER_FRAMEイベントを処理していたため、異常に重くなってしまったんだと予想している。

では、何故そうなったのだろうか?AS3的にはそれが当然と言う認識なのか?関数loopへの参照がどこかのイベントリスナー用の領域に残り、そこからTeraFireインスタンスへの参照が繋がり、結果インスタンスが生き残ったのか、単に「ガーベッジコレクションのタイミングが来るより早く、処理がいっぱいいっぱいになった」だけなのか?ただ、いくら待ってもメモリは減らなかったので、やはり参照が生き残って消えるべきオブジェクトがメモリから消えていかなかったのが悪いとは思うんだが・・・わからん!

スポンサーリンク
スポンサーリンク
  • このエントリーをはてなブックマークに追加

フォローする

スポンサーリンク
スポンサーリンク