WanderingAI_Thief.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using UnityEngine.AI;
  4. [DefaultExecutionOrder(10)]
  5. [RequireComponent(typeof(InstantiatePrefab))]
  6. public class WanderingAI_Thief : MonoBehaviour
  7. {
  8. [Header("Only for Debugging (do not set from here)")]
  9. public Transform[] waypoints;
  10. public List<Transform> waypointsList;
  11. public float[] wanderTimer;
  12. [Header("Scattering around the target")]
  13. public float targetScattering = 2.0f; //Streuung
  14. [Header("Leave Settings")]
  15. public float leaveTimer = 120f;
  16. public Vector3 leavePosition;
  17. // Settings Leave Market
  18. private bool leaveMarket = false;
  19. // Settings Humans
  20. private Vector3[][] target;
  21. private int[][] humansPrio;
  22. // Settings Waypoints and Waiting Time
  23. private int maxWPCount;
  24. private List<List<int>> waitingTimer;
  25. private List<int> waitingList;
  26. private float[] currentWait;
  27. // Time
  28. private float[] timer;
  29. private float globalTimer;
  30. private GameObject[][] humansGO;
  31. private NavMeshAgent[][] humansNMA;
  32. private Animator[][] humansA;
  33. private const string isWalking = "isWalking";
  34. void Start()
  35. {
  36. // Get initialized Variables from InstantiatePrefab
  37. humansGO = gameObject.GetComponent<InstantiatePrefab>().humanGameObject;
  38. humansNMA = gameObject.GetComponent<InstantiatePrefab>().humanNavMeshAgent;
  39. humansA = gameObject.GetComponent<InstantiatePrefab>().humanAnimator;
  40. wanderTimer = gameObject.GetComponent<InstantiatePrefab>().wanderTimer;
  41. waypoints = gameObject.GetComponent<InstantiatePrefab>().waypointsArray;
  42. humansPrio = gameObject.GetComponent<InstantiatePrefab>().humanPriorities;
  43. // Set length of variables to humanGo.Length
  44. target = new Vector3[humansGO.Length][];
  45. timer = new float[humansGO.Length];
  46. // Create copies
  47. waypointsList = new List<Transform>(waypoints);
  48. waitingTimer = new List<List<int>>(gameObject.GetComponent<InstantiatePrefab>().waitingTimer);
  49. waitingList = new List<int>(waitingTimer[0]);
  50. currentWait = new float[humansGO.Length];
  51. maxWPCount = waypointsList.Count;
  52. // Initialize size and content
  53. for (int i = 0; i < humansGO.Length; i++)
  54. {
  55. target[i] = new Vector3[humansGO[i].Length];
  56. timer[i] = 0;
  57. }
  58. globalTimer = 0;
  59. }
  60. // To ensure that all market stalls (WPs) are visited, all entries in the WP-list are first edited in a random order
  61. // and each edited one is appended to the end of the list so that all unedited ones can be selected by decreasing the range.
  62. // Since the order of the WP-list is the same as the first dimension of the waitingTimer (WT) matrix,
  63. // the WT matrix can be edited with the same index as for the selection of the WP. So the arrangement is always the same.
  64. // The second dimension (the times of the individual market stalls) of the WT matrix is first completely emptied each time and then refilled.
  65. private void FixedUpdate()
  66. {
  67. if (waypoints.Length == 0) return;
  68. // Leave Market if time is up
  69. if(globalTimer >= leaveTimer)
  70. leaveMarket = true;
  71. for (int i = 0; i < humansGO.Length; ++i)
  72. {
  73. // Set a new destination if the waiting time is over
  74. if (timer[i] >= currentWait[i] && !leaveMarket)
  75. {
  76. for (int j = 0; j < humansGO[i].Length; ++j)
  77. {
  78. // Set new destination iff the human reached his destination or (only in first iteration) target vector is zero
  79. if (target[i][j] == Vector3.zero ||
  80. humansNMA[i][j].velocity.magnitude <= 0.01f ||
  81. humansNMA[i][j].remainingDistance <= humansNMA[i][j].stoppingDistance ||
  82. humansNMA[i][j].pathStatus == NavMeshPathStatus.PathComplete ||
  83. !humansNMA[i][j].hasPath)
  84. {
  85. // Random index for waypointsList
  86. if (maxWPCount <= 0) maxWPCount = waypointsList.Count;
  87. int currentWPIndex = Random.Range(0, maxWPCount);
  88. target[i][j] = CheckTarget(waypointsList[currentWPIndex].transform.position, targetScattering);
  89. // Set destination and animation
  90. if (target[i][0].x != float.PositiveInfinity)
  91. {
  92. // Random index for waitingTimer
  93. int currentWaitingIndex = Random.Range(0, waitingTimer[currentWPIndex].Count);
  94. currentWait[i] = waitingTimer[currentWPIndex][currentWaitingIndex];
  95. RemoveIndexFromWaitingTimer(currentWPIndex, currentWaitingIndex);
  96. // Fill waiting timer with default, if it is empty, to get a new waiting time for the next iteration
  97. if (waitingTimer[currentWPIndex].Count <= 0)
  98. waitingTimer[currentWPIndex] = new List<int>(waitingList);
  99. if (humansNMA[i][j].isActiveAndEnabled)
  100. humansNMA[i][j].SetDestination(target[i][j]);
  101. humansA[i][j].SetBool(isWalking, humansNMA[i][j].velocity.magnitude > 0.01f);
  102. // Append the current WP and the waiting time from both lists to the end to ensure that all WPs and waiting timers are visited at the frequency indicated,
  103. // afterwords decrement the max length by 1
  104. AppendIndexToEndWP(currentWPIndex);
  105. AppendIndexToEndWT(currentWPIndex);
  106. maxWPCount--;
  107. timer[i] = 0;
  108. }
  109. }
  110. }
  111. }
  112. // if leaving time has arrived, then leave market
  113. else if (leaveMarket)
  114. {
  115. for (int j = 0; j < humansGO[i].Length; ++j)
  116. {
  117. Vector3 endTarget = leavePosition;
  118. if (humansNMA[i][j].isActiveAndEnabled)
  119. humansNMA[i][j].SetDestination(endTarget);
  120. humansA[i][j].SetBool(isWalking, humansNMA[i][j].velocity.magnitude > 0.01f);
  121. }
  122. }
  123. // Wait until the waiting time is over
  124. else
  125. {
  126. for (int j = 0; j < humansGO[i].Length; ++j)
  127. {
  128. // Set walking state and priority; increment timer
  129. humansA[i][j].SetBool(isWalking, humansNMA[i][j].velocity.magnitude > 0.01f);
  130. if (humansNMA[i][j].remainingDistance <= humansNMA[i][j].stoppingDistance)
  131. timer[i] += Time.deltaTime;
  132. // if humans are not walking, then set priority to 0 so that the other humans dont disturb them
  133. if (humansNMA[i][j].velocity.magnitude <= 0.01f)
  134. humansNMA[i][j].avoidancePriority = 0;
  135. else
  136. humansNMA[i][j].avoidancePriority = humansPrio[i][j];
  137. }
  138. }
  139. }
  140. globalTimer += Time.deltaTime;
  141. }
  142. public static Vector3 CheckTarget(Vector3 target, float dist)
  143. {
  144. Vector3 modifiedTarget = Random.insideUnitSphere * dist + target;
  145. // SamplePosition also checks the y axis. However, this is not relevant for me, so valid positions (x & z) are also discarded.
  146. // 0.0f is the only valid entry for y
  147. modifiedTarget = new Vector3(modifiedTarget.x, 0f, modifiedTarget.z);
  148. NavMeshHit navHit;
  149. NavMesh.SamplePosition(modifiedTarget, out navHit, 1.0f, -1);
  150. return navHit.position;
  151. }
  152. public void AppendIndexToEndWP(int index)
  153. {
  154. waypointsList.Add(waypointsList[index]);
  155. waypointsList.RemoveAt(index);
  156. }
  157. public void AppendIndexToEndWT(int index)
  158. {
  159. waitingTimer.Add(waitingTimer[index]);
  160. waitingTimer.RemoveAt(index);
  161. }
  162. public void RemoveIndexFromWaitingTimer(int i, int removeIndex)
  163. {
  164. waitingTimer[i].RemoveAt(removeIndex);
  165. }
  166. private void LateUpdate()
  167. {
  168. for (int i = 0; i < humansGO.Length; ++i)
  169. {
  170. for (int j = 0; j < humansGO[i].Length; ++j)
  171. {
  172. humansA[i][j].speed = 0.5f + (humansNMA[i][j].velocity.magnitude / gameObject.GetComponent<InstantiatePrefab>().speedMinMax.y);
  173. }
  174. }
  175. }
  176. }