【LEAP motion・Unity】どの指で接触したかを判別する

f:id:kamata0satisfy:20141013001334j:plain
UnityでLEAP motionを使う際、あらかじめ用意されたHandControllerというオブジェクトを設置するだけで手を画面内に出現させることができます。

今回はLEAP motionによって生まれた手でUnity空間内のものに触れた際に、
「どの指でさわっているのか?」を知る方法について書き残しておきます。

LEAP motionをUnityに導入し、HandControllerオブジェクトをシーンの中に設置したところから解説を始めます。

まず、LEAP motionで動かす手の物理モデルは、以下に格納されています。

Assets>LeapMotion>Prefabs>HandPhysics

f:id:kamata0satisfy:20141013002245j:plain

最初はRigidHand とThickRigidHand の2つがあるはずです。この2つはパーツの大きさが違うだけで、大きな違いはありません(多分)。

では、RigidHand の構成を見てみましょう。

デフォルトのProjectウィンドウではディレクトリを展開しきれないので、いったんHierarchyにドラッグ&ドロップし、ゲーム内に入れて見てみましょう。

f:id:kamata0satisfy:20141013003227j:plain

RigidHandの親子関係は、『手 - 指 - 指の節』という構成になっています。

手:RigidHand

指:index,middle,....(人さし指、中指)...,palm(手の平)

指の節:bone1,bone2,bone3

f:id:kamata0satisfy:20141013003850j:plain

手の平であるpalm以外、5本の指には3つの子オブジェクト(bone)があります。

物理モデルの末端であるboneとpalmに接触判定スクリプトをアタッチして、接触している指の情報を得ます。



衝突検出には、以下のようなスクリプトを使います。使用しているのは、
なにかと物体が衝突(Collision)したときに呼び出される関数
”OnCollisionEnter”
と、
衝突から離れたときの関数
”OnCollisionExit”
の2つです。

public class ChildColliderEnter: MonoBehaviour {

	void OnCollisionEnter(Collision collision)
	{
		//(処理を記述)
	}
		
	void OnCollisionExit(Collision collision)
	{
		//(処理を記述)
	}
}

これを使って、例えばbool型の変数を宣言し、ぶつかったらtrueに、離れたらfalseに・・・という処理を組めば、物体と衝突しているかどうかを検出できます。

さらにこのようなスクリプトを、親指、人さし指、中指、薬指、小指、手の平の6つぶん用意してやり、それぞれのboneとpalmにアタッチすればどの指で検出しているか?がわかるようになります(少々強引ですが・・・)。

以下のコードは少し応用的に、「当たった物体の情報を親である指(INDEXなど)に送信する子スクリプトと、「子から送られてきた情報に応じて接触状態を表す変数を変える親スクリプトになります。
それぞれ、子であるbone、親であるINDEXなどにアタッチします。
まずは子のスクリプト

public class ChildColliderEnter: MonoBehaviour {

	GameObject parent;
		
	void Start () {
		parent = gameObject.transform.parent.gameObject;
	}
		
	void OnCollisionEnter(Collision collision)
	{
		parent.SendMessage("RedirectedOnCollisionEnter", collision);
	}
		
	void OnCollisionExit(Collision collision)
	{
		parent.SendMessage("RedirectedOnCollisionExit", collision);
	}
}

次に親スクリプト。以下のようなコードを指の本数ぶん用意します。(以下はINDEXのものです)

public class IndexCollision : MonoBehaviour {

	public static bool isIndexTouch;

	void RedirectedOnCollisionEnter (Collision collision)
	{
		isIndexTouch = true;
	}
		
	void RedirectedOnCollisionExit (Collision collision)
	{
		isIndexTouch = false;
	}
}

子のスクリプトではSendMessage関数によって、親のRedirectedOnCollisionEnter/Exit を操作しています。
どの指で接触しているかを親に任せることにより、子スクリプトは単純な接触判定だけですみ、どのboneでも同じスクリプトを使うことができるようになります。

スクリプトの接触変数はstaticにすることで、他のスクリプトでも使用が可能になります。
ですがこのままだと煩雑になるので、できれば他に各指の接触状態変数をまとめて宣言するスクリプトをつくって、これらのスクリプトからその変数にアクセスして変更するという形がよいでしょう。