[衝突検出]
Farseer Physics Engine は使いやすい4つの異なる区分を含むコリジョンシステムを提供します:
1. 広域な衝突検出
2. 厳密な衝突検出
3. AABB (Axis Aligned Bounding Box)
4. グリッドの衝突検出
それぞれのシステムについて、以下に説明します:
[広域な衝突検出]
広域な衝突検出は、エンジンがしなければならない仕事を減らして衝突検出の速度を上げるために、先進のアルゴリズムに頼ります。現在、3種類の広域な衝突検出アルゴリズムがあります:
1. Sweep And Prune (SAP と呼ばれます)
2. Selective Sweep
3. Brute Force
Sweep And Prune アルゴリズムはフレームコヒーレントです。これは画面外に多くのオブジェクトがある場合には、悪い選択となるかもしれないということを意味しますまた、これはオブジェクトが近い位置にあるのなら、最後のフレームにあり、このアルゴリズムは良いことを意味します。SAP アルゴリズムではオブジェクトがテレポートする、またはすごい速さでワールドの端から端まで移動する、または弾丸が好ましくないことに注意して下さい。それにより壊れてしまい、信頼できない衝突を引き起こすかもしれません。SAP の詳細情報は
ここと
ここで見ることができます。
Selective Sweep アルゴリズムは BioSlayer 氏によって開発されました。Farseer Physics Engine のデフォルトは SS アルゴリズムです。SS は当初、 Sweep And Prune の上に構築されましたが、SAP よりもパフォーマンスを向上させるためにいくつかの変更がなされました。More information on SS can be found here.
SS の詳細情報は
ここで見ることができます。
Brute Force アルゴリズムはこれらの中で最もシンプルです。しかし3つの中で最も実行速度が遅いですワールド上の全てのジオメトリを辿り、その AABB を比較します。Brute Force アルゴリズムの計算量は O(n^2) です。しかしジオメトリの数が少なければとても速いです。
[厳密な衝突検出]
厳密な衝突検出は、広域な衝突検出で衝突した全てのペアで発生し、さらにそれらの計算を行います。全ての厳密な衝突検出のコードは、
Arbiter クラスの中にあります。下記に厳密な衝突検出で行われることの概要を記載します。広域な衝突検出によって、アービターオブジェクトの中に衝突しているジオメトリのペアが検出されていると仮定します。
1) 最初のジオメトリが持つ全てのワールド座標上の頂点を辿ります。
2) 現在のベクトルが2番目のジオメトリと交差するか?
a) false (交差しない): 頂点リストの次のベクトルを計算します。
b) true (交差する): 接触情報を作成し、接触情報リストに挿入します。
3) 1) と 2) を2番目のジオメトリで行います。
4) 接触情報リストに何らかの接触情報があれば、OnCollision イベントに2つのジオメトリと接触情報リストを渡し、呼び出して下さい。
Arbiter クラスは、衝突するときにジオメトリに適用される衝撃力を計算するのにも使用されます。
[AABB]
AABB は Axis Aligned Bounding Box の略で、名前が示す通り、軸に平行なバウンディングボックスです。全てのジオメトリはそれぞれの更新時に再計算される AABB を持っています。AABB は比較的安価で2つのジオメトリがお互いに近くにある (もしくは接触している) かどうかを高速にテストするのに使用されます。
下記のようにすると、2つのジオメトリがお互いに近くにあるかどうかをテストできます:
if (AABB.Intersect(_circleGeom.AABB,_rectangleGeom.AABB))
{
//The 2 AABB's intersect
}
AABB は回転せず、またアウトラインはジオメトリの長方形ですので、2つの AABB の交差判定を行ったとき、実際には接触していないかもしれないということを覚えておいて下さい。
下の図を見てください。
AABB は黒いジオメトリのアウトラインです。見ての通り、それらは回転しておらず、また軸に平行です。本当に接触していれば、衝突するときに生成された赤い接触点が表示されます。
[グリッド]
全てのジオメトリはグリッドオブジェクトを含んでいます。厳密な衝突判定で使用されており、そして距離グリッドを使用します。距離グリッドは、グリッドのそれぞれの点のジオメトリ上で最も近い点とその点の法線を事前計算しています。(法線についての詳細は「物理」の章に記載しています)
ジオメトリの内側にある全てのグリッド点の距離は負の数になります。下にグリッドとその点の図を示します:
距離グリッドは一度、事前計算されると、どんなグリッドの中間点の距離と法線も、分かっている点の値で補完することで計算できます。Geom のコンストラクタ、または GeomFactory を使用したとき、グリッドは指定されたグリッドのセルサイズから計算されますグリッドのセルサイズが小さければ、グリッドがより正確なり、またそれによって衝突検出が正確になります。
グリッドの計算はかなり時間がかかることがありますので、事前にすべてのジオメトリの具体例を示して、ちょうど良いグリッドのセルサイズを選ぶという方法は、良いアイデアかもしれません。この詳細については「パフォーマンス」の章に記載しています。
デフォルトで、全てのジオメトリはコリジョングループ 0 にあり、これは他の全てのジオメトリと衝突することを意味します。2つのジオメトリが同じコリジョングループにある場合は、お互いに衝突しませんが、コリジョングループ 0 は例外です。
下記にをどのようにジオメトリのコリジョングループを設定するかを示します:
Body rectBody = BodyFactory.Instance.CreateRectangleBody(PhysicsSimulator, 128, 128, 1);
rectBody.Position = new Vector2(250, 400);
Geom rectGeom = GeomFactory.Instance.CreateRectangleGeom(PhysicsSimulator, rectBody, 128, 128);
rectGeom.CollisionGroup = 10;
Body circleBody = BodyFactory.Instance.CreateCircleBody(PhysicsSimulator, 64, 1);
circleBody.Position = new Vector2(300, 400);
Geom circleGeom = GeomFactory.Instance.CreateCircleGeom(PhysicsSimulator, circleBody, 64, 20);
circleGeom.CollisionGroup = 10;
もし rectGeom と circleGeom が重なり合っているとしても、お互いに衝突しません。コリジョングループは使いやすいですが、できる事が非常に制限されます。それがコリジョンカテゴリーも存在している理由です。
コリジョンカテゴリーを使用するときに興味深い2つのプロパティがあります:
1) CollisionCategories
a) デフォルトは CollisionCategory.All です。
b) ジオメトリがどこのメンバーなのかを定義するために使用されます。
2) CollidesWith
a) デフォルトは CollisionCategory.All です。
b) ジオメトリがどこのメンバーと衝突するのかを定義するために使用されます。
コリジョンカテゴリーは CollisionCategory と呼ばれる列挙型を使い、その特別なフラグを有効にします。これはビット演算子で行うことができます。(詳細はここを見て下さい)
例:
Body rectBody = BodyFactory.Instance.CreateRectangleBody(PhysicsSimulator, 128, 128, 1);
rectBody.Position = new Vector2(250, 400);
Geom rectGeom = GeomFactory.Instance.CreateRectangleGeom(PhysicsSimulator, rectBody, 128, 128);
rectGeom.CollisionCategory = CollisionCategories.Cat5;
rectGeom.CollidesWith = CollisionCategories.All & ~CollisionCategories.Cat4;
Body circleBody = BodyFactory.Instance.CreateCircleBody(PhysicsSimulator, 64, 1);
circleBody.Position = new Vector2(300, 400);
Geom circleGeom = GeomFactory.Instance.CreateCircleGeom(PhysicsSimulator, circleBody, 64, 20);
circleGeom.CollisionCategory = CollisionCategories.Cat4;
circleGeom.CollidesWith = CollisionCategories.All & ~CollisionCategories.Cat5;
今回、rectGeom は Cat5 (Category 5) のメンバーで、Cat4 以外の全てと衝突します。
circleGeom Cat4 のメンバーで、Cat5 以外の全てと衝突します。
これは2つのジオメトリがお互いに衝突しないことを意味します。
1. OnCollision (in Geom class)
2. OnSeparation (in Geom class)
3. OnBroadPhaseCollision (in IBroadPhaseCollider interface)
OnCollision イベントはジオメトリが他のジオメトリと衝突したときに呼び出されます。イベントメソッドは衝突によって何かが起こったのかどうかを boolean で返す必要があります。
OnSeparation イベントはジオメトリが他のジオメトリと切り離された後に呼び出されます。
OnBroadPhaseCollision イベントは OnCollision イベントと似ていますが、広域な衝突検出のときにもうすでに呼び出されます。
このイベントをキャンセルするとアービターが作成されません。これは衝撃力が適用されず、また関係しているジオメトリの厳密な衝突検出も行われないことを意味します。
イベントの登録方法を下記に記します:
Body circleBody = BodyFactory.Instance.CreateCircleBody(PhysicsSimulator, 64, 1);
Geom circleGeom = GeomFactory.Instance.CreateCircleGeom(PhysicsSimulator, circleBody, 64, 20);
circleGeom.OnSeparation += OnSeperation;
circleGeom.OnCollision += OnCollision;
PhysicsSimulator.BroadPhaseCollider.OnBroadPhaseCollision += OnBroadPhaseCollision;
OnBroadPhaseCollision イベントは PhysicsSimulator の BroadPhaseCollider に登録されることに注意して下さい。
そして、イベントが発生したときにメソッドが実行されます:
private bool OnCollision(Geom geom1, Geom geom2, ContactList contactList)
{
return true;
}
private void OnSeperation(Geom geom1, Geom geom2)
{
}
private bool OnBroadPhaseCollision(Geom geom1, Geom geom2)
{
return true;
}
1. 衝突の反応
2. 手動の衝撃力
衝突の反応は、2つのジオメトリがお互いに衝突したときに発生します。
「厳密な衝突検出」の章に記載されている Arbiter クラスは、衝突が発生したときに衝撃力の計算に対して責任を持ちます。もう少し技術的に考えると、接触点が厳密な衝突判定で計算され、衝撃力が適用されることにより、ジオメトリは実際の物理のように振る舞います。
衝突の反応は、下記のように ジオメトリの CollisionResponseEnabled に false を設定することで無効にすることができます:
Geom circleGeom = GeomFactory.Instance.CreateCircleGeom(PhysicsSimulator, circleBody, 64, 20);
circleGeom.CollisionResponseEnabled = false;
衝突の反応を無効にするということは、全てのジオメトリをすり抜けることを意味します。「コリジョンイベント」の章に記載しているコリジョンイベントは発生します。
また、手動でボディに衝撃力を適用することもできます。(ボディはダイナミクスをコントロールし、ジオメトリは衝突をコントロールしますが、アービターは衝突に関するジオメトリの衝撃力をコントロールすることを覚えておいて下さい。)
ボディにフォース/衝撃力を適用する方法が3つあります。
以下にフォースとそのメソッドのリストを示します:
1. フォース
1. ApplyForce
2. ApplyForceAtLocalPoint
3. ApplyForceAtWorldPoint
4. ClearForce
2. 衝撃力
1. ApplyImpulse
2. ClearImpulse
3. ApplyAngularImpulse
3. トルク
1. ApplyTorque
2. ClearTorque
フォースはボディを加速させるのに使用されます。ボディまたはボディ上の特定の点に適用することができます。これは、あなたのキャラクターに時間の経過とともに加速するジェットパックを追加するのに使用できます。
衝撃力はボディに衝撃を与えるのに使用されます。衝撃力は、フォースのようにボディを加速させる代わりに、ボディの速度を更新します。ゲームのキャラクターをジャンプさせるのにこれを使用することができます。ジャンプは、瞬時の変化であり、加速はしません。
トルクはボディのトルク (回転) として適用されます。ホイールを回転させるか、ボーダーに丘を駈け上がらせることができます。
[物理プロパティ]
Farseer はボディとジオメトリの物理プロパティを変更するための素晴らしいインターフェースを持っています。ほとんどのプロパティは物理エンジン実行中に変更することができ、ダイナミックな振る舞いを作成することを可能にします。
以下に物理プロパティとどのようなものか簡単な説明を示します:
Body クラスのプロパティ:
AngularVelocity
回転速度はボディが回転している比率です。これはラジアン/秒で計測されます。比率を大きくするとボディが速く回転します。
LinearVelocity
ベロシティは位置変更の比率として定義されます。また、ボディの単位時間の置き換えとしても定義されます。これは、ただ移動量を伝えるだけでなく、方向も含まれているベクトルです。
LinearDragCoefficient
抗力は、流体またはガス (空気) によってボディの動きに抵抗する力です。空気中を速く移動しているボディがあると抗力によって徐々に失速します。Farseer Physics では、媒体 (流体またはガス) をボディに入れることができないため、手動でボディの抗力係数を設定しなければなりません。高い抗力係数は、ボディを動かすためにより多くの力が必要で、それにより失速します。
RotationalDragCoefficientほとんど LinearDragCoefficient と同じですが、回転するときにもいくらかの抵抗があります。回転の抗力係数が 0 でボディを回転させると、永遠に回転し続けます。回転の抗力係数が高ければ高いほどボディの回転は速く失速します。
Moment of Inertia (MOI
2Dでボディの慣性モーメントは、どれくらい質量中心についてボディを回転させるのが難しいか (または難しくないか) を示すスカラー値です。
Geom クラスのプロパティ:
RestitutionCoefficient
反発係数は衝撃による前後間の速度比です。反発係数に 1 を設定すると、完全な弾みを持ちます。(地面に跳ね返るボールをイメージして下さい) また、0 に設定すると、全く弾まなくなります。
FrictionCoefficient
摩擦力は、2つの物質面がお互いと接触して、運動に対して相対的に抵抗を与える基本的な力です。物質の摩擦力が大きくなると、他の物質と比較してより動きにくくなります。
例えば鋼の氷で、非常に小さい摩擦係数を持ちます。お互いにすぐに滑ってしまいます。一方、舗道の上のゴムは、非常に高い摩擦係数を持っており、ほとんど滑りません。
Farseer Physics は摩擦力を扱う2つの異なった方法がありますので注意して下さい。PhysicsSimulator オブジェクトの FrictionType に次の2つの内1つを設定して下さい。
○ Average
ジオメトリ (物質) の1つが摩擦係数 5 でもう一方が摩擦係数 3 のとき、平均の摩擦係数 4 になります。
○ Minimum
2つのジオメトリ (物質) の内、低い方の摩擦係数になります。
2つの摩擦係数が 5 と 3 であれば、低い方の 3 になります。
Farseer Physics のデフォルトは Average です。
[Farseer の拡張]
Farseer Physics Engine 2.0 から共通の作業の補助を行ういくつかの先進的なメソッドが含まれています。拡張は、物理学エンジンの振る舞いを変えないように高レベルの機能のみです。
[テクスチャからの頂点作成]
Farseer Physics Engine 2.0 では、ポリゴン情報の格納されている uint[] 配列を検索し、位置をマッピングするアルゴリズムを含んでいます。これは人が自作のテクスチャからジオメトリを作成するのを簡単にします。
テクスチャから頂点を作成するアルゴリズムを使うには、以下のようにして下さい:
//Load texture that will represent the physics body
Texture2D polygonTexture = ScreenManager.ContentManager.Load<Texture2D>("Content/Texture");
//Create an array to hold the data from the texture
uint[] data = new uint[polygonTexture.Width * polygonTexture.Height];
//Transfer the texture data to the array
polygonTexture.GetData(data);
//Calculate the vertices from the array
Vertices verts = Vertices.CreatePolygon(data, polygonTexture.Width, polygonTexture.Height);
//Make sure that the origin of the texture is the centroid (real center of geometry)
Vector2 polygonOrigin = verts.GetCentroid();
//Use the body factory to create the physics body
Body polygonBody = BodyFactory.Instance.CreatePolygonBody(PhysicsSimulator, verts, 5);
polygonBody.Position = new Vector2(500, 400);
GeomFactory.Instance.CreatePolygonGeom(PhysicsSimulator, polygonBody, verts, 0);
[パスジェネレーター]
Farseer Physics Engine 2.0 では、パスジェネレーターと呼ばれるものを含んでいます。カーブ、ボディ間のジョイントまたはスプリングに沿ってボディを作成することができます。これは色々な場合で役に立ちます:
○ キャタピラを作る
○ ロープボディ
○ 鎖
少しだけ言及することがあります。
パスジェネレーターは Path クラス内にあり、ComplexFactory が鎖とロープを作成するときに使用します。パスジェネレーターを使用することで、制限を想像するだけで連結されたボディを作成します。パスジェネレーターに全てのボディのジオメトリを作成させることもできます。これは他のオブジェクトとの衝突を有効にします。
[パフォーマンス]
パフォーマンスは多くのアプリケーションタイプで本当に重要です。しかし、開発が完了するまでは決して最適化すべきではありません。例えば開発の初期段階からマルチスレッドを導入すると、同期、ロッキング、条件の競合など多くの頭痛の種を持つことになってしまいます。
綺麗なコードを書き、コンパイラを信頼して仕事をすることは最も重要な事です。したがって、綺麗な設計は 1,000 マイクロ秒の最適化よりも遥かに重要です。
以下に、アプリケーションのパフォーマンスを向上させるいくつかの
Tips とコツを示します:
1. 背景のチャンクアップ
広域な衝突判定は衝突チェックに AABB を使用しているので、一区画に大きな背景があると、常に背景全体を厳密な衝突判定の対象としてしまいます。これは大きなパフォーマンスダウンを引き起こします。
解決策として複数のチャンクに背景を作成し、現在プレーヤーが立っている領域だけを厳密な衝突のチェックをします。
2. シンプルなジオメトリ
上の背景画像で分かるように、点はカーブのトップを定義しており、アウトラインの全ての詳細を取得するのに必要です。
より多くの点、より大きな詳細。
これは、パフォーマンスの潜在的問題です。
Farseer Physics は高いパフォーマンスで計算するには点の数が少ないことが必要になるため、アウトラインの詳細を維持している間は点の総数を少なく保つことが正しい方法です。
もう一つ注意してほしいことは、プレイヤーが触れることがないのであれば、背景上には多くの点は必要ありません。そのため、底辺には少しも余分な点も置く必要はありません。
3. グリッドのセルサイズは大きく保つ
「グリッド」の章で記載しているように、グリッドはセルサイズを衝突判定の精度を決定するのに使用します。小さいグリッドのセルサイズは、より正確な衝突検出を意味しますが、計算するには長い時間がかかります。
最初にデフォルトサイズを渡すことで、正常なグリッドのセルサイズを手動で見つけることができます。(ジオメトリの AABB に必要な大きさの最小は 1/10 です。詳細情報は「既知の問題」の章の「Geometries going into each other」を見て下さい。)そして衝突に信頼性がなくなるまで大きくして下さい。
最適なグリッドのセルサイズを見つけることで、より良いパフォーマンスを与えることができますが、衝突に予測できない問題が起こるかもしれません。これはアプリケーションの開発が完了しているときのみに行うということを忘れないで下さい。
4. ボディ/ジオメトリの最小数
パフォーマンスを向上させる最も簡単で論理的な方法は、一度にアクティブになるボディとジオメトリの数を少なくすることです。もしマップが大きく、プレーヤーがもう一つの区画を開始するまでに長い間がかかるのであれば、プレーヤーが到着するまでの間、マップのその部分を非アクティブにしておくことができます。
これはいくつかのゲームで本当に簡単にできます。例えば、マップのある一定の場所をセンサーと見なすことができました。(注意:ジオメトリの IsSensor に true を設定して下さい)そしてプレーヤーがそのセンサーに達するとき、マップの次の部分をアクティブにします。
このような方法は多くあります。それは全て、あなたが開発しているゲームの種類に依存します。
5. キャッシング
もう一つの非常に簡単な実装は、オブジェクトの高速交換キャッシングです。敵または弾丸を大量に量産しているのなら、敵/弾丸を作り上げるボディとジオメトリを事前に作成できます。
Farseer Physics はアービターにプール(キャッシュ)を使用して、その作成を大幅に高速化します。
このプールが実際はパブリックなので、オブジェクトをキャッシュするのに Farseer からジェネリックの Pool クラスを使用できます。
To create a pool of 10 soldiers, you could do something like this:
Pool<Enemy> pool = new Pool<Enemy>();
for (int i = 0; i < 100; i++)
{
Enemy enemy = new Enemy(EnemyType.Soldier, Health.100);
pool.Insert(enemy);
}
そして、ゲーム中に兵士を必要とすると:
Enemy enemy = pool.Fetch();
enemy.Shoot();
このようにプールがほしい理由が2つあります。そのうちの一つがガーベージコレクター、もう一つがインスタンスの事前作成です。
ガーベージコレクターは後片付けをします。しかしこれは、敵を作成し、Dispose() を実行することを意味します。ガーベージコレクターは敵が死ぬときにシステムメモリからそれを取り除きます。
しかし、プールの中に敵があれば、Dispose() を呼ぶ必要はありません。その敵を非アクティブにするだけです。(そして、敵を描画しません)これにより少しのガーベージコレクションで済みます。
インスタンスの事前作成も Farser Physics が距離グリッドと呼ばれているものを使う場合にとても有効です。このグリッドは、新しくジオメトリが作成されたときに計算されます。そしてそれはとても時間がかかることがあります。
そのため、ゲームを開始する(または、新しいマップをロードする)ときに、敵のプールを作成することで敵の作成時間を大きく高速化できます。
6. リリースモードでコンパイルすることを忘れないで下さい。
アプリケーションを公開リリースするときは、リリースモードに設定されているかどうかを確認してコンパイルして下さい。.net プラットホームは最初に C#/Vb.net コードを IL コード(中間コード) にコンパイルし、それから JIT(Just-In-Time コンパイラ) はそのコードをネイティブのマシン語にコンパイルし、アプリケーションを実行します。
リリース構成でアプリケーションをコンパイルするとき、生成された IL コードでは、JIT が IL を実行するとき、最適化を実行しなければならないとしています。
これはアプリケーションの速度を向上させ、またアプリケーションの全体的なサイズを減少させるかもしれません。
7. ベクトルと行列を参照で渡す
XNA ではベクトル (Vector2, Vector3) と行列 (Matrix) は構造体または値型と呼ばれています.
値型がメソッドのパラメータとして渡されると、それはコピーされます。大きな行列または多くのベクトルがあると、コードを遅くすることになります。
ベクトルまたは行列を参照で渡すことによって、アプリケーションを少し高速化することができるかもしれません。
Farseer は特定の場所で値型渡しをサポートしており、例を下記に示します:
Body body = BodyFactory.Instance.CreateCircleBody(PhysicsSimulator, 64, 1);
Vector2 force = new Vector2(10,10);
for (int i = 0; i < 100; i++)
{
body.ApplyForce(ref force);
}
この例は ref キーワードを使い、100個の force ベクトルをコピーしています。これによりコードの何箇所かは、多くの恩恵を受けるかもしれません。
8. マルチスレッディング
マルチスレッディングはとても手間のかかることで、正しく実装するのが困難な場合があります。マルチスレッド化によりゲームパフォーマンスが向上し、画面上に同時発生できるエレメントの数は少し向上します。実装の詳細はこのマニュアルの範囲外ですが、Farseer Physics Engine 2.0 はマルチスレッディングの例を含んでいます。詳細はサンプル(デモ4)を見て下さい。
9. 非アクティブコントローラー
Farseer Physics Engine 2.0 は、非アクティブコントローラーと呼ばれている新しい機能を含んでいます。このコントローラーは、「休止ボディ」と呼ばれているものを有効にします。ゲームにほとんど動かないエレメントが多く含んでいる場合、それらをしばらくの間、非アクティブにすることによって、いくらかパフォーマンスを向上させることができます。
非アクティブコントローラーはこのためにあります。物理シミュレーターでそれを有効にし、いくつかの基本設定を行うだけです。詳細情報は「非アクティブコントローラー」の章を見て下さい。
10. スケーリング
スケーリングは、Farseer Physics Engine 2.0 のもう一つの新機能です。ゲームが処理が集中しているところがあり、またフレームレートの低下(通常60fps)を許容できるのであれば、エンジンを少し減速し、後で再び速めさせることができます。
しなければならないことは、下記のようにスケーリングコントローラーをアクティブにするだけです:
PhysicsSimulator.Scaling.Enabled = true;
そして、MaximumUpdateInterval を設定して下さい。要求された更新レート のデフォルトは 0.001f、最大更新レートのデフォルトは 0.01fです。
コードを最適化する方法が他にも多くあります。
Farseer Physics 限定でないものについては詳細を述べませんが、ショートリストをここに記載します;
1. Inline performance critical methods
Before:
if (IsColorBlack(new Color(10, 4, 1)))
{
}
private bool IsColorBlack(Color color)
{
return color == Color.Black;
}
After:
if (new Color(10, 4, 1) == Color.Black)
{
2. Inline vectors instead of referencing
Before:
Vector2 distance = Vector2.Zero;
Vector2.Subtract(ref GeometryB.Body.Position, ref GeometryA.Body.position, out distance);
After:
Vector2 distance = Vector2.Zero;
distance.X = GeometryB.Body.Position.X - GeometryA.Body.position.X;
distance.Y = GeometryB.Body.Position.Y - GeometryA.Body.position.Y;
3. Inline constructors
Before:
Vector2 distance = new Vector2(10,10);
After:
Vector2 distance = new Vector2();
distance.X = 10;
distance.Y = 10;
[既知の問題]
Farseer Physics に関する既知の問題がいくつかあります。これらの問題のいくつかは Farseer Physics だけでなく、他の多くの物理エンジンでも見られます。この問題はパフォーマンスやユーザビリティを犠牲せずに修正するのは簡単ではありません。
1. トンネリング
オブジェクトが高速で移動するとき、壁にぶつかり内部で動けなくなるか、または衝突することなく突き抜ける現象が発生します。
Farseer Physics 2.0 は、現在、これの良い解決策を何も持っていません。しかしこれが起こるのを防ぐ CCD(Continuous Collision Detection) を実装する計画があります。
以下はその時までの選択肢になります:
1) オブジェクトを遅く動くようにする。
2) オブジェクトを大きくする。
3) タイムステップを小さくする。
4) Ray-casting を使う。
5) スイープ衝突検出(情報は
ここにあります)
6) マルチサンプリング
2. ジオメトリがお互いに入り込む
これは原因が2つあります:ジオメトリは真っ直ぐなエッジでさえ、鋭い点または少なすぎる点を持っています。
鋭い点を持っているならば、デフォルトより小さいグリッドのサイズ値を使う必要があるかもしれません。デフォルトでは、CreatePolygonBody メソッドは collisionGridCellSize に 0 を渡すと、 AABB のサイズを元に collisionGridCellSize を計算します。
もし鋭い点を持ったジオメトリだとしたら、恐らくデフォルトより小さい値になります。0 でないの値を渡し、GeomFactory.GridCellSizeAABBFactor のプロパティを設定することによって、デフォルトの計算を調節することができます。
このプロパティはデフォルトの collisionGridCellSize を計算するのに使用されます。現在、.1 を設定すると、collisionGridCellSize にジオメトリの AABB の最小に必要な値の 1/10 を意味します。
他にできることとして、ジオメトリにより多くの点を挿入してみて下さい。Farseer Physics には、このためのヘルパーメソッドがあります。SubDivideEdges() と呼ばれており、Vertices クラスの中にあります。
下記に例を示します:
Body rectBody = BodyFactory.Instance.CreateRectangleBody(128, 128, 1);
Vertices vertices = new Vertices();
vertices.Add(new Vector2(-64, -64));
vertices.Add(new Vector2(64, -64));
vertices.Add(new Vector2(64, 64));
vertices.Add(new Vector2(-64, 64));
vertices.SubDivideEdges(10);
Geom rectGeom = new Geom(rectBody, vertices, 11);
注意:ボディとジオメトリを物理シミュレーターに追加するのを忘れないで下さい。
Before SubDivideEdges() |
After SubDivideEdges() |
|
|
3. センターへの描画
Farseer Physics Engine 1.0.0.4 からジオメトリファクトリーの CreatePolygonGeom メソッドが頂点の重心をセンターに取ることをサポートしています。
このスレッドで説明されている方法に従うか、または CreatePolygonGeom メソッドで Vector2 のオフセットパラメータを使用して、動作させることができます。
最終更新:2009年08月02日 20:24