using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; /// /// Spawns a given Drone prefab when there is not one already in the scene. /// 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. /// 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). /// Used in the ZED Drone Battle example scene. /// public class DroneSpawner : MonoBehaviour { /// /// The Drone prefab to spawn. /// [Tooltip("The Drone prefab to spawn.")] public GameObject dronePrefab; /// /// The warning message to spawn at the start of the scene. Text is set by WarningDisplay() but the prefab holds the UI elements. /// [Tooltip("The warning message to spawn at the start of the scene. Text is set by WarningDisplay() but the prefab holds the UI elements.")] public GameObject spawnWarning; /// /// How far a point must be from real geometry to be considered a valid spawn location. /// [Tooltip("How far a point must be from real geometry to be considered a valid spawn location.")] public float clearRadius = 1f; /// /// How many times should we check near a potential spawn point to see if there are any obstacles around it. /// Higher numbers reduce the chance of spawning partially inside an object but may cause stutters. /// [Tooltip("How many times should we check near a potential spawn point to see if there are any obstacles around it. " + "Higher numbers reduce the chance of spawning partially inside an object but may cause stutters.")] public int radiusCheckRate = 200; /// /// 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. /// [Tooltip("The maximum number of collisions detected near a spawn point allowed. " + "Higher values make it less likely for a drone to spawn inside an object, but too high and it may not spawn at all.")] public float percentagethreshold = 0.4f; /// /// How long we wait (in seconds) after a drone's death before spawning a new one. /// [Tooltip("How long we wait (in seconds) after a drone's death before spawning a new one.")] public float respawnTimer = 2f; /// /// The maximum distance from a player that a drone can be spawned. /// [Tooltip("The maximum distance from a player that a drone can be spawned.")] public float maxSpawnDistance = 6f; /// /// The random position to be used when spawning a new drone. /// Assigned only once a valid position has been found. /// private Vector3 newspawnposition; /// /// Time counting down before spawning a new drone. /// private float respawncountdown; /// /// The last spawned drone that is still active. /// private Drone currentdrone; /// /// Main ZEDManager to use for determining where the drone spawns and what it's looking at when it does. /// If left empty, will choose the first available ZEDManager in the scene. /// [Tooltip("Main ZEDManager to use for determining where the drone spawns and what it's looking at when it does. " + "If left empty, will choose the first available ZEDManager in the scene. ")] public ZEDManager zedManager = null; /// /// Needed for various calls to ZEDSupportFunctions.cs, so it can transform ZED depth info into world space. /// private Camera cam; /// /// Whether we already displayed the warning message before spawning the drones. /// private bool displayedwarning; /// /// Last check before starting to spawn drones, to allow warning message to be displayed. /// private bool canspawn; private bool _readyToSpawn { get { if (currentdrone == null && respawncountdown <= 0) return true; else return false; } } // Use this for initialization void Start() { zedManager = FindObjectOfType (); //Set the countdown Timer; respawncountdown = respawnTimer; //Get the ZED camera cam = zedManager.GetMainCamera(); } // Update is called once per frame void Update() { //Reposition the screen in front our the Camera when its ready if (zedManager && zedManager.IsZEDReady && !displayedwarning) { StartCoroutine(WarningDisplay()); displayedwarning = true; } if (!canspawn) return; //Tick down the respawn timer if applicable if (respawncountdown > 0) { respawncountdown -= Time.deltaTime; } if (_readyToSpawn) //We've got no drone and the respawn timer has elapsed { //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. if (CheckRandomSpawnLocation(out newspawnposition)) { currentdrone = SpawnDrone(newspawnposition); } } } Text msg; /// /// Positions, configures and displays the warning text at the start of the game. /// Sets canspawn (which defaults to false) to true once finished, so that drones only spawn afterward. /// /// IEnumerator WarningDisplay() { GameObject warningMsg = Instantiate(spawnWarning); //Spawn the message prefab, which doesn't have the correct text yet. if (zedManager != null) { warningMsg.transform.position = zedManager.OriginPosition + zedManager.OriginRotation * (Vector3.forward * 2); Quaternion newRot = Quaternion.LookRotation (zedManager.OriginPosition - warningMsg.transform.position, Vector3.up); warningMsg.transform.eulerAngles = new Vector3 (0, newRot.eulerAngles.y + 180, 0); } Text msg = warningMsg.GetComponentInChildren(); //Find the text in the prefab. yield return new WaitForSeconds(1f); //Add the letters to the message one at a time for effect. int i = 0; string oldText = "WARNING! DEPLOYING DRONES!"; string newText = ""; while (i < oldText.Length) { newText += oldText[i++]; yield return new WaitForSeconds(0.15F); msg.text = newText; } yield return new WaitForSeconds(3f); //Let the user read it for a few seconds. //Change the warning message by clearing it and adding letters one at a time like before. i = 0; oldText = "DEFEND YOURSELF!"; newText = ""; while (i < oldText.Length) { newText += oldText[i++]; yield return new WaitForSeconds(0.1F); msg.text = newText; } yield return new WaitForSeconds(3f);//Let the user read it for a few seconds. Destroy(warningMsg); canspawn = true; //Drones can now spawn. } /// /// Looks for a random point in a radius around itself. /// Upon collision, the point is moved slightly towards the camera and if its too far it's set to "maxSpawnDistance". /// A more thorough search is then done for any other obstacles around it, also, in a radius. /// If the number of collision doesn't exeeds the set threshold, then return true and output the new position. /// /// true, if random spawn location was checked, false otherwise. /// Random position. private bool CheckRandomSpawnLocation(out Vector3 newRandomPos) { //We can't do anything if the ZED isn't initialized. if(!zedManager.IsZEDReady) { newRandomPos = Vector3.zero; return false; } //Pick a screen position at random between 0.25 and 0.75. Vector2 randomScreenPoint = new Vector2(Random.Range(0.25f, 0.75f) * Screen.width, Random.Range(0.25f, 0.75f) * Screen.height); //Get the world position of that position in the real world Vector3 randomWorldPoint; bool foundWorldPoint = ZEDSupportFunctions.GetWorldPositionAtPixel(zedManager.zedCamera,randomScreenPoint, cam, out randomWorldPoint); if (!foundWorldPoint) //We can't read depth from that point. { newRandomPos = Vector3.zero; return false; } float firstDistance = Vector3.Distance (cam.transform.position, randomWorldPoint); float newClearRadius; //Check that the distance isn't too far. if (firstDistance > maxSpawnDistance) newClearRadius = firstDistance - maxSpawnDistance; else newClearRadius = clearRadius; //If we spawn the drone at that world point, it'll spawn inside a wall. Bring it between you and that wall. Quaternion directionToCamera = Quaternion.LookRotation(cam.transform.position - randomWorldPoint, Vector3.up); Vector3 closerWorldPoint = randomWorldPoint + directionToCamera * Vector3.forward * newClearRadius; //Check that distance isn't too close float secondDistance = Vector3.Distance(cam.transform.position, closerWorldPoint); if (secondDistance < 1f) { newRandomPos = Vector3.zero; return false; } //Also check nearby points in a sphere of radius=ClearRadius to make sure the whole drone has a clear space. if (ZEDSupportFunctions.HitTestOnSphere(zedManager.zedCamera, cam, closerWorldPoint, 1f, radiusCheckRate, percentagethreshold)) { //Not clear. newRandomPos = Vector3.zero; return false; } else { //Clear. newRandomPos = closerWorldPoint; return true; } } /// /// Spawns the drone. /// /// The drone. /// Spawn position. public Drone SpawnDrone(Vector3 spawnPosition) { //Spawn the drone GameObject dronego = Instantiate(dronePrefab); Drone dronecomponent = dronego.GetComponentInChildren(); if (!dronecomponent) { Debug.LogError("Drone prefab spawned by DroneSpawner does not contain the Drone.cs component."); } //Give the drone a reference to this object so it can clear its reference and set the timer properly when it dies dronecomponent.SetMySpawner(this); //Set the drone's transform values dronego.transform.position = newspawnposition; //Assign the random Pos generated in CheckRandomSpawnLocation(); dronego.transform.rotation = Quaternion.LookRotation(zedManager.GetMainCameraTransform().position - spawnPosition, Vector3.up); //Make it look at the player. return dronecomponent; } /// /// Clears the drone reference, which will cause the script to start trying to spawn a new one again. /// Should be called by the drone itself in its OnDestroy(). /// public void ClearDrone() { currentdrone = null; respawncountdown = respawnTimer; print("Drone destroyed"); } }