Unityで位置情報取得 for iOS/Android

####目標
- スマートフォンのGPS機能で位置情報を取得する（緯度・経度・高度(メートル)）

####動作確認した開発環境
- OS: Mac Catalina 10.15.7
- Unity: 2020.3.10
- iOS: 14.6

####ReadMe
- 今回はUniTaskを用いてstaticクラス・メソッドの実装にしました。プロジェクトに適当に配置してタスクを呼び出すだけなので非常に扱いやすいです。

- 参照する際は例外処理は忘れずにおいてください。(try/catch)

- 初回利用時はパーミッション表示が出ます。そこで許可がもらえればそのまま返り値受け取れる挙動になっています。が、初回パーミッション付与から初回情報取得まで数秒ラグがありました。用法によっては、初めて使うよりは前に、パーミッションは許可しておいてもらえるのが良さそうです。

- UnityはLocationServiceのインターフェースを用意してくれていて簡単に位置情報が取得できるようになっていますが、これはiOSとAndroidにしか対応しておらず、Win/Macでの対応はOS側の機能などを使う必要があるようです。




####実装

```C#
 using UnityEngine;
 using Cysharp.Threading.Tasks;
 using System;
 
 /// <summary>位置情報を簡易に取得するstaticクラス。例外処理を考慮すること</summary>
 public static class GPSLocation
 {
     public class Location
     {
         public float latitude;
         public float longitude;
         public float altitude;
     }
     public static Location location { get; private set; }
     static bool isOnTask = false;
     public static async UniTask<Location> GetLocation(int tryNum = 6)
     {
         var isSuceeded = false;
         //一時的な取得失敗の可能性もあるため、何回かチャンスを与えたい。デフォルトでは6回で作りました。
         int count = 0;
         string exceptionText = "";
         while (!isSuceeded)
         {
             try
             {
                 location = await GetLocationCycle();
                 isSuceeded = true;
             }
             catch (Exception e)
             {
                 count++;
                 exceptionText += e;
                 exceptionText += ", ";
                 if (count > 6) break;
             }
         }
         if (!isSuceeded)
         {
             throw new Exception(exceptionText);
         }
         return location;
     }
     static async UniTask<Location> GetLocationCycle()
     {
         if (location == null) location = new Location();
         if (isOnTask)
         {
             throw new Exception("This Task may be going on double.");
         }
         else
         {
             isOnTask = true;
         }
 
         // First, check if user has location service enabled
         if (!Input.location.isEnabledByUser)
         {
             isOnTask = false;
             throw new Exception("GPS is not enabled");
         }
 
         // Start service before querying location
         Input.location.Start();
         var count = 0f;
         while (Input.location.status == LocationServiceStatus.Initializing)
         {
             Debug.Log(Input.location.status + ", time count = " + count);
             if (count > 20f)
             {
                 Input.location.Stop();
                 isOnTask = false;
                 throw new Exception("Time Out!");
             }
             count += Time.deltaTime;
             await UniTask.Yield();
         }
         if (Input.location.status == LocationServiceStatus.Failed)
         {
             Input.location.Stop();
             isOnTask = false;
             throw new Exception("Unable to determine device location");
         }
 
         // Debug.Log(Input.location.status);
         location.latitude = Input.location.lastData.latitude;
         location.longitude = Input.location.lastData.longitude;
         location.altitude = Input.location.lastData.altitude;
         Debug.Log(location.latitude + ", " + location.longitude + ", " + location.altitude);
         Input.location.Stop();
         isOnTask = false;
         return location;
     }
 }
```