AR + GPS Location  3.0.0
All Classes Namespaces Functions Variables Properties Events Pages
ARLocationOrientation.cs
1 using System;
2 using System.Collections.Generic;
3 using UnityEngine;
4 using UnityEngine.Events;
5 // ReSharper disable UnusedMember.Global
6 
7 namespace ARLocation
8 {
9  using Utils;
10 
11 
16  [DisallowMultipleComponent]
17  [HelpURL("https://http://docs.unity-ar-gps-location.com/guide/#arlocationorientation")]
18  public class ARLocationOrientation : Singleton<ARLocationOrientation>
19  {
20  [Serializable]
21  public class OnBeforeOrientationUpdatedEvent : UnityEvent<float> {}
22 
23  [Header("Update Settings")]
24 
25  [Tooltip("The maximum number of orientation updates. The updates will be paused after this amount. Zero means there is no limit and " +
26  "the updates won't be paused automatically.")]
27  public uint MaxNumberOfUpdates = 4;
28 
29 
33  [Tooltip("Only update after measuring the heading N times, and take the average."), Range(1, 500)]
34  [Header("Averaging")]
35  public int AverageCount = 150;
36 
40  [Tooltip("If set to true, use raw heading values until measuring the first average.")]
41  public bool UseRawUntilFirstAverage = true;
42 
46  [Tooltip("The smoothing factor. Zero means disabled.")]
47  [Header("Smoothing")]
48  [Range(0.0f, 1.0f)]
49  public float MovementSmoothingFactor = 0.015f;
50 
54  [Tooltip("A custom offset to the device-calculated true north direction. When set to a value other than zero, the device's true north will be ignored, and replaced by the " +
55  "magnetic heading added to this offset.")]
56  [Header("Calibration")]
57  public float TrueNorthOffset;
58 
59 
60  [Tooltip("If true, apply a tilt-compensation algorithm on Android devices. Only disable this if you run into any issues.")]
61  public bool ApplyCompassTiltCompensationOnAndroid = true;
62 
63  [Tooltip("This is the low pass filter factor applied to the heading values to reduce jitter. A zero value disables the low-pass filter, while a value" +
64  " of 1 will make the filter block all value changes. Not applied on iOS, only on Android when the tilt-compensation is enabled.")]
65  [Range(0, 1)]
66  public double LowPassFilterFactor = 0.9;
67 
68  [Header("Events")]
69  [Tooltip("Called after the orientation has been updated.")]
70  public UnityEvent OnOrientationUpdated = new UnityEvent();
71 
72  [Tooltip("Called just before the orientation has been updated.")]
73  public OnBeforeOrientationUpdatedEvent OnBeforeOrientationUpdated = new OnBeforeOrientationUpdatedEvent();
74 
75  ARLocationProvider locationProvider;
76 
77  private int updateCounter;
78  private List<float> values = new List<float>();
79  private bool isFirstAverage = true;
80  private float targetAngle;
81  private bool isChangingOrientation;
82  private Transform mainCameraTransform;
83  private bool waitingForARTracking;
84 
88  public void Restart()
89  {
90  isFirstAverage = true;
91  updateCounter = 0;
92  values = new List<float>();
93  targetAngle = 0;
94  isChangingOrientation = false;
95 
96  targetAngle = mainCameraTransform ?
97  mainCameraTransform.rotation.eulerAngles.y : 0;
98  }
99 
100  // Use this for initialization
101  void Start()
102  {
103  // Look for the LocationProvider
104  locationProvider = ARLocationProvider.Instance;
105 
106  mainCameraTransform = ARLocationManager.Instance.MainCamera.transform;
107 
108  targetAngle = mainCameraTransform.rotation.eulerAngles.y;
109 
110  if (LowPassFilterFactor > 0)
111  {
112  locationProvider.Provider.SetCompassLowPassFactor(LowPassFilterFactor);
113  }
114 
115  locationProvider.Provider.ApplyCompassTiltCompensationOnAndroid = ApplyCompassTiltCompensationOnAndroid;
116 
117  if (ARLocationManager.Instance.WaitForARTrackingToStart)
118  {
119  waitingForARTracking = true;
120  ARLocationManager.Instance.OnARTrackingStarted(() =>
121  {
122  waitingForARTracking = false;
123  });
124  }
125 
126  // Register compass update delegate
127  locationProvider.OnCompassUpdatedEvent(OnCompassUpdatedHandler);
128  }
129 
130  private void OnCompassUpdatedHandler(HeadingReading newHeading, HeadingReading lastReading)
131  {
132  if (waitingForARTracking) return;
133 
134  if (!newHeading.isMagneticHeadingAvailable)
135  {
136  Debug.LogWarning("[AR+GPS][ARLocationOrientation]: Magnetic heading data not available.");
137  return;
138  }
139 
140  if (MaxNumberOfUpdates > 0 && updateCounter >= MaxNumberOfUpdates)
141  {
142  return;
143  }
144 
145  var trueHeading = (Mathf.Abs(TrueNorthOffset) > 0.000001f) ? newHeading.magneticHeading + TrueNorthOffset : newHeading.heading;
146 
147 
148  float currentCameraHeading = mainCameraTransform.rotation.eulerAngles.y;
149  float value = Misc.GetNormalizedDegrees(currentCameraHeading - ((float)trueHeading));
150 
151  if (Mathf.Abs(value) < 0.0000001f)
152  {
153  return;
154  }
155 
156  // If averaging is not enabled
157  if (AverageCount <= 1)
158  {
159  if (updateCounter == 0)
160  {
161  transform.localRotation = Quaternion.AngleAxis(value, Vector3.up);
162  TrySetOrientation(value, true);
163  }
164  else
165  {
166  TrySetOrientation(value);
167  }
168 
169  return;
170  }
171 
172  values.Add(value);
173 
174  if (updateCounter == 0 && values.Count == 1)
175  {
176  TrySetOrientation(value, true);
177  return;
178  }
179 
180 
181  if (isFirstAverage && UseRawUntilFirstAverage)
182  {
183  TrySetOrientation(value, true);
184  return;
185  }
186 
187  if (values.Count >= AverageCount)
188  {
189  if (isFirstAverage)
190  {
191  isFirstAverage = false;
192  }
193 
194  var average = Misc.FloatListAverage(values);
195  values.Clear();
196 
197  TrySetOrientation(average);
198  }
199  }
200 
201  private void TrySetOrientation(float angle, bool isFirstUpdate = false)
202  {
203  if (isFirstUpdate)
204  {
205  targetAngle = angle;
206 
207  OnBeforeOrientationUpdated?.Invoke(targetAngle);
208  transform.localRotation = Quaternion.AngleAxis(angle, Vector3.up);
209  OnOrientationUpdated?.Invoke();
210 
211  updateCounter++;
212  return;
213  }
214 
215  if (MaxNumberOfUpdates > 0 && updateCounter >= MaxNumberOfUpdates)
216  {
217  return;
218  }
219 
220  targetAngle = angle;
221  OnBeforeOrientationUpdated?.Invoke(targetAngle);
222  isChangingOrientation = true;
223  updateCounter++;
224  }
225 
226  private void Update()
227  {
228  if (locationProvider.Provider == null || !locationProvider.Provider.IsCompassEnabled)
229  {
230  return;
231  }
232 
233  if (Mathf.Abs(transform.rotation.eulerAngles.y - targetAngle) <= 0.001f)
234  {
235  if (isChangingOrientation)
236  {
237  isChangingOrientation = false;
238  OnOrientationUpdated?.Invoke();
239  }
240  return;
241  }
242 
243  var t = 1.0f - Mathf.Pow(MovementSmoothingFactor, Time.deltaTime);
244  var value = Mathf.LerpAngle(transform.rotation.eulerAngles.y, targetAngle, t);
245 
246  transform.localRotation = Quaternion.AngleAxis(value, Vector3.up);
247  }
248 
249  private void OnDestroy()
250  {
251  locationProvider.OnCompassUpdateDelegate -= OnCompassUpdatedHandler;
252  }
253 
254  }
255 }
ARLocation.ARLocationOrientation.AverageCount
int AverageCount
Only update after measuring the heading N times, and take the average.
Definition: ARLocationOrientation.cs:35
ARLocation.ARLocationProvider
Definition: ARLocationProvider.cs:17
ARLocation.ARLocationManager
This Component manages all positioned GameObjects, synchronizing their world position in the scene wi...
Definition: ARLocationManager.cs:30
ARLocation.ARLocationProvider.OnCompassUpdatedEvent
void OnCompassUpdatedEvent(CompassUpdateDelegate compassUpdateDelegate)
Register a delegate to compass/heading updates.
Definition: ARLocationProvider.cs:295
ARLocation.ARLocationOrientation.OnBeforeOrientationUpdatedEvent
Definition: ARLocationOrientation.cs:21
ARLocation.Utils.Singleton.Instance
static T Instance
Access singleton instance through this propriety.
Definition: Singleton.cs:18
ARLocation.ARLocationOrientation.UseRawUntilFirstAverage
bool UseRawUntilFirstAverage
If set to true, use raw heading values until measuring the first average.
Definition: ARLocationOrientation.cs:41
ARLocation.ARLocationOrientation.Restart
void Restart()
Restarts the orientation tracking.
Definition: ARLocationOrientation.cs:88
ARLocation.Utils.Singleton
Definition: Singleton.cs:8
ARLocation.ARLocationProvider.Provider
ILocationProvider Provider
Returns the current location provider.
Definition: ARLocationProvider.cs:63
ARLocation.ARLocationOrientation.TrueNorthOffset
float TrueNorthOffset
A custom offset to the device-calculated true north direction.
Definition: ARLocationOrientation.cs:57
ARLocation.ARLocationOrientation.MovementSmoothingFactor
float MovementSmoothingFactor
The smoothing factor. Zero means disabled. Values around 100 seem to give good results.
Definition: ARLocationOrientation.cs:49
ARLocation
Definition: ARLocationConfigInspector.cs:7
ARLocation.ARLocationOrientation
This component should be placed on the "ARLocationRoot" GameObject (which should be a child of the "A...
Definition: ARLocationOrientation.cs:19