ActionScript3 で 3D 振り子を作るサンプル

3D描画ライブラリ Papervision と 3D物理演算ライブラリ WOW-Engine を利用して 3D 振り子を作ります。(Papervision3D 逆引きシリーズ)

3D 振り子を作るにあたって必要な Papervision3D の使い方や WOW-Engine の導入法・使い方は過去のエントリを見てね。今回新しく出てきて肝となる部分はすごく少ないです。

まずパーティクルを作成

振り子の「支点」と「おもり」用に2つのパーティクル*1を作成します。支点のほうのパーティクルはコンストラクタの第5引数を false にして固定しておくのを忘れずに。こうすると、物理現象の影響を受けないパーティクルになり直接座標を指定しない限りは動かなくなります。

// 支点を作成
var particle1:WSphere = new WSphere(0, 100, 0, 30, true);
wow.addParticle(particle1);

// おもりを作成
var particle2:WSphere = new WSphere(0, 0, 0, 30, false, 1);
wow.addParticle(particle2);

WSpringConstraint

支点とおもりを作ったら、今度はこの2つを繋ぐ「ひも」を作ります。2つのパーティクルを繋ぐには WSpringConstraint クラスを使います。

// 二つのパーティクルを接続
// particle1 と particle2 を強さ 1 で接続
var spring:WSpringConstraint = new WSpringConstraint(particle1, particle2, 1);
spring.restLength = 100;
wow.addConstraint(spring);
new WSpringConstraint()
コンストラクタではどのパーティクルを繋ぐかと接続の強度を指定します。接続の強度は0から1の数値で1ならば2つのパーティクル間の距離は常に一定に保たれるようになり、逆に0だと二つのパーティクルは接続されていないかのようにふるまいます。中間の値を指定するとばねで接続されたようになります。
spring.length
lenght プロパティでは二つのパーティクル間の距離を設定します。
addConstraint
最後に WOW-Engine にこの WSpringConstraint を追加します。

たったこれだけ!

あとは過去のエントリと同じように Papervision3D の初期化や WOW-Engine の初期化。フレーム毎に座標の更新などを行えば振り子の完成です。
WSpringConstraint は応用が効いて、パーティクルを平面に並べて接続すれば布の表現ができるし、立方体の頂点にパーティクルを配置して接続すれば「回転」を含めた箱の表現ができるし、いろいろ面白そうです。

全体のコード

package
{
    import flash.display.Sprite;
    import flash.events.Event;
    import fr.seraf.wow.constraint.WSpringConstraint;
    import fr.seraf.wow.core.data.WVector;
    import fr.seraf.wow.core.WOWEngine;
    import fr.seraf.wow.primitive.WBoundArea;
    import fr.seraf.wow.primitive.WSphere;
    import org.papervision3d.lights.PointLight3D;
    import org.papervision3d.materials.shadematerials.GouraudMaterial;
    import org.papervision3d.materials.utils.MaterialsList;
    import org.papervision3d.materials.WireframeMaterial;
    import org.papervision3d.objects.primitives.Sphere;
    import org.papervision3d.objects.primitives.Plane;
    import org.papervision3d.view.BasicView;

    [SWF(width="320", height="240", backgroundColor="#FFFFFF")]
    public class Sample005 extends Sprite
    {
        public function Sample005 ()
        {
            // 物理計算のために WOWEngine を作成
            var wow:WOWEngine = new WOWEngine(1);
            wow.addMasslessForce(new WVector(0, -4, 0));
            
            // バウンドエリアを設定
            var boundArea:WBoundArea = new WBoundArea(1000, 300, 1000);
            wow.setBoundArea(boundArea);
            
            // 支点を作成
            var particle1:WSphere = new WSphere(0, 100, 0, 30, true);
            wow.addParticle(particle1);
            
            // おもりを作成
            var particle2:WSphere = new WSphere(0, 0, 0, 30, false, 1);
            wow.addParticle(particle2);

            // 二つのパーティクルを接続
            // particle1 と particle2 を強さ 1 で接続
            var spring:WSpringConstraint = new WSpringConstraint(particle1, particle2, 1);
            spring.restLength = 100;
            wow.addConstraint(spring);

            // 表示のために PaperVision のビューを作成
            var view:BasicView = new BasicView();
            view.camera.y = 500;
            view.camera.x = 600;
            view.camera.z = -900;
            addChild(view);
            view.startRendering();

            // 光源を作る
            var light:PointLight3D = new PointLight3D(false);
            light.x = 500;
            light.y = 800;
            light.z = 0;
            
            // 地面表示用 3D オブジェクトを作る
            var plane:Plane = new Plane(new WireframeMaterial(0xff99bb), 1000, 1000, 3, 3);
            plane.rotationX = 90;
            plane.y = -150;
            view.scene.addChild(plane);
            
            // パーティクルに対応する 3D オブジェクトを作る
            var sphere1:Sphere = new Sphere(new GouraudMaterial(light, 0xeeeeee, 0x666666), 30, 30, 30);
            var sphere2:Sphere = new Sphere(new GouraudMaterial(light, 0xeeeeee, 0x666666), 30, 30, 30);
            view.scene.addChild(sphere1);
            view.scene.addChild(sphere2);
            
            // 毎フレームごとの処理
            var c:int = 0;
            addEventListener(Event.ENTER_FRAME, function():void {
                // 計算を行い WOW-Engine のオブジェクトを次の位置へ
                wow.step();
                // 新たな位置を PaperVision3D のオブジェクトへ設定
                sphere1.x = particle1.px;
                sphere1.y = particle1.py;
                sphere1.z = particle1.pz;
                sphere2.x = particle2.px;
                sphere2.y = particle2.py;
                sphere2.z = particle2.pz;
                // 支点を移動
                particle1.px = 150*Math.sin(3.9*c*Math.PI/180);
                particle1.pz = 150*Math.cos(12.0*c*Math.PI/180);
                // カメラを移動
                view.camera.x = 900*Math.sin(c*Math.PI/180);
                view.camera.z = 900*Math.cos(c*Math.PI/180);
                c++;
            });
        }
    }
}

*1:WOW-Engine で物理演算の対象になるオブジェクト