Guest User

Untitled

a guest
Feb 19th, 2016
213
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 17.55 KB | None | 0 0
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using Pathfinding;
  5. using Pathfinding.RVO;
  6. using plyBloxKit;
  7.  
  8. /** AI for following paths.
  9.  * This AI is the default movement script which comes with the A* Pathfinding Project.
  10.  * It is in no way required by the rest of the system, so feel free to write your own. But I hope this script will make it easier
  11.  * to set up movement for the characters in your game. This script is not written for high performance, so I do not recommend using it for large groups of units.
  12.  * \n
  13.  * \n
  14.  * This script will try to follow a target transform, in regular intervals, the path to that target will be recalculated.
  15.  * It will on FixedUpdate try to move towards the next point in the path.
  16.  * However it will only move in the forward direction, but it will rotate around it's Y-axis
  17.  * to make it reach the target.
  18.  *
  19.  * \section variables Quick overview of the variables
  20.  * In the inspector in Unity, you will see a bunch of variables. You can view detailed information further down, but here's a quick overview.\n
  21.  * The #repathRate determines how often it will search for new paths, if you have fast moving targets, you might want to set it to a lower value.\n
  22.  * The #target variable is where the AI will try to move, it can be a point on the ground where the player has clicked in an RTS for example.
  23.  * Or it can be the player object in a zombie game.\n
  24.  * The speed is self-explanatory, so is turningSpeed, however #slowdownDistance might require some explanation.
  25.  * It is the approximate distance from the target where the AI will start to slow down. Note that this doesn't only affect the end point of the path
  26.  * but also any intermediate points, so be sure to set #forwardLook and #pickNextWaypointDist to a higher value than this.\n
  27.  * #pickNextWaypointDist is simply determines within what range it will switch to target the next waypoint in the path.\n
  28.  * #forwardLook will try to calculate an interpolated target point on the current segment in the path so that it has a distance of #forwardLook from the AI\n
  29.  * Below is an image illustrating several variables as well as some internal ones, but which are relevant for understanding how it works.
  30.  * Note that the #forwardLook range will not match up exactly with the target point practically, even though that's the goal.
  31.  * \shadowimage{aipath_variables.png}
  32.  * This script has many movement fallbacks.
  33.  * If it finds a NavmeshController, it will use that, otherwise it will look for a character controller, then for a rigidbody and if it hasn't been able to find any
  34.  * it will use Transform.Translate which is guaranteed to always work.
  35.  */
  36. [RequireComponent(typeof(Seeker))]
  37. [AddComponentMenu("Pathfinding/AI/AIPath (generic)")]
  38. public class AIPath : MonoBehaviour {
  39.  
  40.     public bool Path;
  41.     public GameObject Ampel_Green;
  42.     public GameObject Ampel_Red;
  43.     public GameObject Attention;
  44.  
  45.     /** Determines how often it will search for new paths.
  46.      * If you have fast moving targets or AIs, you might want to set it to a lower value.
  47.      * The value is in seconds between path requests.
  48.      */
  49.     public float repathRate = 0.5F;
  50.  
  51.     /** Target to move towards.
  52.      * The AI will try to follow/move towards this target.
  53.      * 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.
  54.      */
  55.     public Transform target;
  56.  
  57.     /** Enables or disables searching for paths.
  58.      * Setting this to false does not stop any active path requests from being calculated or stop it from continuing to follow the current path.
  59.      * \see #canMove
  60.      */
  61.     public bool canSearch = true;
  62.  
  63.     /** Enables or disables movement.
  64.       * \see #canSearch */
  65.     public bool canMove = true;
  66.  
  67.     /** Maximum velocity.
  68.      * This is the maximum speed in world units per second.
  69.      */
  70.     public float speed = 3;
  71.  
  72.     /** Rotation speed.
  73.      * Rotation is calculated using Quaternion.SLerp. This variable represents the damping, the higher, the faster it will be able to rotate.
  74.      */
  75.     public float turningSpeed = 5;
  76.  
  77.     /** Distance from the target point where the AI will start to slow down.
  78.      * Note that this doesn't only affect the end point of the path
  79.      * but also any intermediate points, so be sure to set #forwardLook and #pickNextWaypointDist to a higher value than this
  80.      */
  81.     public float slowdownDistance = 0.6F;
  82.  
  83.     /** Determines within what range it will switch to target the next waypoint in the path */
  84.     public float pickNextWaypointDist = 2;
  85.  
  86.     /** Target point is Interpolated on the current segment in the path so that it has a distance of #forwardLook from the AI.
  87.       * See the detailed description of AIPath for an illustrative image */
  88.     public float forwardLook = 1;
  89.  
  90.     /** Distance to the end point to consider the end of path to be reached.
  91.      * When this has been reached, the AI will not move anymore until the target changes and OnTargetReached will be called.
  92.      */
  93.     public float endReachedDistance = 0.2F;
  94.  
  95.     /** Do a closest point on path check when receiving path callback.
  96.      * Usually the AI has moved a bit between requesting the path, and getting it back, and there is usually a small gap between the AI
  97.      * and the closest node.
  98.      * If this option is enabled, it will simulate, when the path callback is received, movement between the closest node and the current
  99.      * AI position. This helps to reduce the moments when the AI just get a new path back, and thinks it ought to move backwards to the start of the new path
  100.      * even though it really should just proceed forward.
  101.      */
  102.     public bool closestOnPathCheck = true;
  103.  
  104.     protected float minMoveScale = 0.05F;
  105.  
  106.     /** Cached Seeker component */
  107.     protected Seeker seeker;
  108.  
  109.     /** Cached Transform component */
  110.     protected Transform tr;
  111.  
  112.     /** Time when the last path request was sent */
  113.     protected float lastRepath = -9999;
  114.  
  115.     /** Current path which is followed */
  116.     protected Path path;
  117.  
  118.     /** Cached CharacterController component */
  119.     protected CharacterController controller;
  120.  
  121.     /** Cached NavmeshController component */
  122.     protected NavmeshController navController;
  123.  
  124.     protected RVOController rvoController;
  125.  
  126.     /** Cached Rigidbody component */
  127.     protected Rigidbody rigid;
  128.  
  129.     /** Current index in the path which is current target */
  130.     protected int currentWaypointIndex = 0;
  131.  
  132.     /** Holds if the end-of-path is reached
  133.      * \see TargetReached */
  134.     protected bool targetReached = false;
  135.  
  136.     /** Only when the previous path has been returned should be search for a new path */
  137.     protected bool canSearchAgain = true;
  138.  
  139.     protected Vector3 lastFoundWaypointPosition;
  140.     protected float lastFoundWaypointTime = -9999;
  141.  
  142.     /** Returns if the end-of-path has been reached
  143.      * \see targetReached */
  144.     public bool TargetReached {
  145.         get {
  146.             return targetReached;
  147.         }
  148.     }
  149.  
  150.     /** Holds if the Start function has been run.
  151.      * Used to test if coroutines should be started in OnEnable to prevent calculating paths
  152.      * in the awake stage (or rather before start on frame 0).
  153.      */
  154.     private bool startHasRun = false;
  155.  
  156.     /** Initializes reference variables.
  157.      * If you override this function you should in most cases call base.Awake () at the start of it.
  158.       * */
  159.     protected virtual void Awake () {
  160.         seeker = GetComponent<Seeker>();
  161.  
  162.         //This is a simple optimization, cache the transform component lookup
  163.         tr = transform;
  164.  
  165.         //Cache some other components (not all are necessarily there)
  166.         controller = GetComponent<CharacterController>();
  167.         navController = GetComponent<NavmeshController>();
  168.         rvoController = GetComponent<RVOController>();
  169.         if ( rvoController != null ) rvoController.enableRotation = false;
  170.         rigid = GetComponent<Rigidbody>();
  171.     }
  172.  
  173.     /** Starts searching for paths.
  174.      * If you override this function you should in most cases call base.Start () at the start of it.
  175.      * \see OnEnable
  176.      * \see RepeatTrySearchPath
  177.      */
  178.     protected virtual void Start () {
  179.         startHasRun = true;
  180.         OnEnable ();
  181.     }
  182.  
  183.     /** Run at start and when reenabled.
  184.      * Starts RepeatTrySearchPath.
  185.      *
  186.      * \see Start
  187.      */
  188.     protected virtual void OnEnable () {
  189.  
  190.         lastRepath = -9999;
  191.         canSearchAgain = true;
  192.  
  193.         lastFoundWaypointPosition = GetFeetPosition ();
  194.  
  195.         if (startHasRun) {
  196.             //Make sure we receive callbacks when paths complete
  197.             seeker.pathCallback += OnPathComplete;
  198.  
  199.             StartCoroutine (RepeatTrySearchPath ());
  200.         }
  201.     }
  202.  
  203.     public void OnDisable () {
  204.         // Abort calculation of path
  205.         if (seeker != null && !seeker.IsDone()) seeker.GetCurrentPath().Error();
  206.  
  207.         // Release current path
  208.         if (path != null) path.Release (this);
  209.         path = null;
  210.  
  211.         //Make sure we receive callbacks when paths complete
  212.         seeker.pathCallback -= OnPathComplete;
  213.     }
  214.  
  215.     /** Tries to search for a path every #repathRate seconds.
  216.       * \see TrySearchPath
  217.       */
  218.     protected IEnumerator RepeatTrySearchPath () {
  219.         while (true) {
  220.             float v = TrySearchPath ();
  221.             yield return new WaitForSeconds (v);
  222.         }
  223.     }
  224.  
  225.     /** Tries to search for a path.
  226.      * Will search for a new path if there was a sufficient time since the last repath and both
  227.      * #canSearchAgain and #canSearch are true and there is a target.
  228.      *
  229.      * \returns The time to wait until calling this function again (based on #repathRate)
  230.      */
  231.     public float TrySearchPath () {
  232.         if (Time.time - lastRepath >= repathRate && canSearchAgain && canSearch && target != null) {
  233.             SearchPath ();
  234.             return repathRate;
  235.         } else {
  236.             //StartCoroutine (WaitForRepath ());
  237.             float v = repathRate - (Time.time-lastRepath);
  238.             return v < 0 ? 0 : v;
  239.         }
  240.     }
  241.  
  242.     /** Requests a path to the target */
  243.     public virtual void SearchPath () {
  244.  
  245.         if (target == null) throw new System.InvalidOperationException ("Target is null");
  246.  
  247.         lastRepath = Time.time;
  248.         //This is where we should search to
  249.         Vector3 targetPosition = target.position;
  250.  
  251.         canSearchAgain = false;
  252.  
  253.         //Alternative way of requesting the path
  254.         //ABPath p = ABPath.Construct (GetFeetPosition(),targetPoint,null);
  255.         //seeker.StartPath (p);
  256.  
  257.         //We should search from the current position
  258.         seeker.StartPath (GetFeetPosition(), targetPosition);
  259.     }
  260.  
  261.     public virtual void OnTargetReached () {
  262.         //End of path has been reached
  263.         //If you want custom logic for when the AI has reached it's destination
  264.         //add it here
  265.         //You can also create a new script which inherits from this one
  266.         //and override the function in that script
  267.     }
  268.  
  269.     /** Called when a requested path has finished calculation.
  270.       * A path is first requested by #SearchPath, it is then calculated, probably in the same or the next frame.
  271.       * Finally it is returned to the seeker which forwards it to this function.\n
  272.       */
  273.     public virtual void OnPathComplete (Path _p) {
  274.        
  275.         if (_p.error) {
  276.             Path =false;
  277.             //Ampel_Red.SetActive(true);
  278.             //Ampel_Green.SetActive(false);
  279.  
  280.             plyBloxGlobal.Instance.SetVarValue("Puzzle", true);
  281.             Attention.SetActive(false);
  282.  
  283.             Debug.Log("PathInvalid");
  284.            
  285.         } else {
  286.             Path =true;
  287.             //Ampel_Green.SetActive(true);
  288.             //Ampel_Red.SetActive(false);
  289.  
  290.             Attention.SetActive(true);
  291.             plyBloxGlobal.Instance.SetVarValue("Puzzle", false);
  292.  
  293.  
  294.             Debug.Log("PathComplete");
  295.  
  296.         }
  297.  
  298.         ABPath p = _p as ABPath;
  299.         if (p == null) throw new System.Exception ("This function only handles ABPaths, do not use special path types");
  300.  
  301.         canSearchAgain = true;
  302.  
  303.         //Claim the new path
  304.         p.Claim (this);
  305.  
  306.         // Path couldn't be calculated of some reason.
  307.         // More info in p.errorLog (debug string)
  308.         if (p.error) {
  309.             p.Release (this);
  310.             return;
  311.         }
  312.  
  313.         //Release the previous path
  314.         if (path != null) path.Release (this);
  315.  
  316.         //Replace the old path
  317.         path = p;
  318.  
  319.         //Reset some variables
  320.         currentWaypointIndex = 0;
  321.         targetReached = false;
  322.  
  323.         //The next row can be used to find out if the path could be found or not
  324.         //If it couldn't (error == true), then a message has probably been logged to the console
  325.         //however it can also be got using p.errorLog
  326.         //if (p.error)
  327.  
  328.         if (closestOnPathCheck) {
  329.             Vector3 p1 = Time.time - lastFoundWaypointTime < 0.3f ? lastFoundWaypointPosition : p.originalStartPoint;
  330.             Vector3 p2 = GetFeetPosition ();
  331.             Vector3 dir = p2-p1;
  332.             float magn = dir.magnitude;
  333.             dir /= magn;
  334.             int steps = (int)(magn/pickNextWaypointDist);
  335.  
  336. #if ASTARDEBUG
  337.             Debug.DrawLine (p1,p2,Color.red,1);
  338. #endif
  339.  
  340.             for (int i=0;i<=steps;i++) {
  341.                 CalculateVelocity (p1);
  342.                 p1 += dir;
  343.             }
  344.  
  345.         }
  346.     }
  347.  
  348.     public virtual Vector3 GetFeetPosition () {
  349.         if (rvoController != null) {
  350.             return tr.position - Vector3.up*rvoController.height*0.5f;
  351.         } else
  352.         if (controller != null) {
  353.             return tr.position - Vector3.up*controller.height*0.5F;
  354.         }
  355.  
  356.         return tr.position;
  357.     }
  358.  
  359.     public virtual void Update () {
  360.  
  361.         if (!canMove) { return; }
  362.  
  363.         Vector3 dir = CalculateVelocity (GetFeetPosition());
  364.  
  365.         //Rotate towards targetDirection (filled in by CalculateVelocity)
  366.         RotateTowards (targetDirection);
  367.  
  368.         if (rvoController != null) {
  369.             rvoController.Move (dir);
  370.         } else
  371.         if (navController != null) {
  372. #if FALSE
  373.             navController.SimpleMove (GetFeetPosition(),dir);
  374. #endif
  375.         } else if (controller != null) {
  376.             controller.SimpleMove (dir);
  377.         } else if (rigid != null) {
  378.             rigid.AddForce (dir);
  379.         } else {
  380.             tr.Translate (dir*Time.deltaTime, Space.World);
  381.         }
  382.     }
  383.  
  384.     /** Point to where the AI is heading.
  385.       * Filled in by #CalculateVelocity */
  386.     protected Vector3 targetPoint;
  387.     /** Relative direction to where the AI is heading.
  388.      * Filled in by #CalculateVelocity */
  389.     protected Vector3 targetDirection;
  390.  
  391.     protected float XZSqrMagnitude (Vector3 a, Vector3 b) {
  392.         float dx = b.x-a.x;
  393.         float dz = b.z-a.z;
  394.         return dx*dx + dz*dz;
  395.     }
  396.  
  397.     /** Calculates desired velocity.
  398.      * Finds the target path segment and returns the forward direction, scaled with speed.
  399.      * A whole bunch of restrictions on the velocity is applied to make sure it doesn't overshoot, does not look too far ahead,
  400.      * and slows down when close to the target.
  401.      * /see speed
  402.      * /see endReachedDistance
  403.      * /see slowdownDistance
  404.      * /see CalculateTargetPoint
  405.      * /see targetPoint
  406.      * /see targetDirection
  407.      * /see currentWaypointIndex
  408.      */
  409.     protected Vector3 CalculateVelocity (Vector3 currentPosition) {
  410.         if (path == null || path.vectorPath == null || path.vectorPath.Count == 0) return Vector3.zero;
  411.  
  412.         List<Vector3> vPath = path.vectorPath;
  413.  
  414.         if (vPath.Count == 1) {
  415.             vPath.Insert (0,currentPosition);
  416.         }
  417.  
  418.         if (currentWaypointIndex >= vPath.Count) { currentWaypointIndex = vPath.Count-1; }
  419.  
  420.         if (currentWaypointIndex <= 1) currentWaypointIndex = 1;
  421.  
  422.         while (true) {
  423.             if (currentWaypointIndex < vPath.Count-1) {
  424.                 //There is a "next path segment"
  425.                 float dist = XZSqrMagnitude (vPath[currentWaypointIndex], currentPosition);
  426.                     //Mathfx.DistancePointSegmentStrict (vPath[currentWaypointIndex+1],vPath[currentWaypointIndex+2],currentPosition);
  427.                 if (dist < pickNextWaypointDist*pickNextWaypointDist) {
  428.                     lastFoundWaypointPosition = currentPosition;
  429.                     lastFoundWaypointTime = Time.time;
  430.                     currentWaypointIndex++;
  431.                 } else {
  432.                     break;
  433.                 }
  434.             } else {
  435.                 break;
  436.             }
  437.         }
  438.  
  439.         Vector3 dir = vPath[currentWaypointIndex] - vPath[currentWaypointIndex-1];
  440.         Vector3 targetPosition = CalculateTargetPoint (currentPosition,vPath[currentWaypointIndex-1] , vPath[currentWaypointIndex]);
  441.  
  442.  
  443.         dir = targetPosition-currentPosition;
  444.         dir.y = 0;
  445.         float targetDist = dir.magnitude;
  446.  
  447.         float slowdown = Mathf.Clamp01 (targetDist / slowdownDistance);
  448.  
  449.         this.targetDirection = dir;
  450.         this.targetPoint = targetPosition;
  451.  
  452.         if (currentWaypointIndex == vPath.Count-1 && targetDist <= endReachedDistance) {
  453.             if (!targetReached) { targetReached = true; OnTargetReached (); }
  454.  
  455.             //Send a move request, this ensures gravity is applied
  456.             return Vector3.zero;
  457.         }
  458.  
  459.         Vector3 forward = tr.forward;
  460.         float dot = Vector3.Dot (dir.normalized,forward);
  461.         float sp = speed * Mathf.Max (dot,minMoveScale) * slowdown;
  462.  
  463. #if ASTARDEBUG
  464.         Debug.DrawLine (vPath[currentWaypointIndex-1] , vPath[currentWaypointIndex],Color.black);
  465.         Debug.DrawLine (GetFeetPosition(),targetPosition,Color.red);
  466.         Debug.DrawRay (targetPosition,Vector3.up, Color.red);
  467.         Debug.DrawRay (GetFeetPosition(),dir,Color.yellow);
  468.         Debug.DrawRay (GetFeetPosition(),forward*sp,Color.cyan);
  469. #endif
  470.  
  471.         if (Time.deltaTime  > 0) {
  472.             sp = Mathf.Clamp (sp,0,targetDist/(Time.deltaTime*2));
  473.         }
  474.         return forward*sp;
  475.     }
  476.  
  477.     /** Rotates in the specified direction.
  478.      * Rotates around the Y-axis.
  479.      * \see turningSpeed
  480.      */
  481.     protected virtual void RotateTowards (Vector3 dir) {
  482.  
  483.         if (dir == Vector3.zero) return;
  484.  
  485.         Quaternion rot = tr.rotation;
  486.         Quaternion toTarget = Quaternion.LookRotation (dir);
  487.  
  488.         rot = Quaternion.Slerp (rot,toTarget,turningSpeed*Time.deltaTime);
  489.         Vector3 euler = rot.eulerAngles;
  490.         euler.z = 0;
  491.         euler.x = 0;
  492.         rot = Quaternion.Euler (euler);
  493.  
  494.         tr.rotation = rot;
  495.     }
  496.  
  497.     /** Calculates target point from the current line segment.
  498.      * \param p Current position
  499.      * \param a Line segment start
  500.      * \param b Line segment end
  501.      * The returned point will lie somewhere on the line segment.
  502.      * \see #forwardLook
  503.      * \todo This function uses .magnitude quite a lot, can it be optimized?
  504.      */
  505.     protected Vector3 CalculateTargetPoint (Vector3 p, Vector3 a, Vector3 b) {
  506.         a.y = p.y;
  507.         b.y = p.y;
  508.  
  509.         float magn = (a-b).magnitude;
  510.         if (magn == 0) return a;
  511.  
  512.         float closest = AstarMath.Clamp01 (AstarMath.NearestPointFactor (a, b, p));
  513.         Vector3 point = (b-a)*closest + a;
  514.         float distance = (point-p).magnitude;
  515.  
  516.         float lookAhead = Mathf.Clamp (forwardLook - distance, 0.0F, forwardLook);
  517.  
  518.         float offset = lookAhead / magn;
  519.         offset = Mathf.Clamp (offset+closest,0.0F,1.0F);
  520.         return (b-a)*offset + a;
  521.     }
  522. }
Advertisement
Add Comment
Please, Sign In to add comment