サイトのトップへ戻る

libGDX ドキュメント 日本語訳

サイト内検索

Bullet ラッパーを使用する



Bulletを初期化する

Bulletを使用する前に、ライブラリを読み込む必要があります。 create メソッド内に以下のコードを追加することでこれを行えます:

Bullet.init();

初期化する前にBullet を使用しないよう気をつけてください。 例えば、以下のコードではライブラリを読み込む前にbtGhostPairCallbackを作成しているのでエラーが発生します。

public class InvokeRuntimeExceptionTest  {
  final static btGhostPairCallback ghostPairCallback = new btGhostPairCallback();
}


Bullet ラッパーでの作業

このラッパーでは、オリジナルのbullet のクラス名を遵守するようにしています。 つまり、ほとんどのクラス名の頭にbt”が付いています。 これにはいくつかの例外があります。そのほとんどは入れ子状態の構造体の場合です。 これらは、 com.badlogic.gdx.physics.bulletパッケージ内に直接カスタム実装されています。 Unfortunately some nested structs and some base classes are not suitable for a one on one translation. これらの詳細についてはカスタムクラスの項目を参照してください。 足りないクラスを見つけた場合はフォーラムや問題トラッカー(https://github.com/libgdx/libgdx/issues)に投稿できるので、 そうすればラッパーに追加されるでしょう。



Callbacks

コールバックを使う際にはいくつか特別な注意が必要です。 既定では、このラッパーでは一方方向の相互作用(Java からへ C++)のみをサポートしています。 コールバックインタフェース内のカスタム実装では、C++がJavaコードを呼び出しています。 まだ実装されていないコールバックインタフェースを見つけた場合は、フォーラムに投稿できるので そうすればラッパーに追加されるでしょう。

コールバックインタフェースの一覧 (完全ではない可能性があります):

  • LocalShapeInfo
  • LocalRayResult
  • RayResultCallback
  • ClosestRayResultCallback
  • AllHitsRayResultCallback
  • LocalConvexResult
  • ConvexResultCallback
  • ClosestConvexResultCallback
  • ContactResultCallback
  • btMotionState
  • btIDebugDraw
  • InternalTickCallback
  • ContactListener
  • ContactCache


プロパティ

各プロパティはgetter メソッドと setter メソッドによってカプセル化されています。 getter メソッドと setter メソッドの命名では、接頭語の m_ は省略されています。 例えば、ネイティブクラスbtCollisionObjectWrapperのメンバーm_collisionObjectは、 getCollisionObject() およびsetCollisionObject(...)として実装されています。



オブジェクトの作成と破棄

Javaでbulletクラスを作成する度に、 C++側でも対応するクラスが作成されます。 Java オブジェクトはガベージコレクターによって管理されますが、 C++ オブジェクトはそうではありません。 誰も管理してくれない C++オブジェクトがメモリリークを起こさないようにするために、既定ではガベージコレクターによってJavaオブジェクトが破棄された時にC++オブジェクトも自動的に破棄されます。

これはいくつかの画面では役立ちますが、単なるフェイルセーフ機能なので全面的に依存するべきではありません。 ガベージコレクターは制御することはできないので、オブジェクトが実際に破棄されるかどうかいつどのような順序で破棄されるかを制御できません。 したがってガベージコレクターによってオブジェクトが自動的に破棄された時にラッパーはエラーを記録します。 Bullet.init() メソッドの第二引数を使ってこのエラーのログ出力を無効にできますが、以下の段落に記載している方法を使った方が良いでしょう。

正確なガベージコレクションを行うために、作成した全てのオブジェクトへの参照はそれが不要になるまで保持しておき、自分自身で破棄するようにしましょう。 Javaオブジェクトの.dispose()メソッドを実行することで、C++側のオブジェクトを破棄することができます。 その後は使用できなくなるので、Javaオブジェクトへの全ての参照を削除する必要があります。

上記の内容は、あなたが責任を持つべきオブジェクトに対してのみ当てはまります。 責任を持つべきオブジェクトとは、new キーワードを使って作成した全てのBulletクラスと、ヘルパーメソッドを使って作成したクラスのことです。 通常のメソッドの戻り値として取得したオブジェクトや、コールバックメソッド内で引数として渡されるオブジェクトは破棄する必要はありません。



オブジェクトへの参照

前述のように、全てのBullet クラスへの参照を保持してそれが必要なくなったらdispose メソッドを呼び出す必要があります。 あなたのアプリケーションがより複雑になってオブジェクトのが他の複数のオブジェクト間で共有される場合、参照の追跡が難しくなる可能性があります。 そのため、 bullet ラッパーでは参照の集計をサポートしています。

既定では、参照の集計は無効になっています。有効にするには、第一引数にtrueを設定してBullet.init(); を実行します:

Bullet.init(true);

参照集計を使用する場合は、参照が必要な各オブジェクトで obtain() メソッドを呼び出さなければなりません。 オブジェクトを参照する必要がなくなったら、release()メソッドを呼び出さなければなりません。 そのオブジェクトに対してそれ以上参照がない場合、release メソッドはそのオブジェクトを破棄します。

いくつかのラッパークラスは参照の管理に役立ちます。 例えばbtCompoundShapeクラスは、その子Shapeへの全ての参照を取得して自身が破棄される場合はその子Shapeでもreleaseを実行します。



クラスを拡張する

bullet クラスを拡張することはできますが、Callback クラス以外は拡張しないことを推奨します(Callback クラスの拡張も、目的のメソッドを上書きのみにすべきです)。 クラスに追加した情報はC++側では使用できません。 Furthermore the result of any method of the bullet wrapper that returns a class you’ve overridden will not implement that class. 例えば:

btCollisionShape shape = collisionObjectA.getCollisionShape();

上記では、拡張クラスを一切実装していない Java のbtCollisionShape クラスが新規に作成されます。

これについて、btCollisionObjectの場合は例外が1つあります。ラッパーが同じJavaクラスを再利用しようとするのです。 さらにbtCollisionObject クラスのJava側の実装では、オブジェクトに追加のデータを付与するのに使える userDataメンバが追加されています。 これを実現するために、ラッパーは全てのbtCollisionObject インスタンスへの参照を持つ配列を管理しています。 静的フィールドbtCollisionObject.instancesを使ってその配列にアクセスできます。 これの詳細については、btCollisionObjectの項目を参照してください。

この問題が原因で、アップキャストする方法は存在しません。 java側で作成されたクラスの場合はアップキャストは必要ありません。そうしたクラスは直接キャストできます。



クラスを比較する

equals()メソッドを使って、ラッパークラスを比較することができます。このメソッドではクラスが両方とも同一ネイティブクラスを内包しているかどうかを比較します。 基底 C++クラスへのポインタを取得するには、そのオブジェクトのgetCPointerメソッドを使用します。 これらのポインタを比較して、そのJavaクラスが同じC++クラスを内包しているかどうかを比較することもできます。



共通クラス

Bulletは、libgdx coreでも使用可能ないくつかのクラスを使っています。 あなたはこれらのbullet クラスを使用することができますが、ラッパーでは可能な限りlibgdx のクラスを使用しようとします。 現時点では、これらのクラスの実装は以下のようになっています:

Bullet Libgdx
btVector3 Vector3
btQuaternion Quaternion
btMatrix3x3 Matrix3
btTransform Matrix4
btScalar float

btTransform は原点と回転の情報のみを保持しているため、Matrix4 からbtTransform へ変換した場合はいくつかの情報が失われる可能性があるので注意してください。 さらに、btScalar はプリミティブ型floatと同義語なので注意してください。

これら共通クラスのオブジェクトの作成を避けるため、ラッパーでは同じインスタンスを再利用しています。 したがって、以下二つの場合に注意してください:

  1. そのようなクラスを戻り値として返すラッパーメソッドの結果は、同じ型を戻り値として返す次のメソッドによって上書きされます:
  2. // 間違ったやり方:
    Matrix4 transformA = collisionObjectA.getWorldTransform();
    // この時点でtransformA は collisionObjectAのworldTransform を保持しています
    Matrix4 transformB = collisionObjectB.getWorldTransform();
    // transformA とtransformB は同じオブジェクトで、この時点では両方ともcollsionObjectBのworldTransform を保持しています。
    
    // 正しいやり方:
    transformA.set(collisionObjectA.getWorldTransform());
    transformB.set(collisionObjectB.getWorldTransform());
    
  1. そのようなクラスを引数に持つコールバックインタフェースの引数は、呼出し後は使用できません:
  2. // 間違ったやり方:
    @Override
    public void setWorldTransform (final Matrix4 worldTrans) {
    	transform = worldTrans;
    }
    // 正しいやり方:
    @Override
    public void setWorldTransform (final Matrix4 worldTrans) {
    	transform.set(worldTrans);
    }


配列を使用する

可能であれば、ラッパーはByteBuffer オブジェクトを直接使ってJava からC++へ配列を渡します。 これにより呼び出し時に配列をコピーすることがなくなり、 OpenGL ES とBulletの両方で同じバイトバッファを共有できます。 必要であればBufferUtils.newUnsafeByteBufferを使ってByteByffer を新規に作成することができます。 その場合はBufferUtils.disposeUnsafeByteBufferを使って手動で削除をする必要があります。

ByteBuffer が使用できない場合、もしくはByteBuffer の使用が望ましくない場合は、通常の配列が使用されます。 つまり既定では、配列はメソッドの開始時に反復処理を使ってJava からC++へコピーされ、methodの終了時にはC++からJavaへコピーが戻ってきます。 このオーバヘッドを避けるため、ラッパーは可能な場合はクリティカルな配列を使って、C++内からJava配列を直接使うよう試みます。 そうしたメソッドの処理中はJavaのガベージコレクションがブロックされます。 そのようなメソッドの一例としては、 btBroadphasePairArray.getCollisionObjectsがあります。