RandomInstancing.cs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. using System.Collections;
  2. using UnityEngine;
  3. using System.Collections.Generic;
  4. using UnityEngine.AI;
  5. namespace Unity.AI.Navigation.Samples
  6. {
  7. /// <summary>
  8. /// Fill 5x5 tiles around the local position procedurally by instantiating prefabs at random positions/orientations
  9. /// </summary>
  10. [DefaultExecutionOrder(-200)]
  11. public class RandomInstancing : MonoBehaviour
  12. {
  13. public GameObject m_Prefab;
  14. public int m_PoolSize = 250;
  15. public int m_InstancesPerTile = 10;
  16. public bool m_RandomPosition = true;
  17. public bool m_RandomOrientation = true;
  18. public float m_Height;
  19. public int m_BaseHash = 347652783;
  20. public float m_Size = 100.0f;
  21. [SerializeField]
  22. Transform parent;
  23. [SerializeField]
  24. NavMeshAgent trackedAgent;
  25. List<Transform> m_Instances = new List<Transform>();
  26. int m_Used;
  27. int m_LocX, m_LocZ;
  28. void Awake()
  29. {
  30. for (int i = 0; i < m_PoolSize; ++i)
  31. {
  32. var go = Instantiate(m_Prefab, Vector3.zero, Quaternion.identity, parent) as GameObject;
  33. go.SetActive(false);
  34. m_Instances.Add(go.transform);
  35. }
  36. }
  37. void OnEnable()
  38. {
  39. m_LocX = ~0;
  40. m_LocZ = ~0;
  41. UpdateInstances();
  42. }
  43. void OnDestroy()
  44. {
  45. for (int i = 0; i < m_Instances.Count; ++i)
  46. {
  47. if (m_Instances[i])
  48. Destroy(m_Instances[i].gameObject);
  49. }
  50. m_Instances.Clear();
  51. }
  52. void Update()
  53. {
  54. if (trackedAgent.remainingDistance > 0.1f)
  55. {
  56. UpdateInstances();
  57. }
  58. }
  59. void UpdateInstances()
  60. {
  61. var x = (int)Mathf.Floor(transform.position.x / m_Size);
  62. var z = (int)Mathf.Floor(transform.position.z / m_Size);
  63. if (x == m_LocX && z == m_LocZ)
  64. return;
  65. m_LocX = x;
  66. m_LocZ = z;
  67. m_Used = 0;
  68. for (var i = x - 2; i <= x + 2; ++i)
  69. {
  70. for (var j = z - 2; j <= z + 2; ++j)
  71. {
  72. var count = UpdateTileInstances(i, j);
  73. if (count != m_InstancesPerTile)
  74. return;
  75. }
  76. }
  77. // Deactivate the remaining active elements in the pool.
  78. // Here we assume all active elements are contiguous and first in the list.
  79. for (int i = m_Used; i < m_PoolSize && m_Instances[i].gameObject.activeSelf; ++i)
  80. m_Instances[i].gameObject.SetActive(false);
  81. }
  82. int UpdateTileInstances(int i, int j)
  83. {
  84. var seed = Hash2(i, j) ^ m_BaseHash;
  85. var count = System.Math.Min(m_InstancesPerTile, m_PoolSize - m_Used);
  86. for (var end = m_Used + count; m_Used < end; ++m_Used)
  87. {
  88. float x = 0;
  89. float y = 0;
  90. if (m_RandomPosition)
  91. {
  92. x = Random(ref seed);
  93. y = Random(ref seed);
  94. }
  95. var pos = new Vector3((i + x) * m_Size, m_Height, (j + y) * m_Size);
  96. if (m_RandomOrientation)
  97. {
  98. float r = 360.0f * Random(ref seed);
  99. m_Instances[m_Used].rotation = Quaternion.AngleAxis(r, Vector3.up);
  100. }
  101. m_Instances[m_Used].position = pos;
  102. m_Instances[m_Used].gameObject.SetActive(true);
  103. }
  104. if (count < m_InstancesPerTile)
  105. Debug.LogWarning("Pool exhausted", this);
  106. return count;
  107. }
  108. static int Hash2(int i, int j)
  109. {
  110. return (i * 73856093) ^ (j * 19349663);
  111. }
  112. static float Random(ref int seed)
  113. {
  114. seed = (seed ^ 123459876);
  115. var k = seed / 127773;
  116. seed = 16807 * (seed - k * 127773) - 2836 * k;
  117. if (seed < 0)
  118. seed = seed + 2147483647;
  119. float ran0 = seed * 1.0f / 2147483647.0f;
  120. seed = (seed ^ 123459876);
  121. return ran0;
  122. }
  123. }
  124. }