DroneSpawner.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5. /// <summary>
  6. /// Spawns a given Drone prefab when there is not one already in the scene.
  7. /// It will only spawn drones in front of the user and when it can find a location where it wouldn't intersect the real world.
  8. /// It will also not spawn drones if one already exists, or if _canSpawn is set to false, which is the case on start until the warning message finishes displaying).
  9. /// Used in the ZED Drone Battle example scene.
  10. /// </summary>
  11. public class DroneSpawner : MonoBehaviour
  12. {
  13. /// <summary>
  14. /// The Drone prefab to spawn.
  15. /// </summary>
  16. [Tooltip("The Drone prefab to spawn.")]
  17. public GameObject dronePrefab;
  18. /// <summary>
  19. /// The warning message to spawn at the start of the scene. Text is set by WarningDisplay() but the prefab holds the UI elements.
  20. /// </summary>
  21. [Tooltip("The warning message to spawn at the start of the scene. Text is set by WarningDisplay() but the prefab holds the UI elements.")]
  22. public GameObject spawnWarning;
  23. /// <summary>
  24. /// How far a point must be from real geometry to be considered a valid spawn location.
  25. /// </summary>
  26. [Tooltip("How far a point must be from real geometry to be considered a valid spawn location.")]
  27. public float clearRadius = 1f;
  28. /// <summary>
  29. /// How many times should we check near a potential spawn point to see if there are any obstacles around it.
  30. /// Higher numbers reduce the chance of spawning partially inside an object but may cause stutters.
  31. /// </summary>
  32. [Tooltip("How many times should we check near a potential spawn point to see if there are any obstacles around it. " +
  33. "Higher numbers reduce the chance of spawning partially inside an object but may cause stutters.")]
  34. public int radiusCheckRate = 200;
  35. /// <summary>
  36. /// The maximum number of collisions detected near a spawn point allowed. Higher values make it less likely for a drone to move inside an object, but too high and it may not move at all.
  37. /// </summary>
  38. [Tooltip("The maximum number of collisions detected near a spawn point allowed. " +
  39. "Higher values make it less likely for a drone to spawn inside an object, but too high and it may not spawn at all.")]
  40. public float percentagethreshold = 0.4f;
  41. /// <summary>
  42. /// How long we wait (in seconds) after a drone's death before spawning a new one.
  43. /// </summary>
  44. [Tooltip("How long we wait (in seconds) after a drone's death before spawning a new one.")]
  45. public float respawnTimer = 2f;
  46. /// <summary>
  47. /// The maximum distance from a player that a drone can be spawned.
  48. /// </summary>
  49. [Tooltip("The maximum distance from a player that a drone can be spawned.")]
  50. public float maxSpawnDistance = 6f;
  51. /// <summary>
  52. /// The random position to be used when spawning a new drone.
  53. /// Assigned only once a valid position has been found.
  54. /// </summary>
  55. private Vector3 newspawnposition;
  56. /// <summary>
  57. /// Time counting down before spawning a new drone.
  58. /// </summary>
  59. private float respawncountdown;
  60. /// <summary>
  61. /// The last spawned drone that is still active.
  62. /// </summary>
  63. private Drone currentdrone;
  64. /// <summary>
  65. /// Main ZEDManager to use for determining where the drone spawns and what it's looking at when it does.
  66. /// If left empty, will choose the first available ZEDManager in the scene.
  67. /// </summary>
  68. [Tooltip("Main ZEDManager to use for determining where the drone spawns and what it's looking at when it does. " +
  69. "If left empty, will choose the first available ZEDManager in the scene. ")]
  70. public ZEDManager zedManager = null;
  71. /// <summary>
  72. /// Needed for various calls to ZEDSupportFunctions.cs, so it can transform ZED depth info into world space.
  73. /// </summary>
  74. private Camera cam;
  75. /// <summary>
  76. /// Whether we already displayed the warning message before spawning the drones.
  77. /// </summary>
  78. private bool displayedwarning;
  79. /// <summary>
  80. /// Last check before starting to spawn drones, to allow warning message to be displayed.
  81. /// </summary>
  82. private bool canspawn;
  83. private bool _readyToSpawn
  84. {
  85. get
  86. {
  87. if (currentdrone == null && respawncountdown <= 0) return true;
  88. else return false;
  89. }
  90. }
  91. // Use this for initialization
  92. void Start()
  93. {
  94. zedManager = FindObjectOfType<ZEDManager> ();
  95. //Set the countdown Timer;
  96. respawncountdown = respawnTimer;
  97. //Get the ZED camera
  98. cam = zedManager.GetMainCamera();
  99. }
  100. // Update is called once per frame
  101. void Update()
  102. {
  103. //Reposition the screen in front our the Camera when its ready
  104. if (zedManager && zedManager.IsZEDReady && !displayedwarning)
  105. {
  106. StartCoroutine(WarningDisplay());
  107. displayedwarning = true;
  108. }
  109. if (!canspawn)
  110. return;
  111. //Tick down the respawn timer if applicable
  112. if (respawncountdown > 0)
  113. {
  114. respawncountdown -= Time.deltaTime;
  115. }
  116. if (_readyToSpawn) //We've got no drone and the respawn timer has elapsed
  117. {
  118. //Try to spawn a drone in a random place in front of the player. We'll do this only once per frame to avoid stuttering.
  119. if (CheckRandomSpawnLocation(out newspawnposition))
  120. {
  121. currentdrone = SpawnDrone(newspawnposition);
  122. }
  123. }
  124. }
  125. Text msg;
  126. /// <summary>
  127. /// Positions, configures and displays the warning text at the start of the game.
  128. /// Sets canspawn (which defaults to false) to true once finished, so that drones only spawn afterward.
  129. /// </summary>
  130. /// <returns></returns>
  131. IEnumerator WarningDisplay()
  132. {
  133. GameObject warningMsg = Instantiate(spawnWarning); //Spawn the message prefab, which doesn't have the correct text yet.
  134. if (zedManager != null) {
  135. warningMsg.transform.position = zedManager.OriginPosition + zedManager.OriginRotation * (Vector3.forward * 2);
  136. Quaternion newRot = Quaternion.LookRotation (zedManager.OriginPosition - warningMsg.transform.position, Vector3.up);
  137. warningMsg.transform.eulerAngles = new Vector3 (0, newRot.eulerAngles.y + 180, 0);
  138. }
  139. Text msg = warningMsg.GetComponentInChildren<Text>(); //Find the text in the prefab.
  140. yield return new WaitForSeconds(1f);
  141. //Add the letters to the message one at a time for effect.
  142. int i = 0;
  143. string oldText = "WARNING! DEPLOYING DRONES!";
  144. string newText = "";
  145. while (i < oldText.Length)
  146. {
  147. newText += oldText[i++];
  148. yield return new WaitForSeconds(0.15F);
  149. msg.text = newText;
  150. }
  151. yield return new WaitForSeconds(3f); //Let the user read it for a few seconds.
  152. //Change the warning message by clearing it and adding letters one at a time like before.
  153. i = 0;
  154. oldText = "DEFEND YOURSELF!";
  155. newText = "";
  156. while (i < oldText.Length)
  157. {
  158. newText += oldText[i++];
  159. yield return new WaitForSeconds(0.1F);
  160. msg.text = newText;
  161. }
  162. yield return new WaitForSeconds(3f);//Let the user read it for a few seconds.
  163. Destroy(warningMsg);
  164. canspawn = true; //Drones can now spawn.
  165. }
  166. /// <summary>
  167. /// Looks for a random point in a radius around itself.
  168. /// Upon collision, the point is moved slightly towards the camera and if its too far it's set to "maxSpawnDistance".
  169. /// A more thorough search is then done for any other obstacles around it, also, in a radius.
  170. /// If the number of collision doesn't exeeds the set threshold, then return true and output the new position.
  171. /// </summary>
  172. /// <returns><c>true</c>, if random spawn location was checked, <c>false</c> otherwise.</returns>
  173. /// <param name="newRandomPos">Random position.</param>
  174. private bool CheckRandomSpawnLocation(out Vector3 newRandomPos)
  175. {
  176. //We can't do anything if the ZED isn't initialized.
  177. if(!zedManager.IsZEDReady)
  178. {
  179. newRandomPos = Vector3.zero;
  180. return false;
  181. }
  182. //Pick a screen position at random between 0.25 and 0.75.
  183. Vector2 randomScreenPoint = new Vector2(Random.Range(0.25f, 0.75f) * Screen.width, Random.Range(0.25f, 0.75f) * Screen.height);
  184. //Get the world position of that position in the real world
  185. Vector3 randomWorldPoint;
  186. bool foundWorldPoint = ZEDSupportFunctions.GetWorldPositionAtPixel(zedManager.zedCamera,randomScreenPoint, cam, out randomWorldPoint);
  187. if (!foundWorldPoint) //We can't read depth from that point.
  188. {
  189. newRandomPos = Vector3.zero;
  190. return false;
  191. }
  192. float firstDistance = Vector3.Distance (cam.transform.position, randomWorldPoint);
  193. float newClearRadius;
  194. //Check that the distance isn't too far.
  195. if (firstDistance > maxSpawnDistance)
  196. newClearRadius = firstDistance - maxSpawnDistance;
  197. else
  198. newClearRadius = clearRadius;
  199. //If we spawn the drone at that world point, it'll spawn inside a wall. Bring it between you and that wall.
  200. Quaternion directionToCamera = Quaternion.LookRotation(cam.transform.position - randomWorldPoint, Vector3.up);
  201. Vector3 closerWorldPoint = randomWorldPoint + directionToCamera * Vector3.forward * newClearRadius;
  202. //Check that distance isn't too close
  203. float secondDistance = Vector3.Distance(cam.transform.position, closerWorldPoint);
  204. if (secondDistance < 1f)
  205. {
  206. newRandomPos = Vector3.zero;
  207. return false;
  208. }
  209. //Also check nearby points in a sphere of radius=ClearRadius to make sure the whole drone has a clear space.
  210. if (ZEDSupportFunctions.HitTestOnSphere(zedManager.zedCamera, cam, closerWorldPoint, 1f, radiusCheckRate, percentagethreshold))
  211. {
  212. //Not clear.
  213. newRandomPos = Vector3.zero;
  214. return false;
  215. }
  216. else
  217. {
  218. //Clear.
  219. newRandomPos = closerWorldPoint;
  220. return true;
  221. }
  222. }
  223. /// <summary>
  224. /// Spawns the drone.
  225. /// </summary>
  226. /// <returns>The drone.</returns>
  227. /// <param name="spawnPosition">Spawn position.</param>
  228. public Drone SpawnDrone(Vector3 spawnPosition)
  229. {
  230. //Spawn the drone
  231. GameObject dronego = Instantiate(dronePrefab);
  232. Drone dronecomponent = dronego.GetComponentInChildren<Drone>();
  233. if (!dronecomponent)
  234. {
  235. Debug.LogError("Drone prefab spawned by DroneSpawner does not contain the Drone.cs component.");
  236. }
  237. //Give the drone a reference to this object so it can clear its reference and set the timer properly when it dies
  238. dronecomponent.SetMySpawner(this);
  239. //Set the drone's transform values
  240. dronego.transform.position = newspawnposition; //Assign the random Pos generated in CheckRandomSpawnLocation();
  241. dronego.transform.rotation = Quaternion.LookRotation(zedManager.GetMainCameraTransform().position - spawnPosition, Vector3.up); //Make it look at the player.
  242. return dronecomponent;
  243. }
  244. /// <summary>
  245. /// Clears the drone reference, which will cause the script to start trying to spawn a new one again.
  246. /// Should be called by the drone itself in its OnDestroy().
  247. /// </summary>
  248. public void ClearDrone()
  249. {
  250. currentdrone = null;
  251. respawncountdown = respawnTimer;
  252. print("Drone destroyed");
  253. }
  254. }