Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use the Input.Location and mock location in the unity editor

Tags:

c#

unity3d

I am building a location based game, a bit like pokemon go. I am reading the location on my android phone without any problem, but I can't get any location data when I'm developing in the unity editor since Input.location.isEnabledByUser is false in the editor

It would be ok to mock/hard code a location, just so I can try it without deploying to my phone.

I tried to hard code it like this:

LocationInfo ReadLocation(){
    #if UNITY_EDITOR

    var location = new LocationInfo();
    location.latitude = 59.000f;
    location.longitude = 18.000f;
    location.altitude= 0.0f;
    location.horizontalAccuracy = 5.0f;
    location.verticalAccuracy = 5.0f;

    return location;
    #elif
    return Input.location.lastData;
    #endif
}

But all of the properties of the location are read only, so I'm getting compilation errors. Is there a way to enable location service in the editor, or hardcode a location?

like image 897
Emil Avatar asked Aug 08 '16 12:08

Emil


People also ask

How do I get a country in unity?

You'd need access to the device's location services (which would require a permissions request first) to pull that data. Otherwise, if you want it selected manually, you can make a list of countries with their associated country codes, and when they select the country from the list, get the matching code and use that.


2 Answers

Is there a way to enable location service in the editor, or hardcode a location?

This is one of the reasons why Unity Remote was made. Setup Unity Remote then connect your mobile device to the Editor. You can now get a real location from the Editor.

But all of the properties of the location are read only

If you really want to develop a way to mock the location, you have to abandon Unity's LocationInfo structure. Make your own custom LocationInfo and name it LocationInfoExt. Ext is = Extended.

Do the-same thing for LocationService too, then Wrap the official LocationService into your custom LocationServiceExt class. You can use LocationServiceExt to decide if you should mock location by using LocationInfoExt or not mock location by using LocationInfo internally to provide the result.

In the example below, the official LocationService, LocationInfo and LocationServiceStatus class/struct/enum are replaced with LocationServiceExt, LocationInfoExt and LocationServiceStatusExt. They also have the-same functions and properties implemented. The only difference is that you can pass true/false to the constructor of LocationServiceExt in order to use it in the Editor.

LocationServiceExt wrapper class:

Create a class called LocationServiceExt then copy the code below into it: It has every function and property from the original LocationService class.

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

public class LocationServiceExt
{
    private LocationService realLocation;

    private bool useMockLocation = false;
    private LocationInfoExt mockedLastData;
    private LocationServiceStatusExt mockedStatus;
    private bool mIsEnabledByUser = false;

    public LocationServiceExt(bool mockLocation = false)
    {
        this.useMockLocation = mockLocation;

        if (mockLocation)
        {
            mIsEnabledByUser = true;
            mockedLastData = getMockLocation();
        }
        else
        {
            realLocation = new LocationService();
        }
    }

    public bool isEnabledByUser
    {
        //realLocation.isEnabledByUser seems to be failing on Android. Input.location.isEnabledByUser is the fix
        get { return useMockLocation ? mIsEnabledByUser : Input.location.isEnabledByUser; }
        set { mIsEnabledByUser = value; }
    }


    public LocationInfoExt lastData
    {
        get { return useMockLocation ? mockedLastData : getRealLocation(); }
        set { mockedLastData = value; }
    }

    public LocationServiceStatusExt status
    {
        get { return useMockLocation ? mockedStatus : getRealStatus(); }
        set { mockedStatus = value; }
    }

    public void Start()
    {
        if (useMockLocation)
        {
            mockedStatus = LocationServiceStatusExt.Running;
        }
        else
        {
            realLocation.Start();
        }
    }

    public void Start(float desiredAccuracyInMeters)
    {
        if (useMockLocation)
        {
            mockedStatus = LocationServiceStatusExt.Running;
        }
        else
        {
            realLocation.Start(desiredAccuracyInMeters);
        }
    }

    public void Start(float desiredAccuracyInMeters, float updateDistanceInMeters)
    {
        if (useMockLocation)
        {
            mockedStatus = LocationServiceStatusExt.Running;
        }
        else
        {
            realLocation.Start(desiredAccuracyInMeters, updateDistanceInMeters);
        }
    }

    public void Stop()
    {
        if (useMockLocation)
        {
            mockedStatus = LocationServiceStatusExt.Stopped;
        }
        else
        {
            realLocation.Stop();
        }
    }

    //Predefined Location. You always override this by overriding lastData from another class 
    private LocationInfoExt getMockLocation()
    {
        LocationInfoExt location = new LocationInfoExt();
        location.latitude = 59.000f;
        location.longitude = 18.000f;
        location.altitude = 0.0f;
        location.horizontalAccuracy = 5.0f;
        location.verticalAccuracy = 5.0f;
        location.timestamp = 0f;
        return location;
    }

    private LocationInfoExt getRealLocation()
    {
        if (realLocation == null)
            return new LocationInfoExt();

        LocationInfo realLoc = realLocation.lastData;
        LocationInfoExt location = new LocationInfoExt();
        location.latitude = realLoc.latitude;
        location.longitude = realLoc.longitude;
        location.altitude = realLoc.altitude;
        location.horizontalAccuracy = realLoc.horizontalAccuracy;
        location.verticalAccuracy = realLoc.verticalAccuracy;
        location.timestamp = realLoc.timestamp;
        return location;
    }

    private LocationServiceStatusExt getRealStatus()
    {
        LocationServiceStatus realStatus = realLocation.status;
        LocationServiceStatusExt stats = LocationServiceStatusExt.Stopped;

        if (realStatus == LocationServiceStatus.Stopped)
            stats = LocationServiceStatusExt.Stopped;

        if (realStatus == LocationServiceStatus.Initializing)
            stats = LocationServiceStatusExt.Initializing;

        if (realStatus == LocationServiceStatus.Running)
            stats = LocationServiceStatusExt.Running;

        if (realStatus == LocationServiceStatus.Failed)
            stats = LocationServiceStatusExt.Failed;

        return stats;
    }
}

public struct LocationInfoExt
{
    public float altitude { get; set; }
    public float horizontalAccuracy { get; set; }
    public float latitude { get; set; }
    public float longitude { get; set; }
    public double timestamp { get; set; }
    public float verticalAccuracy { get; set; }
}

public enum LocationServiceStatusExt
{
    Stopped = 0,
    Initializing = 1,
    Running = 2,
    Failed = 3,
}

Usage:

Create a mock location

LocationServiceExt locationServiceExt = new LocationServiceExt(true);

Create a real location

LocationServiceExt locationServiceExt = new LocationServiceExt(false);

Modify the location later on

LocationInfoExt locInfo = new LocationInfoExt();
locInfo.latitude = 59.000f;
locInfo.longitude = 18.000f;
locInfo.altitude = -3.0f; //0.0f;
locInfo.horizontalAccuracy = 5.0f;
locInfo.verticalAccuracy = 5.0f;

locationServiceExt.lastData = locInfo; //Apply the location change

Full ported working example from Unity Doc.

public Text text;
IEnumerator StartGPS()
{
    text.text = "Starting";
    //Pass true to use mocked Location. Pass false or don't pass anything to use real location
    LocationServiceExt locationServiceExt = new LocationServiceExt(true);

    LocationInfoExt locInfo = new LocationInfoExt();
    locInfo.latitude = 59.000f;
    locInfo.longitude = 18.000f;
    locInfo.altitude = -3.0f; //0.0f;
    locInfo.horizontalAccuracy = 5.0f;
    locInfo.verticalAccuracy = 5.0f;
    locationServiceExt.lastData = locInfo;

    // First, check if user has location service enabled
    if (!locationServiceExt.isEnabledByUser)
    {
        text.text = "Not Enabled";
        yield break;
    }
    else
    {
        text.text = "Enabled!";
    }

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


    // Wait until service initializes
    int maxWait = 20;
    while (locationServiceExt.status == LocationServiceStatusExt.Initializing && maxWait > 0)
    {
        text.text = "Timer: " + maxWait;
        yield return new WaitForSeconds(1);
        maxWait--;
    }

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

    // Connection has failed
    if (locationServiceExt.status == LocationServiceStatusExt.Failed)
    {
        print("Unable to determine device location");
        text.text = "Unable to determine device location";
        yield break;
    }
    else
    {
        // Access granted and location value could be retrieved
        string location = "Location: " + locationServiceExt.lastData.latitude + " "
            + locationServiceExt.lastData.longitude + " " + locationServiceExt.lastData.altitude
            + " " + locationServiceExt.lastData.horizontalAccuracy + " " + locationServiceExt.lastData.timestamp;
        Debug.Log(location);
        text.text = location;
    }

    // Stop service if there is no need to query location updates continuously
    locationServiceExt.Stop();
}
like image 147
Programmer Avatar answered Oct 17 '22 03:10

Programmer


Steps to follow:

First you need to start the GPS calling:

Input.location.Start();

The "start" takes some time initializing all the needed processes so you could do this:

IEnumerator InitLocationTracker()
    {
        // Start service before querying location
        Input.location.Start();

        // Wait until service initializes

        if (true == disableGPSMode)
        {
            //delegate to call again "InitLocationTracker"
            if (null != OnRetryLocation)
            {
                OnRetryLocation ();
            }
            //variable to check the location available
            isLocationAvailable = false;

            yield return null;
        }
        // I chose 20s to retry the call
        int maxWait = 20;
        while (Input.location.status == LocationServiceStatus.Initializing && maxWait > 0)
        {
            // Service didn't initialize in 20 seconds
            if (maxWait < 1)
            {
                Debug.Log("LocationTracker: Timed out");

                //GameObject to show error text
                errorText.text = "LocationTracker: Timed out";
                errorPanel.SetActive(true);

                //delegate to call again "InitLocationTracker"
                if (null != OnRetryLocation)
                {
                    isLocationAvailable = false;
                    OnRetryLocation ();

                    yield break;
                }


            }

            yield return new WaitForSeconds(1f);
            maxWait--;
        }

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

            errorText.text = "LocationTracker: Unable to determine device location";
            errorPanel.SetActive(true);


            if (null != OnRetryLocation)
            {
                OnRetryLocation ();
            }

            isLocationAvailable = false;
        }
        else
        {
            Debug.Log("LocationTracker Location: " + current_lat + " " + current_lon + " " + Input.location.lastData.timestamp);
            isLocationAvailable = true;


            StartCoroutine(TrackLocation());
        }
    }

Then you can call and use the location data calling:

private float current_lat;
private float current_lon;

current_lat = Input.location.lastData.latitude;
current_lon = Input.location.lastData.longitude;

However I would suggest you calling a coroutine and make an infinite loop to check that you have GPS signal at any time. The pseudo code could be something like this:

 IEnumerator TrackLocation()
{
    while (true) 
    {
        //5 seconds for example
        yield return new WaitForSeconds (5f);

        if (Input.location.status == LocationServiceStatus.Running)
        {
            //Get the location data here
        }
    }
}

One last note, if you need the GPS data in certain points you could move the coroutine to the zone where you need it. Is up to you how to program the GPS check and data usage.

Resource links (From Unity documentation)

LocationInfo

LocationService

like image 23
Cabrra Avatar answered Oct 17 '22 04:10

Cabrra