using System; using System.Collections; using UnityEngine; using UnityEngine.Events; using UnityEngine.Serialization; // ReSharper disable UnusedMember.Global namespace ARLocation { using Utils; [AddComponentMenu("AR+GPS/AR Location Provider")] [HelpURL("https://http://docs.unity-ar-gps-location.com/guide/#arlocationprovider")] [DisallowMultipleComponent] public class ARLocationProvider : Singleton { [Serializable] public class LocationEnabledUnityEvent : UnityEvent {} [Serializable] public class LocationUpdatedUnityEvent : UnityEvent {} [Serializable] public class CompassUpdatedUnityEvent: UnityEvent {} [FormerlySerializedAs("LocationUpdateSettings")] [Tooltip("The options for the Location Provider.")] [Header("Update Settings")] public LocationProviderOptions LocationProviderSettings = new LocationProviderOptions(); [Tooltip("The data of mock location. If present, overrides the Mock Location above.")] [Header("Mock Data")] public LocationData MockLocationData; [Tooltip("The maximum wait time to wait for location initialization.")] [Header("Initialization")] public uint MaxWaitTime = 200; [Tooltip("Wait this many seconds before starting location services. Useful when using Unity Remote.")] public uint StartUpDelay; [Header("Debug")] [Tooltip("When debug mode is enabled, this component will print relevant messages to the console. Filter by 'ARLocationProvider' in the log output to see the messages.")] public bool DebugMode; [Header("Events")] [Tooltip("Called after the first location is read.")] public LocationEnabledUnityEvent OnEnabled = new LocationEnabledUnityEvent(); [Tooltip("Called after each new location update.")] public LocationUpdatedUnityEvent OnLocationUpdated = new LocationUpdatedUnityEvent(); [Tooltip("Called after each new raw device GPS data is obtained.")] public LocationUpdatedUnityEvent OnRawLocationUpdated = new LocationUpdatedUnityEvent(); [Tooltip("Called after each new compass update.")] public CompassUpdatedUnityEvent OnCompassUpdated = new CompassUpdatedUnityEvent(); /// /// Returns the current location provider. /// public ILocationProvider Provider { get; private set; } /// /// If true, the location provider has received the first location data. /// public bool IsEnabled => Provider.IsEnabled; /// /// If true, the location provider has started, but no location data has been read. /// public bool HasStarted => Provider.HasStarted; /// /// The number of location updates so far. /// public int LocationUpdateCount => Provider.LocationUpdateCount; /// /// If true, updates are paused. /// public bool IsPaused => Provider.Paused; /// /// The latest location data. /// public LocationReading CurrentLocation => Provider.CurrentLocation; /// /// The previous location data. /// public LocationReading LastLocation => Provider.LastLocation; /// /// The current heading data. /// public HeadingReading CurrentHeading => Provider.CurrentHeading; /// /// Time since the location provider has started. /// public float TimeSinceStart => Time.time - Provider.StartTime; /// /// The distance from the initial measured position. /// public double DistanceFromStartPoint => Provider.DistanceFromStartPoint; private int measurementCount; private bool mute; public event LocationUpdatedDelegate OnLocationUpdatedDelegate; public event CompassUpdateDelegate OnCompassUpdateDelegate; public event Action OnRestartDelegate; public override void Awake() { base.Awake(); #if UNITY_EDITOR Provider = new MockLocationProvider(); if (MockLocationData != null) { Logger.LogFromMethod("ARLocationProvider", "Awake", $"Using mock location {MockLocationData}", DebugMode); ((MockLocationProvider) Provider).mockLocation = MockLocationData.Location; } #elif ARGPS_CUSTOM_PROVIDER // If you want to use a custom location provider, add 'ARGPS_CUSTOM_PROVIDER' to the define symbols in the Player // settings, create a implementation of ILocationProvider, and instantiate it in the line below. Provider = new ARGpsCustomLocationProvider(); #else Provider = new UnityLocationProvider(); #endif Logger.LogFromMethod("ARLocationProvider", "Awake",": Using provider " + Provider.Name, DebugMode); } private void InitProviderEventListeners() { Logger.LogFromMethod("ARLocationProvider", "InitProviderEventListeners","Initializing location provider listeners.", DebugMode); Provider.LocationUpdated += Provider_LocationUpdated; Provider.CompassUpdated += Provider_CompassUpdated; Provider.LocationUpdatedRaw += ProviderOnLocationUpdatedRaw; Provider.OnEnabled(OnProviderEnabledDelegate); if (Provider.IsEnabled) { ForceLocationUpdate(); } } private void ProviderOnLocationUpdatedRaw(LocationReading currentLocation, LocationReading lastLocation) { OnRawLocationUpdated?.Invoke(currentLocation.ToLocation()); } private void OnProviderEnabledDelegate() { Logger.LogFromMethod("ARLocationProvider", "OnProviderEnabledDelegate","Provider enabled; emitting 'OnEnabled' event.", DebugMode); OnEnabled?.Invoke(CurrentLocation.ToLocation()); } IEnumerator Start() { InitProviderEventListeners(); Provider.Options = LocationProviderSettings; Logger.LogFromMethod("ARLocationProvider", "Start","Starting the location provider", DebugMode); yield return StartCoroutine(Provider.Start(MaxWaitTime, StartUpDelay)); } public void Mute() { Logger.LogFromMethod("ARLocationProvider", "Mute","Muting ARLocationProvider.", DebugMode); mute = true; } public void Unmute(bool emit = true) { Logger.LogFromMethod("ARLocationProvider", "Mute","Un-muting ARLocationProvider.", DebugMode); mute = false; if (Provider.IsEnabled && emit) ForceLocationUpdate(); } private void Provider_CompassUpdated(HeadingReading heading, HeadingReading lastReading) { if (mute) return; OnCompassUpdateDelegate?.Invoke(heading, lastReading); OnCompassUpdated?.Invoke(heading); } private void Provider_LocationUpdated(LocationReading currentLocation, LocationReading lastLocation) { if (mute) return; measurementCount++; if ((LocationProviderSettings.MaxNumberOfUpdates > 0) && (measurementCount >= LocationProviderSettings.MaxNumberOfUpdates)) { Provider.Pause(); } Logger.LogFromMethod("ARLocationProvider", "Provider_LocationUpdated",$"New location {currentLocation}.", DebugMode); OnLocationUpdatedDelegate?.Invoke(currentLocation, lastLocation); OnLocationUpdated?.Invoke(currentLocation.ToLocation()); } /// /// Force the provider to emit a location update event. This wont force a new read of location, just emit /// the last available measurement. /// public void ForceLocationUpdate() { Logger.LogFromMethod("ARLocationProvider", "ForceLocationUpdate","Emitting a forced location update", DebugMode); Provider.ForceLocationUpdate(); } void Update() { if (Provider == null || !Provider.HasStarted) { return; } Provider.Update(); } /// /// Pauses location updates /// public void Pause() { Logger.LogFromMethod("ARLocationProvider", "Pause","Pausing the location provider.", DebugMode); Provider?.Pause(); } /// /// Resumes location updates /// public void Resume() { Logger.LogFromMethod("ARLocationProvider", "Resume","Resuming the location provider.", DebugMode); Provider?.Resume(); } /// /// Resets the location provider. /// public void Restart() { Logger.LogFromMethod("ARLocationProvider", "Restart","Restarting the location provider.", DebugMode); Provider?.Restart(); OnRestartDelegate?.Invoke(); } /// /// Register a delegate to location updates. /// /// The `useRawIfEnabled` method if for situations where we want the latest data, /// like when we are adding objects at runtime. /// /// /// /// public void OnLocationUpdatedEvent(LocationUpdatedDelegate locationUpdatedDelegate, bool useRawIfEnabled = false) { if (IsEnabled) { locationUpdatedDelegate(CurrentLocation, useRawIfEnabled ? Provider.LastLocationRaw : LastLocation); } OnLocationUpdatedDelegate += locationUpdatedDelegate; } public void OnProviderRestartEvent(Action del) { OnRestartDelegate += del; } /// /// Register a delegate to compass/heading updates. /// /// public void OnCompassUpdatedEvent(CompassUpdateDelegate compassUpdateDelegate) { OnCompassUpdateDelegate += compassUpdateDelegate; } /// /// RegisterRegister delegate for when the provider enables location updates. /// /// Del. public void OnEnabledEvent(LocationEnabledDelegate del) { Provider.OnEnabled(del); } /// /// Register a delegate for when the provider fails to initialize location services. /// /// Del. public void OnFailedEvent(LocationFailedDelegate del) { Provider.OnFail(del); } } }