3D オブジェクト操作

このチュートリアルでは、ジェスチャとスケルトン入力を使用して、Unity で作成するゲームとアプリケーションのユーザー インターフェイスを強化する方法について説明します。 手のジェスチャによって制御される 3D カーソルを作成します。 このカーソルを使用して、シーン内のオブジェクトを選択し、3D 空間で移動します。

このチュートリアルは、完了までに約 30 分かかります。

最終結果をダウンロードする

このチュートリアルで取得した最終的な Unity プロジェクトは、 GitHub のオープンソース サンプル リポジトリにあります。 リポジトリを複製した後、次の手順に従ってアプリケーションを実行します。

  1. Unity を起動し、[ プロジェクト ] タブで [ 開く] を選択します。
  2. 複製されたリポジトリ内の Unity\Tutorials\3D オブジェクト操作 ディレクトリを参照します。
  3. 再生ボタン (または Ctrl + P) を押してシーンを実行します。

前提条件

このチュートリアルでは、C# プログラミング言語に関する基本的な知識と、Unity 環境に関する経験があることを前提としています。 Unity プロジェクト、シーン、ゲーム オブジェクト、スクリプトを作成する方法を知っていると仮定します。

このチュートリアルを開始する前に 、概要 チュートリアルを完了することをお勧めします。ここで説明した Unity 用 Project Prague ツールキット のプレハブについて理解していることを前提としています。

手順 1 - シーンの準備

  1. このチュートリアルは、「 3D オブジェクト操作 - シーンの準備 」チュートリアルで取得したシーンから始めます。 このシーンには、マウスで制御されたカーソルが含まれており、オブジェクトの上にマウスポインターを置き、それらを"つかむ"、空間内の新しい場所に移動することができます。 現在のチュートリアルでは、ジェスチャとモーションのマウスを置き換え、代わりに手を使用してカーソルを制御します。

    3D オブジェクト操作 - シーンの準備に関するチュートリアルを完了するか、次の手順に従って最終的な製品を入手してください。 3D オブジェクト操作 - シーン準備のチュートリアルでは、Unity 用 Project Prague ツールキットに関連するマテリアルは含まれていないため、自由にスキップできます。

  2. Unity で、 3D オブジェクト操作 - シーン準備 チュートリアルで作成した最終的なプロジェクトを読み込みます。 3D オブジェクト操作シーンを再生します。 マウスを使用してオブジェクトをつかんで移動できることを確認します。

    つかんで移動する手順

    手順 3 では、手を使用してオブジェクト (上の図の番号 2) をドラッグする方法について説明します。手順 4 では、手を使用してオブジェクトを離れてカメラ (上の図の数値 3) に向かって移動する方法を学習します。

手順 2 - ハンド カーソル

  1. GesturesManagerUIManager プレハブをシーンに追加します (MicrosoftGesturesToolkit\Prefabs から)。 これらのプレハブを使用して 、ジェスチャ サービスと通信します。 詳細については、 概要チュートリアルの手順 2 を参照してください。

  2. 手でカーソルを制御するには、まず手のスケルトン情報にアクセスする必要があります。 Gestures Service はハンドスケルトンを計算し、フレーム単位でサブスクライブしているすべてのクライアントに通信します。 シーン 内の GesturesManager ゲーム オブジェクトは 、Gestures Service のクライアントとして機能します。 GesturesManagerRegisterToSkeleton() メソッドと UnregisterFromSkeleton() メソッドを使用すると、ハンドスケルトン ストリームをサブスクライブおよびサブスクライブ解除できます。

    Cursor ゲーム オブジェクトがアクティブな場合は常に、ハンドスケルトン情報を受け取りたいと考えます。 OnEnable() メソッドと OnDisable() メソッドの次の実装を Cursor スクリプトに追加してください。

    警告

    ご覧になっていたサンプルが移動されたようです。 この問題の修正に取り組んでおりますので、ご安心ください。

  3. 次に、 ジェスチャ サービスから到着する手のひらの位置を、他のスケルトン情報とバンドルして抽出します。 手のひらの位置を使用して、画面上の カーソル 位置を計算します。

    ハンド スケルトンは、次の左手座標系でミリメートル単位で提供されます。

    手の骨格座標系ここでは RealSense™ カメラを使用して、座標系の軸を示しました。Project Prague でサポートされているすべての 深度カメラ には、同じ座標系が適用されることに注意してください。

    理想的には、私たちのシーンの メインカメラ があなたの目の視点からあなたの手を見たいと思います。 これを実現できる場合 - 画面への 3D カーソルの投影は、自然な感じの方法であなたの手に従います。

    目的のパースペクティブを近似するために、次の係数を使用して、(深度カメラのビュー空間で指定される) ハンドスケルトンを 3D カーソル ( メイン カメラのビュー空間で表現する) にマップします。 Cursor.cs に次のパブリック メンバーを追加します。

    警告

    ご覧になっていたサンプルが移動されたようです。 この問題の修正に取り組んでおりますので、ご安心ください。

    このマッピングでは、10 倍のスケールダウンも実行されることに注意してください。これは、実際にはミリメートルからセンチメートルへの単位変換です。 これは、カーソル位置のダイナミック レンジがシーン内のオブジェクトのサイズ (1 から 10 Unity 単位の大きさ) に適している必要があるためです。

    この準備により、手のひらの位置からカーソル位置への実際の変換を計算する準備ができました。 Cursor スクリプトに GetPalmCameraPosition() メソッドを追加します。

    警告

    ご覧になっていたサンプルが移動されたようです。 この問題の修正に取り組んでおりますので、ご安心ください。

    GetPalmCameraPosition() での SmoothDefaultSkeleton プロパティの使用に注意してください。 このプロパティは、深度カメラで現在見られるハンド スケルトンのスムーズ バージョンを提供します。 スムージングは、最後のいくつかのフレームで受け取ったスケルトンの平均化によって実現されます。 平均化に使用するフレームの数を制御できます。[インスペクター] ウィンドウで GesturesManager を調べ、[Smooth Moving Average Window Size]\(スムーズ移動平均ウィンドウ サイズ\) フィールドを変更します。

    スケルトンPalmPosition プロパティは、手の中心の位置に対応します。

    パーム位置ランドマーク

  4. GetCursorScreenPosition() を次の内容に置き換えます。

    警告

    ご覧になっていたサンプルが移動されたようです。 この問題の修正に取り組んでおりますので、ご安心ください。

  5. ジェスチャ サービスが実行されていることを確認します。 シーンを再生し、深度カメラの前にいずれかの手を持ち込む。 手を動かすことでカーソルを制御できるはずです。

手順 3 - ジェスチャを使用してオブジェクトを取得および移動する

ジェスチャを導入し、それを使用してカーソルをトリガーして"グラブ モード" に入り、そのままにします。 グラブ モードの場合、つかんだオブジェクトはカーソルの後 (手の後に続く) に従い、新しい場所に移動できます。

  1. [プロジェクト] ウィンドウで、[MicrosoftGesturesToolkit\Prefabs] の下にある GestureTrigger プレハブを見つけます。 これを [階層 ] ウィンドウにドラッグ アンド ドロップして、シーンに新しい GestureTrigger ゲーム オブジェクトを作成します。

  2. [インスペクター] ウィンドウで GestureTrigger ゲーム オブジェクトを調べ、[XAML ジェスチャ] ラジオ ボタンを選択し、[ジェスチャ XAML] セクションを展開し、次のジェスチャ定義に貼り付けます。

    <Gesture Name="GrabReleaseGesture"
             xmlns="http://schemas.microsoft.com/gestures/2015/xaml">
        <Gesture.Segments>
            <IdleGestureSegment Name="Idle" />
            <HandPose Name="InitSpreadPose">
                <PalmPose Context="{AnyHand}" Direction="Forward|Down" />
                <FingerPose Context="Index, Middle, Ring, Pinky" Flexion="Open" />
            </HandPose>
            <HandPose Name="GrabPose">
                <PalmPose Context="{AnyHand}" />
                <FingerPose Context="Index, Middle, Ring, Pinky" Flexion="Folded" />
            </HandPose>
            <HandPose Name="FinalSpreadPose">
                <PalmPose Context="{AnyHand}" />
                <FingerPose Context="Index, Middle, Ring, Pinky" Flexion="Open" />
            </HandPose>
        </Gesture.Segments>
        <Gesture.SegmentsConnections>
            <SegmentConnections From="Idle" To="Idle, InitSpreadPose" />
            <SegmentConnections From="InitSpreadPose" To="GrabPose" />
            <SegmentConnections From="GrabPose" To="FinalSpreadPose" />
            <SegmentConnections From="FinalSpreadPose" To="Idle" />
        </Gesture.SegmentsConnections>
    </Gesture>
    

    完了すると、 GestureTriggerInspector ビューは次のようになります。

    GrabReleaseGesture ジェスチャ定義

    ヒント

    ジェスチャの XAML 表現を生成するには、 C# ジェスチャ オブジェクト を作成し、 その ToXaml() メソッドを 呼び出します。 C# でのジェスチャの作成については、 概要ページ を参照してください。

    GrabReleaseGesture は、次のステート マシンに示すように、3 つのポーズで構成されています。

    GrabReleaseGesture

    ステート マシンとしてのジェスチャの概念の詳細については、 概要ページを参照してください。

  3. 次の方法で GrabReleaseGesture を使用します

    • GrabPose 検出により、カーソルがグラブ モードに入ります。つまり、 StartGrab()をトリガーする必要があります。
    • アイドル 状態の検出により、カーソルはグラブ モードを終了します。つまり、 StopGrab() をトリガーする必要があります。

    注意

    アイドル状態は、すべてのジェスチャの初期状態です。 ユーザーが完了するまでジェスチャを実行するか、実行中にジェスチャを破棄するたびに、ステート マシンは アイドル 状態にフォールバックします。

    [インスペクター] ウィンドウで GestureTrigger ゲーム オブジェクトを調べ、[ジェスチャ セグメントの追加] イベント ボタンを2 回押します。 これにより、Segment #1 と Segment #2 という 2 つの新しい UI (ユーザー インターフェイス) セクションが生成されます。

     - In the **Segment #1** drop down list, select the **GrabPose** (1), then click the **+** sign in the **On Trigger ()** pane (2). Drag the **Cursor** object to the **None (Object)** box (3) and select the **Cursor → StartGrab()** method from the **No Function** drop-down list (4):
    
     ![GrabPose gesture trigger](Images/UnityGrabGestureTrigger.png)
    
     - In the **Segment #2** drop down list, select the **Idle** (1), then click the **+** sign in the **On Trigger ()** pane (2). Drag the **Cursor** object to the **None (Object)** box (3) and select the **Cursor → StopGrab()** method from the **No Function** drop-down list (4):
    
     ![Idle gesture trigger](Images/UnityIdleGestureTrigger.png)
    
  4. シーンを実行します。 この手順で追加した機能をテストします。

    • カーソルを持つオブジェクトの上にカーソルを置きます。
    • 拳にあなたの手を握り締め、それをつかみます,
    • オブジェクトを新しい場所に移動します。
    • 指を離してオブジェクトを離します。

手順 4 - オブジェクトをカメラの近くに移動する

この手順では、グラブされたオブジェクトも放射状方向に移動できるようにします。

  1. Cursor.cs に次のプライベート メンバーを追加します。

    警告

    ご覧になっていたサンプルが移動されたようです。 この問題の修正に取り組んでおりますので、ご安心ください。

    このメンバーは、オブジェクトをつかむたびに初期化する必要があります。 これを行うには、 StartGrab() メソッドの末尾に次の行を追加します。

    警告

    ご覧になっていたサンプルが移動されたようです。 この問題の修正に取り組んでおりますので、ご安心ください。

  2. Cursor.csGetCursorDistanceCoefficient() メソッドの内容を次のように置き換えます。

    警告

    ご覧になっていたサンプルが移動されたようです。 この問題の修正に取り組んでおりますので、ご安心ください。

  3. シーンを実行してみてください。 オブジェクトをつかみ、深度カメラに向かって手を動かしたり、遠ざけたりします。 シーン内のオブジェクトは、仮想シーン内でそれぞれ移動して、手に従う必要があります。

    ヒント

    深度カメラに手を近づけないでください。 カメラには 視錐台 型の視野があります。手を近づけると、検出可能な領域が小さくなり、シーン内のオブジェクトを操作するための動きの範囲が少なくなります。