書籍転載:KINECT for Windows SDKプログラミング Kinect for Windows v2センサー対応版(4)
Kinectでユニティちゃんを動かす
― Chapter 7 SDK v2を使ったアプリケーション例 7.3 ―
Kinect+Unityの面白さを体感! Kinectで得られた全身骨格の動きを、コードを書かずに、3Dモデルのユニティちゃんに反映させてみよう。
UnityでKinectを活用する方法を紹介します。前回はKinectの座標系とUnityの座標系を合わせる方法を解説しました。今回は次のステップとして、より実践的なサンプルを作成します。
書籍転載について
本コーナーは、秀和システム発行の書籍『KINECT for Windows SDKプログラミング Kinect for Windows v2センサー対応版』の中から、特にBuild Insiderの読者に有用だと考えられる項目を編集部が選び、同社の許可を得て転載したものです。
『KINECT for Windows SDKプログラミング Kinect for Windows v2センサー対応版』の詳細や購入は秀和システムのサイトや目次ページをご覧ください。プログラムのダウンロードも、秀和システムのサイトから行えます。
ご注意
本記事は、書籍の内容を改変することなく、そのまま転載したものです。このため用字用語の統一ルールなどはBuild Insiderのそれとは一致しません。あらかじめご了承ください。
■
7.3 ユニティちゃんを動かす
Kinectで全身の骨格を追跡できるようになったら、まずやってみたいのは3Dモデルへの動作の反映でしょう。Kinect v1当時はXNAのようなゲーム開発用のライブラリでゴリゴリコードを書かなければ難しかったことも、今ではUnityのようなゲームエンジンで、コードを書かずに3Dモデルを動かすアプリが作れるようになりました。
7.3.1 ユニティちゃんアセットについて
このアセットは「ユニティちゃんライセンス」で提供されています。
このアセットをご利用される場合は「キャラクター利用のガイドライン」も併せてご確認ください。
7.3.2 アセットをインポートする
6章「Kinect for Windows SDK v2をUnityで使う」の手順にしたがって、Kinectv2の環境を設定します。続いて、Unityのアセットストアから「unity chan」でユニティちゃんのアセットを検索し、インポートします。
7.3.3 プロジェクトを設定する
Projectビューにユニティちゃんのアセット一式が追加されます。「UnityChan|Prefabs」にある「unitychan」がモデルデータなので、これを「Hierarchy」ビューに追加します。PrefabsにはAnimator、Idle Changer、Face Updateが設定されていますが、すべてチェックを外して無効にします。
続いて空のGameObjectを追加し(ここではKinectAvaterと名前を変えています)、KinectAvatarスクリプトおよびBodySourceManagerスクリプトを設定します。KinectAvatarスクリプトにはユニティちゃんのプレファブとBodySourceManagerを設定する場所がありますので、それぞれ設定します。
Is Mirrorにチェックを入れる(鏡状態にする)と、プレイヤーが右手を上げることで、ユニティちゃんの左手が上がります。チェックを外すと、プレイヤーが右手を上げることで、ユニティちゃんの右手が上がります。
ここまでの手順でユニティちゃんが動くようになります。
7.3.4 ユニティちゃんを動かす
ユニティちゃんを動かすためのコードはKinectAvatarスクリプトにあります。
まずフィールドとしてユニティちゃんの関節オブジェクトを定義しています。今回は便宜上ユニティちゃんのモデルをベースにコード内で関節オブジェクトを取得しています。Unityエディター上で設定すれば、今回とは異なった構造の3Dモデルにも対応できるでしょう。
public GameObject Ref;
public GameObject Hips;
public GameObject LeftUpLeg;
public GameObject LeftLeg;
public GameObject RightUpLeg;
public GameObject RightLeg;
public GameObject Spine1;
public GameObject Spine2;
public GameObject LeftShoulder;
public GameObject LeftArm;
public GameObject LeftForeArm;
public GameObject LeftHand;
public GameObject RightShoulder;
public GameObject RightArm;
public GameObject RightForeArm;
public GameObject RightHand;
public GameObject Neck;
public GameObject Head;
|
Start()ではユニティちゃんのモデルをベースに各関節のGameObjectを取得しています。それぞれの関連についてはモデルの関連(親子関係)をそのまま使っています。
void Start () {
Ref =
_UnityChan.transform.FindChild( "Character1_Reference" ).gameObject;
Hips =
Ref.gameObject.transform.FindChild( "Character1_Hips" ).gameObject;
LeftUpLeg =
Hips.transform.FindChild( "Character1_LeftUpLeg" ).gameObject;
LeftLeg =
LeftUpLeg.transform.FindChild( "Character1_LeftLeg" ).gameObject;
RightUpLeg =
Hips.transform.FindChild( "Character1_RightUpLeg" ).gameObject;
RightLeg =
RightUpLeg.transform.FindChild( "Character1_RightLeg" ).gameObject;
Spine1 =
Hips.transform.FindChild( "Character1_Spine" ).gameObject.
transform.FindChild( "Character1_Spine1" ).gameObject;
Spine2 =
Spine1.transform.FindChild( "Character1_Spine2" ).gameObject;
LeftShoulder =
Spine2.transform.FindChild( "Character1_LeftShoulder" ).gameObject;
LeftArm =
LeftShoulder.transform.FindChild( "Character1_LeftArm" ).gameObject;
LeftForeArm =
LeftArm.transform.FindChild( "Character1_LeftForeArm" ).gameObject;
LeftHand =
LeftForeArm.transform.FindChild( "Character1_LeftHand" ).gameObject;
RightShoulder =
Spine2.transform.FindChild( "Character1_RightShoulder" ).gameObject;
RightArm =
RightShoulder.transform.FindChild( "Character1_RightArm" ).gameObject;
RightForeArm =
RightArm.transform.FindChild( "Character1_RightForeArm" ).gameObject;
RightHand =
RightForeArm.transform.FindChild( "Character1_RightHand" ).gameObject;
Neck =
Spine2.transform.FindChild( "Character1_Neck" ).gameObject;
Head =
Neck.transform.FindChild( "Character1_Head" ).gameObject;
}
|
続いてUpdate()です。BodyManagerからBodyデータを取得し、最初に追跡している人を、モデルを動かす人とします。モデルを動かすためには関節の角度を合わせていきます。関節の角度はBody.JointOrientationsに格納されています。それぞれ対応する関節の角度を取得します。
Kinect SDKの回転角はVector4型で定義されています。これをUnityの回転であるQuaternion型に変換しています。また、Mirrorモードにするために、VectorのY,Zの符号を反転させています。この変換についてはVectorExtensionsスクリプトを作成し、Vector4の拡張クラスとして定義しています。
using UnityEngine;
public static class VectorExtensions
{
public static Quaternion ToQuaternion( this Windows.Kinect.Vector4 vactor, Quaternion comp )
{
return Quaternion.Inverse( comp ) *
new Quaternion( -vactor.X, -vactor.Y, vactor.Z, vactor.W );
}
public static Windows.Kinect.Vector4 ToMirror( this Windows.Kinect.Vector4 vector )
{
return new Windows.Kinect.Vector4()
{
X = vector.X,
Y = -vector.Y,
Z = -vector.Z,
W = vector.W
};
}
}
|
UnityのQuaternionに変換する際に、地面の傾きを考慮します。地面の傾きはKinect SDKのBodyFrameReader.FloorClipPlaneで取得できますが、BodySourceManagerスクリプトでこの値を取得していないので、BodySourceManagerスクリプトに次のコードを追加しています。
public class BodySourceManager : MonoBehaviour
{
…
public Windows.Kinect.Vector4 FloorClipPlane
{
get;
private set;
}
void Update ()
{
if (_Reader != null)
{
var frame = _Reader.AcquireLatestFrame();
if (frame != null)
{
if (_Data == null)
{
_Data = new Body[_Sensor.BodyFrameSource.BodyCount];
}
frame.GetAndRefreshBodyData(_Data);
// FloorClipPlaneを取得する
FloorClipPlane = frame.FloorClipPlane;
frame.Dispose();
frame = null;
}
}
}
}
|
さて、KinectAvatarスクリプトに戻って、関節を回転させていきます。まずは地面の傾き、関節の回転角度を取得します。
// 床の傾きを取得する
var floorPlane = _BodyManager.FloorClipPlane;
var comp = Quaternion.FromToRotation(
new Vector3( floorPlane.X, floorPlane.Y, floorPlane.Z ), Vector3.up );
// 関節の回転を取得する
var joints = body.JointOrientations;
|
これらを使って関節の回転角度を取得し、ToQuaternion()でUnityのQuaternionに変換します。このときに先に取得した地面の傾きを設定します。Mirrorモードの場合は、Kinectからの回転角度を調整する必要があるので、ToMirror()を挟みます。また左右を逆にするため、Kinectの左(~Left)をモデルの右に、Kinectの右(~Right)をモデルの左に設定します。
Quaternion SpineBase;
Quaternion SpineMid;
Quaternion SpineShoulder;
Quaternion ShoulderLeft;
Quaternion ShoulderRight;
Quaternion ElbowLeft;
Quaternion WristLeft;
Quaternion HandLeft;
Quaternion ElbowRight;
Quaternion WristRight;
Quaternion HandRight;
Quaternion KneeLeft;
Quaternion AnkleLeft;
Quaternion KneeRight;
Quaternion AnkleRight;
// 鏡
if ( IsMirror ) {
SpineBase =
joints[JointType.SpineBase].Orientation.ToMirror().ToQuaternion( comp );
SpineMid =
joints[JointType.SpineMid].Orientation.ToMirror().ToQuaternion( comp );
SpineShoulder =
joints[JointType.SpineShoulder].Orientation.ToMirror().ToQuaternion( comp );
ShoulderLeft =
joints[JointType.ShoulderRight].Orientation.ToMirror().ToQuaternion( comp );
ShoulderRight =
joints[JointType.ShoulderLeft].Orientation.ToMirror().ToQuaternion( comp );
ElbowLeft =
joints[JointType.ElbowRight].Orientation.ToMirror().ToQuaternion( comp );
WristLeft =
joints[JointType.WristRight].Orientation.ToMirror().ToQuaternion( comp );
HandLeft =
joints[JointType.HandRight].Orientation.ToMirror().ToQuaternion( comp );
ElbowRight =
joints[JointType.ElbowLeft].Orientation.ToMirror().ToQuaternion( comp );
WristRight =
joints[JointType.WristLeft].Orientation.ToMirror().ToQuaternion( comp );
HandRight =
joints[JointType.HandLeft].Orientation.ToMirror().ToQuaternion( comp );
KneeLeft =
joints[JointType.KneeRight].Orientation.ToMirror().ToQuaternion( comp );
AnkleLeft =
joints[JointType.AnkleRight].Orientation.ToMirror().ToQuaternion( comp );
KneeRight =
joints[JointType.KneeLeft].Orientation.ToMirror().ToQuaternion( comp );
AnkleRight =
joints[JointType.AnkleLeft].Orientation.ToMirror().ToQuaternion( comp );
}
// そのまま
else {
SpineBase =
joints[JointType.SpineBase].Orientation.ToQuaternion( comp );
SpineMid =
joints[JointType.SpineMid].Orientation.ToQuaternion( comp );
SpineShoulder =
joints[JointType.SpineShoulder].Orientation.ToQuaternion( comp );
ShoulderLeft =
joints[JointType.ShoulderLeft].Orientation.ToQuaternion( comp );
ShoulderRight =
joints[JointType.ShoulderRight].Orientation.ToQuaternion( comp );
ElbowLeft =
joints[JointType.ElbowLeft].Orientation.ToQuaternion( comp );
WristLeft =
joints[JointType.WristLeft].Orientation.ToQuaternion( comp );
HandLeft =
joints[JointType.HandLeft].Orientation.ToQuaternion( comp );
ElbowRight =
joints[JointType.ElbowRight].Orientation.ToQuaternion( comp );
WristRight =
joints[JointType.WristRight].Orientation.ToQuaternion( comp );
HandRight =
joints[JointType.HandRight].Orientation.ToQuaternion( comp );
KneeLeft =
joints[JointType.KneeLeft].Orientation.ToQuaternion( comp );
AnkleLeft =
joints[JointType.AnkleLeft].Orientation.ToQuaternion( comp );
KneeRight =
joints[JointType.KneeRight].Orientation.ToQuaternion( comp );
AnkleRight =
joints[JointType.AnkleRight].Orientation.ToQuaternion( comp );
}
|
Kinectの関節回転情報をUnityのクォータニオン(Quaternion)には変換しましたが、回転する前の標準状態におけるKinectのモデルとUnityちゃんのモデルでは姿勢が全く違います。それを合わせるためにcomp2という補正(compensation)項を定義してかけてやります。Kinectのモデルの標準状態は下図を参考にしてください。これは「気をつけ」の姿勢をKinect v2で認識させたとき、(1,0,0)、(0,1,0)、(0,0,1)を各関節のクォータニオンで回転させた方向を3種類の矢印で示したものです。各関節の矢印が左上の矢印と同じ向きになるように回転させた状態が標準状態です。
ユニティちゃんモデルの標準状態を知るには、チェックしたいパーツとその親であるすべてのパーツのRotationをすべて0に設定し、そのパーツの向きを確認してください。
その状態をもとに、それぞれのパーツを回転させていきます
Quaternion q = transform.rotation;
transform.rotation = Quaternion.identity;
var comp2 = Quaternion.AngleAxis( 90, new Vector3( 0, 1, 0 ) ) *
Quaternion.AngleAxis( -90, new Vector3( 0, 0, 1 ) );
Spine1.transform.rotation = SpineMid * comp2;
RightArm.transform.rotation = ElbowRight * comp2;
RightForeArm.transform.rotation = WristRight * comp2;
RightHand.transform.rotation = HandRight * comp2;
LeftArm.transform.rotation = ElbowLeft * comp2;
LeftForeArm.transform.rotation = WristLeft * comp2;
LeftHand.transform.rotation = HandLeft * comp2;
RightUpLeg.transform.rotation = KneeRight * comp2;
RightLeg.transform.rotation = AnkleRight * comp2;
LeftUpLeg.transform.rotation = KneeLeft *
Quaternion.AngleAxis( -90, new Vector3( 0, 0, 1 ) );
LeftLeg.transform.rotation = AnkleLeft *
Quaternion.AngleAxis( -90, new Vector3( 0, 0, 1 ) );
transform.rotation = q;
|
最後にユニティちゃんを移動できるように、腰位置を反映させます。
var pos = body.Joints[JointType.SpineMid].Position;
Ref.transform.position = new Vector3( -pos.X, pos.Y, -pos.Z );
|
【COLUMN】Unreal Engine 4でKinect v2を使う
Unityのようなゲーム開発環境には、他にもUnreal Engineというゲーム開発環境があります。2015年に月ごとの定額制が廃止され、無料で利用できるようになりました。プロのゲーム開発者も使用するエンジンであるため、非常に注目を集めています。
Kinect v2は2015年4月時点で公式にはUnreal Engineに対応していません。しかしマーケットを覗いてみるとKinect v2に対応されたプラグインが配布されています。Kinect 4 Unreal Introductionもその一つです。Kinect 4 Unreal IntroductionではカラーやDepthなどの基本機能のほかにも、3Dモデルを動かすアバター機能が標準で搭載されています。
Unreal Engineを利用しているユーザーも、このようなプラグインを使ってKinect v2の世界とつなげることができます。
■
次回は、Unityではなく、WPFアプリでKinectを活用したい方に向けて、Kinect WPF Controlsの概要と基本的な活用方法を解説します。
※以下では、本稿の前後を合わせて5回分(第1回~第5回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
1. Kinect開発者のための、Unityの基礎知識とインストール
UnityでKinectを活用しよう。Unityのインストール方法と、Kinectを使うためのパッケージの導入方法、Visual Studio Tools for Unityの概要を解説する。
2. UnityでKinectを使う(カラー画像/赤外線画像/Depthデータ/Body)
Unity+Kinectの開発環境が整ったら、実際にUnityからKinectの機能を使ってみよう。UnityによるKinect活用の基礎を解説。
3. Kinectの座標とUnityの座標を合わせる
UnityでKinectを使う際にポイントとなる、Kinectの座標系とUnityの座標系を合わせる方法を、実際のサンプルコードを通して説明する。
4. 【現在、表示中】≫ Kinectでユニティちゃんを動かす
Kinect+Unityの面白さを体感! Kinectで得られた全身骨格の動きを、コードを書かずに、3Dモデルのユニティちゃんに反映させてみよう。
5. Kinect WPF ControlsでWPFアプリでもジェスチャーを活用しよう
WPFアプリにKinectを操作するための機能を実装する場合にはKinect WPF Controlsが便利だ。その機能概要を紹介する。