BioErrorLog Tech Blog

試行錯誤の記録

Unityで位置情報(緯度/経度/高度)を取得する | Android / iOSアプリ

UnityでGPS位置情報(緯度/経度/高度)を取得する方法を整理します。


はじめに

ふと、位置情報を利用したアプリを作ってみたいと思い立ちました。

今回はその第一歩目として、Unityを用いて位置情報を取得する方法を整理します。


Unityバージョン:
2019.4.13.f1 LTS

Unityで位置情報を取得する

最終的なアプリ

最終的なUnityプロジェクトのソースコードは以下に置いています。

github.com

以下のように、10秒毎にスマホ(Android / iOS)から緯度/経度/高度を取得し、画面出力するシンプルなアプリケーションです。

スマホから緯度/高度/経度を取得し、テキスト表示する

以下、要点を説明します。

位置情報を取得するスクリプト

位置情報を取得するC#スクリプトを以下に示します。
位置情報の取得には、UnityのLocationService機能を利用します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Location : MonoBehaviour
{
    public static Location Instance { set; get; }

    public float latitude;
    public float longitude;
    public float altitude;

    private void Start()
    {
        Instance = this;
        DontDestroyOnLoad(gameObject);
        StartCoroutine(StartLocationService());
    }

    private IEnumerator StartLocationService()
    {
        // First, check if user has location service enabled
        if (!Input.location.isEnabledByUser)
        {
            Debug.Log("GPS not enabled");
            yield break;
        }

        // Start service before querying location
        Input.location.Start();

        // Wait until service initializes
        int maxWait = 20;
        while (Input.location.status == LocationServiceStatus.Initializing && maxWait > 0)
        {
            yield return new WaitForSeconds(1);
            maxWait--;
        }

        // Service didn't initialize in 20 seconds
        if (maxWait <= 0)
        {
            Debug.Log("Timed out");
            yield break;
        }

        // Connection has failed
        if (Input.location.status == LocationServiceStatus.Failed)
        {
            Debug.Log("Unable to determine device location");
            yield break;
        }

        // Set locational infomations
        while (true) {
            latitude = Input.location.lastData.latitude;
            longitude = Input.location.lastData.longitude;
            altitude = Input.location.lastData.altitude;
            yield return new WaitForSeconds(10);
        }
    }
}

コードは、Unityドキュメントに記載されているサンプルコードを参考・改変したものです。

流れとしては、コルーチンの中で、

  • GPSアクセス権限の確認
  • LocationServiceの起動
  • LocationService起動失敗時の処理
  • LocationServiceから位置情報の取得

を行っています。


        // First, check if user has location service enabled
        if (!Input.location.isEnabledByUser)
        {
            Debug.Log("GPS not enabled");
            yield break;
        }

まず、Input.location.isEnabledByUserを参照し、スマホの位置情報へのアクセスが許可されているかを確認します。 アクセスが許可されていない場合は、yield break;でコルーチンを終了します。


        // Start service before querying location
        Input.location.Start();

        // Wait until service initializes
        int maxWait = 20;
        while (Input.location.status == LocationServiceStatus.Initializing && maxWait > 0)
        {
            yield return new WaitForSeconds(1);
            maxWait--;
        }

次に、Input.location.Start();でLocationServiceを起動します。 最大で20秒間の起動時間を設け、LocationServiceの起動を待機します。

起動待機の実装としては、yield return new WaitForSeconds(1);とすることで1秒後にコルーチンを再開させ、LocationServiceのステータスを監視させています。


        // Service didn't initialize in 20 seconds
        if (maxWait <= 0)
        {
            Debug.Log("Timed out");
            yield break;
        }

        // Connection has failed
        if (Input.location.status == LocationServiceStatus.Failed)
        {
            Debug.Log("Unable to determine device location");
            yield break;
        }

次は、LocationServiceの起動に失敗した時の処理を入れています。 20秒経ってもLocationServiceが起動しなかったときmaxWait <= 0、LocationServiceのステータスがFailedとなったときInput.location.status == LocationServiceStatus.Failedに、それぞれコルーチンを終了させます。


        // Set locational infomations
        while (true) {
            latitude = Input.location.lastData.latitude;
            longitude = Input.location.lastData.longitude;
            altitude = Input.location.lastData.altitude;
            yield return new WaitForSeconds(10);
        }

最後に、いよいよ緯度/経度/高度を取得します。 それぞれ、Input.location.lastData.から緯度latitude / 経度longitude / 高度altitudeを取得します。

今回は数秒毎に定期で取得したかったので、whileループの中で位置情報取得毎に10秒の待機を挟んでいます。


以上で、位置情報が取得出来ました。

この位置情報を外部から参照するには、上記スクリプトで作成したクラスLocation内で作成したInstanceを介して参照します。

例えばTextとして位置情報を表示するためには、以下のようなスクリプトを別途立てて、Location.Instanceから位置情報を参照します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UpdateLocationText : MonoBehaviour
{
    public Text location;

    private void Update()
    {
        location.text = $"緯度: {Location.Instance.latitude}\n経度: {Location.Instance.longitude}\n高度: {Location.Instance.altitude}\n\nCount: {Location.Instance.gps_count}\nMessage:\n{Location.Instance.message}";
    }
}

詳しくは、以下ソースプロジェクトを参照ください。

github.com

おわりに

今回は、Unityを用いて位置情報の取得方法の備忘録を書きました。

スマホ端末のGPS位置情報機能をこのように手軽に利用できるのは、Unityの強いところだと感じます。 (Godot Engineでは位置情報にアクセスする機能はまだ提供されていないようです)

この機能を使って、なにか位置情報アプリでも作れたらと思っています。

[関連記事]

www.bioerrorlog.work

参考

Unity - Scripting API: LocationService

Unity - Scripting API: LocationInfo

Unity - Manual: Coroutines

- YouTube