2009年3月28日土曜日

Verlet法

今回の投稿は、ActionScriptとは直接関係ない話。プログラム上で物体の動きを再現する上で避けて通れないのが、物理法則。そして、それらを計算するための数学…とくに積分。
ある瞬間の位置をr0、次の瞬間の位置をr1として、さらに次の瞬間の位置をr2し、r1からr2へ移動する間の加速度の和をatとすると、
r2= 2 * r1 - r0 + at
と書き表せるらしい。これをVerlet法(Verlet積分法)と呼ぶそうだ。

Euler法と比べると、精度もよく、計算量も少ないので、ある条件下では非常に便利である。その条件とは?プログラム上速さを制御する必要がない場合である。計算量が少なくてすむのは、ご覧のとおり、いちいち、速さを介在させて位置を計算しないからである。逆にそれが、仇となり、初速度v0で動かしたいとか、空気抵抗が速さに比例するなどとなってくると、少々面倒だ。

ただ、そういう場合でも、正確さがあまり必要でない場合、ある程度ごまかせるので、やはり便利だ。

2009年3月21日土曜日

ENTER FRAME イベント その4

前回は、ENTER_FRAMEイベントの登録、開放を意識することなく、ENTER_FRAMEイベントを利用するシンプルな方法を示した。しかし、実際には、その処理をもう少し制御できないと不便なことが多い。そこで、今回は、このライブラリを使って処理を制御する方法を示す。FrameHelperを使ってセットアップしたENTER_FRAMEイベントの処理は、次のようにして、そのループを一時停止したり、再開したりできる。setupメソッドの戻り値として得たヘルパークラスのインスタンスのメソッドを呼ぶことで実現できる。

package {
  import com.toritoriworks.helpers.FrameHelper;
  import com.toritoriworks.helpers.IHelper;

  import flash.display.Sprite;
  import flash.events.Event;
  import flash.events.MouseEvent;

  [SWF(width="240",height="180",frameRate="24",backgroundColor="0x336699")]
  public class EnterFrame3 extends Sprite  {
    private var _sprite:Sprite;
    private var _helper:IHelper ;
    public function EnterFrame3() {
      _sprite = new Sprite();
      _sprite.graphics.beginFill(0x339900);
      _sprite.graphics.drawRoundRect(0,0,50,50,5);
      _sprite.graphics.endFill();
      _sprite.y = 100;
      addChild(_sprite);

      _helper = FrameHelper.setup(_sprite,null,mainLoop,null);
      _sprite.addEventListener(MouseEvent.CLICK, pauseHandler);
    }

    private function pauseHandler(event:Event):void {
      _sprite.removeEventListener(MouseEvent.CLICK, pauseHandler);
      _helper.pause();
      _sprite.addEventListener(MouseEvent.CLICK, resumeHandler);
    }

    private function resumeHandler(event:Event):void {
      _sprite.removeEventListener(MouseEvent.CLICK, resumeHandler);
      _helper.resume();
      _sprite.addEventListener(MouseEvent.CLICK, pauseHandler);
    }

    private function mainLoop():Boolean {
      _sprite.x += 5;
      if (_sprite.x> 240) {
        _sprite.x = 0;
      }
      return true;
    }
  }
}


実行結果は、次のとおりである。矩形をクリックすると、それが止まったり右へ動いたりを交互に繰り返す。



ここでは、pause()、resume()の例を示したが、他にrevert()、skip()、cancel()がある。pause(),resume(),revert(),skip()の動作については、「ENTER FRAME イベント その2」のチャートを見てもらいたい。cancel()については、複雑になるので、あえてチャート上に表示しなかった。

cancel()メソッドは、その場で、FrameHelperの処理をすべて中断し、setupメソッドで与えられた関数を実行することなく、処理を終了する。

2009年3月15日日曜日

ENTER FRAME イベント その3

前回、予告したクラスがFrameHelperというクラスである。これを用いると、ENTER_FRAMEイベントを意識することなく、そのイベントを利用できる。開発者をaddEventListener、removeEventListenerの呪縛から開放してくれる(もちろん、メモリリークのリスクがゼロになるわけではない)。「ENTER FRAME イベント その1」で紹介したコードは、次のように書き換えられる。

package {
  import com.toritoriworks.helpers.FrameHelper;

  import flash.display.Shape;
  import flash.display.Sprite;

  [SWF(width="240",height="180",frameRate="24",backgroundColor="0x336699")]
  public class EnterFrame extends Sprite {
    private var _shape:Shape;
    public function EnterFrame() {
      _shape = new Shape();
      _shape.graphics.beginFill(0x339900);
      _shape.graphics.drawRoundRect(0,0,10,10,5);
      _shape.graphics.endFill();
      _shape.y = 100;
      addChild(_shape);

      FrameHelper.setup(_shape,null,mainLoop,null);
    }

    private function mainLoop():Boolean {
        _shape.x += 5;
        if (_shape.x > 240) {
          _shape.x = 0;
        }
        return true;
    }
  }
}



ここで、mainLoop()関数がfalseで終了するようにすると、ENTER_FRAMEのイベントリスナーは勝手に解放されるようになっている。それを利用した実装例を次に示す。矩形がズームインしてくる例である。



package {

  import com.toritoriworks.helpers.FrameHelper;

  import flash.display.Shape;
  import flash.display.Sprite;

  [SWF(width="240",height="180",frameRate="24",backgroundColor="0x336699")]
  public class EnterFrame2 extends Sprite {
    private var _shape:Shape;
    public function EnterFrame2() {
      _shape = new Shape();
      _shape.graphics.beginFill(0x339900);
      _shape.graphics.drawRoundRect(-40,-40,80,80,5);
      _shape.graphics.endFill();
      addChild(_shape);
      _shape.x = 100;
      _shape.y = 100;

      FrameHelper.setup(_shape,begin,mainLoop,finish);
    }

    private function begin():void {
      _shape.scaleX = .1;
      _shape.scaleY = .1;
    }

    private function mainLoop():Boolean {
      _shape.scaleX += (1-_shape.scaleX) * .1;
      _shape.scaleY += (1-_shape.scaleY) * .1;
      return _shape.scaleX < .98;
    }

    private
function finish():void {
      _shape.scaleX = 1;
      _shape.scaleY = 1;
    }
  }
}


ご覧のとおり、ENTER_FRAMEのイベントの登録・開放を意識することなく実装できる。これは、開発者への負荷をかなり軽減するものと信じる。

上記二例では、単にENTER_FRAMEイベント内の処理が終わるのをただ待つだけである。しかし、「ENTER_FRAME イベント その2」のチャートで示したとおり、処理の流れを制御することもできる。次回ではその例を示したいと思う。

FrameHelperクラスのダウンロードは、こちらから。