InstantiatePrefab.cs 7.0 KB

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