🐸

【Unity】WebGLプラットフォームでGPS情報を取得する

2021/03/04に公開

UnityのWebGLプラットフォームプロジェクトでスマホのGPS情報を取得してみました。

大前提

下記の通り、モバイル端末前提のUnity WebGL開発は推奨できません。それでもやってみたい、という方がいればこの記事を役立ててほしいです。

https://docs.unity3d.com/ja/2018.4/Manual/webgl-browsercompatibility.html

Unity WebGL コンテンツは現在モバイル端末ではサポートされていないことに注意してください。一部の機器、特にハイエンドなものでは動く場合もありますが、現在の大抵の端末は性能不足で Unity WebGL を十分に動かすだけのメモリがありません。そのため、モバイルブラウザー上でコンテンツを起動しようとすると、 Unity WebGL によって警告メッセージが表示される場合があります (この機能は必要に応じて無効にすることもできます)。

課題

Unity WebGL コンテンツは現在モバイル端末ではサポートされていないため、ネイティブアプリのようにUnityEngine.Input.locationで現在地を取得することができません。

解決

UnityEngineでは取得できないため、ブラウザースクリプトで現在地を取得し、その結果をUnityに渡して解決しました。

実装

ブラウザースクリプトとの相互作用は下記に記載されています。
https://docs.unity3d.com/ja/2018.4/Manual/webgl-interactingwithbrowserscripting.html

以下は具体的な実装例です。

環境

開発端末:Windows10
Unity:2020.1
モバイル端末:IPhone8 iOS13
ブラウザ:safari

Unityから呼び出すJavaScript関数の宣言

Unityプロジェクト内にAssets/Plugins/sample.jslib というファイルを作成しインポートします。
Unityから呼び出す関数を宣言します。「GetCurrentPosition」という関数を定義することにしました。

mergeInto(LibraryManager.library, {
  GetCurrentPosition: function () {
  
  },
});

続いて、JavaScriptを呼び出したいC#のクラスに下記のように定義します。

public class LocationManager : MonoBehaviour
{
    [DllImport("__Internal")]
    private static extern void GetCurrentPosition();
}

現在地の取得

Assets/Plugins/sample.jslibのGetCurrentPositionに現在値を取得するコードを書きます。

mergeInto(LibraryManager.library, {
  GetCurrentPosition: function () {
    navigator.geolocation.getCurrentPosition(
      function(position) {
        // 現在値を取得したときの処理 
      });
  },
});

navigator.geolocation.getCurrentPositionは現在地を戻り値として返すわけではなく、引数に指定されたコールバック関数に渡します。
なので、さらにコールバック関数からUnityの関数を呼び出し現在地を渡す必要があります。

JavaScriptから呼び出すUnity関数の宣言

引数で現在地を受け取り、画面に表示する関数を作成します。
JavaScriptからUnityへは、1つの引数しかうけとれないため、今回は緯度と経度を「,」でつないだ文字列として渡す仕様にしました。

public class LocationManager : MonoBehaviour
{
    [SerializeField] Text LatitudeText;  // 緯度
    [SerializeField] Text LongitudeText; // 経度
    
    [DllImport("__Internal")]
    private static extern void GetCurrentPosition();
    
    /*
     * JavaScriptから呼び出す関数。
     * 緯度と経度を画面に表示する。
    */
    public void ShowLocation(String location)
    {
        string[] locations = location.Split(',');
        double latitude = double.Parse(locations[0]);
        double longitude = double.Parse(locations[1]);

        LatitudeText.text = "latitude: " + latitude;
        LongitudeText.text = "longitude: " + longitude;
    }
}

JavaScriptからUnity関数を呼び出す

上で宣言した関数をJavaScriptから呼び出します。呼び出しにはSendMessage関数を使用します。

mergeInto(LibraryManager.library, {
  GetCurrentPosition: function () {
    navigator.geolocation.getCurrentPosition(
      function(position) {
        xy = position.coords.latitude + "," + position.coords.longitude;   
	SendMessage('LocationManager', 'ShowLocation', xy); 
      }
    );
  },
});

Unity関数からJavaScriptを呼び出す

最後に任意のタイミングでGetCurrentPosition()を呼び出します。
今回は60フレームごとに現在地を取得するように実装しました。

public class LocationManager : MonoBehaviour
{
    [SerializeField] Text LatitudeText;  // 緯度
    [SerializeField] Text LongitudeText; // 経度
    
    [DllImport("__Internal")]
    private static extern void GetCurrentPosition();
    
    int frameCount = 0;
    
    void Update()
    {
        // 60フレームごとに現在地を取得する
        if (frameCount == 60)
        {
            // JavaScriptの呼び出し
            GetCurrentPosition(); 
            frameCount = 0;
        }
        frameCount++;
    }
    
    /*
     * JavaScriptから呼び出す関数。
     * 緯度と経度を画面に表示する。
    */
    public void ShowLocation(String location)
    {
        string[] locations = location.Split(',');
        double latitude = double.Parse(locations[0]);
        double longitude = double.Parse(locations[1]);

        LatitudeText.text = "latitude: " + latitude;
        LongitudeText.text = "longitude: " + longitude;
    }
}

最後に

  • 今回GPS情報を取得するためにnavigator.geolocation.getCurrentPositionを使用しましたが、他にもnavigator.geolocation.watchPositionを使用する方法もあります。
    ただ、navigator.geolocation.watchPositionは想定通りに動作させることができませんでした。原因もわからず、諦めました。
  • 「.jslib」ファイルにJavascriptを書くのはデバックしにくい。多用する場合は「.js」ファイルに切り分けたほうがよさそうです。

Discussion