3.0Starlingフレームワークで、物理演算エンジンを使ってみましょう。CPUの負荷が上がりやすい物理演算は、Stage3Dを試したい処理のひとつです。本稿は、使いやすさで注目されている「Nape」を採上げます[*1]。Starlingフレームワークの基本的な使い方については「Starlingフレームワークを使う」をお読みください。スクリプト004(CS6形式/Zip圧縮/約29KB)StarlingフレームワークとNapeのインストールおよび設定が必要です。本稿は、2012年12月13日にリリースされたバージョン2.0.3にもとづきます。以前のバージョンについては旧稿をご参照ください。NapeをインストールするNapeはオープンソースの物理エンジンです。ダウンロードページから、SWCファイルが手に入ります(図001)。Starlingフレームワークには「AS3」のSWCを選びます。release版とdebug版とは、本稿のスクリプトを試すには、どちらでも構いません。図001■ダウンロードページにリンクされた「AS3」用のSWCファイルダウンロードしたSWCファイルはライブラリパスに保存します。Starlingフレームワークで設定したライブラリパスのフォルダに一緒に入れて構いません(「Starlingフレームワークをインストールする」03「[パブリッシュ設定]によるActionScriptファイルのパスの指定」の項参照)。Starlingフレームワークで[ライブラリ]のビットマップをステージに表示するNapeの説明に入る前に、道具立てを整えなければなりません。アニメーションはボールを落として弾ませますので、そのボールを用意しましょう。Flashの[ライブラリ]に背景が抜かれたボール用の円形のビットマップを納め、クラスを設定しておきます(図002)。図002■[ライブラリ]に用意したボール用ビットマップにクラスを設定するさて、この[ライブラリ]のビットマップをどうやってStarlingフレームワークにもっていくかです。いうのは、前出「Starlingフレームワークを使う」01「Starlingフレームワークで何からつくり始めるか」に述べたとおり、「Starlingフレームワークの表示リストには、Starlingの表示オブジェクトしか加えられません」。そこで使うのが、Textureクラスです。テクスチャ(texture)というのは、Starlingフレームワークでイメージを表示するために用いられるビットマップデータのことです。TextureクラスにはTexture.fromBitmapData()という静的メソッドが備わっていて、BitmapDataオブジェクトからTextureインスタンスがつくれます。Textureインスタンスは表示オブジェクトではないので、そのままではStarlingフレームワークのステージには置けません。Imageクラスのコンストラクタメソッドに引数として渡したうえで、そのImageオブジェクトを表示リストに加えます。Image(myTexture);addChild(myImage);Starlingフレームワークの表示リスト最上位(ルート)に置く表示オブジェクトとして以下のクラス(MySprite)を定めると(スクリプト001)、[ライブラリ]にクラス(Pen)を設定したビットマップがステージの中央に表示されます。FLAファイル(ASファイルと同階層)には、つぎのフレームアクションが書かれているものとします(前出「Starlingフレームワークを使う」03「Starlingフレームワークの初期化」スクリプト002参照)。myImage;}}}図003■[ライブラリ]のビットマップがStarlingフレームワークでステージ中央に表示された初期設定の関数(initialize())は、[ライブラリ]のビットマップに設定したクラスからインスタンスをつくり、Texture.fromBitmapData()メソッドの引数に渡してTextureインスタンスをつくっています(スクリプト001第19〜20行目)。ボールのインスタンスをつくる関数(setBall())は別に定めました。後のNapeを使ったサンプルでは、ボールのインスタンスを複数つくるためです。Imageインスタンスはもうひとつの関数(createBall())を呼出してつくり、表示リストに加えて、位置を定めました(スクリプト001第25〜28行目)。Bodyクラス物理シミュレーションになじみのない方のために、その考え方を先にご説明します。物理演算は計算ですので、結果は数値です。そのままでは、アニメーションが再生されたりはしません。演算した数値を、ステージに描いたオブジェクトのプロパティに当てはめて、初めてオブジェクトの動きとして目に見えるのです。ステージのオブジェクトは、いわば操り人形です。Box2Dが黒子のように、自分自身の姿は隠し、オブジェクトを操って物理シミュレーションを演じて見せるということです。Napeで物体としてボールをつくります。body)」と呼ばれ[*2]、Bodyクラスで定めます。コンストラクタメソッドのシンタックスは、つぎのとおりです。位置)引数のうち、種類はBodyTypeクラスの定数で渡します。動かす剛体は、BodyType.DYNAMICにします。位置は、Napeに備わる2次元ベクトルのクラスVec2で定めます。剛体のかたちは、Body.shapesプロパティにShapeList.add()メソッドで、Shapeオブジェクトとして加えます。円形はCircleクラスで、コンストラクタメソッドのシンタックスはつぎのとおりです。材質)半径は数値で、ボールのテクスチャに合わせます。重心はVec2オブジェクトです。定めないときはnullを渡せば結構です。材質はMaterialオブジェクトで、コンストラクタの第1引数には弾性を数値で定めます。ここまでは、黒子である物理世界の定めです。人形のテクスチャをもつImageオブジェクトと、関わりができていません。人形のオブジェクトとの絡みはInteractor.userDataプロパティに与えます。このプロパティはダイナミックなオブジェクトですので、どのようなデータでもプロパティとして納められます。そこで、Imageオブジェクトの参照をBody.userDataプロパティに加えます。前掲スクリプト001に加える剛体のBodyオブジェクトをつくる処理はつぎのようになります。ボールの剛体は新たな関数(addPhysics())を定めてつくりました。ステートメント行頭の番号は、後に全体を掲げるスクリプト002にもとづきます。myImage;}ボールのBodyオブジェクトをつくる関数(addPhysics())には、第1引数としてImageオブジェクト、第3および第4引数にxy座標を定め、座標のデフォルト値はともに0としました(第57行目)。第2引数のSpaceオブジェクトは、呼出す側のメソッド(setBall())が受取った引数をそのまま渡しています(第46および第49行目)。Spaceオブジェクトについては、項を改めてご説明します。「剛体」をご参照ください。SpaceクラスNapeで剛体のBodyオブジェクトをつくったら、つぎにそれを物理空間に置きます。物理空間を定めるのはSpaceクラスです。コンストラクタメソッドの第1引数には、重力をVec2オブジェクトで定めます。Space(重力)剛体を物理空間に置くには、Spaceオブジェクトを剛体のBody.spaceプロパティに与えます。前掲スクリプト001には、さらにつぎのような手を加えます。ボールをつくるメソッド(setBall())の引数にSpaceオブジェクトを与え(第30〜31および第46行目)、物理演算を定めるメソッド(addPhysics())に渡すことにします(第49および第57行目)。物理演算をイメージに反映させる剛体のBodyオブジェクトをつくり、物理空間をSpaceオブジェクトで定めたら、初めの状態が決まりました。物理空間における剛体の動きをシミュレートするには、物理空間の時間を進めなければなりません。そのためには、Space.step()メソッドに経過時間を与えます。Spaceオブジェクト.step(経過時間)物理演算シミュレーションは、Flashの表示オブジェクト(DisplayObject)と異なり、画面の再描画で処理が進むという決まりはありません。アニメーションとして見せたいなら、フレームの描画のたびにSpace.step()メソッドで演算を進めなければならないのです。そこで、メソッドはEvent.ENTER_FRAMEイベントで呼出すことにします。すると、進める時間はフレームレートの逆数です。これだけでは黒子であるNapeの中の計算に過ぎません。黒子の演算結果をImageオブジェクトに反映させて、ようやく人形が動き始めます。物理空間でBodyオブジェクトをシミュレートした変化は、メソッドを定めてImageオブジェクトの動きに移します。そのメソッドはBody.userDataプロパティにコールバックとして加えます。後掲スクリプト002は、つぎのようにEvent.ENTER_FRAMEイベントのリスナーメソッド(refreshScreen())から、Space.step()メソッドを呼出しています(第35行目)。物理演算を定めるメソッド(addPhysics())の中で、Body.userDataプロパティにコールバック(graphicUpdate)を与えました(第62行目)。このメソッド(updateGraphic())は、これから新たに定義します。updateGraphic;}人形のオブジェクトを動かすコールバックの呼出しは、Event.ENTER_FRAMEイベントのリスナーメソッド(refreshScreen())がSpace.step()メソッドで物理演算を進めた後に加えます[*3]。物理空間(mySpace)には、剛体がいくつも含まれているかもしれません。それらは、Space.liveBodies()メソッドでBodyListオブジェクトとして得られます(第36行目)。その中にあるBodyオブジェクトの数は、BodyList.lengthプロパティで調べます(第37行目)。forステートメントで、すべての人形を順に動かしていきます(第38〜44行目)。BodyList.at()は、引数に渡したインデックスのBodyオブジェクトを取出します(第39行目)。剛体がすべて動くとはかぎりません。中には大道具のようなBodyオブジェクトもあります。動かない人形には、コールバックを定めません。したがって、コールバックのプロパティ名(graphicUpdate)を決めておき、あったらそれを呼出します(第40〜43行目)。コールバックのメソッド(updateGraphic())は、使い回せるようにBodyオブジェクトを引数に受取ります(第42および第64行目)。そのBody.userDataプロパティから動かす人形のオブジェクトを取出し、剛体の動きを移し替えます(第65行目)。剛体の位置はBody.position、回転角はBody.rotationプロバティで調べ、これらの値をImageオブジェクトに当てると、物理演算がアニメーションとして表れます(第66〜69行目)。myBody.rotation;}できあがったStarlingルートクラス(MySprite)の定義全体は、以下のスクリプト002のとおりです。これで晴れて物理シミュレーションにもとづいて、ボールのイメージがステージ上を自然落下します(図004)。[ムービープレビュー]で確かめてみましょう。以前のバージョンでは、Space.step()メソッドを呼出せば、Bodyオブジェクトに定めたコールバックも呼ばれました。新バージョンは物理演算エンジンの標準的な仕組みを採入れ、Spaceオブジェクトに含まれるBodyオブジェクトを取出して扱うようになりました。固定された剛体を置くボールがステージの下まで落ちてしまわないように床を置きましょう。剛体のBodyオブジェクトをつくります。床は動きませんので、コンストラクタメソッドに渡す第1引数の種類はBodyType.STATICです(第75行目)。床は矩形ですので、Polygonクラスでつくります。引数は配列で、エレメントには4角の座標をVec2オブジェクトで定めます。y)座標に幅と高さを渡せばその配列をつくってくれる静的メソッドPolygon.rect()が用意されています(第74行目)。床をつくる関数(setFloor())は、ボールをつくる関数(setBall())と同じように、Spaceオブジェクトを引数にして初期化の関数(initialize())から呼出すことにします(第33行目)。mySpace;}}床の剛体が矩形(Polygonオブジェクト)で固定(BodyType.STATIC)されているというだけで、手順はボールの剛体をつくるのと同じです。0)とみなされます(第75行目)。でき上がったStarlingルートクラス(MySprite)の定義は、つぎのスクリプト003のとおりです。[ムービープレビュー]を確かめると、落下したボールはステージ下端を床にして跳ね返り、バウンドを繰返します。落下する剛体を増やすでは仕上げに、ボールの数を100個ほどに増やしてみましょう。Timerオブジェクトを使って、時間差でボールを落とします。Bodyオブジェクトの数が増えますので、Vectorインスタンス(Bodyベース型)にまとめることにします。加える処理は、もうStarlingフレームワークもNapeも関わりません。細かい解説は省きます。手を入れるところはつぎのとおりです。Math.random());}Timerオブジェクトのリスナー関数(addBall())を用いて、0.2秒間隔でランダムな水平位置からボールが計100個落とされます(図005)。できあがったStarlingルートクラス(MySprite)の定義は、スクリプト004のとおりです。mySpace;}}}図005■ランダムな水平位置から時間差で100個のボールが落とされて互いにぶつかり合うStarlingフレームワークに関わる説明をひとつだけ加えます。前掲スクリプト004では、ボールと同じ数だけImageとBodyオブジェクトが増えました。[ライブラリ]のビットマップからつくったTextureインスタンスはひとつで、それをすべてのImageオブジェクトが共有しています。データが大きくなりやすいテクスチャは、複数の表示オブジェクトで使い回すことにより、メモリの消費を抑えることができるのです。Imageオブジェクトがステージに数多く表れても、実態は容れ物にすぎません。使われるTextureインスタンスのデータを管理することが重要です。