WanderingAI.cs 8.0 KB

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