Guest User

Untitled

a guest
Oct 18th, 2017
420
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.60 KB | None | 0 0
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4.  
  5.  
  6.  
  7.  
  8.  
  9. [AddComponentMenu("Final Integrated Stuff/AI Controller")]
  10. public class AIController : UnitController {
  11.  
  12. //------------------------
  13. // AI Controller Component
  14. // By: Nicholas J. Hylands
  15. // me@nickhylands.com
  16. // github.com/nyxeka
  17. //------------------------
  18.  
  19. // Generic ground-unit AI controller
  20. // requires a unit component
  21. #region variables
  22. public enum AIState
  23. {
  24. initialize, idle, patrolling, hunting, escaping, following
  25. }
  26.  
  27. //if you don't declare the scope of a variable, it defaults to private.
  28. AIState currentState;
  29.  
  30. // use ability components for moving, or built-in functions.
  31. public bool abilityControlMovement;
  32.  
  33. // finite state machine running
  34. public bool AI_RUNNING = true;
  35.  
  36. bool attacking = false;
  37. public float moveAfterAttackDelay = 0.0f;
  38.  
  39. // for non-ability-controlled movement.
  40. [Header("Speed in meters per second")]
  41. public float speedmps = 1.0f;
  42.  
  43. public float jumpHeight = 10.0f;
  44. public float jumpForwardsForce = 7.0f;
  45.  
  46. // range from the player in which we will try to trigger attacks.
  47. public float attackRange = 10.0f;
  48.  
  49. public float attackFrequency = 1.0f;
  50.  
  51. // use for the triggercollider methods in the vision child game object
  52. EnemyVision vis;
  53.  
  54. public float visionRange = 10.0f;
  55.  
  56. //since we're basically only going to ever be targetting the player for now.
  57. Transform player;
  58.  
  59. //our list of patrol-points.
  60. public List<Transform> patrolPointList;
  61. public int currentPatrolPointTarget = 0;
  62.  
  63.  
  64. //distance from the target location we need to be before we decide to stop mgoing forwards.
  65. public float distanceToStop = 0.0f;
  66.  
  67. // this is the position that we'll be trying to move towards.
  68. Vector3 targetLocation;
  69.  
  70. // the current number of attempts we've made to traverse obstacles between us and target.
  71. int numTriesForPoint = 0;
  72.  
  73. // if the unit comes accross an obstacle, it will only try to get to its target
  74. // this many times before switching to another target.
  75. public int maxAttemptsToTravel = 6;
  76.  
  77. // the delay between progress checks.
  78. // minimum every 0.02 seconds.
  79. public float progressCheckDelay = 0.5f;
  80.  
  81. // we've attempted the jump. May be deprecated soon.
  82. bool attemptedJump = false;
  83.  
  84. // tell the AI to make its next move a jump.
  85. bool nextMoveIsLeap = false;
  86.  
  87. // minimum distance in which we can say we've "reached the target"
  88. public float minDistance = 0.5f;
  89.  
  90. // for general use
  91. float distanceFromTarget = 0;
  92.  
  93. /// <summary>
  94. /// Move check target location
  95. /// for the "have we moved closer to our target?" method.
  96. /// </summary>
  97. Vector3 moveCheckTL;
  98.  
  99. float distanceFromMoveCheckTL;
  100.  
  101. float oldDistanceFromTarget = 0;
  102.  
  103. public bool printDebug;
  104.  
  105. // update move check target location. For later use,
  106. // while it's true we'll be updating the current pos relative to the last tracked
  107. // target position.
  108. bool updateMCTL = false;
  109.  
  110. [SerializeField]
  111. bool checkFloorMissing = true;
  112.  
  113. [SerializeField]
  114. float gapCheckIncrement = 1.0f;
  115. //in meters. Tells the AI to check for missing ground.
  116.  
  117. [SerializeField]
  118. float gapCheckDepth = 3.0f;
  119.  
  120. float moveDelay = 0;
  121.  
  122. public bool startInIdle = false;
  123.  
  124. #endregion
  125.  
  126. void Start(){
  127. //since we are overriding start method from unitController parent:
  128. _unit = gameObject.GetComponent<GameUnit> ();
  129.  
  130. vis = gameObject.GetComponentInChildren<EnemyVision>();
  131.  
  132. if (!vis)
  133. {
  134. GameObject visGO;
  135. visGO = Instantiate(Resources.Load<GameObject>("Vision"), transform,false);
  136. vis = visGO.GetComponent<EnemyVision>();
  137. }
  138.  
  139. //set start state
  140. currentState = AIState.initialize;
  141.  
  142. if (_unit)
  143. AI_RUNNING = true;
  144. else
  145. AI_RUNNING = false;
  146.  
  147. if (progressCheckDelay <= 0.02f)
  148. {
  149.  
  150. progressCheckDelay = 0.02f;
  151.  
  152. }
  153.  
  154. // we want to make sure there are patrol points that have been put in here.
  155.  
  156.  
  157.  
  158. // begin the finite state machine.
  159. StartCoroutine (AI_FSM ());
  160.  
  161. }
  162.  
  163. #region methods
  164. void updateTargetLocation()
  165. {
  166.  
  167. targetLocation = patrolPointList[currentPatrolPointTarget].transform.position;
  168.  
  169. }
  170.  
  171. //assuming enemy is facing player, which it should be, especially if the player is in the AI zone.
  172. public void MoveForwards(){
  173.  
  174. if (nextMoveIsLeap) {
  175. //leap!
  176. StartCoroutine( JumpForwards ());
  177. nextMoveIsLeap = false;
  178. }else if (abilityControlMovement) {
  179.  
  180. if (_unit.TriggerAbility("Move"))
  181. StartCoroutine(CheckIfMovedToTarget());
  182.  
  183. } else {
  184. _unit.unitRB.MovePosition(transform.position + (Vector3.right * (_unit.directionMult * speedmps * Time.deltaTime)));
  185. }
  186.  
  187. }
  188.  
  189. IEnumerator JumpForwards(){
  190. debug ("Trying a Leap");
  191. if (abilityControlMovement) {
  192. if (_unit.TriggerAbility ("Leap")) {
  193. debug ("Lept!");
  194. StartCoroutine(CheckIfMovedToTarget());
  195. yield return new WaitForSeconds (0.3f);
  196. _unit.ForceTriggerAbility ("Move");
  197. }
  198.  
  199. } else {
  200.  
  201. if (_unit.TriggerAbility("Leap"))
  202. {
  203. //Debug.Log("We're still going up!!");
  204. yield return new WaitForSeconds(0.3f);
  205. _unit.unitRB.AddForce((_unit.directionMult * jumpForwardsForce), jumpHeight / 2, .0f, ForceMode.Impulse);
  206. }
  207.  
  208. }
  209.  
  210. attemptedJump = true;
  211. }
  212.  
  213. public void turnAround(){
  214.  
  215. if (_unit.legDir == LegDir.left)
  216. _unit.legDir = LegDir.right;
  217. else
  218. _unit.legDir = LegDir.left;
  219.  
  220. _unit.armDir = ArmDir.forwards;
  221.  
  222. attemptedJump = false;
  223.  
  224. }
  225.  
  226. void nextPoint(){
  227.  
  228. currentPatrolPointTarget = (currentPatrolPointTarget + 1) % patrolPointList.Count;
  229. attemptedJump = false;
  230. }
  231.  
  232.  
  233. bool IsFloorMissing(){
  234. //basically in here, we're going to scan the floor before we move. A few simple raycasts should do the trick.
  235. //if we notice the floor is missing, we don't move forwards again, instead we jump.
  236. //if it's to far, we turn around.
  237. if (checkFloorMissing)
  238. {
  239. bool groundIsThere = true;
  240.  
  241. Ray cast = new Ray(transform.position + Vector3.right * _unit.directionMult * gapCheckIncrement, Vector3.down);
  242.  
  243. groundIsThere = Physics.Raycast(cast, gapCheckDepth);
  244.  
  245. Debug.DrawLine(cast.origin, cast.origin + (Vector3.down * gapCheckDepth), Color.black, 0.5f);
  246.  
  247. return !groundIsThere;
  248. }
  249.  
  250. return false;
  251.  
  252. }
  253.  
  254. void useAttackAbility()
  255. {
  256.  
  257. _unit.TriggerAbility("Attack");
  258.  
  259. }
  260.  
  261. bool isFacingPlayer()
  262. {
  263.  
  264. float dirToPlayer = player.transform.position.x - transform.position.x;
  265.  
  266. //Debug.Log("Player in sight!");
  267.  
  268. if ((dirToPlayer / Mathf.Abs(dirToPlayer)) == _unit.directionMult)
  269. {
  270.  
  271. return true;
  272.  
  273. }
  274.  
  275. return false;
  276.  
  277. }
  278.  
  279. public void DelayMoving(float time)
  280. {
  281.  
  282. moveDelay = time;
  283.  
  284. }
  285.  
  286. #endregion
  287.  
  288. #region coroutines
  289.  
  290. IEnumerator UpdateMCTL() //update move check target location.
  291. {
  292. /*
  293. basically we want to be doing this for a fixed amount of time.
  294. */
  295. float time = 0.0f;
  296. updateMCTL = true;
  297. while (updateMCTL)
  298. {
  299. time = time + Time.deltaTime;
  300. distanceFromMoveCheckTL = Vector3.Distance(transform.position, moveCheckTL);
  301.  
  302. if (time > 10.0f) //this should never last longer than 10 seconds.
  303. {
  304. break;
  305. }
  306.  
  307. yield return null;
  308.  
  309. }
  310.  
  311. }
  312.  
  313.  
  314. IEnumerator CheckIfMovingToTargetContinuousMovement()
  315. {
  316.  
  317. moveCheckTL = targetLocation;
  318. //debug ("Started routine: check if moving towards target");
  319. //if the old distance is less than the new distance, then we haven't made progress.
  320. yield return new WaitForSeconds(progressCheckDelay);
  321. while (true)
  322. {
  323. if (!attacking)
  324. {
  325. distanceFromMoveCheckTL = Vector3.Distance(transform.position, moveCheckTL);
  326.  
  327. if (Mathf.Abs(distanceFromMoveCheckTL - oldDistanceFromTarget) < 0.01)
  328. {
  329.  
  330. // we've moved less than 0.01 meters since the last movement check.
  331. if (!attemptedJump)
  332. nextMoveIsLeap = true;
  333. else
  334. turnAround();
  335.  
  336. }
  337. else if (distanceFromMoveCheckTL > oldDistanceFromTarget)
  338. {
  339.  
  340. // we've been moving away from our target. Turn around!
  341. turnAround();
  342.  
  343. }
  344. else
  345. {
  346.  
  347. attemptedJump = false;
  348.  
  349. }
  350.  
  351. if (IsFloorMissing())
  352. {
  353.  
  354. turnAround();
  355.  
  356. }
  357.  
  358. oldDistanceFromTarget = distanceFromMoveCheckTL;
  359.  
  360. moveCheckTL = targetLocation;
  361. }
  362. yield return new WaitForSeconds(progressCheckDelay);
  363. }
  364.  
  365. }
  366.  
  367. IEnumerator CheckIfMovedToTarget(){
  368.  
  369. moveCheckTL = targetLocation;
  370. StartCoroutine(UpdateMCTL());
  371. //debug ("Started routine: check if moving towards target");
  372. //if the old distance is less than the new distance, then we haven't made progress.
  373. debug("██████RUNNING MOVEMENT SCAN██████");
  374.  
  375. oldDistanceFromTarget = distanceFromMoveCheckTL;
  376.  
  377. debug("old distance:" + oldDistanceFromTarget.ToString());
  378. //wait-until: Create a delegate for that boolean, and check it every frame until its true!
  379. debug("waiting for movement to start");
  380. //wait for movement to start.
  381. //yield return new WaitUntil(() => _unit.unitRB.velocity.magnitude > 0.01);
  382. yield return new WaitForSeconds(0.1f);
  383. debug("waiting for movement to end");
  384. //Now that movement has started, we want to wait until its over.
  385. yield return new WaitUntil(() => _unit.unitRB.velocity.magnitude < 0.01);
  386. debug("checking for movement:");
  387.  
  388. if (IsFloorMissing())
  389. {
  390. turnAround();
  391. numTriesForPoint++;
  392.  
  393. }else if ((Mathf.Abs (oldDistanceFromTarget - distanceFromMoveCheckTL) < 0.1f)) { //no movement and we haven't jumped yet.
  394. nextMoveIsLeap = true;
  395. numTriesForPoint++;
  396. debug("numTriesForPoint: " + numTriesForPoint.ToString());
  397. }else if (oldDistanceFromTarget < distanceFromMoveCheckTL) {
  398. debug("new distance = " + distanceFromMoveCheckTL.ToString());
  399. debug("Turning around because I'm moving away from my target.");
  400. turnAround ();
  401.  
  402. } else
  403. {
  404. numTriesForPoint = 0;
  405. }
  406.  
  407.  
  408.  
  409. if (numTriesForPoint > maxAttemptsToTravel)
  410. {
  411. nextPoint();
  412. if (!IsFloorMissing())
  413. {
  414. turnAround();
  415. }
  416.  
  417. targetLocation = patrolPointList[currentPatrolPointTarget].transform.position;
  418. numTriesForPoint = 0;
  419. debug("Switching Targets, since this just isn't working.");
  420. }
  421.  
  422. debug("█ █ █ █Scan Over█ █ █ █");
  423. updateMCTL = false;
  424.  
  425. }
  426.  
  427. #endregion
  428.  
  429. #region States
  430.  
  431. IEnumerator initialize(){
  432.  
  433. yield return new WaitForSeconds (2);
  434.  
  435. debug ("Ran init on AI controller for " + gameObject.name);
  436.  
  437. StartCoroutine(_unit.doMovementChecking());
  438.  
  439. player = GameObject.FindGameObjectWithTag("Player").transform;
  440. if (patrolPointList == null)
  441. {
  442.  
  443. patrolPointList = new List<Transform>();
  444.  
  445. }
  446. if (patrolPointList.Count > 0)
  447. {
  448. for (int i = 0; i < patrolPointList.Count; i++)
  449. {
  450.  
  451. if (patrolPointList[i] == null)
  452. {
  453.  
  454. Debug.Log("Warning! No patrol point set for: " + name + " on slot: " + (i + 1).ToString() + "!\nConsider reducing the size of the list or adding an object to the list.");
  455. patrolPointList[i] = transform;
  456.  
  457. }
  458.  
  459. }
  460. }
  461. else
  462. {
  463.  
  464. Debug.Log("Warning, patrol point list empty! Adding self as patrolPoint");
  465. patrolPointList.Add(transform);
  466. currentPatrolPointTarget = 0;
  467.  
  468. }
  469.  
  470. if (player != null)
  471. {
  472. if (startInIdle)
  473. {
  474. currentState = AIState.idle;
  475. }
  476. else {
  477. currentState = AIState.patrolling;
  478. }
  479.  
  480. } else
  481. {
  482.  
  483. currentState = AIState.idle;
  484.  
  485. }
  486.  
  487. yield return null;
  488.  
  489. }
  490.  
  491. IEnumerator idle(){
  492. //More or less the same as patrolling, except while standing still.
  493. //will queue the abilities as usual.
  494. while (currentState == AIState.idle)
  495. {
  496. if (vis.enemyInSight)
  497. {
  498.  
  499. if (isFacingPlayer())
  500. {
  501.  
  502. currentState = AIState.hunting;
  503. targetLocation = player.position;
  504.  
  505. }
  506.  
  507. }
  508.  
  509. yield return null;
  510.  
  511. }
  512.  
  513. }
  514.  
  515. IEnumerator patrolling(){
  516.  
  517. debug("Entered Patrolling State");
  518. //WalkAround
  519. //if point A and point B are set, walk to those.
  520. //bool goingToPoint = true;
  521. bool atTarget = false;
  522.  
  523. //float oldDistanceToPoint;
  524.  
  525. if (!abilityControlMovement)
  526. {
  527.  
  528. StartCoroutine("CheckIfMovingToTargetContinuousMovement");
  529.  
  530. }
  531.  
  532. while (currentState == AIState.patrolling) {
  533.  
  534. targetLocation = patrolPointList[currentPatrolPointTarget].transform.position;
  535. distanceFromTarget = Vector3.Distance (transform.position, targetLocation);
  536.  
  537.  
  538. if (distanceFromTarget > distanceToStop)
  539. {
  540. MoveForwards();
  541. }
  542.  
  543. if (distanceFromTarget < minDistance && !atTarget) {
  544.  
  545. nextPoint ();
  546. debug("target set to: " + patrolPointList [currentPatrolPointTarget].gameObject.name);
  547. atTarget = true;
  548.  
  549. }
  550. else if (atTarget)
  551. {
  552.  
  553. if (distanceFromTarget > minDistance)
  554. {
  555.  
  556. atTarget = false;
  557.  
  558. }
  559.  
  560. }
  561.  
  562. if (vis.enemyInSight)
  563. {
  564.  
  565. if (isFacingPlayer()) {
  566.  
  567. currentState = AIState.hunting;
  568. targetLocation = player.position;
  569.  
  570. }
  571.  
  572. }
  573.  
  574. yield return null;
  575.  
  576. }
  577.  
  578. StopCoroutine ("CheckIfMovedToTarget");
  579. StopCoroutine("CheckIfMovingToTargetContinuousMovement");
  580. }
  581.  
  582. IEnumerator hunting(){
  583.  
  584. float attackTimer = 0;
  585.  
  586. //float checkDist;
  587.  
  588. float jumpDelay = 1.5f;
  589.  
  590. float jumpTimer = 0.0f;
  591.  
  592. while (currentState == AIState.hunting)
  593. {
  594. attackTimer += Time.deltaTime;
  595.  
  596. targetLocation = player.position;
  597. distanceFromTarget = Vector3.Distance(transform.position, targetLocation);
  598.  
  599. if (!isFacingPlayer())
  600. {
  601. //Debug.Log("not facing player!");
  602. turnAround();
  603.  
  604. }
  605.  
  606. if (moveDelay > 0)
  607. {
  608. yield return new WaitForSeconds(moveDelay);
  609. moveDelay = 0;
  610.  
  611. }
  612.  
  613. if (distanceFromTarget > distanceToStop)
  614. {
  615. MoveForwards();
  616. }
  617.  
  618. if (distanceFromTarget > visionRange)
  619. {
  620.  
  621. currentState = AIState.patrolling;
  622. updateTargetLocation();
  623.  
  624. }
  625.  
  626. if (distanceFromTarget < attackRange)
  627. {
  628. if (attackTimer > attackFrequency)
  629. {
  630. if (isFacingPlayer())
  631. {
  632. useAttackAbility();
  633. attackTimer = 0;
  634. attacking = true;
  635. yield return new WaitForSeconds(moveAfterAttackDelay);
  636. attacking = false;
  637. }
  638. }
  639. }
  640.  
  641. yield return null;
  642.  
  643. }
  644. }
  645.  
  646. IEnumerator escaping(){
  647.  
  648. yield return null;
  649.  
  650. }
  651.  
  652. IEnumerator following(){
  653.  
  654. yield return null;
  655.  
  656. }
  657.  
  658. IEnumerator AI_FSM() {
  659.  
  660. while (true) {
  661. if (AI_RUNNING) {
  662. // this pauses until the state is finished.
  663. yield return StartCoroutine(currentState.ToString ());
  664. }
  665. }
  666.  
  667. }
  668. #endregion
  669.  
  670. void debug(string text)
  671. {
  672. if (printDebug)
  673. Debug.Log(text);
  674. }
  675.  
  676. }
Add Comment
Please, Sign In to add comment