AS3:物理演算ライブラリのNapeを使ってみた(その1)

AS3:物理演算ライブラリのNapeを使ってみた(その1)Box2Dよりも演算が高速とのことだったので試してみました。
英語のマニュアルが充実していますが、残念ながら英語は苦手です(悲)
日本語の情報も少なくて分からないことが多いのでメモします。

1回目はマニュアルの「2 Hello Nape」を参考に基本の部分を、2回目があればPhysicsEditerで書き出したデータの使い方などを書いてみたいです。ちなみに、公式サイトのSampleを見た方が分かりやすいと思います(笑)。

Nape Physics Engine(公式)
ライブラリはnape-release.swcを使いました。
http://napephys.com/

空間(Space)を作る

空間は Nape が演算用に用意する画面表示とは別に存在していて、通常は直接見ることができません。ここで演算した結果をプログラム側で画像などに置き換えて画面表示をする事になります。ただし、デバッグ表示をONにすれば簡易表示ができます。

最初に重力と設定して、それを引数に空間を作ります。重力の引数は(X方向, Y方向)で、単位は「ピクセル/秒/秒」。下記は1秒間にY方向に+600pxの重力です。

var gravity:Vec2 = new Vec2(0, 600);
var space:Space = new Space(gravity);

デバッグ表示を設定

デバッグ表示をするインスタンスを作って addChild します。

var debug:Debug = new BitmapDebug(stage.stageWidth, stage.stageHeight, 0xFFFFFF);
addChild(debug.display);

空間へ配置できる物体(Body)は3種類

物体は「Body」と呼ばれていて、形とか特性とかをひとまとめにできます。種類は3つあり、下記のように使えばいいんじゃないかな〜って解釈しています。

STATIC(静的):壁とか基本的に動かない物体に使う。
KINEMATIC(運動学的):人とか車とか命令を出してコントロールしたい物体に使う。
DYNAMIC(動的):ボールなどの外的な要因によって動く物体に使う。

空間に地面を配置

地面は動かないものなので、STATIC型で Body インスタンスを作成します。Body には、形の情報が入っていないので、Polygon() を使って地面を作成します。画面の下部に天地50pxの長方形を作りたいので Polygon.rect() を使います。

var floorBody:Body = new Body(BodyType.STATIC);
var floorShape:Polygon = new Polygon(Polygon.rect(0, stage.height-50, stage.width, 50));

Polygon() の引数はArray型で、下記の形が作れます。

var rectangleVertices:Array = Polygon.rect(top-left-x, top-left-y, width, height);
var boxVertices :Array = Polygon.box(width, height);
var pentagonVertices :Array = Polygon.regular(width, height, 5);

Body へ地面の形状を追加してから、空間へ追加します。

floorBody.shapes.add(floorShape);
space.bodies.add(floorBody);

空間にボールを配置

ボールは外的要因で動くものなので、DYNAMIC型で Body インスタンスを作成します。引数を省略すると DYNAMIC になります。地面と同じく、Body には形の情報が入っていないので、今度は Circle() を使って円を作成します。引数は「半径」です。

var circleBody:Body = new Body();
var circleShape:Circle = new Circle(50);

円に弾力と密度を設定します(ちゃんと調べてない)。

circleShape.material.elasticity = 1;
circleShape.material.density = 4;

Body へ円の形状、配置する座標、速度(ピクセル/秒)を追加してから、空間へ追加します。

circleBody.shapes.add(circleShape);
circleBody.position.setxy(stage.width/2, 0);
circleBody.velocity.setxy(0, 100);
space.bodies.add(circleBody);

動かす

イベントリスナーの ENTER_FRAME 処理で空間の時間を進めます。
単位は「1秒/フレームレート」。

space.step(1 / stage.frameRate);

デバッグ用の表示をします。

debug.clear();
debug.draw(space);
debug.flush();

おわりに

Box2Dに比べて処理がかなり速いです。同時にたくさんの物体を動かしてみるとその差は歴然です。日本語の情報は少ないですが、Box2Dもバージョンが変わってから記述が変わってしまって、参考になる情報を探すのが大変だったりするので、最初から覚えるなら Nape もおすすめなんじゃないかなと思います。

ソースコード

package {

  import flash.display.Sprite;
  import flash.events.Event;
  import nape.geom.Vec2;
  import nape.space.Space;
  import nape.util.BitmapDebug;
  import nape.util.Debug;
  import nape.phys.Body;
  import nape.phys.BodyType;
  import nape.shape.Polygon;
  import nape.shape.Circle;

  public class Main extends Sprite {

    private var space:Space;// 空間
    private var debug:Debug;// デバッグ用

    // コンストラクタ
    public function Main() {

      if (stage != null) initialise(null);
      else addEventListener(Event.ADDED_TO_STAGE, initialise);

    }

    // 初期設定
    private function initialise(e:Event):void {

      if (e != null) removeEventListener(Event.ADDED_TO_STAGE, initialise);

      // 空間を作成
      var gravity:Vec2 = new Vec2(0, 600);// 重力
      space = new Space(gravity);

      // デバッグ表示を設定
      debug = new BitmapDebug(stage.stageWidth, stage.stageHeight, 0xFFFFFF);
      addChild(debug.display);

      // 空間に地面を配置
      var floorBody:Body = new Body(BodyType.STATIC);// Body
      var floorShape:Polygon = new Polygon(Polygon.rect(0, stage.height-50, stage.width, 50));// 形状
      floorBody.shapes.add(floorShape);// 形状をBodyへ追加
      space.bodies.add(floorBody);// Bodyを空間へ追加

      // 空間にボールを配置
      var circleBody:Body = new Body();// Body
      var circleShape:Circle = new Circle(50);// 形状
      circleShape.material.elasticity = 1;// 弾力
      circleShape.material.density = 4;// 密度
      circleBody.shapes.add(circleShape);// 形状をBodyへ追加
      circleBody.position.setxy(stage.width/2, 0);// 空間内の座標
      circleBody.velocity.setxy(0, 100);// 空間内の初速
      space.bodies.add(circleBody);// Bodyを空間へ追加

      // フレーム処理を開始
      addEventListener(Event.ENTER_FRAME, enterFrameHandler);

    }

    // フレーム処理
    private function enterFrameHandler(e:Event):void {

      // 空間の時間を進めます。
      space.step(1 / stage.frameRate);

      // デバッグ用の表示
      debug.clear();
      debug.draw(space);
      debug.flush();

    }

  }

}


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です