InstantiatePrefab.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. /*
  2. * InstantiatePrefab.cs must be run before any other custom scripts because
  3. * humanGameObject, wanderTimer and waypointsArray may be needed in other scripts.
  4. * This can be set in Uniy3d under "Edit -> Project Settings ... -> Script Execution Order".
  5. */
  6. using System.Collections.Generic;
  7. using UnityEngine;
  8. using UnityEngine.AI;
  9. [DefaultExecutionOrder(0)]
  10. public class InstantiatePrefab : MonoBehaviour
  11. {
  12. [Header("Game Objects")]
  13. public GameObject prefabHuman;
  14. [SerializeField]
  15. private GameObject waypoints;
  16. public int[] waypointsFrequency;
  17. public Vector2[] individualWaitingTimer;
  18. [Header("Number of Humans and Groups")]
  19. public int amount = 100;
  20. public int maxNumPerGroup = 1;
  21. [Header("Set Spawn point")]
  22. public Vector3[] spawnPointArray;
  23. [Header("Steering")]
  24. public Vector2 speedMinMax = new Vector2(4.0f, 7.0f);
  25. public Vector2 wanderTimerMinMax = new Vector2(10.0f, 15.0f);
  26. public float stoppingDistance = 1;
  27. //Can be passed on
  28. [HideInInspector]
  29. public GameObject[][] humanGameObject;
  30. [HideInInspector]
  31. public Animator[][] humanAnimator;
  32. [HideInInspector]
  33. public NavMeshAgent[][] humanNavMeshAgent;
  34. [HideInInspector]
  35. public int[][] humanPriorities;
  36. [HideInInspector]
  37. public float[] wanderTimer;
  38. [HideInInspector]
  39. public List<List<int>> waitingTimer; // waitingTime (1-5 s) X WPs
  40. [HideInInspector]
  41. public Transform[] waypointsArray;
  42. // Start is called before the first frame update
  43. void Start()
  44. {
  45. // Adding frequency for increasing probability to visit some waypoints
  46. int freqLength = 0;
  47. for (int freq = 0; freq < waypointsFrequency.Length; ++freq)
  48. {
  49. freqLength += waypointsFrequency[freq];
  50. }
  51. //Save all waypoints from GameObject into Transform array
  52. Transform[] wpArray = waypoints.GetComponentsInChildren<Transform>();
  53. waypointsArray = new Transform[freqLength];
  54. freqLength = 0;
  55. for(int k = 1; k < wpArray.Length; ++k)
  56. {
  57. for(int freq = 0; freq < waypointsFrequency[k-1]; ++freq)
  58. {
  59. waypointsArray[freqLength] = wpArray[k];
  60. freqLength++;
  61. }
  62. }
  63. //gameObject.GetComponent<WanderingAITest>().waypoints = waypointsArray;
  64. //Create priority list for different NavMeshAgent priorities
  65. List<int> priorityList = new List<int>();
  66. // NavMeshAgents max avoidancePriority is 99 (0 high Priority is already used)
  67. for (int i = 0; i < 99; ++i)
  68. priorityList.Add(i + 1); //1 .. 99
  69. //Calculate number of groups
  70. int numGroups = 1;
  71. if(amount > 0)
  72. numGroups = (int) Mathf.Ceil((float)amount / (float)maxNumPerGroup);
  73. //Set size of attributes that can be passed on
  74. humanGameObject = new GameObject[numGroups][];
  75. humanAnimator = new Animator[numGroups][];
  76. humanNavMeshAgent = new NavMeshAgent[numGroups][];
  77. humanPriorities = new int[numGroups][];
  78. //Set wanderTimer
  79. wanderTimer = new float[humanGameObject.Length];
  80. // Set waitingTimer
  81. waitingTimer = new List<List<int>>();
  82. for (int i = 0; i < waypointsArray.Length; ++i)
  83. // create Matrix waitingTime X WPs with waitingTime from 1-5 seconds, bc we want all WP vistited the same duration
  84. waitingTimer.Add(new List<int> { 1, 2, 3, 4, 5 });
  85. int humansLeft = amount;
  86. //for positioning the instances
  87. //float inRow = Mathf.Ceil(Mathf.Sqrt(amount));
  88. int count = 1;
  89. int offsetx = 0;
  90. int offsetz = 0;
  91. int spawnPointIndex = 0;
  92. int maxPerSP = (int)Mathf.Ceil(amount / 4f);
  93. // not enought space for more humans (incl. 0 - 4)
  94. float inRow = 4;
  95. // instantiate humans, save in array and modifie
  96. for (int i = 0; i < humanGameObject.Length; ++i)
  97. {
  98. //Calculate Group Size
  99. int groupSize = (humansLeft > maxNumPerGroup) ? maxNumPerGroup : humansLeft;
  100. humanGameObject[i] = new GameObject[groupSize];
  101. humansLeft -= groupSize;
  102. //Set Animator and NavMeshAgent
  103. humanAnimator[i] = new Animator[groupSize];
  104. humanNavMeshAgent[i] = new NavMeshAgent[groupSize];
  105. humanPriorities[i] = new int[groupSize];
  106. //Nav Mesh Agent - speed
  107. float speed = Random.Range(speedMinMax.x, speedMinMax.y);
  108. //Wandering AI Waypoints (Script)
  109. wanderTimer[i] = Random.Range(wanderTimerMinMax.x, wanderTimerMinMax.y);
  110. // instantiate humans per group and save them in matrix
  111. for (int j = 0; j < humanGameObject[i].Length; ++j)
  112. {
  113. // Instantiate human [i][j]
  114. humanGameObject[i][j] = Instantiate(prefabHuman,
  115. new Vector3(spawnPointArray[spawnPointIndex].x - offsetx, 0, spawnPointArray[spawnPointIndex].z - offsetz),
  116. Quaternion.identity,
  117. this.transform);
  118. // Calculate offset
  119. if (offsetx < inRow * 2)
  120. offsetx += 2;
  121. else
  122. {
  123. offsetx = 0;
  124. offsetz += 2;
  125. }
  126. if(count != 0 && count % maxPerSP == 0)
  127. {
  128. spawnPointIndex++;
  129. offsetx = 0;
  130. offsetz = 0;
  131. }
  132. count++;
  133. // Set Humans initial values
  134. humanGameObject[i][j].tag = this.transform.tag;
  135. humanGameObject[i][j].GetComponent<NavMeshAgent>().stoppingDistance = stoppingDistance;
  136. humanGameObject[i][j].GetComponent<NavMeshAgent>().speed = speed;
  137. humanGameObject[i][j].GetComponent<Animator>().speed = 0.5f + (speed / speedMinMax.y);
  138. //Set Humans Priority
  139. int priority = GetRandomPriority(i, j, maxNumPerGroup, priorityList);
  140. humanGameObject[i][j].GetComponent<NavMeshAgent>().avoidancePriority = priority;
  141. humanPriorities[i][j] = priority;
  142. // Set Components
  143. humanAnimator[i][j] = humanGameObject[i][j].GetComponent<Animator>();
  144. humanNavMeshAgent[i][j] = humanGameObject[i][j].GetComponent<NavMeshAgent>();
  145. }
  146. }
  147. }
  148. public int GetRandomPriority(int i, int j, int maxNumPerGroup, List<int> priorityList)
  149. {
  150. int index = i * maxNumPerGroup + j ;
  151. if(index % 99 == 0)
  152. {
  153. priorityList = Shuffle(priorityList);
  154. }
  155. return priorityList[index % 99];
  156. }
  157. // Fisher-Yates shuffle
  158. public List<int> Shuffle(List<int> list)
  159. {
  160. int n = list.Count;
  161. while(n > 1)
  162. {
  163. n--;
  164. int k = Random.Range(0, n + 1);
  165. int tmp = list[k];
  166. list[k] = list[n];
  167. list[n] = tmp;
  168. }
  169. return list;
  170. }
  171. }