読者です 読者をやめる 読者になる 読者になる

shimada-kの日記

ソフトウェア・エンジニアのブログです

UnityでLeap Motionが認識した手を動かす

UnityでLeap Motionのビジュアライザーのように認識した手を動かしてみました。手は以下のオブジェクトで表現しました。

  • 手首:Cube
  • 手のひら:Sphere
  • 間接:Sphere
  • 骨:Line Renderer

Leap MotionSDKのバージョンはもちろんv2 Betaです。Unity側スクリプト言語C#を使いました。ソース一式はgithubにあげてあります。

f:id:shimada-k:20140726225215p:plain

以下ハマった点

1つめ

正規化した座標をUnityのオブジェクトに適応させるときにlocalPositionに代入していましたが、それだと間接の座標だけ他と位置が離れてしまって、パーツの配置に違和感が生じていました。positionに代入しないといけませんでした。

unityWrist.transform.position = utils.getScaledUnityPosition(hand.WristPosition,
                                                                interactionBox);

utils.getScaledUnityPositionは自作dllの関数です。

2つめ

手首のCubuオブジェクトの回転は直近フレームを保存して、RotationAngleでxyzの軸別に差分角度を取得してtransform.Rotateで動かす方法をとっています。

Unityでオブジェクトを回転させる時はtransform.rotationで絶対値を代入するとか、Quaternion.Slerpを使うとかいくつか方法があるようですが、Leap MotionSDKのAPIと相性がいい方法をチョイスしました。

Hand.RotationAngleで直近フレームとの差分の角度を取得するんですが、こいつがラジアン角で、Unityのtransform.Rotateで回転させるためにはオイラー角である必要があります。

なのでラジアンオイラーに変換する必要があったんですが、そこに気づくのに半日かかりました。

float angleX = hand.RotationAngle(basisFrame, basis.xBasis);
float angleY = hand.RotationAngle(basisFrame, basis.yBasis);
float angleZ = hand.RotationAngle(basisFrame, basis.zBasis);

// 回転する
unityWrist.transform.Rotate(new Vector3(
                                LMUtils.rot2Dir(angleX),
                                LMUtils.rot2Dir(angleY),
                                -1 * (LMUtils.rot2Dir(angleZ))));

LMUtils.rot2Dirは自作dllの関数です。z方向に-1をかけているのは認識した手の向きを変えるためです(positionも反転させています)。

動作中のスクリーンをキャプチャしました。Line RendererがPalmSphereを貫通してますがそこは気にせず。(Leap Motionのビジュアライザーだと第3間接のSphereを横につなげて見た目を整えてますが、、)。

両手対応してます。ソースは1つにしたかったので、Unityのオブジェクトにタグをふっておいて、スクリプト側ではHandListでforeachしてタグとisLeftやisRightの結果を比較して描画し分けています。

Line Rendererで骨を表現するとlengthを気にしなくていいので楽です。あと親指は他の指より間接が一つ少ない(というかlengthが0の骨がある)ことを考慮する必要あります。

ベースはできたのでこれからUnityとLeap Motionを遊び尽くす。