Android XR

Unity を使用して Android XR で空間コンテンツを設計する

スピーカー

  • Levana Chen

    ソフトウェア エンジニア

  • Jennifer Stanton

    シニア ソフトウェア エンジニア

概要

講演について

Unity と Android XR を使用して、3D エクスペリエンスを現実世界に固定して維持する方法を学びます。このセッションでは、Spatial Entities フレームワーク、Cloud Anchors、Geospatial API について説明し、物理的な配置を記憶してセッション間でマルチユーザーの空間データを同期する、コンテキストを認識するアプリを構築する方法を紹介します。

空間エンティティの背後にあるアーキテクチャを調べ、デジタル アセットを物理的なオブジェクト、サーフェス、地理的位置にアンカーする方法を理解します。空間のマッピング、セッションをまたいだコンテンツの保持、共有された位置認識 AR / XR エクスペリエンスの作成に役立つ、Unity の実践的なワークフローを習得できます。

概要

  • AR Foundation は、OpenXR 拡張機能を使用して、XR プラットフォーム全体で統一されたスクリプト API を提供します。平面やメッシュなどの現実世界の対象物を表現してクエリできます。
  • 永続アンカーは、セッションやデバイスの再起動をまたいでデジタル コンテンツを安定させます。TrySaveAnchorAsync を使用して永続化し、シリアル化された GUID を使用して再読み込みできます。
  • Spatial Entities フレームワークは、追跡可能な拡張機能に代わるもので、クロスプラットフォーム サポートを提供し、境界ボックスやマーカーなどのアイテムの追跡パフォーマンスを向上させます。
  • マネージャー コンポーネントは、Android XR の権限が付与されるまで無効にする必要があります。これにより、機密性の高い環境データへのアクセス制限が原因でサブシステムの初期化に失敗することを回避できます。
  • Geospatial API は、Google Cloud ARCore サービスと VPS を使用して、現実世界の屋外の場所にコンテンツを配置します。

リソース

Gemini Notebook

トピック

  • android-xr
  • 調和
  • ar-foundation
  • openxr
  • spatial-entities
  • アンカー広告
  • geospatial-api

文字起こし

概要と AR Foundation の概要

  • それでは

  • 皆様、ようこそ。

  • これは、Unity を使用した Android XR での空間コンテンツの設計です。

  • Jennifer Stanton と申します。

  • Unity でシニア ソフトウェア エンジニアとして働いています。

  • 私は XR 機能チームで、特に AR Foundation と、拡張現実と複合現実を Unity にもたらす関連パッケージを担当しています。

  • Google の XR ゲームエンジン チームの Levana Chen です。

  • Google のチームは、強化された XR 機能とシームレスなゲームエンジン統合を実現するプラグインとパッケージの提供に注力しました。

  • デベロッパーの皆様からよくお聞きする大きな課題の一つは、同じコードベースで複数の XR プラットフォームをサポートするには多大な労力を要するということです。

  • AR Foundation は、Unity の高レベルのエクステンデッド リアリティ API レイヤです。

  • コードを大幅に書き直したり、複数のコードベースを管理したりすることなく、複数の XR プラットフォームをターゲットにできます。また、どのプラットフォームでも AR 機能用のスクリプト API を 1 つ使用できます。

  • これにより、デベロッパーはアプリ、ゲーム、エクスペリエンスの作成に集中でき、Unity がプラットフォームの詳細を処理します。

  • AR Foundation がこれを実現する方法の一つは、OpenXR 拡張機能を使用することです。

  • これらは、Khronos Group によって定義された、業界標準のオープン API のセットです。

  • OpenXR も空間エンティティを作成しています。

  • これは、デジタル コンテンツが現実世界を表現し、現実世界とやり取りするためのフレームワークです。

  • 空間エンティティは、現実世界の要素をデジタル オブジェクトとして表現するための統合モデルを提供します。

  • 今回は、XR 向けのエクスペリエンスを作成する旅を始めることができるように、AR Foundation に関する最新情報の一部をご紹介します。

  • 次に、デジタル コンテンツが現実世界とインタラクションできるように、AR Foundation の機能について説明します。

  • 次に、パッケージやその他の依存関係を含むプロジェクトの設定について説明します。これにより、この機能をプロジェクトに実装できるようになります。

  • 最後に、Android XR 独自の機能について説明します。

AR Foundation の主な機能

  • AR Foundation が現実世界をデジタルで表現する方法の 1 つが平面です。

  • これらは、床、壁、天井、テーブル、ソファなど、物理的な環境における平らな面です。

  • メッシュは、物理世界のジオメトリを表す 3D 構造です。

  • また、現実世界とやり取りする方法として、レイキャストもあります。

  • これらによって、光線がデジタル コンテンツと交差する場所が決まります。

  • レーザーポインターのようなもので、レーザーポインターがどこを照らしているかに関する情報を取得できます。

  • また、空間内のポイントを安定させて再配置するのに役立つアンカーもあります。

  • アンカーについては、このプレゼンテーションの後半で詳しく説明します。

  • これら 4 つはすべて AR Foundation の機能と見なされます。

  • このスライドに記載されていない機能もいくつかあります。

  • 具体的には、平面とアンカーは ARTrackable クラスを拡張するため、トラッキング可能と見なされ、そのマネージャーはランタイムからの更新をトラッキングします。

  • では、平面について説明します。

  • 平面には、平らな面をリアルタイムで識別する ARPlane コンポーネントがあります。

  • クエリ可能なセマンティック ラベルがあるため、平面が床、テーブル、壁などであるかどうかを把握できます。

  • 平面には動的な境界もあります。ユーザーが現実世界を移動して、より多くのサーフェスを見ることができるようになると、平面の境界はリアルタイムで動的に拡張、統合、調整されます。

  • また、物理演算を処理するために、メッシュ コライダーを平面にアタッチすることもできます。

  • これは平面検出の例です。

  • これは、Android XR デバイスで実行されている AR Foundation サンプルのレイキャスト シーンです。

  • リアルタイムの動的な 2D ポリゴンを検出します。

  • この機能を備えたアプリを構築するには、プロジェクト設定ガイドに沿って操作するか、サンプルまたはテンプレートを使用します。

  • 次に、メッシュです。

  • メッシュは、環境の 3D メッシュを提供する AR 機能です。

  • 生成されたメッシュにメッシュ コライダーをアタッチすることで、デジタル コンテンツが現実世界のオブジェクトとインタラクションできるようになります。

  • メッシュを使用すると、オブジェクト オクルージョンを自然に表現できます。

  • そのため、仮想オブジェクトが物理オブジェクトの背後にあるように見えることがあります。

  • デジタル オブジェクトが空間内を移動できる場合は、同じジオメトリを使用して、現実世界のオブジェクトを回避するパスを見つけることができます。

  • 次に、レイ キャスティングです。

  • レイ キャスティングは、仮想的な光線を 3D シーンに投影して交差を検出する手法です。複雑な数学を気にする必要はありません。

  • 投影されたレイは、環境内の複数の 3D オブジェクトと交差する可能性があります。

  • レイキャストは、特定の種類のデジタル コンテンツのみにヒットするように構成できます。

  • たとえば、平面にヒットせず、メッシュとのみ交差するように構成できます。

パッケージと依存関係の設定

  • 次に、関連するパッケージを見てみましょう。

  • これらのパッケージはすべて連携して相互に通信し、完全な XR エクスペリエンスを実現します。

  • 前述のように、AR Foundation は平面やアンカーなどの機能を処理します。

  • OpenXR プラグインは、さまざまな XR プラットフォームでネイティブ API を処理します。そのため、デバイスで OpenXR を使用している場合は、このパッケージを使用することをおすすめします。

  • OpenXR プラグインは、必要に応じて OpenXR Spatial Entities パッケージと組み合わせることで、Spatial Entities 拡張機能にアクセスできます。これについては後ほど詳しく説明します。

  • XR ヘッドセットや有線 XR グラスなど、Android XR デバイスを特に対象とすることがわかっている場合は、Unity OpenXR Android XR パッケージが必要です。

  • また、これらはすべてプラグイン パターンに従っているため、コードへの影響は最小限に抑えられます。

  • すべての機能がすべての AR Foundation パッケージで利用できるわけではありません。

  • たとえば、この表を見ると、Unity OpenXR Spatial Entities パッケージでは現在、メッシュ生成が利用できないことがわかります。

  • XR プラグイン管理を使用すると、プロジェクトで選択されている OpenXR 機能を管理できます。

  • Unity エディタで [Edit]、[プロジェクト設定] の順に移動すると、左側のサイドバーに [XR Plug-in Management] が表示されます。

  • ここで OpenXR の機能と機能グループを確認し、ビルドに含める機能と機能グループを選択または選択解除できます。

  • たとえば、このスクリーンショットでは、Android XR OpenXR パッケージに付属するすべての機能が表示されています。

  • 必要に応じて、これらを空間エンティティと組み合わせることもできます。

  • こちらが Package Manager ウィンドウです。

  • 一般的な XR プロジェクトのセットアップで表示されるパッケージをいくつかハイライト表示しました。

  • ご覧のとおり、前述の AR Foundation があります。

  • また、OpenXR プラグインもあります。

  • Android XR パッケージがあります。

  • また、複数のプラットフォームにコンパイルするプロジェクトを作成できるため、他のプラットフォームの機能もいくつか紹介しています。

  • また、右下にはその他のパッケージもいくつかあります。

  • OpenXR デバイスで動作する Composition Layers、XR 関連のあらゆる処理に非常に役立つ Core Utils、そして入力の取得に不可欠な XR Hands と XR Interaction Toolkit です。

  • 次はプロジェクトの検証です。

  • これも XR プラグイン管理の一部です。

  • プロジェクトの検証では、重複する機能のほか、プロジェクトを実行するために修正が必要なその他の問題を検出できます。

  • このスクリーンショットでは、重複する特徴をハイライト表示しています。

  • つまり、これは基本的に、2 つの同じ機能(ここではアンカー、平面、レイキャストなど)があることを示しています。

  • ユーザーが複数のパッケージからこれらの機能の重複を含めることを選択したため、どちらか一方を選択するようユーザーに求めているだけです。

ローカル アンカーと永続性

  • そこで、ローカル アンカーと永続性について説明します。

  • アンカーを使用すると、AR セッションのトラッキング状態の変化に関係なく、デジタル コンテンツを環境内の目的の場所に配置できます。

  • AR プラットフォームは、アンカーのポーズを時間の経過とともに自動的に更新して、アンカーが一貫した位置に留まるようにします。アンカーは、アンカーから離れてから戻ったときに、コンテンツが元の位置に留まるようにするための最適な方法です。

  • ユーザーがアプリを閉じて、後で再起動し、別のセッションでアプリに戻った場合はどうなるでしょうか。

  • 永続アンカーを使用すると、AR セッションでアンカーを保存し、後続のセッションで読み込むことができます。

  • アプリのライフサイクルやデバイスの再起動の影響を受けません。

  • ここに示されているのは、AR Foundation Samples プロジェクトの永続アンカーのサンプルです。

  • サンプルシーンには、アンカーの保存や読み込みなど、そのプラットフォームでサポートされているすべての機能を含むメニューがあります。

  • これで、永続アンカーのライフサイクルについて説明しました。

  • まず、アンカーをローカルに作成します。このアンカーはまだどこにも保存されておらず、永続ストレージにも保存されていません。単なるローカル アンカーです。

  • その後、ストレージに永続化します。

  • その後、アプリを再起動して、ストレージから読み込むことができます。

  • Android XR を含む一部のプラットフォームでは、アンカーの消去や永続化解除もサポートされています。

  • Spatial Entities プロバイダは、保存されたすべてのアンカーの自動読み込みをサポートしているため、各アンカーを ID で手動でリクエストする必要はありません。

  • XR プラグイン管理の設定で、[Spatial Entities: Anchors] 機能の [Automatically Load Saved Anchors] オプションを選択できます。

  • アンカーの保存に必要なコードは数行のみです。

  • まず、Unity エディタで、XR Origin ゲーム オブジェクトに AR Anchor Manager コンポーネントを追加して、シーンの準備ができていることを確認します。

  • マネージャーは、ランタイムから追跡されたアンカーを使用してシーンを更新し続けます。

  • それでは、コードに進みましょう。

  • アンカーを追加するには、TryAddAnchorAsync を呼び出して、アンカーを配置する場所のポーズを渡します。

    async void CreateAnchorAsync(ARAnchorManager manager, Pose pose) {
        var result = await manager.TryAddAnchorAsync(pose);
        if (result.status.IsError()) {
            // handle error
            return;
        }
        var anchor = result.value;
    }
  • 次に、アンカーの削除について説明します。

    void RemoveAnchor(ARAnchorManager manager, ARAnchor anchor) {
        var result = manager.TryRemoveAnchor(anchor.trackableId);
        if (result.status.IsError()) {
            // handle error
            return;
        }
    }
    
    void DestroyAnchorComponent(ARAnchor anchor) {
        Destroy(anchor);
    }
  • 同様に、ほぼ 1 行のコードです。

  • TryRemoveAnchor を呼び出し、アンカーのトラッキング可能な ID を渡します。

  • アンカーが作成されたときに、追跡可能な ID がすでに付与されています。

  • そして TrySaveAnchorAsync です。

    async void SaveAnchorAsync(ARAnchorManager manager, ARAnchor anchor) {
        var result = await manager.TrySaveAnchorAsync(anchor);
        if (result.status.IsError()) {
            // handle error
            return;
        }
    
        // Save this value, then return it as an input parameter
        SerializableGuid guid = result.value;
    }
  • AR アンカーを入力として渡します。

  • この関数は、永続ストレージ内のアンカーを識別する SerializableGuid を返します。

  • この GUID は、後でアンカーを読み込むときに使用できます。

  • そこで、アンカーの読み込みについて説明します。

    async void LoadAnchorAsync(ARAnchorManager manager, SerializableGuid guid) {
        var result = await manager.TryLoadAnchorAsync(guid);
        if (result.status.IsError()) {
            // handle error
            return;
        }
    
        ARAnchor anchor = result.value;
    }
  • ここで呼び出す関数は TryLoadAnchorAsync で、以前にシリアル化された GUID を渡します。

  • 結果として AR アンカーが返されます。

  • そんなところでしょうか。

  • これらの関数を使用すると、ローカル アンカーと永続アンカーの両方を簡単に管理できます。

  • そのため、アンカー トラッキングは、ほとんどの AR プラットフォームでかなり負荷の高いタスクです。

  • アンカーを操作する際は、次のベスト プラクティスを考慮してください。複数のアンカーを互いに非常に近い場所に作成しないようにする、別のアンカーを作成する代わりに近くのアンカーを使用できるかどうかを検討する、使用しなくなったアンカーを削除する、アンカーの目的のポーズを計算するために別の AR 機能を有効にする場合は、アンカーが正常に作成されたらその機能を無効にできるかどうかを検討する。

  • それでは、Levana にバトンタッチします。

Unity 向け Android XR SDK

  • Jennifer さん、ありがとうございました。

  • 次に、Unity での Android XR の取り組みに対する Google の貢献について説明します。

  • そのため、ジェニファーが紹介した Unity OpenXR プラグインに加えて、Google は Android XR プラットフォームで利用可能な追加機能を備えた Unity 向け Android XR SDK を提供しています。

  • この SDK には多くの機能とパッケージが含まれていますが、この講演では、Unity パッケージ用の Android XR 拡張機能に焦点を当てます。

  • そのため、OpenXR Android XR や OpenXR Spatial Entities と同様に、Android XR Extension for Unity パッケージは実際には OpenXR プロバイダです。

  • このパッケージのアーキテクチャは次のようになります。これらは並列または独立して動作できます。

  • そのため、他のすべての OpenXR プロバイダと同様に、Google のパッケージは標準の OpenXR インターフェース上に直接構築されています。

  • ここでは、他のすべてのプロバイダとの比較として、さまざまな機能を示しています。

  • そのため、いつ、なぜ一方を他方よりも優先して選択すべきかを判断するのに役立ちます。

  • この表に示されているトラッキング可能なエンティティ マーカーと空間エンティティ マーカーについて、疑問に思われるかもしれません。

  • ここで補足ですが、以前に Android XR でアプリやエクスペリエンスを構築したことがある方のために、この機能は exten- trackable 拡張機能を通じて提供されており、Android XR でのみ動作します。

  • その後、ジェニファーが述べたように、OpenXR オープングループとして活動し、右側の Spatial Entities Extension をリリースして、一貫性のあるクロスプラットフォーム エクスペリエンスを提供しました。

  • 現在、追跡可能な拡張機能は非推奨となり、Spatial Entities フレームワークが推奨されています。

  • 後者の方がパフォーマンスがはるかに高いだけでなく、クロスプラットフォームにも対応しています。

空間エンティティを使用したビルド

  • 次に、空間エンティティについて詳しく見ていきましょう。

  • Jennifer が言及した平面、レイ キャスティング、アンカーのほかに、空間エンティティ プロバイダがサポートする機能もあります。たとえば、マーカーは標準の 2D バーコードを検出、デコードして 3D 空間で位置を追跡します。また、画像は既知の 2D グラフィックを認識して 3D 空間で追跡します。

  • そして、キーボード、ノートパソコン、マウスなどの物理的なオブジェクトを識別する境界ボックスと、3D ポーズでの境界ボックスのトラッキングです。

  • 次の例では、Google の拡張機能パッケージで提供される境界ボックス機能を構成します。

  • このビューは、すべての OpenXR 機能でほぼ同じです。

  • まず Android XR プラットフォームに切り替えてから、OpenXR プロバイダを有効にし、ビルドに使用する OpenXR 機能を選択します。たとえば、ここでは空間エンティティの境界ボックス機能が示されています。その後、プロジェクトの検証に関する問題を修正します。

  • これにより、特定の機能に必要なすべての依存関係を特定し、複数の機能が同じサブシステムに貢献することによって生じる競合を解決できます。

  • アンカー、平面、境界ボックスなど、使用する機能は 1 つだけ選択すればよい場合がほとんどです。

  • 最後に、サンプルシーンまたはテストシーンをビルドします。

  • 実行時に追跡可能なオブジェクトにアクセスするには、AR 境界ボックス マネージャー コンポーネントを追加するだけです。これは AR アンカー マネージャーと同様のコンポーネントで、AR Foundation インターフェースを使用してアクセスします。

  • また、拡張機能パッケージで提供されている空間エンティティのサンプルをテンプレートとしてインポートして、作業を開始することもできます。

  • Android XR で AR Foundation 機能を使用する場合の主な違いは次のとおりです。

  • ユーザーの周囲の環境を把握するために、通常、アプリは環境に関する機密データにアクセスする必要があります。

  • また、ユーザーを保護するため、Android XR はシステム、つまり権限の背後にあるデータアクセスを制限します。

  • カメラへのアクセスや位置情報へのアクセスなど、通常の Android 権限呼び出しについては、すでにご存じかもしれません。

  • ここでのベスト プラクティスは、右側に示すようにエディターから、または Awake イベントなどのスクリプトから、起動時にマネージャー コンポーネントを無効にすることです。

    void Awake() {
        planeManager = GetComponent<ARPlaneManager>();
        planeManager.enabled = false;
        boundingBoxManager = GetComponent<ARBoundingBoxManager>();
        boundingBoxManager.enabled = false;
        permissionUtil = GetComponent<AndroidXRPermissionUtil>();
    }
    
    void OnEnable() {
        StartCoroutine(WaitForPermissions());
    }
  • 必要な権限をすべて付与したら、スクリプトからマネージャーを有効にできます。これは、WaitForPermissions ヘルパーが行う処理と同様です。

    IEnumerator WaitForPermissions() {
        planeManager.enabled = false;
        boundingBoxManager.enabled = false;
    
        yield return new WaitUntil(() =>
            permissionUtil.AllPermissionGranted());
    
        // Enable after granted permissions and
        // restart subsystems.
        planeManager.enabled = true;
        boundingBoxManager.enabled = true;
    
        // Save permission status.
        permissionGranted = true;
    }
  • これにより、権限不足によるサブシステムの初期化エラーを回避できます。

  • これは特別な機能です。

  • ジェニファーが AR Foundation を使用したアンカーについて説明したように、Android XR でアンカー機能を拡張するために、このパッケージには共有可能なアンカーという新機能も用意されています。

  • この機能は、AR Foundation のアンカー機能が現在サポートしている機能を複製したもので、地理空間アンカーなどの追加機能も備えています。

  • ご想像のとおり、アンカーは仮想世界と物理世界をつなぐ真の架け橋です。

  • Jennifer が作成、保存、読み込みについて説明してくれましたが、これらはすべて、ユーザーのリビングルームなどのローカル空間で行われました。

  • では、その他の国はどうでしょうか。

  • また、それを互いに共有するにはどうすればよいでしょうか。

Geospatial API

  • ここで Geospatial API の出番です。

  • お気に入りの書店や広い公園の待ち合わせ場所など、現実世界の場所に基づいて地理空間アンカーを作成できます。

  • Geospatial API は、VPS(仮想ポジショニング システム)を介して正確な地理位置情報トラッカーを提供します。これは GPS に似ていますが、屋外のストリートビュー データを使用して、より正確なトラッキング結果を提供します。

  • ARCore の Geospatial 機能を以前に使用したことがある方なら、このビューに見覚えがあるかもしれません。

  • そのため、Android XR で Geospatial API にアクセスするには、まず Google Cloud Platform でホストされているバックエンドに接続する必要があります。

  • Geospatial バックエンドは、実際には Google Cloud の ARCore サービスを利用しているため、まったく同じ手順で、ARCore API アクセスを使用する Google Cloud プロジェクトを構成できます。

  • これにより、拡張機能パッケージで提供される Android XR Google Cloud 機能を使用できるようになります。

  • Geospatial API を有効にして Google Cloud プロジェクトから API キーを取得したら、この機能を有効にして、ここに表示されている OpenXR 設定メニューから認証を構成できます。または、TrySetAuthenticationAsync というスクリプト API を使用して手動で構成することもできます。

    async Task TrySetAuthentication() {
        googleCloudFeature =
            OpenXRSettings.Instance
                .GetFeature<XRGoogleCloudFeature>();
        var result = await googleCloudFeature
            .TrySetAuthenticationAsync(
                strategy, credential);
        if (result.IsSuccess()) {
            // Ready to use for geospatial tracker.
        } else if (result.IsUnsupported())  {
            // Google Cloud feature is not supported
        } else if (result.IsKeylessError()) {
            // Confirm if keyless has been set up properly.
        } else {
            // Handling other common OpenXR errors.
        }
    }
  • 現時点では、Android XR プラットフォームは API キー認証のみをサポートしていますが、今後、より多くの認証情報がサポートされる予定です。

  • 認証が完了すると、Geospatial Manager を使用して Geospatial の機能にアクセスできるようになります。

  • 統合する前に、まず Google マップで VPS の対象範囲を確認することを強くおすすめします。

  • そのため、この機能を使用する際にはネットワーク アクセスも必要になります。

  • XR Geospatial Manager は、AR Foundation が提供する AR Session コンポーネントと同様に機能します。

  • そのため、システムのサポートと Geospatial トラッカーのトラッキング状態を示す静的な状態プロパティが付属しています。

  • 次のようなコールバックで状態変更イベントを登録できます。

    void OnGeospatialStateChanged(XRGeospatialStateChangedEventArgs eventArgs) {
        bool isInitFailed = eventArgs.CurrState == XRGeospatialState.InitializationFailed;
        if (isInitFailed && XRGeospatialManager.InitializationResult.IsCloudAuthError()) {
            // Handle cloud authentication issues.
        } else if (isInitFailed) {
            // Check XRGeospatialManager.InitializationResult for other common OpenXR errors.
        } else {
            // Iterate runtime state change, and handle accordingly.
        }
    }
    
    private void OnEnable() {
        XRGeospatialManager.OnStateChanged += OnGeospatialStateChanged;
    }
  • Geospatial の状態が実行中になると、このマネージャー インスタンスを以下のすべての機能に使用できます。

  • まず、指定された GPS 位置情報で VPS が利用可能かどうかを確認するために、Unity の Location API を使用して GPS 値を取得し、VPS が利用可能かどうかを確認します。

    async Task CheckVPSAvailability(CancellationToken token) {
        var location = Input.location.lastData;
        try {
            var result = await geospatialManager.TryCheckVPSAvailabilityAsync(
                location.latitude, location.longitude);
            token.ThrowIfCancellationRequested();
            if (result.status.nativeStatusCode == (int)XrResultAndroid.GeospatialCoordinateInvalid) {
                // Invalid coordinates, try with another location.
            } else if (result.status.IsError()) {
                // Handle common OpenXR error: result.status.ToOpenXRStatus()
            }  else if (result.value == XRVPSAvailability.Available)  {
                // You may use this pose to attach geospatial anchors.
                var currentGeospatialPose = new XRGeospatialPose(location);
            }
        }
    }
  • つまり、地図で VPS のサポートを確認したくない場合は、基本的にこの方法で代用できますが、屋外に出て実際の GPS ステータスを確認する必要があります。

  • また、TryLocate を使用して、Unity のポーズと Geospatial のポーズの間でポーズを変換することもできます。

    // Geospatial Pose -> Unity Pose
    var geospatialPose =
        new XRGeospatialPose(Input.location.lastData);
    if (geospatialManager.TryLocate(
            geospatialPose, out XRPoseResult result)) {
        if (result.IsPositionValid ||
            result.IsPositionTracked) {
            transform.position = result.Pose.position;
        }
        if (result.IsRotationValid ||
            result.IsRotationTracked) {
            transform.rotation = result.Pose.rotation;
        }
    }
    // Unity Pose -> Geospatial Pose
    Pose pose = new Pose(
        transform.position, transform.rotation);
    if (geospatialManager.TryLocate(
          pose, out XRGeospatialPoseResult result)) {
        if (result.IsPositionValid ||
            result.IsRotationValid) {
            // You may use for anchor attachment.
        }
    }
  • VPS チェックまたはポーズ変換の結果を取得したら、空間アンカーを特定の地理空間ポーズに、または地形や屋上などの既知のサーフェス タイプにアタッチできます。

    async void PlaceAnchor(XRGeospatialPose pose) {
        try {
            Result<ARShareableAnchor> anchorResult;
            XRGeospatialSurfaceType typeToPlace = /* Choose your target type */;
            if (typeToPlace == XRGeospatialSurfaceType.Default) {
                anchorResult = geospatialManager.TryAttachGeospatialAnchor(pose);
            } else {
                pose.Altitude = 0;
                anchorResult = await geospatialManager.TryAttachSurfaceAnchorAsync(typeToPlace, pose);
            }
            if (anchorResult.status.IsSuccess()) {
                // ARShareableAnchorManager will track the anchor and update it.
            } else {
                // Handle OpenXR failures.
            }
        }
    }
  • これらをすべて組み合わせることで、現実世界を前面に押し出した、真に魔法のような空間体験を構築できます。

  • そこで、現実世界の目的の場所にユーザーを誘導するための矢印を配置します。

  • 違いますね。

  • Android XR SDK は、Unity デベロッパーに提供される唯一のパッケージではありません。

  • Google の Unity 向け Android XR SDK に、Android XR Interaction Framework と Android XR Audio パッケージも含まれるようになりました。

  • また、Android XR SDK を拡張して、より多くの機能サポートと空間エンティティを追加できることを嬉しく思います。

まとめとリソース

  • これで、セッションは終了です。

  • Unity のロードマップをオンラインで確認して、今後の予定を確認できます。

  • 来年初頭には、次世代の Unity である Unity 7 がリリースされる予定です。

  • それまでの間、これらのツールを試して、複合現実テンプレートを使用してプロジェクトを作成したり、GitHub で AR Foundation のサンプル プロジェクトを確認したりできます。

  • また、Spatial Entity と Unity パッケージのサポートについて詳しくは、Android デベロッパー サイトのページをご覧ください。

  • そして、皆様、ありがとうございました。

  • AR Foundation と Android XR の Spatial Entity を使用して、皆様がどのようなものを構築されるのか楽しみにしています。

コード

コード スニペット

「ローカル アンカーと永続性」の 10:34 の C# スニペット
async void CreateAnchorAsync(ARAnchorManager manager, Pose pose) {
    var result = await manager.TryAddAnchorAsync(pose);
    if (result.status.IsError()) {
        // handle error
        return;
    }
    var anchor = result.value;
}
「ローカル アンカーと永続性」の 10:43 の C# スニペット
void RemoveAnchor(ARAnchorManager manager, ARAnchor anchor) {
    var result = manager.TryRemoveAnchor(anchor.trackableId);
    if (result.status.IsError()) {
        // handle error
        return;
    }
}

void DestroyAnchorComponent(ARAnchor anchor) {
    Destroy(anchor);
}
「ローカル アンカーと永続性」の 11:00 の C# スニペット
async void SaveAnchorAsync(ARAnchorManager manager, ARAnchor anchor) {
    var result = await manager.TrySaveAnchorAsync(anchor);
    if (result.status.IsError()) {
        // handle error
        return;
    }

    // Save this value, then return it as an input parameter
    SerializableGuid guid = result.value;
}
「ローカル アンカーと永続性」の 11:17 の C# スニペット
async void LoadAnchorAsync(ARAnchorManager manager, SerializableGuid guid) {
    var result = await manager.TryLoadAnchorAsync(guid);
    if (result.status.IsError()) {
        // handle error
        return;
    }

    ARAnchor anchor = result.value;
}
「Building with spatial entities」の 16:45 の C# スニペット
void Awake() {
    planeManager = GetComponent<ARPlaneManager>();
    planeManager.enabled = false;
    boundingBoxManager = GetComponent<ARBoundingBoxManager>();
    boundingBoxManager.enabled = false;
    permissionUtil = GetComponent<AndroidXRPermissionUtil>();
}

void OnEnable() {
    StartCoroutine(WaitForPermissions());
}
「空間エンティティを使用した構築」の 16:58 の C# スニペット
IEnumerator WaitForPermissions() {
    planeManager.enabled = false;
    boundingBoxManager.enabled = false;

    yield return new WaitUntil(() =>
        permissionUtil.AllPermissionGranted());

    // Enable after granted permissions and
    // restart subsystems.
    planeManager.enabled = true;
    boundingBoxManager.enabled = true;

    // Save permission status.
    permissionGranted = true;
}
「Geospatial API」の 19:12 の C# スニペット
async Task TrySetAuthentication() {
    googleCloudFeature =
        OpenXRSettings.Instance
            .GetFeature<XRGoogleCloudFeature>();
    var result = await googleCloudFeature
        .TrySetAuthenticationAsync(
            strategy, credential);
    if (result.IsSuccess()) {
        // Ready to use for geospatial tracker.
    } else if (result.IsUnsupported())  {
        // Google Cloud feature is not supported
    } else if (result.IsKeylessError()) {
        // Confirm if keyless has been set up properly.
    } else {
        // Handling other common OpenXR errors.
    }
}
「Geospatial API」の 20:24 の C# スニペット
void OnGeospatialStateChanged(XRGeospatialStateChangedEventArgs eventArgs) {
    bool isInitFailed = eventArgs.CurrState == XRGeospatialState.InitializationFailed;
    if (isInitFailed && XRGeospatialManager.InitializationResult.IsCloudAuthError()) {
        // Handle cloud authentication issues.
    } else if (isInitFailed) {
        // Check XRGeospatialManager.InitializationResult for other common OpenXR errors.
    } else {
        // Iterate runtime state change, and handle accordingly.
    }
}

private void OnEnable() {
    XRGeospatialManager.OnStateChanged += OnGeospatialStateChanged;
}
「Geospatial API」の 20:38 の C# スニペット
async Task CheckVPSAvailability(CancellationToken token) {
    var location = Input.location.lastData;
    try {
        var result = await geospatialManager.TryCheckVPSAvailabilityAsync(
            location.latitude, location.longitude);
        token.ThrowIfCancellationRequested();
        if (result.status.nativeStatusCode == (int)XrResultAndroid.GeospatialCoordinateInvalid) {
            // Invalid coordinates, try with another location.
        } else if (result.status.IsError()) {
            // Handle common OpenXR error: result.status.ToOpenXRStatus()
        }  else if (result.value == XRVPSAvailability.Available)  {
            // You may use this pose to attach geospatial anchors.
            var currentGeospatialPose = new XRGeospatialPose(location);
        }
    }
}
「Geospatial API」の 21:03 の C# スニペット
// Geospatial Pose -> Unity Pose
var geospatialPose =
    new XRGeospatialPose(Input.location.lastData);
if (geospatialManager.TryLocate(
        geospatialPose, out XRPoseResult result)) {
    if (result.IsPositionValid ||
        result.IsPositionTracked) {
        transform.position = result.Pose.position;
    }
    if (result.IsRotationValid ||
        result.IsRotationTracked) {
        transform.rotation = result.Pose.rotation;
    }
}
「Geospatial API」の 21:03 の C# スニペット
// Unity Pose -> Geospatial Pose
Pose pose = new Pose(
    transform.position, transform.rotation);
if (geospatialManager.TryLocate(
      pose, out XRGeospatialPoseResult result)) {
    if (result.IsPositionValid ||
        result.IsRotationValid) {
        // You may use for anchor attachment.
    }
}
「Geospatial API」の 21:11 の C# スニペット
async void PlaceAnchor(XRGeospatialPose pose) {
    try {
        Result<ARShareableAnchor> anchorResult;
        XRGeospatialSurfaceType typeToPlace = /* Choose your target type */;
        if (typeToPlace == XRGeospatialSurfaceType.Default) {
            anchorResult = geospatialManager.TryAttachGeospatialAnchor(pose);
        } else {
            pose.Altitude = 0;
            anchorResult = await geospatialManager.TryAttachSurfaceAnchorAsync(typeToPlace, pose);
        }
        if (anchorResult.status.IsSuccess()) {
            // ARShareableAnchorManager will track the anchor and update it.
        } else {
            // Handle OpenXR failures.
        }
    }
}