You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

152 lines
6.4 KiB

  1. using System;
  2. using UnityEngine;
  3. namespace UnityStandardAssets.Utility
  4. {
  5. public class WaypointProgressTracker : MonoBehaviour
  6. {
  7. // This script can be used with any object that is supposed to follow a
  8. // route marked out by waypoints.
  9. // This script manages the amount to look ahead along the route,
  10. // and keeps track of progress and laps.
  11. [SerializeField] private WaypointCircuit circuit; // A reference to the waypoint-based route we should follow
  12. [SerializeField] private float lookAheadForTargetOffset = 5;
  13. // The offset ahead along the route that the we will aim for
  14. [SerializeField] private float lookAheadForTargetFactor = .1f;
  15. // A multiplier adding distance ahead along the route to aim for, based on current speed
  16. [SerializeField] private float lookAheadForSpeedOffset = 10;
  17. // The offset ahead only the route for speed adjustments (applied as the rotation of the waypoint target transform)
  18. [SerializeField] private float lookAheadForSpeedFactor = .2f;
  19. // A multiplier adding distance ahead along the route for speed adjustments
  20. [SerializeField] private ProgressStyle progressStyle = ProgressStyle.SmoothAlongRoute;
  21. // whether to update the position smoothly along the route (good for curved paths) or just when we reach each waypoint.
  22. [SerializeField] private float pointToPointThreshold = 4;
  23. // proximity to waypoint which must be reached to switch target to next waypoint : only used in PointToPoint mode.
  24. public enum ProgressStyle
  25. {
  26. SmoothAlongRoute,
  27. PointToPoint,
  28. }
  29. // these are public, readable by other objects - i.e. for an AI to know where to head!
  30. public WaypointCircuit.RoutePoint targetPoint { get; private set; }
  31. public WaypointCircuit.RoutePoint speedPoint { get; private set; }
  32. public WaypointCircuit.RoutePoint progressPoint { get; private set; }
  33. public Transform target;
  34. private float progressDistance; // The progress round the route, used in smooth mode.
  35. private int progressNum; // the current waypoint number, used in point-to-point mode.
  36. private Vector3 lastPosition; // Used to calculate current speed (since we may not have a rigidbody component)
  37. private float speed; // current speed of this object (calculated from delta since last frame)
  38. // setup script properties
  39. private void Start()
  40. {
  41. // we use a transform to represent the point to aim for, and the point which
  42. // is considered for upcoming changes-of-speed. This allows this component
  43. // to communicate this information to the AI without requiring further dependencies.
  44. // You can manually create a transform and assign it to this component *and* the AI,
  45. // then this component will update it, and the AI can read it.
  46. if (target == null)
  47. {
  48. target = new GameObject(name + " Waypoint Target").transform;
  49. }
  50. Reset();
  51. }
  52. // reset the object to sensible values
  53. public void Reset()
  54. {
  55. progressDistance = 0;
  56. progressNum = 0;
  57. if (progressStyle == ProgressStyle.PointToPoint)
  58. {
  59. target.position = circuit.Waypoints[progressNum].position;
  60. target.rotation = circuit.Waypoints[progressNum].rotation;
  61. }
  62. }
  63. private void Update()
  64. {
  65. if (progressStyle == ProgressStyle.SmoothAlongRoute)
  66. {
  67. // determine the position we should currently be aiming for
  68. // (this is different to the current progress position, it is a a certain amount ahead along the route)
  69. // we use lerp as a simple way of smoothing out the speed over time.
  70. if (Time.deltaTime > 0)
  71. {
  72. speed = Mathf.Lerp(speed, (lastPosition - transform.position).magnitude/Time.deltaTime,
  73. Time.deltaTime);
  74. }
  75. target.position =
  76. circuit.GetRoutePoint(progressDistance + lookAheadForTargetOffset + lookAheadForTargetFactor*speed)
  77. .position;
  78. target.rotation =
  79. Quaternion.LookRotation(
  80. circuit.GetRoutePoint(progressDistance + lookAheadForSpeedOffset + lookAheadForSpeedFactor*speed)
  81. .direction);
  82. // get our current progress along the route
  83. progressPoint = circuit.GetRoutePoint(progressDistance);
  84. Vector3 progressDelta = progressPoint.position - transform.position;
  85. if (Vector3.Dot(progressDelta, progressPoint.direction) < 0)
  86. {
  87. progressDistance += progressDelta.magnitude*0.5f;
  88. }
  89. lastPosition = transform.position;
  90. }
  91. else
  92. {
  93. // point to point mode. Just increase the waypoint if we're close enough:
  94. Vector3 targetDelta = target.position - transform.position;
  95. if (targetDelta.magnitude < pointToPointThreshold)
  96. {
  97. progressNum = (progressNum + 1)%circuit.Waypoints.Length;
  98. }
  99. target.position = circuit.Waypoints[progressNum].position;
  100. target.rotation = circuit.Waypoints[progressNum].rotation;
  101. // get our current progress along the route
  102. progressPoint = circuit.GetRoutePoint(progressDistance);
  103. Vector3 progressDelta = progressPoint.position - transform.position;
  104. if (Vector3.Dot(progressDelta, progressPoint.direction) < 0)
  105. {
  106. progressDistance += progressDelta.magnitude;
  107. }
  108. lastPosition = transform.position;
  109. }
  110. }
  111. private void OnDrawGizmos()
  112. {
  113. if (Application.isPlaying)
  114. {
  115. Gizmos.color = Color.green;
  116. Gizmos.DrawLine(transform.position, target.position);
  117. Gizmos.DrawWireSphere(circuit.GetRoutePosition(progressDistance), 1);
  118. Gizmos.color = Color.yellow;
  119. Gizmos.DrawLine(target.position, target.position + target.forward);
  120. }
  121. }
  122. }
  123. }