サイトのトップへ戻る

libGDX ドキュメント 日本語訳

サイト内検索

Bullet ラッパーの Contact コールバック

Contact コールバックを使用することで、二つのオブジェクト上で接触/衝突が発生した時に通知を受け取ることができます(詳細情報とパフォーマンス関連の警告)。

既定では、 onContactAddedonContactProcessedonContactDestroyed の三つのコールバックがあります。 このラッパーではさらに onContactStarted and onContactEndedの二つのコールバックが追加されています(詳細情報)。 このコールバックはグローバルに動作し(例えば collision world内に制限されません)、指定した時点のアクティブなコールバックごとに一つの実装しかできません。



Contact のリスナー

ContactListener クラスを拡張して複数のコールバックを実装することができます。:

public class MyContactListener extends ContactListener {
	@Override
	public void onContactStarted (btCollisionObject colObj0, btCollisionObject colObj1) {
		// implementation
	}
	@Override
	public void onContactProcessed (int userValue0, int userValue1) {
		// implementation
	}
}

各コールバックにつき、一度に一つのリスナーしか有効にできないので注意してください。 enable(); メソッドを使うとリスナーを有効にでき、それによりそのコールバック上の他のリスナーは全て無効になります。 特定のコールバックに対する通知を停止するにはdisable();メソッドを使用してください。 リスナーのインスタンスを作成すると自動的にそのコールバックは有効になり、破棄(dispose(); メソッド)をすると自動的に無効になります。

ContactListener クラスには、コールバックごとに、上書き可能な複数のメソッドシグネチャがあります。 例えばonContactAddedコールバックでは、以下のシグネチャを使って上書きを行うことができます。:

boolean onContactAdded(btManifoldPoint cp, btCollisionObjectWrapper colObj0Wrap, int partId0, int index0, btCollisionObjectWrapper colObj1Wrap, int partId1, int index1);

boolean onContactAdded(btManifoldPoint cp, btCollisionObject colObj0, int partId0, int index0, btCollisionObject colObj1, int partId1, int index1);

boolean onContactAdded(btManifoldPoint cp, int userValue0, int partId0, int index0, int userValue1, int partId1, int index1);

boolean onContactAdded(btCollisionObjectWrapper colObj0Wrap, int partId0, int index0, btCollisionObjectWrapper colObj1Wrap, int partId1, int index1);

boolean onContactAdded(btCollisionObject colObj0, int partId0, int index0, btCollisionObject colObj1, int partId1, int index1);

boolean onContactAdded(int userValue0, int partId0, int index0, int userValue1, int partId1, int index1);

ご覧の通り、上の三つのメソッドでは btManifoldPointが引数として渡されており、下の三つでは渡されていません。 実際に衝突したオブジェクトを引数として渡すには、 btCollisionObjectWrapperbtCollisionObjectuserValueのうちいずれかを使います。

実際に使用する引数のみを渡すメソッドを上書きするようにしてください。 例えばbtManifoldPointを使用しない場合、コールバックが呼び出される度にその引数のオブジェクトを作成するのは無意味です。 同様に、 btCollisionObjectを使うと btCollisionObjectWrapperを使うよりもパフォーマンスが向上します。 btCollisionObjectは再利用されているからです。 userValueは内部で紐付け処理が一切行われないため、使用するとさらにパフォーマンスが向上します (useValueの使用方法については btCollisionObjectを参照してください)。

衝突したボディのうち少なくとも一つで CF_CUSTOM_MATERIAL_CALLBACKが設定されていた場合のみ、 onContactAddedコールバックが引き起こされます。:

body.setCollisionFlags(e.body.getCollisionFlags() | btCollisionObject.CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK);

追加や処理や破棄がされたコールバックからcontact を特定するには、 コールバックで引数として渡されるbtManifoldPointインスタンスのsetUserValue(int);getUserValue(); を使用します。 この値は、 ContactListenerクラスのonContactDestroyed(int)メソッドにも引数として渡されます。 onContactDestroyedコールバックはユーザー値が0以外の場合にのみ引き起こされてるので注意してください。



Contact フィルタリング

Contact コールバックは大量に呼び出されます。コールバック毎に C++ とJavaの橋渡しをしているJNI では大量のオーバーヘッドが発生し、パフォーマンスが低下します。 そのため、bullet ラッパーを使って受け取りたいContact オブジェクトを指定できます。これはcontact フィルタリングによって行います。

衝突フィルタリングと同様に、各 btCollisionObjectごとに、setContactCallbackFlag(int);メソッドを使ってフラグを設定してsetContactCallbackFilter(int); メソッドを使ってフィルタをすることができます。 A.filter & B.flag == B.flagの場合、オブジェクトAのフィルターはオブジェクトBと一致します。 フィルターの一つもしくは両方が一致した場合のみ、contact はリスナーへ引数として渡されます。

static int PLAYER_FLAG = 2; // second bit
static int COIN_FLAG = 4; // third bit
btCollisionObject player;
btCollisionObject coin;
...
player.setContactCallbackFlag(PLAYER_FLAG);
coin.setContactCallbackFilter(PLAYER_FLAG);
// The listener will only be called if a coin collides with player

既定ではbtCollisionObjectcontactCallbackFlagには1が設定されており、contactCallbackFilterには0が設定されています。 フラグを0に設定すると、そのオブジェクトのコールバックは常に呼び出されるようになるので注意してください(x & 0 == 0のため)。

contact フィルタリングが使用されるかどうかは、あなたが上書きしたメソッドシグネチャによって決まります。 contact フィルタリングをサポートしている全てのコールバックにおいて、ContactListener クラスではboolean match0引数とboolean match1引数の付いたメソッドシグネチャが用意されています。 そうしたメソッドを上書きした場合、そのメソッド上ではcontact フィルタリングが使用されます。 boolean match引数を持たないメソッドを上書きした場合、そのメソッドでcontact フィルタリングは使用されません。

boolean match0 の値とboolean match1の値を使って、どちらのフィルターと一致するかを確認できます。

public class MyContactListener extends ContactListener {
	@Override
	public void onContactEnded (int userValue0, boolean match0, int userValue1, boolean match1) {
		if (match0) {
			// collision object 0 (userValue0) matches
		}
		if (match1) {
			// collision object 1 (userValue1) matches
		}
	}
}

contact フィルタを使用した場合でさえも、衝突時には非常に多くのコールバックが呼び出される可能性があります。 これを避けるためには、処理を行った後そのフィルタに0を設定します。 例えば、プレイヤーとコインで衝突処理を行う場合、プレイヤーをコインに衝突させるのではなく、コインをプレイヤーに衝突させてその最初の接触時にフィルターを0にするのがベストです。




エンジェル戦記