123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- using System;
- using UnityEngine;
- using UnityEngine.Serialization;
- // ReSharper disable UnusedMember.Global
- namespace ARLocation
- {
- using Utils;
- /// <summary>
- /// This component, when attached to a GameObject, makes it traverse a
- /// path that interpolates a given set of geographical locations.
- /// </summary>
- [AddComponentMenu("AR+GPS/Move Along Path")]
- [HelpURL("https://http://docs.unity-ar-gps-location.com/guide/#movealongpath")]
- [DisallowMultipleComponent]
- public class MoveAlongPath : MonoBehaviour
- {
- [Serializable]
- public class PathSettingsData
- {
- /// <summary>
- /// The LocationPath describing the path to be traversed.
- /// </summary>
- [Tooltip("The LocationPath describing the path to be traversed.")]
- public LocationPath LocationPath;
- /// <summary>
- /// The number of points-per-segment used to calculate the spline.
- /// </summary>
- [Tooltip("The number of points-per-segment used to calculate the spline.")]
- public int SplineSampleCount = 250;
- /// <summary>
- /// If present, renders the spline in the scene using the given line renderer.
- /// </summary>
- [FormerlySerializedAs("lineRenderer")] [Tooltip("If present, renders the spline in the scene using the given line renderer.")]
- public LineRenderer LineRenderer;
- }
- [Serializable]
- public class PlaybackSettingsData
- {
- /// <summary>
- /// The speed along the path.
- /// </summary>
- [Tooltip("The speed along the path.")]
- public float Speed = 1.0f;
- /// <summary>
- /// The up direction to be used for orientation along the path.
- /// </summary>
- [Tooltip("The up direction to be used for orientation along the path.")]
- public Vector3 Up = Vector3.up;
- /// <summary>
- /// If true, play the path traversal in a loop.
- /// </summary>
- [Tooltip("If true, play the path traversal in a loop.")]
- public bool Loop = true;
- /// <summary>
- /// If true, start playing automatically.
- /// </summary>
- [Tooltip("If true, start playing automatically.")]
- public bool AutoPlay = true;
- [FormerlySerializedAs("offset")] [Tooltip("The parameters offset; marks the initial position of the object along the curve.")]
- public float Offset;
- }
- [Serializable]
- public class PlacementSettingsData
- {
- [Tooltip("The altitude mode. The altitude modes of the individual path locations are ignored, and this will be used instead.")]
- public AltitudeMode AltitudeMode = AltitudeMode.DeviceRelative;
- [Tooltip(
- "The maximum number of times this object will be affected by GPS location updates. Zero means no limits are imposed.")]
- public uint MaxNumberOfLocationUpdates = 4;
- }
- [Serializable]
- public class StateData
- {
- public uint UpdateCount;
- public Vector3[] Points;
- public int PointCount;
- public bool Playing;
- public Spline Spline;
- public Vector3 Translation;
- public float Speed;
- }
- public PathSettingsData PathSettings = new PathSettingsData();
- public PlaybackSettingsData PlaybackSettings = new PlaybackSettingsData();
- public PlacementSettingsData PlacementSettings = new PlacementSettingsData();
- public float Speed
- {
- get => state.Speed;
- set => state.Speed = value;
- }
- [Space(4.0f)]
- [Header("Debug")]
- [Tooltip("When debug mode is enabled, this component will print relevant messages to the console. Filter by 'MoveAlongPath' in the log output to see the messages.")]
- public bool DebugMode;
- [Space(4.0f)]
- private StateData state = new StateData();
- private ARLocationProvider locationProvider;
- private float u;
- private GameObject arLocationRoot;
- private Transform mainCameraTransform;
- private bool useLineRenderer;
- private bool hasInitialized;
- private GroundHeight groundHeight;
- private bool HeightRelativeToDevice => PlacementSettings.AltitudeMode == AltitudeMode.DeviceRelative;
- private bool HeightGroundRelative => PlacementSettings.AltitudeMode == AltitudeMode.GroundRelative;
- /// <summary>
- /// Change the `LocationPath` the GameObject will traverse. This will
- /// have the effect of reseting the movement to the start of the path.
- /// </summary>
- /// <param name="path"></param>
- public void SetLocationPath(LocationPath path)
- {
- PathSettings.LocationPath = path;
- state.PointCount = PathSettings.LocationPath.Locations.Length;
- state.Points = new Vector3[state.PointCount];
- u = 0;
- BuildSpline(locationProvider.CurrentLocation.ToLocation());
- }
- void Start()
- {
- if (PathSettings.LocationPath == null)
- {
- throw new NullReferenceException("[AR+GPS][MoveAlongPath]: Null Path! Please set the 'LocationPath' property!");
- }
- locationProvider = ARLocationProvider.Instance;
- mainCameraTransform = ARLocationManager.Instance.MainCamera.transform;
- arLocationRoot = ARLocationManager.Instance.gameObject; // Misc.FindAndLogError("ARLocationRoot", "[ARLocationMoveAlongPath]: ARLocationRoot GameObject not found.");
- Initialize();
- hasInitialized = true;
- }
- private void Initialize()
- {
- state.PointCount = PathSettings.LocationPath.Locations.Length;
- state.Points = new Vector3[state.PointCount];
- state.Speed = PlaybackSettings.Speed;
- Debug.Log(state.PointCount);
- Debug.Log(state.Points);
- useLineRenderer = PathSettings.LineRenderer != null;
- transform.SetParent(arLocationRoot.transform);
- state.Playing = PlaybackSettings.AutoPlay;
- u += PlaybackSettings.Offset;
- groundHeight = GetComponent<GroundHeight>();
- if (PlacementSettings.AltitudeMode == AltitudeMode.GroundRelative)
- {
- if (!groundHeight)
- {
- groundHeight = gameObject.AddComponent<GroundHeight>();
- groundHeight.Settings.DisableUpdate = true;
- }
- }
- else
- {
- if (groundHeight)
- {
- Destroy(groundHeight);
- groundHeight = null;
- }
- }
- if (!hasInitialized)
- {
- locationProvider.OnProviderRestartEvent(ProviderRestarted);
- }
- locationProvider.OnLocationUpdatedEvent(LocationUpdated);
- //if (locationProvider.IsEnabled)
- // {
- // LocationUpdated(locationProvider.CurrentLocation, locationProvider.LastLocation);
- //}
- }
- private void ProviderRestarted()
- {
- state.UpdateCount = 0;
- }
- public void Restart()
- {
- state = new StateData();
- Initialize();
- }
- /// <summary>
- /// Starts playing or resumes the playback.
- /// </summary>
- public void Play()
- {
- state.Playing = true;
- }
- /// <summary>
- /// Moves the object to the spline point corresponding
- /// to the given parameter.
- /// </summary>
- /// <param name="t">Between 0 and 1</param>
- public void GoTo(float t)
- {
- u = Mathf.Clamp(t, 0, 1);
- }
- /// <summary>
- /// Pauses the movement along the path.
- /// </summary>
- public void Pause()
- {
- state.Playing = false;
- }
- /// <summary>
- /// Stops the movement along the path.
- /// </summary>
- public void Stop()
- {
- state.Playing = false;
- u = 0;
- }
- private void BuildSpline(Location location)
- {
- for (var i = 0; i < state.PointCount; i++)
- {
- var loc = PathSettings.LocationPath.Locations[i];
- state.Points[i] = Location.GetGameObjectPositionForLocation(arLocationRoot.transform,
- mainCameraTransform, location, loc, HeightRelativeToDevice || HeightGroundRelative);
- Logger.LogFromMethod("MoveAlongPath", "BuildSpline", $"({gameObject.name}): Points[{i}] = {state.Points[i]}, geo-location = {loc}", DebugMode);
- }
- state.Spline = Misc.BuildSpline(PathSettings.LocationPath.SplineType, state.Points, PathSettings.SplineSampleCount, PathSettings.LocationPath.Alpha);
- }
- private void LocationUpdated(LocationReading location, LocationReading _)
- {
- Logger.LogFromMethod("MoveAlongPath", "LocationUpdated", $"({gameObject.name}): New device location {location}", DebugMode);
- if (PlacementSettings.MaxNumberOfLocationUpdates > 0 && state.UpdateCount > PlacementSettings.MaxNumberOfLocationUpdates)
- {
- Logger.LogFromMethod("MoveAlongPath", "LocationUpdated", $"({gameObject.name}): Max number of updates reached! returning", DebugMode);
- return;
- }
- BuildSpline(location.ToLocation());
- state.Translation = new Vector3(0, 0, 0);
- state.UpdateCount++;
- }
- private void Update()
- {
- if (!state.Playing)
- {
- return;
- }
- // If there is no location provider, or spline, do nothing
- if (state.Spline == null || !locationProvider.IsEnabled)
- {
- return;
- }
- // Get spline point at current parameter
- var s = state.Spline.Length * u;
- var data = state.Spline.GetPointAndTangentAtArcLength(s);
- var tan = arLocationRoot.transform.InverseTransformVector(data.tangent);
- transform.position = data.point;
- var groundY = 0.0f;
- if (groundHeight)
- {
- var position = transform.position;
- groundY = groundHeight.CurrentGroundY;
- position = MathUtils.SetY(position, position.y + groundY);
- transform.position = position;
- }
- // Set orientation
- transform.localRotation = Quaternion.LookRotation(tan, PlaybackSettings.Up);
- // Check if we reached the end of the spline
- u = u + (state.Speed * Time.deltaTime) / state.Spline.Length;
- if (u >= 1 && !PlaybackSettings.Loop)
- {
- u = 0;
- state.Playing = false;
- }
- else
- {
- u = u % 1.0f;
- }
- // If there is a line renderer, render the path
- if (useLineRenderer)
- {
- PathSettings.LineRenderer.useWorldSpace = true;
- var t = arLocationRoot.transform;
- state.Spline.DrawCurveWithLineRenderer(PathSettings.LineRenderer,
- p => MathUtils.SetY(p, p.y + groundY)); //t.TransformVector(p - state.Translation));
- }
- }
- private void OnDestroy()
- {
- locationProvider.OnLocationUpdatedDelegate -= LocationUpdated;
- locationProvider.OnRestartDelegate -= ProviderRestarted;
- }
- }
- }
|