【Unity】WebGLプラットフォームでGPS情報を取得する
UnityのWebGLプラットフォームプロジェクトでスマホのGPS情報を取得してみました。
大前提
下記の通り、モバイル端末前提のUnity WebGL開発は推奨できません。それでもやってみたい、という方がいればこの記事を役立ててほしいです。
Unity WebGL コンテンツは現在モバイル端末ではサポートされていないことに注意してください。一部の機器、特にハイエンドなものでは動く場合もありますが、現在の大抵の端末は性能不足で Unity WebGL を十分に動かすだけのメモリがありません。そのため、モバイルブラウザー上でコンテンツを起動しようとすると、 Unity WebGL によって警告メッセージが表示される場合があります (この機能は必要に応じて無効にすることもできます)。
課題
Unity WebGL コンテンツは現在モバイル端末ではサポートされていないため、ネイティブアプリのようにUnityEngine.Input.locationで現在地を取得することができません。
解決
UnityEngineでは取得できないため、ブラウザースクリプトで現在地を取得し、その結果をUnityに渡して解決しました。
実装
ブラウザースクリプトとの相互作用は下記に記載されています。
以下は具体的な実装例です。
環境
開発端末: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