CrossRoad

XRを中心とした技術ブログ。 Check also "English" category.

Niantic SDK 3.15のDevice Mappingについて調べてみました

前回に引き続きNiantic SDKを調べています。今回はSample Projectに入っている機能の1つであるDevice Mappinについて調べたことを書きました。

検証した環境
- Unity 6000.053f1 (Mac OS)
- Niantic Lightship AR Plugin 3.15.0-2508040839
- Niantic Lightship Shared AR Client Plugin 3.15.0-2508040839
- AR Foundation 6.06 (依存関係により自動import)

検証に使用したデバイス
- Nothing Phone 3a

1. Device Mappingとは

デバイスマッピングは、現実世界のオブジェクトのスキャンと追跡を行い、現実世界と一致するようにコンテンツを配置できるシステムです。 表面的にはVPSと機能的には似ているが、デバイス・マッピング・システムはすべての処理をデバイス上で行い、ローカライズにネットワーク接続を必要としない。
その代わり、Device Mappingはスキャンされたマップをシリアライズ可能なオブジェクトとして提供し、クライアントデバイスはそれを他のデバイスに配布することができます。その後スキャンされたマップをデシリアライズして再作成できます。
NianticのVPSサーバーに依存することなく、複数のデバイスでマップを共有することができるため、VPSが有効になっていない場所でも、どこでもすぐにマルチプレイを体験することができます。 デバイスマッピングとNiantic Spatial Platformの共有ARライブラリの共有の使用例については、DeviceMappingColocalizationデモシーンを参照してください。

デバイスマッピング | Niantic Spatial Platform

機械翻訳なので少し読みづらいですが、あらかじめ撮影した風景映像からマップを作り、そのマップを使うことで相対位置ベースでARコンテンツを表示する仕組みです。
決められた開始位置から始められれば、VPSの管理サーバを使わずにPersistent (永続的な) なARコンテンツを作ることができます。

上記の文章にもあるように、マップデータを他のデバイスに配布することで他のデバイスでも位置共有が可能になるようです。ただし、マップデータがどんな形式のデータでどこに保存されているのか、そのデータをどういう記述で書けば他のデバイスで登録できるのかは見つけられませんでした。

また、仮にマップデータを他のデバイスで登録しても、この時点ではあくまであらかじめ決めた位置にコンテンツを表示することまでしか共有できません。Player Aの端末で追加/削除されたオブジェクトをPlayer Bの端末で確認するには、リアルタイムに位置を共有するサーバの仕組みが必要です。

この「リアルタイムの位置を共有するサーバ」を端末側で実行する処理は見つけられませんでした。これが「DeviceMappingColocalizationデモシーン」に相当すると思います。

しかし、「DeviceMappingColocalization」というデモは存在せず、内容から判断して「Cloud Persistence」というデモが該当しそうです。これは後述します。

まとめると以下の通りです。

  • 1つのスマートフォンのみで、ある決まった場所に対して永続的なARコンテンツを作りたいときに使える。これはVPSサーバが不要
  • Device Mapを作った後、そのマップデータを他の端末が使用して、オブジェクトの追加/削除などを同期するには「Cloud Persistence」という仕組みが使える
  • 他の端末と共有するアプリケーションを作るには、VPSサーバを経由することが必須

2. Device Mapping (On Device Persistence) の使い方

公式ドキュメントにゼロから作る手順やAPIの簡単な解説がありますが、Sample Projectの"On Device Persistence"の動作を確認しつつソースコードを読む方が理解しやすい印象です。

スマートフォンで実行すると、このように画面が遷移します。

Representative screenshots of 'On Device Persistence'

2でスキャンの処理を呼び出しているのは、Assets/Samples/OnDevicePersistence/Scripts/OnDevicePersistence.cs です。たとえばスキャン時間は5秒に設定されています。

    private void StartScanning()
    {
        _startScanning.interactable = false;
        _statusText.text = "Look Around to create map";
        _mapper._onMappingComplete += MappingComplete;
        float time = 5.0f;
        _mapper.RunMappingFor(time);
        
        _scanningAnimationPanel.SetActive(true);
    }

5秒経つとAssets/Samples/OnDevicePersistence/Scripts/Mapper.csで書かれているRunMappingからStopMappingが呼ばれてスキャンが終了します。

    private IEnumerator RunMapping(float seconds)
    {
        // Reset the ARDeviceMappingManager
        _deviceMappingManager.enabled = false;
        yield return null;
        _deviceMappingManager.enabled = true;
        yield return null;
        //start mapping
        _mappingInProgress = true;
        _deviceMappingManager.SetDeviceMap(new ARDeviceMap());
        _deviceMappingManager.StartMapping();
        
        //end mapping after a few seconds
        yield return new WaitForSeconds(seconds);
        _deviceMappingManager.StopMapping();
        _mappingInProgress = false;
    }

    //called if you hit exit while scanning is happening.
    public void StopMapping()
    {
        if (_mappingInProgress)
        {
            StopCoroutine(currentCo);
            _deviceMappingManager.DeviceMapFinalized -= OnDeviceMapFinalized;
            _deviceMappingManager.StopMapping();
            _mappingInProgress = false;
        }
    }

3でPlace Cubeボタンを押すと、現在の位置(Camera.main.transform.position) からCamera.main.transform.forward*2.0fだけ前方にCubeが配置されます。

これは、Assets/Samples/OnDevicePersistence/Scripts/OnDevicePersistence.cs の下記で定義されていました。

    private void PlaceCube()
    {
        //place a cube 2m in front of the camera.
        var pos = Camera.main.transform.position + (Camera.main.transform.forward*2.0f);
        var go = CreateAndPlaceCube(_tracker.GetAnchorRelativePosition(pos));
        var fileName = OnDevicePersistence.k_objectsFileName;
        var path = Path.Combine(Application.persistentDataPath, fileName);
      
        using (StreamWriter sw = File.AppendText(path))
        {
            sw.WriteLine(go.transform.localPosition);
        }
    }

なお、Unityでこのシーンを開くと、このように表示されます。
'On Device Persistence' scene on Unity6000.053f1

AR Session, XR OriginはUnity標準の機能、Canvasは基本的なボタンやテキストが出るため、ということで、OnDevicePersistenceオブジェクトの中に書かれている上記の処理がキーになります。

参考:

デバイスマップを作成する | Niantic Spatial Platform

デバイスマップを使用して仮想コンテンツを配置する | Niantic Spatial Platform

3. Cloud Persistence (おそらくDevice Mappingのマルチプレイ版) の使い方

Sample ProjectにはCloud Persistenceというシーンもあります。ほとんどはOnDevicePersistence (Device Mapping) と同じ内容ですが、スキャンしたmapデータをcloudに送ってroom管理する機能が追加されていました。

これらは、Assets/Samples/CloudPersistence/Scripts/DataStoreManager.cs の中で書かれています。

下記のようにAPIキーとインターネットの使用が前提であることがわかります。

    public void CreateOrJoinDataStore(string storeName)
    {
        //am i already in the room
        if (_inRoom == true)
            return;
        
        _inRoom = true;
        
        //make a new room
        _roomManager.JoinRoomByName(storeName);

        if (_roomManager.Room == null)
            Debug.Log( "Network not found, do you have internet? and is your api key set");
        
        //cache datastore & network for easy access.
        _datastore = _roomManager.Room.Datastore;
        _network = _roomManager.Room.Networking;

この使い方も基本的にはOnDevicePersistenceと一緒です。ただし、最初にRoomとなるMap情報の名前を決めます。たとえば"test"などです。

その後でCreate Mapを実施して、同じAPIキーを使っているアプリ、あるいはUnity Editorで先ほど登録したMap情報の名前 (ここでは”test") を入力してからLoad Mapを実行します。

すると、最初のMap情報が2つめの端末にもロードされる他、どちらかが何かのオブジェクトを作成あるいは削除すると、別の端末にもそれが反映されるようになります。

これは写真を撮るのが難しくて画像がないのですが、手順通りに進めたらできました。

4. おわりに

Device Mappingがあっても、複数端末とのマルチプレイARを実現するにはNianticが準備するクラウドとの同期が必要になります。一方、単体コンテンツであればVPSサーバなどのクラウド接続不要で使えるのは良いことと思います。

Niantic SDKはまだ試していない機能がいくつかあるので、引き続きみてみたいと思います。