MR 入力 213:モーション コントローラー
Note
Mixed Reality Academy のチュートリアルは、HoloLens (第 1 世代) と Mixed Reality イマーシブ ヘッドセットを念頭に置いて編成されています。 そのため、それらのデバイスの開発に関するガイダンスを引き続き探している開発者のために、これらのチュートリアルをそのまま残しておくことが重要だと考えています。 これらのチュートリアルが、HoloLens 2 に使用されている最新のツールセットや操作に更新されることは "ありません"。 これらは、サポートされているデバイス上で継続して動作するように、保守されます。 HoloLens 2 向けには、新しいチュートリアル シリーズが投稿されています。
Mixed Reality のモーション コントローラーによって、さらにハイ レベルの対話機能が追加されます。 モーション コントローラーを使用すると、リアル タイムでの物理的な操作のように、より自然な方法でオブジェクトを直接操作できるため、没入感が高まり、より楽しくアプリを使用できます。
MR 入力 213 では、単純な空間描画エクスペリエンスを作成して、モーション コントローラーの入力イベントについて詳細を確認します。 このアプリでユーザーは、さまざまな種類のブラシと色を使用して 3 次元空間で描画できます。
このチュートリアルで説明するトピック
コントローラーの視覚化 | コントローラーの入力イベント | カスタム コントローラーと UI |
Unity のゲーム モードとランタイムでモーション コントローラー モデルをレンダリングする方法を説明します。 | さまざまな種類のボタン イベントとその応用について説明します。 | コントローラー上に UI 要素を重ね合わせる方法や、UI 要素を完全にカスタマイズする方法を説明します。 |
デバイス サポート
コース | HoloLens | イマーシブ ヘッドセット |
---|---|---|
MR 入力 213:モーション コントローラー | ✔️ |
開始する前に
前提条件
このページにある、イマーシブ ヘッドセットのインストール チェックリストを参照してください。
- このチュートリアルには、Unity 2017.2.1 p2 が必要です
プロジェクト ファイル
- プロジェクトに必要なファイルをダウンロードし、ファイルをデスクトップに展開します。
Note
ダウンロードする前にソース コードを確認する場合は、GitHub で参照できます。
Unity のセットアップ
目標
- Windows Mixed Reality 開発用に Unity を最適化する
- Mixed Reality カメラをセットアップする
- 環境をセットアップします
Instructions
Unity を起動します。
[Open] を選択します。
デスクトップに移動し、先ほどアーカイブ解除した MixedReality213-master フォルダーを見つけます。
[フォルダーの選択] をクリックします。
Unity でプロジェクト ファイルの読み込みが完了すると、Unity エディターを表示できるようになります。
Unity で、[File] (ファイル) > [Build Settings] (ビルド設定) の順に選択します。
[プラットフォーム] リストで [ユニバーサル Windows プラットフォーム] を選択し、[プラットフォームの切り替え] ボタンをクリックします。
[ターゲット デバイス] を [任意のデバイス] に設定します
[Build Type](ビルドの種類) を [D3D] に設定します
SDK を [最新のインストール] に設定します
[Unity C# プロジェクト] をオンにします
- これにより、Unity プロジェクトをリビルドせずに、Visual Studio プロジェクトでスクリプト ファイルを変更できます。
[プレーヤー設定] をクリックします。
[インスペクター] パネルで最下部までスクロールします
[XR 設定] で [仮想現実がサポート済み] をオンにします
[Virtual Reality SDK] で [Windows Mixed Reality] を選択します
[ビルド設定] ウィンドウを閉じます。
プロジェクトの構造
このチュートリアルでは、Mixed Reality ツールキット - Unity を使用します。 こちらのページでリリースを確認できます。
参照用の完成済みシーン
-
[Scenes] フォルダーには、完成した 2 つの Unity シーンがあります。
- MixedReality213: 1 つのブラシを使用した完成済みシーン
- MixedReality213Advanced: 複数のブラシを使用した高度なデザイン用の完成済みシーン
チュートリアル用の新しいシーンの設定
Unity で [ファイル] > [新しいシーン] をクリックします
[メイン カメラ] と [指向性ライト] を削除します
[プロジェクト] パネルで、次のプレハブを検索し、[階層] パネルにドラッグします。
- Assets/HoloToolkit/Input/Prefabs/MixedRealityCamera
- Assets/AppPrefabs/Environment
Mixed Reality ツールキットには、2 つのカメラ プレハブがあります。
- MixedRealityCamera.prefab: カメラのみ
- MixedRealityCameraParent.prefab: カメラ、テレポート、および境界
- このチュートリアルでは、テレポート機能なしの MixedRealityCamera を使用します。 このため、ユーザーが使用しやすいように、基本フロアを含む単純な環境プレハブを追加しました。
- MixedRealityCameraParent でのテレポートの詳細については、「高度な設計 - テレポートと移動」を参照してください
スカイボックスのセットアップ
[ウィンドウ] > [照明] > [設定] をクリックします
[スカイボックス素材] フィールドの右側にある円をクリックします
「gray」と入力し、[SkyboxGray] (Assets/AppPrefabs/Support/Materials/SkyboxGray.mat) を選択します
[スカイボックス] オプションをオンにして、割り当てられている灰色のグラデーション スカイボックスが表示されるようにします
MixedRealityCamera、環境、灰色のスカイボックスのシーンは、次のようになります。
[ファイル] > [名前を付けてシーンを保存] をクリックします
シーンに名前を付け、[Scenes] フォルダーに保存します
第 1 章 - コントローラーの視覚化
目標
- Unity のゲーム モードとランタイムでモーション コントローラー モデルをレンダリングする方法について説明します。
Windows Mixed Reality には、コントローラーを視覚化するためのアニメーション コントローラー モデルが用意されています。 アプリでコントローラーを視覚化するには、いくつかの方法があります。
- 既定値 - 既定のコントローラーを変更せずに使用する
- ハイブリッド - 既定のコントローラーを使用するが、その要素の一部をカスタマイズするか、UI コンポーネントをオーバーレイする
- 置換 - コントローラーに、カスタマイズした独自の 3D モデルを使用する
この章では、これらのコントローラーのカスタマイズの例について説明します。
Instructions
- [プロジェクト] パネルで、検索ボックスに「MotionControllers」と入力します。 Assets/HoloToolkit/Input/Prefabs/ の下で見つけることもできます。
- MotionControllers プレハブを [階層] パネルにドラッグします。
- [階層] パネルで MotionControllers プレハブをクリックします。
MotionControllers プレハブ
MotionControllers プレハブには、代替コントローラー モデル用のスロットを提供する MotionControllerVisualizer スクリプトがあります。 手や剣などの独自のカスタム 3D モデルを割り当て、[常に代替左/右モデルを使用] をオンにすると、既定のモデルではなく、それらが表示されます。 第 4 章では、このスロットを使用して、コントローラー モデルをブラシに置き換えます。
手順
- [インスペクター] パネルで MotionControllerVisualizer スクリプトをダブルクリックして、コードを Visual Studio で表示します
MotionControllerVisualizer スクリプト
MotionControllerVisualizer クラスと MotionControllerInfo クラスは、既定のコントローラー モデルを変更 & アクセスする手段を提供します。 MotionControllerVisualizer は、Unity の InteractionSourceDetected イベントにサブスクライブし、コントローラー モデルが検出されると、それらのモデルを自動的にインスタンス化します。
protected override void Awake()
{
...
InteractionManager.InteractionSourceDetected += InteractionManager_InteractionSourceDetected;
InteractionManager.InteractionSourceLost += InteractionManager_InteractionSourceLost;
...
}
コントローラー モデルは、glTF 仕様に従って提供されています。 この形式は、3D アセットの送信とアンパックの背後にあるプロセスを改善する一方で、共通の形式を提供するために作成されました。 この場合、実行時にコントローラー モデルを取得し、読み込む必要があります。これは、ユーザーのエクスペリエンスを可能な限りシームレスする必要がある一方で、どのバージョンのモーション コントローラーをユーザーが使用しているか保証されないためです。 このコースでは、Mixed Reality ツールキットを通じて、Khronos Group の UnityGLTF プロジェクトの 1 つのバージョンを使用します。
コントローラーが提供されると、スクリプトは MotionControllerInfo を使用して、特定のコントローラー要素の変換を検出できます。これにより、スクリプトはそれらの要素自体を正しく配置できます。
後の章では、これらのスクリプトを使用して UI 要素をコントローラーにアタッチする方法を説明します。
一部のスクリプトには、#if !UNITY_EDITOR または UNITY_WSA を含むコード ブロックがあります。 これらのコード ブロックは、Windows にデプロイするときに UWP ランタイムでのみ実行されます。 これは、Unity エディターと UWP アプリ ランタイムで使用される API のセットが異なるためです。
- シーンを保存し、[再生] ボタンをクリックします。
ヘッドセットで、モーション コントローラーを含むシーンを確認できます。 ボタンのクリック、サムスティックの動き、タッチパッドのタッチ強調表示の詳細なアニメーションを確認できます。
第 2 章 - コントローラーへの UI 要素のアタッチ
目標
- モーション コントローラーの要素について学習する
- コントローラーの特定の部分にオブジェクトをアタッチする方法を学習する
この章では、ユーザーがいつでも簡単にアクセスし、操作できるユーザー インターフェイス要素をコントローラーに追加する方法について説明します。 また、タッチパッド入力を使用する単純なカラー ピッカー UI を追加する方法についても説明します。
Instructions
- [プロジェクト] パネルで MotionControllerInfo スクリプトを検索します。
- 検索結果で MotionControllerInfo スクリプトをダブルクリックして、Visual Studio でコードを表示します。
MotionControllerInfo スクリプト
最初の手順は、UI をアタッチする先のコントローラーの要素を選択することです。 これらの要素は、MotionControllerInfo.cs の ControllerElementEnum で定義されています。
- ホーム
- Menu
- Grasp
- Thumbstick
- Select
- タッチパッド
- Pointing pose - この要素は、前方をポイントするコントローラーの先端を表します。
手順
- [プロジェクト] パネルで AttachToController スクリプトを検索します。
- 検索結果で AttachToController スクリプトをダブルクリックして、Visual Studio でコードを表示します。
AttachToController スクリプト
AttachToController スクリプトを使用すると、任意のオブジェクトを、指定したコントローラーの利き手と要素に簡単にアタッチできます。
AttachElementToController() で、以下を行います。
- MotionControllerInfo.Handedness を使用して利き手を確認する
- MotionControllerInfo.TryGetElement() を使用してコントローラーの特定の要素を取得する
- コントローラー モデルから要素の変換を取得した後、その下のオブジェクトを親にし、オブジェクトのローカル位置 & 回転を 0 に設定します。
public MotionControllerInfo.ControllerElementEnum Element { get { return element; } }
private void AttachElementToController(MotionControllerInfo newController)
{
if (!IsAttached && newController.Handedness == handedness)
{
if (!newController.TryGetElement(element, out elementTransform))
{
Debug.LogError("Unable to find element of type " + element + " under controller " + newController.ControllerParent.name + "; not attaching.");
return;
}
controller = newController;
SetChildrenActive(true);
// Parent ourselves under the element and set our offsets
transform.parent = elementTransform;
transform.localPosition = positionOffset;
transform.localEulerAngles = rotationOffset;
if (setScaleOnAttach)
{
transform.localScale = scale;
}
// Announce that we're attached
OnAttachToController();
IsAttached = true;
}
}
AttachToController スクリプトを使用する最も簡単な方法は、ColorPickerWheel で実行したように、このスクリプトを継承することです。OnAttachToController 関数と OnDetachFromController 関数をオーバーライドするだけで、コントローラーが検出/切断されたときにセットアップ/機能停止を実行できます。
手順
- [プロジェクト] パネルで、検索ボックスに「ColorPickerWheel」と入力します。 これは、Assets/AppPrefabs/ の下で見つけることもできます。
- ColorPickerWheel プレハブを [階層] パネルにドラッグします。
- [階層] パネルで ColorPickerWheel プレハブをクリックします。
- [インスペクター] パネルで ColorPickerWheel スクリプトをダブルクリックして、コードを Visual Studio で表示します。
ColorPickerWheel スクリプト
ColorPickerWheel は AttachToController を継承するため、[利き手] と [要素] が [インスペクター] パネルに表示されます。 左のコントローラーの Touchpad 要素に UI をアタッチします。
ColorPickerWheel は、OnAttachToController と OnDetachFromController をオーバーライドして入力イベントにサブスクライブします。次の章では、これを使用して、タッチパッド入力で色を選択します。
public class ColorPickerWheel : AttachToController, IPointerTarget
{
protected override void OnAttachToController()
{
// Subscribe to input now that we're parented under the controller
InteractionManager.InteractionSourceUpdated += InteractionSourceUpdated;
}
protected override void OnDetachFromController()
{
Visible = false;
// Unsubscribe from input now that we've detached from the controller
InteractionManager.InteractionSourceUpdated -= InteractionSourceUpdated;
}
...
}
- シーンを保存し、[再生] ボタンをクリックします。
コントローラーにオブジェクトをアタッチする別の方法
スクリプトで AttachToController を継承し、OnAttachToController をオーバーライドすることをお勧めします。 ただし、これが常に可能であるとは限りません。 もう 1 つの方法は、スタンドアロン コンポーネントとしてこれを使用することです。 これは、スクリプトをリファクタリングせずに、既存のプレハブをコントローラーにアタッチする場合に便利です。 IsAttached が true に設定されるまでクラスを待機させ、その後にセットアップを実行するだけです。 これを行う最も簡単な方法は、"Start" のコルーチンを使用する方法です。
private IEnumerator Start() {
AttachToController attach = gameObject.GetComponent<AttachToController>();
while (!attach.IsAttached) {
yield return null;
}
// Perform setup here
}
第 3 章 - タッチパッド入力の操作
目標
- タッチパッド入力データ イベントを取得する方法を学習する
- アプリ エクスペリエンスにタッチパッド軸位置情報を使用する方法を学習する
Instructions
- [階層] パネルで ColorPickerWheel をクリックします
- [インスペクター] パネルの [アニメーター] で ColorPickerWheelController をダブルクリックします
- 開かれた [アニメーター] タブを確認できます
Unity のアニメーション コントローラーによる UI の表示/非表示
アニメーションを使用して ColorPickerWheel UI を表示または非表示にするために、Unity のアニメーション システムを使用します。 ColorPickerWheel の Visible プロパティを true または false に設定すると、[表示] および [非表示] アニメーション トリガーが作動します。 [表示] および [非表示] パラメーターは、ColorPickerWheelController アニメーション コントローラーで定義されます。
手順
- [階層] パネルで ColorPickerWheel プレハブを選択します
- [インスペクター] パネルで ColorPickerWheel スクリプトをダブルクリックして、コードを Visual Studio で表示します
ColorPickerWheel スクリプト
ColorPickerWheel は、Unity の InteractionSourceUpdated イベントにサブスクライブしてタッチパッド イベントをリッスンします。
InteractionSourceUpdated() で、スクリプトは最初に次のことを確認します。
- 実際にタッチパッド イベント (obj. state.touchpadTouched) である
- 左のコントローラー (obj.state.source.handedness) が起点である
両方とも true の場合は、タッチパッドの位置 (obj.touchpadPosition) が selectorPosition に割り当てられます。
private void InteractionSourceUpdated(InteractionSourceUpdatedEventArgs obj)
{
if (obj.state.source.handedness == handedness && obj.state.touchpadTouched)
{
Visible = true;
selectorPosition = obj.state.touchpadPosition;
}
}
Update() では、visible プロパティに基づいて、カラー ピッカーのアニメーター コンポーネントで、[表示] および [非表示] アニメーション トリガーが作動します
if (visible != visibleLastFrame)
{
if (visible)
{
animator.SetTrigger("Show");
}
else
{
animator.SetTrigger("Hide");
}
}
Update() では、selectorPosition を使用して、カラー ホイールのメッシュ コライダーでレイキャスティングを行います。これにより、UV 位置が返されます。 次に、この位置を使用して、カラー ホイールのテクスチャのピクセル座標と色の値を見つけることができます。 この値には、SelectedColor プロパティを使用して、他のスクリプトからアクセスできます。
...
// Clamp selector position to a radius of 1
Vector3 localPosition = new Vector3(selectorPosition.x * inputScale, 0.15f, selectorPosition.y * inputScale);
if (localPosition.magnitude > 1)
{
localPosition = localPosition.normalized;
}
selectorTransform.localPosition = localPosition;
// Raycast the wheel mesh and get its UV coordinates
Vector3 raycastStart = selectorTransform.position + selectorTransform.up * 0.15f;
RaycastHit hit;
Debug.DrawLine(raycastStart, raycastStart - (selectorTransform.up * 0.25f));
if (Physics.Raycast(raycastStart, -selectorTransform.up, out hit, 0.25f, 1 << colorWheelObject.layer, QueryTriggerInteraction.Ignore))
{
// Get pixel from the color wheel texture using UV coordinates
Vector2 uv = hit.textureCoord;
int pixelX = Mathf.FloorToInt(colorWheelTexture.width * uv.x);
int pixelY = Mathf.FloorToInt(colorWheelTexture.height * uv.y);
selectedColor = colorWheelTexture.GetPixel(pixelX, pixelY);
selectedColor.a = 1f;
}
// Set the selector's color and blend it with white to make it visible on top of the wheel
selectorRenderer.material.color = Color.Lerp (selectedColor, Color.white, 0.5f);
}
第 4 章 - コントローラー モデルのオーバーライド
目標
- カスタム 3D モデルを使用してコントローラー モデルをオーバーライドする方法を学習する。
Instructions
- [階層] パネルで MotionControllers をクリックします。
- [代替右コントローラー] フィールドの右側にある円をクリックします。
- 「BrushController」と入力し、結果からプレハブを選択します。 Assets/AppPrefabs/BrushController の下で見つけることもできます。
- [常に代替右モデルを使用] をオンにします
BrushController プレハブは、[階層] パネルに含まれている必要ありません。 ただし、子コンポーネントを確認するために、以下を実行します。
- [プロジェクト] パネルで「BrushController」と入力し、BrushController プレハブを [階層] パネルにドラッグします。
BrushController で [先端] コンポーネントを見つけます。 その変換を使用して、線の描画を開始/停止します。
- [階層] パネルから BrushController を削除します。
- シーンを保存し、[再生] ボタンをクリックします。 右側のモーション コントローラーがブラシ モデルに置き換えられていることがわかります。
第 5 章 - Select 入力による描画
目標
- Select ボタン イベントを使用して線の描画を開始および停止する方法を学習する
Instructions
- [プロジェクト] パネルで BrushController プレハブを検索します。
- [インスペクター] パネルで BrushController スクリプトをダブルクリックして、コードを Visual Studio で表示します
BrushController スクリプト
BrushController は、InteractionManager の InteractionSourcePressed イベントと InteractionSourceReleased イベントにサブスクライブします。 InteractionSourcePressed イベントがトリガーされると、ブラシの Draw プロパティが true に設定され、InteractionSourceReleased イベントがトリガーされると、ブラシの Draw プロパティが false に設定されます。
private void InteractionSourcePressed(InteractionSourcePressedEventArgs obj)
{
if (obj.state.source.handedness == InteractionSourceHandedness.Right && obj.pressType == InteractionSourcePressType.Select)
{
Draw = true;
}
}
private void InteractionSourceReleased(InteractionSourceReleasedEventArgs obj)
{
if (obj.state.source.handedness == InteractionSourceHandedness.Right && obj.pressType == InteractionSourcePressType.Select)
{
Draw = false;
}
}
Draw が true に設定されている間は、インスタンス化された Unity LineRenderer でブラシが点を生成します。 このプレハブへの参照は、ブラシの [Stroke プレハブ] フィールドに保持されます。
private IEnumerator DrawOverTime()
{
// Get the position of the tip
Vector3 lastPointPosition = tip.position;
...
// Create a new brush stroke
GameObject newStroke = Instantiate(strokePrefab);
LineRenderer line = newStroke.GetComponent<LineRenderer>();
newStroke.transform.position = startPosition;
line.SetPosition(0, tip.position);
float initialWidth = line.widthMultiplier;
// Generate points in an instantiated Unity LineRenderer
while (draw)
{
// Move the last point to the draw point position
line.SetPosition(line.positionCount - 1, tip.position);
line.material.color = colorPicker.SelectedColor;
brushRenderer.material.color = colorPicker.SelectedColor;
lastPointAddedTime = Time.unscaledTime;
// Adjust the width between 1x and 2x width based on strength of trigger pull
line.widthMultiplier = Mathf.Lerp(initialWidth, initialWidth * 2, width);
if (Vector3.Distance(lastPointPosition, tip.position) > minPositionDelta || Time.unscaledTime > lastPointAddedTime + maxTimeDelta)
{
// Spawn a new point
lastPointAddedTime = Time.unscaledTime;
lastPointPosition = tip.position;
line.positionCount += 1;
line.SetPosition(line.positionCount - 1, lastPointPosition);
}
yield return null;
}
}
カラー ピッカーのホイール UI から、現在選択されている色を使用するには、BrushController に、ColorPickerWheel オブジェクトへの参照がある必要があります。 BrushController プレハブは実行時に交換用コントローラーとしてインスタンス化されるため、シーン内のオブジェクトへの参照は、実行時に設定する必要があります。 この場合、GameObject.FindObjectOfType を使用して ColorPickerWheel を見つけます。
private void OnEnable()
{
// Locate the ColorPickerWheel
colorPicker = FindObjectOfType<ColorPickerWheel>();
// Assign currently selected color to the brush’s material color
brushRenderer.material.color = colorPicker.SelectedColor;
...
}
- シーンを保存し、[再生] ボタンをクリックします。 右側のコントローラーの Select ボタンを使用して、線を描画し、塗りつぶすことができます。
第 6 章 - Select 入力によるオブジェクトの生成
目標
- Select ボタンと Grasp ボタンの入力イベントを使用する方法を学習する
- オブジェクトをインスタンス化する方法を学習する
Instructions
[プロジェクト] パネルで、検索ボックスに「ObjectSpawner」と入力します。 これは、Assets/AppPrefabs/ の下で見つけることもできます
ObjectSpawner プレハブを [階層] パネルにドラッグします。
[階層] パネルで ObjectSpawner をクリックします。
ObjectSpawner には、Color Source という名前のフィールドがあります。
[階層] パネルで、このフィールドに ColorPickerWheel 参照をドラッグします。
[階層] パネルで ObjectSpawner プレハブをクリックします。
[インスペクター] パネルで ObjectSpawner スクリプトをダブルクリックして、コードを Visual Studio で表示します。
ObjectSpawner スクリプト
ObjectSpawner は、プリミティブ メッシュ (立方体、球、円柱) のコピーを空間にインスタンス化します。 InteractionSourcePressed が検出されると、利き手が確認され、またそれが InteractionSourcePressType.Grasp イベントと InteractionSourcePressType.Select イベントのどちらであるかが確認されます。
Grasp イベントの場合、現在のメッシュの種類 (球、立方体、円柱) のインデックスがインクリメントされます
private void InteractionSourcePressed(InteractionSourcePressedEventArgs obj)
{
// Check handedness, see if it is left controller
if (obj.state.source.handedness == handedness)
{
switch (obj.pressType)
{
// If it is Select button event, spawn object
case InteractionSourcePressType.Select:
if (state == StateEnum.Idle)
{
// We've pressed the grasp - enter spawning state
state = StateEnum.Spawning;
SpawnObject();
}
break;
// If it is Grasp button event
case InteractionSourcePressType.Grasp:
// Increment the index of current mesh type (sphere, cube, cylinder)
meshIndex++;
if (meshIndex >= NumAvailableMeshes)
{
meshIndex = 0;
}
break;
default:
break;
}
}
}
Select イベントの場合、SpawnObject() で、新しいオブジェクトがインスタンス化され、親の指定が解除され、ワールドにリリースされます。
private void SpawnObject()
{
// Instantiate the spawned object
GameObject newObject = Instantiate(displayObject.gameObject, spawnParent);
// Detach the newly spawned object
newObject.transform.parent = null;
// Reset the scale transform to 1
scaleParent.localScale = Vector3.one;
// Set its material color so its material gets instantiated
newObject.GetComponent<Renderer>().material.color = colorSource.SelectedColor;
}
ObjectSpawner は、ColorPickerWheel を使用して、表示オブジェクトの素材の色を設定します。 生成されたオブジェクトには、この素材のインスタンスが与えられ、その色が保持されます。
- シーンを保存し、[再生] ボタンをクリックします。
[Grasp] ボタンを使用してオブジェクトを変更でき、また [Select] ボタンを使用してオブジェクトを生成できます。
アプリをビルドし、Mixed Reality ポータルにデプロイする
- Unity で、[File] (ファイル) > [Build Settings] (ビルド設定) の順に選択します。
- [開いているシーンを追加] をクリックして、現在のシーンを [ビルド内のシーン] に追加します。
- [ビルド] をクリックします。
- 「App」という名前の新しいフォルダーを作成します。
- [App] フォルダーを 1 回クリックします。
- [フォルダーの選択] をクリックします。
- Unity での作業が終了すると、エクスプローラー ウィンドウが表示されます。
- [App] フォルダーを開きます。
- Visual Studio ソリューション ファイル、YourSceneName.sln をダブルクリックします。
- Visual Studio の上部ツールバーを使用して、ターゲットを [デバッグ] から [リリース] に変更し、[ARM] から [X64] に変更します。
- [デバイス] ボタンの横にあるドロップダウン矢印をクリックし、[ローカル コンピューター] を選択します。
- メニューで [デバッグ] > [デバッグなしで開始] をクリックするか、Ctrl + F5 キーを押します。
これで、アプリがビルドされ、Mixed Reality ポータルにインストールされます。 これは、Mixed Reality ポータルの [スタート] メニューから再び起動できます。
高度なデザイン - 放射状レイアウトによるブラシ ツール
この章では、既定のモーション コントローラー モデルをカスタムのブラシ ツール コレクションに置き換える方法について説明します。 参照用として、完成したシーン MixedReality213Advanced が [Scenes] フォルダーにあります。
Instructions
[プロジェクト] パネルで、検索ボックスに「BrushSelector」と入力します。 これは、Assets/AppPrefabs/ の下で見つけることもできます
BrushSelector プレハブを [階層] パネルにドラッグします。
編成用に Brushes という名前の空の GameObject を作成します
次のプレハブを [プロジェクト] パネルから Brushes にドラッグします
- Assets/AppPrefabs/BrushFat
- Assets/AppPrefabs/BrushThin
- Assets/AppPrefabs/Eraser
- Assets/AppPrefabs/MarkerFat
- Assets/AppPrefabs/MarkerThin
- Assets/AppPrefabs/Pencil
[階層] パネルで MotionControllers プレハブをクリックします。
[インスペクター] パネルの [モーション コントローラー ビジュアライザー] で [常に代替右モデルを使用する] チェックボックスをオフにします
[階層] パネルで BrushSelector をクリックします
BrushSelector には、ColorPicker という名前のフィールドがあります
[階層] パネルの ColorPickerWheel を、[インスペクター] パネルの [ColorPicker] フィールドにドラッグします。
[階層] パネルの BrushSelector プレハブで Menu オブジェクトを選択します。
[インスペクター] パネルの LineObjectCollection コンポーネントで [Objects array] (オブジェクト配列) ドロップダウンを開きます。 6 個の空のスロットが表示されます。
[階層] パネルから、Brushes GameObject の下の各親プレハブを、これらのスロットに任意の順序でドラッグします。 (必ず、プロジェクト フォルダー内ではなく、シーンからプレハブをドラッグしてください。)
BrushSelector プレハブ
BrushSelector は AttachToController を継承するため、[利き手] オプションと [要素] オプションが [インスペクター] パネルに表示されます。 [右] および [ポインティング ポーズ] を選択して、右手のコントローラーにブラシ ツールを前向きにアタッチします。
BrushSelector は、2 つのユーティリティを使用します。
- Ellipse: 楕円形に沿って、空間で点を生成するために使用されます。
- LineObjectCollection: Line クラス (Ellipse など) によって生成された点を使用してオブジェクトを分布させます。 ここでは、楕円の形状に沿ってブラシを配置するために使用します。
これらのユーティリティを組み合わせて使用すると、放射状メニューを作成できます。
LineObjectCollection スクリプト
LineObjectCollection には、その線に沿って分布するオブジェクトのサイズ、位置、回転のコントロールがあります。 これは、ブラシ セレクターのような放射状メニューを作成する場合に便利です。 選択された中心位置に近づくにつれ、何も表示されていない状態からスケールアップするブラシの外観を作成するために、ObjectScale 曲線は中央を頂点として、両端が下がっています。
BrushSelector スクリプト
BrushSelector で、手続き型アニメーションの使用を選択しました。 最初に、ブラシ モデルが LineObjectCollection スクリプトによって楕円内に分配されます。 次に、各ブラシによって、DisplayMode 値に基づいて、ユーザーの手の中でその位置が維持されます。この値は、選択内容に基づいて変化します。 手続き型の手法を選択したのは、ユーザーがブラシを選択したときに、ブラシの位置の切り替えが中断される可能性が高いためです。 Mecanim アニメーションは中断を適切に処理できますが、単純な Lerp 操作よりも複雑である傾向があります。
BrushSelector は、両方の組み合わせを使用します。 タッチパッド入力が検出されると、各ブラシ オプションが表示され、放射状メニューに沿ってスケール アップされます。 タイムアウト期間が経過する (ユーザーが選択を行ったことを示す) と、各ブラシ オプションが再びスケールダウンされ、選択したブラシだけが残ります。
タッチパッド入力の視覚化
コントローラー モデルが完全に置き換えられている場合でも、元のモデル入力で入力を表示すると便利な場合があります。 これは、実際のユーザーの操作に安心感を与えるために役立ちます。 BrushSelector で、入力を受け取ったときにタッチパッドが短時間表示されることを選択しました。 これを行うために、コントローラーから Touchpad 要素を取得し、その素材をカスタム素材に置き換えてから、最後に受け取ったタッチパッド入力に基づいて、その素材の色にグラデーションを適用しました。
protected override void OnAttachToController()
{
// Turn off the default controller's renderers
controller.SetRenderersVisible(false);
// Get the touchpad and assign our custom material to it
Transform touchpad;
if (controller.TryGetElement(MotionControllerInfo.ControllerElementEnum.Touchpad, out touchpad))
{
touchpadRenderer = touchpad.GetComponentInChildren<MeshRenderer>();
originalTouchpadMaterial = touchpadRenderer.material;
touchpadRenderer.material = touchpadMaterial;
touchpadRenderer.enabled = true;
}
// Subscribe to input now that we're parented under the controller
InteractionManager.InteractionSourceUpdated += InteractionSourceUpdated;
}
private void Update()
{
...
// Update our touchpad material
Color glowColor = touchpadColor.Evaluate((Time.unscaledTime - touchpadTouchTime) / touchpadGlowLossTime);
touchpadMaterial.SetColor("_EmissionColor", glowColor);
touchpadMaterial.SetColor("_Color", glowColor);
...
}
タッチパッド入力によるブラシ ツールの選択
ブラシ セレクターは、タッチパッドを押すことによる入力をが検出すると、入力の位置を調べて、左と右のどちらであるかを判別します。
selectPressedAmount によるストロークの太さ
InteractionSourcePressed() で InteractionSourcePressType.Select イベントを取得する代わりに、selectPressedAmount を通じて、押された量のアナログ値を取得できます。 この値は InteractionSourceUpdated() で取得できます。
private void InteractionSourceUpdated(InteractionSourceUpdatedEventArgs obj)
{
if (obj.state.source.handedness == handedness)
{
if (obj.state.touchpadPressed)
{
// Check which side we clicked
if (obj.state.touchpadPosition.x < 0)
{
currentAction = SwipeEnum.Left;
}
else
{
currentAction = SwipeEnum.Right;
}
// Ping the touchpad material so it gets bright
touchpadTouchTime = Time.unscaledTime;
}
if (activeBrush != null)
{
// If the pressed amount is greater than our threshold, draw
if (obj.state.selectPressedAmount >= selectPressedDrawThreshold)
{
activeBrush.Draw = true;
activeBrush.Width = ProcessSelectPressedAmount(obj.state.selectPressedAmount);
}
else
{
// Otherwise, stop drawing
activeBrush.Draw = false;
selectPressedSmooth = 0f;
}
}
}
}
Eraser スクリプト
Eraser は、基本 Brush の DrawOverTime() 関数をオーバーライドする特殊な種類のブラシです。 Draw が true の場合、Eraser は、その先端が既存のブラシ ストロークと交差しているかどうかを調べます。 これに該当する場合、それらはキューに追加され、縮小され削除されます。
高度な設計 - テレポートと移動
ユーザーがサムスティックを使用してテレポートでシーン内を移動できるようにする場合は、MixedRealityCamera ではなく、MixedRealityCameraParent を使用します。 InputManager と DefaultCursor を追加することも必要です。 MixedRealityCameraParent には、既に MotionControllers と Boundary が子コンポーネントとして含まれているため、既存の MotionControllers プレハブと Environment プレハブを削除する必要があります。
Instructions
[階層] パネルで、MixedRealityCamera、Environment、MotionControllers を削除します
[プロジェクト] パネルで、次のプレハブを検索し、[階層] パネルにドラッグします。
- Assets/AppPrefabs/Input/Prefabs/MixedRealityCameraParent
- Assets/AppPrefabs/Input/Prefabs/InputManager
- Assets/AppPrefabs/Input/Prefabs/Cursor/DefaultCursor
[階層] パネルで Input Manager をクリックします
[インスペクター] パネルで、[シンプル シングル ポインター セレクター] セクションまで下にスクロールします
[階層] パネルから DefaultCursor を [Cursor] フィールドにドラッグします
シーンを保存し、[再生] ボタンをクリックします。 サムスティックを使用して、左/右に回転したり、テレポートしたりできるようになります。
終わりに
これで、このチュートリアルは終了です。 学習した内容は次のとおりです。
- Unity のゲーム モードとランタイムでモーション コントローラー モデルを操作する方法。
- さまざまな種類のボタン イベントを使用する方法とその応用。
- コントローラー上に UI 要素を重ね合わせる方法や、UI 要素を完全にカスタマイズする方法。
これで、モーション コントローラーを使用して、独自のイマーシブ エクスペリエンスを作成できるようになりました。
完成したシーン
- Unity の[プロジェクト] パネルで [Scenes] フォルダーをクリックします。
- 2 つの Unity シーン、MixedReality213 と MixedReality213Advanced が見つかります。
- MixedReality213: 1 つのブラシを使用した完成済みシーン
- MixedReality213Advanced: 選択ボタンの押す量の例を含む複数のブラシがある完成済みシーン