Advertisement
Guest User

Untitled

a guest
Apr 2nd, 2015
1,465
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 13.40 KB | None | 0 0
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using Pathfinding;
  5.  
  6. /** Simple movement script.
  7.  * This movement script will follow the path exactly, it uses linear interpolation to move between the waypoints in the path.
  8.  * This is desirable for some types of games.
  9.  * It also works in 2D.
  10.  *
  11.  * For nicer movement, I recommend adding the Simple Smooth Modifier to the GameObject as well.
  12.  *
  13.  * \ingroup movementscripts
  14.  */
  15. [RequireComponent(typeof(Seeker))]
  16. [AddComponentMenu("Pathfinding/AI/AISimpleLerp (2D,3D generic)")]
  17. public class AISimpleLerp : MonoBehaviour
  18. {
  19.  
  20.     /** Determines how often it will search for new paths.
  21.      * If you have fast moving targets or AIs, you might want to set it to a lower value.
  22.      * The value is in seconds between path requests.
  23.      */
  24.     public float repathRate = 0.5F;
  25.    
  26.     /** Target to move towards.
  27.      * The AI will try to follow/move towards this target.
  28.      * It can be a point on the ground where the player has clicked in an RTS for example, or it can be the player object in a zombie game.
  29.      */
  30.     public Transform target;
  31.    
  32.     /** Enables or disables searching for paths.
  33.      * Setting this to false does not stop any active path requests from being calculated or stop it from continuing to follow the current path.
  34.      * \see #canMove
  35.      */
  36.     public bool canSearch = true;
  37.    
  38.     /** Enables or disables movement.
  39.       * \see #canSearch */
  40.     public bool canMove = true;
  41.    
  42.     /** Speed in world units */
  43.     public float speed = 3;
  44.  
  45.     /** If true, the AI will rotate to face the movement direction */
  46.     public bool enableRotation = true;
  47.  
  48.     /** If true, rotation will only be done along the Z axis */
  49.     public bool rotationIn2D = false;
  50.  
  51.     /** How quickly to rotate */
  52.     public float rotationSpeed = 10;
  53.  
  54.     /** If true, some interpolation will be done when a new path has been calculated.
  55.      * This is used to avoid short distance teleportation.
  56.      */
  57.     public bool interpolatePathSwitches = true;
  58.  
  59.     /** How quickly to interpolate to the new path */
  60.     public float switchPathInterpolationSpeed = 5;
  61.  
  62.     /** Cached Seeker component */
  63.     protected Seeker seeker;
  64.    
  65.     /** Cached Transform component */
  66.     protected Transform tr;
  67.    
  68.     /** Time when the last path request was sent */
  69.     protected float lastRepath = -9999;
  70.    
  71.     /** Current path which is followed */
  72.     protected ABPath path;
  73.    
  74.     /** Current index in the path which is current target */
  75.     protected int currentWaypointIndex = 0;
  76.  
  77.     /** How far the AI has moved on the current segment */
  78.     protected float lerpTime = 0;
  79.  
  80.     /** Holds if the end-of-path is reached
  81.      * \see TargetReached */
  82.     protected bool targetReached = false;
  83.    
  84.     /** Only when the previous path has been returned should be search for a new path */
  85.     protected bool canSearchAgain = true;
  86.  
  87.     /** When a new path was returned, the AI was moving along this ray.
  88.      * Used to smoothly interpolate between the previous movement and the movement along the new path.
  89.      * The speed is equal to movement direction.
  90.      */
  91.     protected Vector3 previousMovementOrigin;
  92.     protected Vector3 previousMovementDirection;
  93.     protected float previousMovementStartTime = -9999;
  94.  
  95.     /** Returns if the end-of-path has been reached
  96.      * \see targetReached */
  97.     public bool TargetReached {
  98.         get {
  99.             return targetReached;
  100.         }
  101.     }
  102.    
  103.     /** Holds if the Start function has been run.
  104.      * Used to test if coroutines should be started in OnEnable to prevent calculating paths
  105.      * in the awake stage (or rather before start on frame 0).
  106.      */
  107.     private bool startHasRun = false;
  108.    
  109.     /** Initializes reference variables.
  110.      * If you override this function you should in most cases call base.Awake () at the start of it.
  111.       * */
  112.     protected virtual void Awake () {
  113.         seeker = GetComponent<Seeker>();
  114.        
  115.         //This is a simple optimization, cache the transform component lookup
  116.         tr = transform;
  117.     }
  118.    
  119.     /** Starts searching for paths.
  120.      * If you override this function you should in most cases call base.Start () at the start of it.
  121.      * \see OnEnable
  122.      * \see RepeatTrySearchPath
  123.      */
  124.     protected virtual void Start () {
  125.         startHasRun = true;
  126.         OnEnable ();
  127.     }
  128.    
  129.     /** Run at start and when reenabled.
  130.      * Starts RepeatTrySearchPath.
  131.      *
  132.      * \see Start
  133.      */
  134.     protected virtual void OnEnable () {
  135.        
  136.         lastRepath = -9999;
  137.         canSearchAgain = true;
  138.        
  139.         if (startHasRun) {
  140.             //Make sure we receive callbacks when paths complete
  141.             seeker.pathCallback += OnPathComplete;
  142.            
  143.             StartCoroutine (RepeatTrySearchPath ());
  144.         }
  145.     }
  146.    
  147.     public void OnDisable () {
  148.         // Abort calculation of path
  149.         if (seeker != null && !seeker.IsDone()) seeker.GetCurrentPath().Error();
  150.        
  151.         // Release current path
  152.         if (path != null) path.Release (this);
  153.         path = null;
  154.        
  155.         //Make sure we receive callbacks when paths complete
  156.         seeker.pathCallback -= OnPathComplete;
  157.     }
  158.    
  159.     /** Tries to search for a path every #repathRate seconds.
  160.       * \see TrySearchPath
  161.       */
  162.     protected IEnumerator RepeatTrySearchPath () {
  163.         while (true) {
  164.             float v = TrySearchPath ();
  165.             yield return new WaitForSeconds (v);
  166.         }
  167.     }
  168.    
  169.     /** Tries to search for a path.
  170.      * Will search for a new path if there was a sufficient time since the last repath and both
  171.      * #canSearchAgain and #canSearch are true and there is a target.
  172.      *
  173.      * \returns The time to wait until calling this function again (based on #repathRate)
  174.      */
  175.     public float TrySearchPath () {
  176.         if (Time.time - lastRepath >= repathRate && canSearchAgain && canSearch && target != null) {
  177.             SearchPath ();
  178.             return repathRate;
  179.         } else {
  180.             float v = repathRate - (Time.time-lastRepath);
  181.             return v < 0 ? 0 : v;
  182.         }
  183.     }
  184.    
  185.     /** Requests a path to the target.
  186.      * Some inheriting classes will prevent the path from being requested immediately when
  187.      * this function is called, for example when the AI is currently traversing a special path segment
  188.      * in which case it is usually a bad idea to search for a new path.
  189.       */
  190.     public virtual void SearchPath () {
  191.         ForceSearchPath ();
  192.     }
  193.  
  194.     /** Requests a path to the target.
  195.      * Bypasses 'is-it-a-good-time-to-request-a-path' checks.
  196.       */
  197.     public virtual void ForceSearchPath () {
  198.         if (target == null) throw new System.InvalidOperationException ("Target is null");
  199.        
  200.         lastRepath = Time.time;
  201.         //This is where we should search to
  202.         Vector3 targetPosition = target.position;
  203.        
  204.         canSearchAgain = false;
  205.        
  206.         //Alternative way of requesting the path
  207.         //ABPath p = ABPath.Construct (GetFeetPosition(),targetPoint,null);
  208.         //seeker.StartPath (p);
  209.        
  210.         //We should search from the current position
  211.         seeker.StartPath (GetFeetPosition(), targetPosition);
  212.     }
  213.  
  214.     /** The end of the path has been reached.
  215.       * If you want custom logic for when the AI has reached it's destination
  216.       * add it here
  217.       * You can also create a new script which inherits from this one
  218.       * and override the function in that script.
  219.       */
  220.     public virtual void OnTargetReached () {
  221.  
  222.     }
  223.    
  224.     /** Called when a requested path has finished calculation.
  225.       * A path is first requested by #SearchPath, it is then calculated, probably in the same or the next frame.
  226.       * Finally it is returned to the seeker which forwards it to this function.\n
  227.       */
  228.     public virtual void OnPathComplete (Path _p) {
  229.         ABPath p = _p as ABPath;
  230.         if (p == null) throw new System.Exception ("This function only handles ABPaths, do not use special path types");
  231.        
  232.         canSearchAgain = true;
  233.        
  234.         //Claim the new path
  235.         p.Claim (this);
  236.        
  237.         // Path couldn't be calculated of some reason.
  238.         // More info in p.errorLog (debug string)
  239.         if (p.error) {
  240.             p.Release (this);
  241.             return;
  242.         }
  243.  
  244.         if (interpolatePathSwitches) {
  245.             ConfigurePathSwitchInterpolation ();
  246.         }
  247.  
  248.         //Release the previous path
  249.         if (path != null) path.Release (this);
  250.        
  251.         //Replace the old path
  252.         path = p;
  253.  
  254.         // Just for the rest of the code to work, if there is only one waypoint in the path
  255.         // add another one
  256.         if (path.vectorPath != null && path.vectorPath.Count == 1) {
  257.             path.vectorPath.Insert (0,GetFeetPosition());
  258.         }
  259.  
  260.         targetReached = false;
  261.  
  262.         //Reset some variables
  263.         ConfigureNewPath ();
  264.     }
  265.  
  266.     protected virtual void ConfigurePathSwitchInterpolation () {
  267.         if (path != null && path.vectorPath != null && path.vectorPath.Count > 1) {
  268.            
  269.             List<Vector3> vPath = path.vectorPath;
  270.            
  271.             // Make sure we stay inside valid ranges
  272.             currentWaypointIndex = Mathf.Clamp (currentWaypointIndex, 1, vPath.Count - 1);
  273.            
  274.             // Current segment vector
  275.             Vector3 segment = vPath [currentWaypointIndex] - vPath [currentWaypointIndex - 1];
  276.             float segmentLength = segment.magnitude;
  277.            
  278.             // Find the approximate length of the path that is left on the current path
  279.             float approximateLengthLeft = segmentLength * Mathf.Clamp01 (1 - lerpTime);
  280.             for (int i = currentWaypointIndex; i < vPath.Count-1; i++) {
  281.                 approximateLengthLeft += (vPath [i + 1] - vPath [i]).magnitude;
  282.             }
  283.            
  284.             previousMovementOrigin = GetFeetPosition ();
  285.             previousMovementDirection = segment.normalized * approximateLengthLeft;
  286.             previousMovementStartTime = Time.time;
  287.         } else {
  288.             previousMovementOrigin = Vector3.zero;
  289.             previousMovementDirection = Vector3.zero;
  290.             previousMovementStartTime = -9999;
  291.         }
  292.     }
  293.  
  294.     public virtual Vector3 GetFeetPosition () {
  295.         return tr.position;
  296.     }
  297.  
  298.     /** Finds the closest point on the current path.
  299.      * Sets #currentWaypointIndex and #lerpTime to the appropriate values.
  300.      */
  301.     protected virtual void ConfigureNewPath () {
  302.         var points = path.vectorPath;
  303.  
  304.         var currentPosition = GetFeetPosition ();
  305.  
  306.         float bestFactor = 0;
  307.         float bestDist = float.PositiveInfinity;
  308.         int bestIndex = 0;
  309.  
  310.         for (int i = 0; i < points.Count-1; i++) {
  311.             float factor = AstarMath.NearestPointFactor (points[i], points[i+1], currentPosition);
  312.             Vector3 point = Vector3.Lerp (points[i], points[i+1], factor);
  313.             float dist = (currentPosition - point).sqrMagnitude;
  314.  
  315.             if (dist < bestDist) {
  316.                 bestDist = dist;
  317.                 bestFactor = factor;
  318.                 bestIndex = i+1;
  319.             }
  320.         }
  321.  
  322.         currentWaypointIndex = bestIndex;
  323.         lerpTime = bestFactor;
  324.     }
  325.  
  326.     protected virtual void Update () {
  327.         if (canMove) {
  328.             Vector3 direction;
  329.             Vector3 nextPos = CalculateNextPosition (out direction);
  330.  
  331.             // Rotate unless we are really close to the target
  332.             if (enableRotation && direction != Vector3.zero) {
  333.  
  334.                 if (rotationIn2D) {
  335.  
  336.                     float angle = Mathf.Atan2(-direction.x, direction.y) * Mathf.Rad2Deg + 180;
  337.                     Vector3 euler = tr.eulerAngles;
  338.                     euler.z = Mathf.LerpAngle (euler.z, angle, Time.deltaTime * rotationSpeed);
  339.                     tr.eulerAngles = euler;
  340.                 } else {
  341.  
  342.                     Quaternion rot = tr.rotation;
  343.                     Quaternion desiredRot = Quaternion.LookRotation (direction);
  344.  
  345.                     tr.rotation = Quaternion.Slerp (rot, desiredRot, Time.deltaTime * rotationSpeed);
  346.                 }
  347.             }
  348.  
  349.             tr.position = nextPos;
  350.         }
  351.     }
  352.  
  353.     /** Calculate the AI's next position (one frame in the future).
  354.      * \param direction The direction of the segment the AI is currently traversing. Not normalized.
  355.      */
  356.     protected virtual Vector3 CalculateNextPosition ( out Vector3 direction ) {
  357.  
  358.         if (path == null || path.vectorPath == null || path.vectorPath.Count == 0) {
  359.             direction = Vector3.zero;
  360.             return Vector3.zero;
  361.         }
  362.  
  363.         List<Vector3> vPath = path.vectorPath;
  364.  
  365.         // Make sure we stay inside valid ranges
  366.         currentWaypointIndex = Mathf.Clamp (currentWaypointIndex, 1, vPath.Count - 1);
  367.  
  368.         // Current segment vector
  369.         Vector3 segment = vPath[currentWaypointIndex] - vPath[currentWaypointIndex - 1];
  370.         float segmentLength = segment.magnitude;
  371.  
  372.         if (segmentLength > 0) {
  373.             // Move forwards
  374.             // lerpTime is between 0 and 1
  375.             lerpTime += Time.deltaTime * speed / segmentLength;
  376.         } else {
  377.             // Make sure zero length segments are handled correctly
  378.             lerpTime = 1;
  379.         }
  380.  
  381.         // Pick the next segment if we have traversed the current one completely
  382.         if (lerpTime > 1 && currentWaypointIndex < vPath.Count-1) {
  383.             float overshootDistance = (lerpTime-1) * segmentLength;
  384.  
  385.             while (true) {
  386.                 currentWaypointIndex++;
  387.  
  388.                 // Next segment vector
  389.                 Vector3 nextSegment = vPath[currentWaypointIndex] - vPath[currentWaypointIndex - 1];
  390.                 float nextSegmentLength = segment.magnitude;
  391.  
  392.                 if (overshootDistance <= nextSegmentLength || currentWaypointIndex == vPath.Count-1) {
  393.                     segment = nextSegment;
  394.                     segmentLength = nextSegmentLength;
  395.                     lerpTime = Mathf.Clamp01 (overshootDistance/nextSegmentLength);
  396.                     break;
  397.                 } else {
  398.                     overshootDistance -= nextSegmentLength;
  399.                 }
  400.             }
  401.         }
  402.  
  403.         if (lerpTime >= 1 && currentWaypointIndex == vPath.Count - 1) {
  404.             if (!targetReached) {
  405.                 OnTargetReached ();
  406.             }
  407.             targetReached = true;
  408.         }
  409.  
  410.         // Find our position along the path using a simple linear interpolation
  411.         Vector3 positionAlongCurrentPath = segment * Mathf.Clamp01 (lerpTime) + vPath [currentWaypointIndex - 1];
  412.  
  413.         direction = segment;
  414.  
  415.         if (interpolatePathSwitches) {
  416.             // Find the approximate position we would be at if we
  417.             // would have continued to follow the previous path
  418.             Vector3 positionAlongPreviousPath = previousMovementOrigin + Vector3.ClampMagnitude (previousMovementDirection, speed * (Time.time - previousMovementStartTime));
  419.  
  420.             // Use this to debug
  421.             //Debug.DrawLine (previousMovementOrigin, positionAlongPreviousPath, Color.yellow);
  422.  
  423.  
  424.             return Vector3.Lerp (positionAlongPreviousPath, positionAlongCurrentPath, switchPathInterpolationSpeed * (Time.time - previousMovementStartTime));
  425.         } else {
  426.             return positionAlongCurrentPath;
  427.         }
  428.     }
  429. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement