はじめに

Vision Proが発売されて1ヶ月程が経ちました。
Vision Proの開発について、できること、できないことがわかってきたので、記事にしようと思います。
今回は導入編として、開発環境の構築と簡単なアプリの作成方法について書いていきます。

※この記事は2024/3/1時点までの調査結果を元に作成しました。

Vision Proの概念的な説明は↓の記事を見てください。

用語

PolySpatial 特有の用語がいくつかあるので、最初に書いておきます。

  • Bounded Volume
    • Shared Space のこと
    • 複数のアプリを同時に開くことができる
  • Unbounded Volume
    • Full Space のこと
    • 1つのアプリが画面を占有し、他のアプリは非表示になる

開発環境

  • Unity 2022.3.18f1
  • PolySpatial 1.0.3
  • VisionOS 1.0.3
  • macOS Sonoma 14.2
  • Xcode 15.2

環境構築

Unityをインストール

PolySpatialの動作環境は1.0.3時点でUnity 2022.3.18f1以降となっています。
合わせて、 iOS Build SupportvisionOS Build Support のモジュールもインストールします。

テンプレートプロジェクトをダウンロード

https://discussions.unity.com/t/visionos-template-update/335386

↑のリンクからテンプレートプロジェクトをダウンロードできます。
自分でプロジェクトを作成してセットアップしていくこともできますが、テンプレートプロジェクトから始めた方が簡単です。

新規プロジェクトのセットアップ

一応、新規プロジェクトのセットアップについても書いておきます。

まず、プロジェクトを新規作成します。
3D(URP)で始めるのが良いと思います。

プロジェクトが開いたら、 XR Plugin Manager をインストールします。

Project Settings/XR Plug-in Management から Apple visionOS を有効化します。
有効化すると、AR Foundation , Apple visionOS XR Plugin などのパッケージがインポートされます。
その後、エディタの再起動が要求されるので、再起動します。

再起動が完了したら、Apple visionOSApp ModeMixed Reality - Volume or Immersive Space に変更します。
変更すると、 PolySpatial 関連のパッケージがインポートされます。

パッケージのインポートが完了したら、

最後に Player/Splash ImageShow Splash Screen を無効化します。

ビルドターゲットを変更

プロジェクトのセットアップが完了したらビルドターゲットを visionOS に変更します。

これで環境構築は完了です。

ビルド

シミュレータへビルド

テンプレートプロジェクトではビルド設定に最初からSampleSceneが設定されているので、このシーンをビルドしてみます。

Target SDK を Simulator SDK に変更してビルドします。
通常のiOSアプリのビルドと同様にXcodeプロジェクトができるので、Xcodeプロジェクトを開いて、シミュレータを対象にビルドします。

ビルドが正常に完了したらシミュレータ上で↓のようなBoundedのシーンが表示されます。

Macと実機をペアリング

今度はVision Pro実機にビルドしてみます。

Target SDK を Device SDK に変更してビルドします。

VisionProはDeveloper StrapがないとMacと有線接続できないので、今回はWiFi経由でインストールします。
WiFi経由でインストールするにはVision ProとMacをペアリングする必要があります。
ペアリングに関しては↓の記事が参考になりました。(同じiCloudアカウントでログインする必要はなさそうです。)

https://www.techno-edge.net/article/2024/02/05/2756.html

Copying shared cache symbols from Vision Pro

https://forums.developer.apple.com/forums/thread/746905
↑のスレッドで話されている、VisionProからXcodeへのShared Cacheのコピーが全然進まない問題が、自分の環境でも発生し、完了するのに1時間半くらいかかってしまいました。

弊社では複数のエンジニアがVisionProの開発を行っており、全員が1時間半待つのは効率が悪すぎるということで、なんとかならないか色々な方法を試しました。
その結果、誰か一人がコピーを完了したら、そのキャッシュを他の人にそのまま渡せば良いことがわかりました。
↓のパスにコピーしたキャッシュがあるので、他の人はキャッシュをもらって、同じパスに配置すればOKです。
/Users/{ユーザー名}/Library/Developer/Xcode/visionOS DeviceSupport

複数人での開発で同じ問題にぶつかっている人はぜひ試してみてください。

実機にビルド

実機にビルドすると↓のようになります。

コンテンツ作成

サンプルを動かすことができたので、自分でも簡単なアプリを作ってみます。

シーンを新規作成

Main CameraDirectional Light が置かれた新規シーンを作りました。
ここに必要なものを追加していきます。

Volume Camera を配置

XR/Setupの Volume Camera をシーン上に配置します。
このオブジェクトがコンテンツの中心となるので、PositionとRotationを全て0にしておいた方が作りやすいと思います。

VolumeCameraWindowConfiguration を作成

次に Volume Window Configration を作成します。
Volume Window Configration はコンテンツの表示方法の設定ファイルです。

Volume Window ConfigrationModeBounded にすると、 Output Dimensions (meters) に設定した大きさのボリュームとしてコンテンツが表示されます。

ModeUnbounded にすると、大きさに制限のないImmersiveなコンテンツとして表示されます。

今回は1m四方のBoundedに設定しました。
Volume Window Configration が作成できたら、 Volume Camera コンポーネントの Volume Window Configration に設定します。

Volume CameraDimensions はエディタ上での大きさです。
そのままでも良いですが、 Output Dimensions (meters) と同じ値に設定しておくと作りやすいと思います。

シーン上に設定した大きさのボリュームが表示されます。
この範囲内にコンテンツを配置していきます。

モデルの配置

これでシーンの準備は完了したので、コンテンツの作成に入っていきます。
今回はボリュームの中にカワセミのモデルを配置してみました。

ピンチ入力の検知

ピンチ入力(親指と人差し指を一瞬くっつけて離す)を検知したら、カワセミの羽ばたくアニメーションが再生されるようにしてみます。

まずモデルのルートオブジェクトに Collider (今回は BoxCollider )と VisionOSHoverEffect コンポーネントをアタッチします。
これでカワセミに視線を向けた時に白く光るようになりました。

※現状だと、Vision ProのMRアプリで視線のホバーエフェクトを自作することはできず、 VisionOSHoverEffect コンポーネントに頼ることになると思います。

次にカワセミのアニメーション周りの設定をします。
基本的にはいつも通りに作ればいいのですが、 Animator コンポーネントの Culling Mode だけは Always Animate に設定する必要があるようです。

↓参考URL
https://discussions.unity.com/t/culled-animation-wont-play-in-mr-scene/320077

シミュレータで見ると↓のようになります。
視線(マウス)をホバーさせている時だけモデルが白く光っています。

ピンチを検出するスクリプトを作成します。
カワセミのアニメーション制御のスクリプト BirdController は今回の記事には関係ないので割愛します。

using Unity.PolySpatial.InputDevices;
using UnityEngine;
using UnityEngine.InputSystem.EnhancedTouch;
using UnityEngine.InputSystem.LowLevel;
using Touch = UnityEngine.InputSystem.EnhancedTouch.Touch; // 使用するTouchクラスはUnityEngine.Touchではない

namespace Gaprot.Behaviours
{
/// <summary>
/// サンプル管理クラス
/// </summary>
public class SampleManager : MonoBehaviour
{
/// <summary>
/// Awake
/// </summary>
private void Awake()
{
EnhancedTouchSupport.Enable(); // EnhancedTouchSupport を有効化
}

/// <summary>
/// OnDestroy
/// </summary>
private void OnDestroy()
{
EnhancedTouchSupport.Disable(); // EnhancedTouchSupport を無効化
}

/// <summary>
/// Update
/// </summary>
private void Update()
{
var activeTouches = Touch.activeTouches; // 現在有効な入力を取得

if(activeTouches.Count <= 0) return; // 有効な入力がない場合は処理終了
if (!EnhancedSpatialPointerSupport.TryGetPointerState(activeTouches[0], out SpatialPointerState state)) return; // SpatialPointerState が取得できない場合処理終了
if (state.phase is not SpatialPointerPhase.Began) return; // ピンチ入力開始時以外は処理終了(ここはコンテンツに合わせてカスタム)

var target = state.targetObject; // ピンチ入力の対象オブジェクトを取得
if(!target.TryGetComponent(out BirdController bird)) return; // BirdController コンポーネントを取得
bird.OnPinch(); // ピンチ時の処理を実行
}
}
}

空オブジェクト Sample Manager を作成し、↑のスクリプトをアタッチします。

あとはカワセミ側にアニメーション制御用のスクリプトをアタッチすれば完成です。

実機にビルドすると↓のようになります。

おわりに

今回のような簡単なコンテンツなら、 Volume Camera を置くだけで作ることができるので、これからもいろいろなアプリを作っていこうと思っています。

次回は応用編として、Unbounded(Immersive)でハンドトラッキングなどを使うコンテンツを作る記事を執筆予定です。



ギャップロを運営しているアップフロンティア株式会社では、一緒に働いてくれる仲間を随時、募集しています。 興味がある!一緒に働いてみたい!という方は下記よりご応募お待ちしております。
採用情報をみる