出典:http://compasstech.com.au/TNS_Authoring/Scripting/script_tut26.html
レッスン26「(3.2)物理エンジンへようこそ(その4)」:セグメントをシェイプとして追加する
前のレッスンからの続きです。下記の変更を適用しても、見た目は何ら変化しません。しかし、我々のおこなったことは、物理シェイプの新たなレパートリーとして、円のほかにセグメントという新たなタイプのシェイプを紹介することです。また、将来的に役立つであろう新たなスクリプティング手法についても学びます。
Bounding our Space
local boundaries
function init(gc)
...
boundaries = {}
...
space = physics.Space()
bounds(W, H)
...
end
function bounds(w, h)
-- Set the boundary walls
for _,wall in ipairs(boundaries) do
space:removeStaticShape(wall)
end
local bound
bound = physics.SegmentShape(nil, physics.Vect(0,0), physics.Vect(w, 0), 1)
: setRestitution(elasticity)
: setFriction(friction)
space:addStaticShape(bound)
table.insert(boundaries, bound)
bound = physics.SegmentShape(nil, physics.Vect(0,H), physics.Vect(w, h), 1)
: setRestitution(elasticity)
: setFriction(friction)
space:addStaticShape(bound)
table.insert(boundaries, bound)
bound = physics.SegmentShape(nil, physics.Vect(0,0), physics.Vect(0, h), 1)
: setRestitution(elasticity)
: setFriction(friction)
space:addStaticShape(bound)
table.insert(boundaries, bound)
bound = physics.SegmentShape(nil, physics.Vect(w,0), physics.Vect(w, h), 1)
: setRestitution(elasticity)
: setFriction(friction)
space:addStaticShape(bound)
table.insert(boundaries, bound)
end
今回のスクリプトの大きな変更はinit函数にあります。動き回るボディーがページの境界内に入っているかどうかを確認する4つのステートメントを、前回のpaint函数から削除し、別の手法に置き換えます。画面の4つの壁は、セグメントを使って定義することにします。このセグメントは見えません。見えるようなシェイプを与えないからです。しかし、このセグメントには厚みがあります(今回の例では1単位の厚み)。また、このセグメントには、反撥力(弾性)および摩擦といった属性を持っていて、この属性は変更が可能です。
こうした手法のメリットがわかりますか?
今回わたくしは、boundsという名前の函数を定義することにします。この函数内で上記のセグメントを定義し、あとはinitルーチンの中でこの函数をコールするだけです。この処理は、スペースを定義した直後、他のオブジェクトを作成するよりも先におこなうことにします。
各セグメントは、boundariesという名前のテーブル内で定義します。したがって当然、スクリプトの先頭のところでローカル変数としてこのテーブルを定義するところから始まります。このテーブルについては、init函数内で空のテーブルとして定義します。
前回は、ボディーとシェイプを定義し、それをスペースに追加しましたね。今回は、少し違った手法を用います。4つの壁は、動かす必要がありませんので、普通のシェイプとしてではなくStaticShapesとして定義できます。動かないわけですから、ボディーの定義は不要です。よく考えてみてください。
ほとんどの物理オブジェクトの場合、ボディーはキー・コンポーネントであり、シェイプは、ボディーに外観と何らかのプロパティーとを与えるためのオプションとしての「仮面」です。静的シェイプにはボディーは不要です。その代わり、静的シェイプを定義したら、上述したように弾性や摩擦などのプロパティーを静的シェイプに与えます。
さらに、removeStaticShapeおよびaddStaticShapeという新たな2つのコマンドを導入します。
bounds函数の先頭数行を見ればわかるでしょうが、これは実は、既存の静的シェイプを消去している部分ですので、on.resizeを複数回コールしても静的シェイプが蓄積することはありません。厳密に言えば、boundariesテーブルは、boundsをコールする直前に空のテーブルとして定義するわけですから、この処理は厳密には不要でしょう。しかし、ipairsコマンドを使ってテーブルの各要素を巡回する素晴らしい方法の1つではあります。
ローカル変数boundを定義したあと、4つの壁それぞれのためにboundという名前のセグメントを作成し、それぞれを順番にboundariesテーブルへ追加します。見てわかるように、セグメントは、physics.SegmentShapeコマンドを使って定義します。このコマンドは、引数として、「ボディー(今回はnil)」「セグメントの始点を表すベクトル」「セグメントの終点を表すベクトル」「セグメントの半径または厚みを表す値」を取り込みます。
その下のスクリプトを見てください。定義したばかりのこの変数に2つのプロパティーを追加するための素晴らしい手法が使われています。bound:setRestitution(elasticity)という、普通の構文は省略できます。boundを定義した直後ですから、先頭にコロンを付けるだけでコールできるのです。
新たなboundは、静的シェイプとしてスペースにそれぞれ追加されたあと、boundariesテーブルへ追加されます。
最終的に、ぶつかってきたボールを跳ね返すだけでなく、実際に弾ませる4つの壁ができあがります。
ここで新たな課題を提示します。気づいたと思いますが、4つの壁は、予測したようにはボールが弾みません。ボールが跳ねすぎる壁もあれば、ほとんど跳ねない壁もあるようです。
どの壁にぶつかったときも同じように跳ねるように修正できますか?
次のレッスン(今のところ今シリーズ最後のレッスン)では、辺の数が無限ではないシェイプについて検討します。言い換えれば、辺の数を好きなように変更するオプションを使って円およびセグメントから多角形へとシェイプを拡張するということです。