本文へスキップ

ワールド・ポーズ・システム(WPS)を使ったアプリの作り方

ナイアンティック・ライトシップは、ワールド・ポーズ・システム(WPS)を提供し、標準的なGPSよりも高い精度と安定性で、位置やコンパスの方位方角を取得することができます。 このチュートリアルでは、UnityプロジェクトにWPSの基本機能を追加し、近くの場所にローカライズするように設定する方法を学びます。

前提条件

ARDKがインストールされているUnityプロジェクト、基本的なARシーン、そしてLightship Occlusionが必要です。 詳しくは、ARDK 3のインストールARシーンの設定実世界オクルージョンの設定方法を参照してください。

また、プレイバック用のスキャンを作成する必要があります。 詳しくは、プレイバック用データセットの作成方法を参照してください。

備考

このハウツーでオクルージョンを設定する場合は、オクルージョンの設定のステップ2(キューブを追加するセクション)をスキップして、オクルージョン抑制の設定の手順に従って、WPSオクルージョンのテスト時に空と地面を除外してください。

UnityプロジェクトにWPSを追加する

UnityプロジェクトにWPSを追加するには、次の手順を行います。

  1. HierarchyXROrigin を選択し、InspectorAdd Component をクリックし、 ARWorldPositioningObjectHelper を追加します。 これにより、 ARWorldPositioningManager コンポーネントも作成されます。
  2. HierarchyXROriginCamera Offset を展開し、Main Camera を表示して選択します。 InspectorCameraClipping Planes を見つけ、 Far の値を1000に設定します。
  3. ARWorldPositioningObjectHelper コンポーネント で、 Altitude ModeCamera-relative with smart averaging に設定します。
  4. Assets フォルダで右クリックし、 Create にマウスを合わせて C#スクリプト を選択します。 新しいスクリプトに AddWPSObjects という名前をつけます。
  5. Hierarchy に戻り、右クリックして Create Empty を選択し、新しい GameObject を追加します。 WPSObjects という名前を付けます。 WPSObjects を選択し、 InspectorAdd Component をクリックし、 AddWPSObjects をスクリプトコンポーネントとして追加します。
WPS components in Unity with proper configuration

ロケーションを取得する

プロジェクトでWPSを使用する前に、プレイバックスキャンを作成した場所の緯度と経度を把握する必要があります。 ロケーションの座標を取得するには、以下の手順を行います。

  1. Googleマップを開き、再生スキャンをキャプチャした場所を探します。
  2. マップをクリックしてマーカーを追加します。
  3. マーカーを右クリックし、メニューの上部に表示される座標を選択してクリップボードにコピーします。

WPSスクリプトを作成する

スクリプトでWPSを基本的に使用するには、通常通り初期化し、 ARWorldPositioningObjectHelper から AddOrUpdateObject を呼び出して、WPSデータを使ってオブジェクトを更新します。 以下の例の AddWPSObjects.cs では、キューブを作成し、 ARWorldPositioningObjectHelper を使用してシーン内に動的に配置します。

備考

この方法を試す際には、スクリプト内の緯度と経度を、自分の近くのロケーション(リアルタイムテストの場合)やプレイバックスキャンの場所(リモートテストの場合)に設定してください。

クリックしてAddWPSObjects.csを表示
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Niantic.Lightship.AR.WorldPositioning;

public class AddWPSObjects :MonoBehaviour
{
[SerializeField] ARWorldPositioningObjectHelper positioningHelper;

// Start は最初のフレーム更新の前に呼び出される
void Start()
{
// ここの座標を自分の位置で置き換える
double latitude = 37.79534850764306;
double longitude = -122.39243231803636;
double altitude = 0.0; // カメラ相対位置決めを使用しているので、キューブをカメラと同じ高さに表示する

// キューブをインスタンス化し、可視性のためにスケールアップして(必要ならさらに大きくして)、その位置を更新する
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.transform.localScale *= 2.0f;
positioningHelper.AddOrUpdateObject(cube, latitude, longitude, altitude, Quaternion.identity);
} // 1フレームにつき1回更新が呼び出される。

// Update はフレームごとに 1 回呼び出される
void Update()
{

}
}.

スクリプトの内容を追加したら、アプリケーションをビルドして実行し、テストしてください。 配置した場所にキューブが表示されます。

WPSとGPSを比較する

WPSとGPSの違いを示すために、GPSを使用して2つ目のキューブを作成し、両方を同時に表示するサンプルスクリプトも用意しました。 実際にお試しの上、その違いを確かめてください。

クリックして比較スクリプトを表示
using UnityEngine;
using Niantic.Lightship.AR.WorldPositioning;
using System;

public class AddWPSObjects :MonoBehaviour
{
[SerializeField] ARWorldPositioningObjectHelper positioningHelper;
[SerializeField] Camera trackingCamera;

// ここの座標を自分の位置で置き換える
double latitude = 37.795328;
double longitude = -122.392394;
double altitude = 0.0; // カメラ相対位置決めを使用しているので、立方体をカメラと同じ高さに表示する

// Start は最初のフレーム更新の前に呼び出される
void Start()
{
// 立方体をインスタンス化し、可視性のために拡大して(必要ならさらに大きくして)、位置を更新する
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.transform.localScale *= 2.0f;
positioningHelper.AddOrUpdateObject(cube, latitude, longitude, altitude, Quaternion.identity);
} // 2つ目のキューブを作成して移動する。

//
private GameObject gpsCube = null;
void Update()
{
// まだキューブがない場合は、2つ目のキューブを作成する:
if(gpsCube == null)
{
gpsCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
gpsCube.GetComponent<Renderer>().material.color = Color.red;
}

if (Input.location.isEnabledByUser)
{
double deviceLatitude = Input.location.lastData.latitude;
double deviceLongitude = Input.location.lastData.longitude;

Vector2 eastNorthOffsetMetres = EastNorthOffset(latitude,longitude, deviceLatitude, deviceLongitude);
Vector3 trackingOffsetMetres = Quaternion.Euler(0, 0, Input.compass.trueHeading)*new Vector3(eastNorthOffsetMetres[0], (float)altitude, eastNorthOffsetMetres[1]);
Vector3 trackingMetres = trackingCamera.transform.localPosition + trackingOffsetMetres;
gpsCube.transform.localPosition = trackingMetres;
}.
}

public Vector2 EastNorthOffset(double latitudeDegreesA, double longitudeDegreesA, double latitudeDegreesB, double longitudeDegreesB)
{
double DEGREES_TO_METRES = 111139.0;
float lonDifferenceMetres = (float)(Math.Cos((latitudeDegreesA+latitudeDegreesB)*0.5* Math.PI / 180.0) * (longitudeDegreesA - longitudeDegreesB) * DEGREES_TO_METRES);
float latDifferenceMetres = (float)((latitudeDegreesA - latitudeDegreesB) * DEGREES_TO_METRES);
return new Vector2(lonDifferenceMetres,latDifferenceMetres);
}.
}

結果例

Occluding a cube with a statue of Gandhi using WPS