WanderingAI_LeaveMarket.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using UnityEngine.AI;
  4. [DefaultExecutionOrder(10)]
  5. [RequireComponent(typeof(InstantiatePrefab))]
  6. public class WanderingAI_LeaveMarket : 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;
  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<List<int>> waitingTimerList;
  26. private List<int> waitingList;
  27. private float[] currentWait;
  28. private int countFilling = 0;
  29. // Time
  30. private float[] timer;
  31. private float globalTimer;
  32. private GameObject[][] humansGO;
  33. private NavMeshAgent[][] humansNMA;
  34. private Animator[][] humansA;
  35. private const string isWalking = "isWalking";
  36. void Start()
  37. {
  38. // Get initialized Variables from InstantiatePrefab
  39. humansGO = gameObject.GetComponent<InstantiatePrefab>().humanGameObject;
  40. humansNMA = gameObject.GetComponent<InstantiatePrefab>().humanNavMeshAgent;
  41. humansA = gameObject.GetComponent<InstantiatePrefab>().humanAnimator;
  42. //wanderTimer = gameObject.GetComponent<InstantiatePrefab>().wanderTimer;
  43. waypoints = gameObject.GetComponent<InstantiatePrefab>().waypointsArray;
  44. humansPrio = gameObject.GetComponent<InstantiatePrefab>().humanPriorities;
  45. // Set length of variables to humanGo.Length
  46. target = new Vector3[humansGO.Length][];
  47. timer = new float[humansGO.Length];
  48. // Create copies
  49. waypointsList = new List<Transform>(waypoints);
  50. waitingTimer = new List<List<int>>(gameObject.GetComponent<InstantiatePrefab>().waitingTimer);
  51. //waitingTimerList = new List<List<int>>(gameObject.GetComponent<InstantiatePrefab>().waitingTimer);
  52. waitingList = new List<int>(waitingTimer[0]);
  53. currentWait = new float[humansGO.Length];
  54. waitingTimerList = new List<List<int>>();
  55. foreach (List<int> timerList in waitingTimer)
  56. {
  57. waitingTimerList.Add(new List<int>(timerList));
  58. }
  59. maxWPCount = waypointsList.Count;
  60. // Initialize size and content
  61. for (int i = 0; i < humansGO.Length; i++)
  62. {
  63. target[i] = new Vector3[humansGO[i].Length];
  64. timer[i] = 0;
  65. }
  66. globalTimer = 0;
  67. }
  68. // To ensure that all market stalls (WPs) are visited, all entries in the WP-list are first edited in a random order
  69. // and each edited one is appended to the end of the list so that all unedited ones can be selected by decreasing the range.
  70. // Since the order of the WP-list is the same as the first dimension of the waitingTimer (WT) matrix,
  71. // the WT matrix can be edited with the same index as for the selection of the WP. So the arrangement is always the same.
  72. // The second dimension (the times of the individual market stalls) of the WT matrix is first completely emptied each time and then refilled.
  73. // After the leaving time is reached all humans sets their destination outside the market, iff the WPs with their timers are visited completely.
  74. private void FixedUpdate()
  75. {
  76. if (waypoints.Length == 0) return;
  77. // Leave Market if time is up
  78. if(globalTimer >= leaveTimer)
  79. leaveMarket = true;
  80. for (int i = 0; i < humansGO.Length; ++i)
  81. {
  82. // Set a new destination if the waiting time is over
  83. if (timer[i] >= currentWait[i])
  84. {
  85. // Let the class know that the first iteration is finished
  86. if (countFilling >= waitingTimer.Count)
  87. gameObject.GetComponent<WriteInCSV>().startWriting = true;
  88. for (int j = 0; j < humansGO[i].Length; ++j)
  89. {
  90. if (humansNMA[i][j].isOnNavMesh)
  91. {
  92. // if all WTs are processed and leaving time is reached, then leave market
  93. if (countFilling >= waitingTimer.Count && leaveMarket)
  94. {
  95. Vector3 endTarget = leavePosition;
  96. if (humansNMA[i][j].isActiveAndEnabled && Vector3.Distance(humansNMA[i][j].destination, endTarget) > 0.01f)
  97. humansNMA[i][j].SetDestination(endTarget);
  98. humansA[i][j].SetBool(isWalking, humansNMA[i][j].velocity.magnitude > 0.01f);
  99. if (leaveMarket && (Vector3.Distance(humansNMA[i][j].transform.position, endTarget) <= humansNMA[i][j].stoppingDistance))
  100. {
  101. humansGO[i][j].SetActive(false);
  102. }
  103. }
  104. // Set new destination iff the human reached his destination or (only in first iteration) target vector is zero
  105. else if (target[i][j] == Vector3.zero ||
  106. humansNMA[i][j].velocity.magnitude <= 0.01f ||
  107. humansNMA[i][j].remainingDistance <= humansNMA[i][j].stoppingDistance ||
  108. humansNMA[i][j].pathStatus == NavMeshPathStatus.PathComplete ||
  109. !humansNMA[i][j].hasPath)
  110. {
  111. // Reset countFilling to start a new iteration
  112. if (countFilling >= waitingTimer.Count && !leaveMarket)
  113. countFilling = 0;
  114. // Random index for waypointsList
  115. if (maxWPCount <= 0) maxWPCount = waypointsList.Count;
  116. int currentWPIndex = Random.Range(0, maxWPCount);
  117. target[i][j] = CheckTarget(waypointsList[currentWPIndex].transform.position, targetScattering);
  118. // Set destination and animation
  119. if (target[i][0].x != float.PositiveInfinity)
  120. {
  121. // Random index for waitingTimer
  122. int currentWaitingIndex = Random.Range(0, waitingTimer[currentWPIndex].Count);
  123. currentWait[i] = waitingTimer[currentWPIndex][currentWaitingIndex];
  124. RemoveIndexFromWaitingTimer(currentWPIndex, currentWaitingIndex);
  125. // Fill waiting timer with default, if it is empty, to get a new waiting time for the next iteration
  126. if (waitingTimer[currentWPIndex].Count <= 0)
  127. {
  128. //waitingTimer[currentWPIndex] = new List<int>(waitingList);
  129. waitingTimer[currentWPIndex] = new List<int>(waitingTimerList[currentWPIndex]);
  130. countFilling++; // to check if ALL waitingTimer entries are processed
  131. // countFilling >= waitingTimer.Count -> All WP and WT entries are processed, the waitingTimer is filled for next iteration
  132. }
  133. if (humansNMA[i][j].isActiveAndEnabled)
  134. humansNMA[i][j].SetDestination(target[i][j]);
  135. humansA[i][j].SetBool(isWalking, humansNMA[i][j].velocity.magnitude > 0.01f);
  136. // 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,
  137. // afterwords decrement the max length by 1
  138. AppendIndexToEndWP(currentWPIndex); // Waypoints
  139. AppendIndexToEndWT(currentWPIndex); // Waiting Timer which is modified
  140. AppendIndexToEndWTDefault(currentWPIndex); // Default Waiting Timer, with all the times for each WP
  141. maxWPCount--;
  142. timer[i] = 0;
  143. }
  144. }
  145. }
  146. }
  147. }
  148. // Wait until the waiting time is over
  149. else
  150. {
  151. for (int j = 0; j < humansGO[i].Length; ++j)
  152. {
  153. // Set walking state and priority; increment timer
  154. humansA[i][j].SetBool(isWalking, humansNMA[i][j].velocity.magnitude > 0.01f);
  155. if (humansNMA[i][j].remainingDistance <= humansNMA[i][j].stoppingDistance)
  156. timer[i] += Time.deltaTime;
  157. // if humans are not walking, then set priority to 0 so that the other humans dont disturb them
  158. if (humansNMA[i][j].velocity.magnitude <= 0.01f)
  159. humansNMA[i][j].avoidancePriority = 0;
  160. else
  161. humansNMA[i][j].avoidancePriority = humansPrio[i][j];
  162. }
  163. }
  164. }
  165. globalTimer += Time.deltaTime;
  166. }
  167. public static Vector3 CheckTarget(Vector3 target, float dist)
  168. {
  169. Vector3 modifiedTarget = Random.insideUnitSphere * dist + target;
  170. // SamplePosition also checks the y axis. However, this is not relevant for me, so valid positions (x & z) are also discarded.
  171. // 0.0f is the only valid entry for y
  172. modifiedTarget = new Vector3(modifiedTarget.x, 0f, modifiedTarget.z);
  173. NavMeshHit navHit;
  174. NavMesh.SamplePosition(modifiedTarget, out navHit, 1.0f, -1);
  175. return navHit.position;
  176. }
  177. public void AppendIndexToEndWP(int index)
  178. {
  179. waypointsList.Add(waypointsList[index]);
  180. waypointsList.RemoveAt(index);
  181. }
  182. public void AppendIndexToEndWT(int index)
  183. {
  184. waitingTimer.Add(waitingTimer[index]);
  185. waitingTimer.RemoveAt(index);
  186. }
  187. public void AppendIndexToEndWTDefault(int index)
  188. {
  189. waitingTimerList.Add(waitingTimerList[index]);
  190. waitingTimerList.RemoveAt(index);
  191. }
  192. public void RemoveIndexFromWaitingTimer(int i, int removeIndex)
  193. {
  194. waitingTimer[i].RemoveAt(removeIndex);
  195. }
  196. private void LateUpdate()
  197. {
  198. for (int i = 0; i < humansGO.Length; ++i)
  199. {
  200. for (int j = 0; j < humansGO[i].Length; ++j)
  201. {
  202. humansA[i][j].speed = 0.5f + (humansNMA[i][j].velocity.magnitude / gameObject.GetComponent<InstantiatePrefab>().speedMinMax.y);
  203. }
  204. }
  205. }
  206. }