using System.Collections; using System.Collections.Generic; using UnityEngine; /// /// Behavior for drone objects in the ZED ArUco Drone Wars sample. /// When other drones are in front of this drone and within range, will fire projectiles until it's dead. /// [RequireComponent(typeof(Collider))] public class ArUcoDrone : MonoBehaviour { /// /// How close other drones have to be for this drone to start firing. Must also be directly in front of the drone. /// [Tooltip("How close other drones have to be for this drone to start firing. Must also be directly in front of the drone.")] public float shootRangeMeters = 0.25f; /// /// Time between each shot when firing. /// [Tooltip("Time between each shot when firing.")] public float shootCooldownSeconds = 0.33f; /// /// How much damage this drone can take before dying. /// [Tooltip("How much damage this drone can take before dying.")] public float maxHealth = 10; /// /// How much health the drone currently has. Goes down when a ArUcoDroneLaser hits it and calls TakeDamage(). /// private float currentHealth; /// /// Time since last shot. Set to zero after it exceeds shootCooldownSeconds. /// private float shootTimer = 0f; /// /// Whether the drone can shoot right now. Simply checks if shootTimer is 0, which the Shoot() coroutine will set it to when finished. /// private bool canShoot { get { return shootTimer <= 0f; } } /// /// Prefab of the projectile object the drone shoots. Should have an ArUcoDroneLaser component on it. /// [Tooltip("Prefab of the projectile object the drone shoots. Should have an ArUcoDroneLaser component on it.")] public GameObject projectilePrefab; /// /// Transform where laser shots are spawned, and from where the drone's laser range is tested. /// [Tooltip("Transform where laser shots are spawned, and from where the drone's laser range is tested.")] public Transform shootAnchorObject; /// /// Object spawned when the drone dies. /// [Tooltip("Object spawned when the drone dies.")] public GameObject explosionPrefab; private Collider col; /// /// Sound played every time the drone fires a laser. Requires an AudioSource attached to this GameObject. /// [Tooltip("Sound played every time the drone fires a laser. Requires an AudioSource attached to this GameObject.")] public AudioClip laserShootSound; private AudioSource audioSource; /// /// If true, will use the ZED's depth to check if there's a real-world object between this drone and its target. /// [Tooltip("If true, will use the ZED's depth to check if there's a real-world object between this drone and its target.")] public bool checkRealWorldObstaclesBeforeShooting = false; // Use this for initialization void Start () { col = GetComponent(); currentHealth = maxHealth; audioSource = GetComponent(); } private void OnEnable() { shootTimer = 0f; //If the drone was disabled from being hidden, this prevents laser not being able to fire. } // Update is called once per frame void Update () { if(canShoot == true && IsAimingAtTarget() == true) { StartCoroutine(Shoot()); } } /// /// Checks if another ArUcoDrone is in front of the drone and within range. /// Will also check if there's a real object in the way, if checkRealWorldObstaclesBeforeShooting is true. /// /// True if there's a valid target in front of the drone. private bool IsAimingAtTarget() { //First make sure there's a valid virtual target in front of the drone. Ray ray = new Ray(shootAnchorObject.position, shootAnchorObject.rotation * Vector3.forward); RaycastHit[] hits = Physics.RaycastAll(ray, shootRangeMeters); bool foundvirtualtarget = false; float nearestdist = Mathf.Infinity; foreach(RaycastHit hit in hits) { ArUcoDrone otherdrone = hit.transform.GetComponent(); if(otherdrone != null && otherdrone != this) { foundvirtualtarget = true; if (hit.distance < nearestdist) nearestdist = hit.distance; } } if (!foundvirtualtarget) return false; if (checkRealWorldObstaclesBeforeShooting) //Make sure there's not a real-world obstacle in the way of the target. { //If there is one, check to make sure there's not a real-world object in the way. Vector3 collisionpoint; //Not used but required for HitTestOnRay function. foreach (ZEDManager manager in ZEDManager.GetInstances()) { bool hitreal = ZEDSupportFunctions.HitTestOnRay(manager.zedCamera, manager.GetLeftCamera(), shootAnchorObject.transform.position, shootAnchorObject.transform.rotation, nearestdist, 0.01f, out collisionpoint, false, 0.1f); if (hitreal) return false; } return true; } else return true; //We're not checking against the real world, and we already found a virtual object, so fire. } /// /// Instantiate a projectilePrefab at the anchor point. /// private void SpawnProjectile() { //SHOOT STUFF. if (!projectilePrefab) return; GameObject lasergo = Instantiate(projectilePrefab, shootAnchorObject.transform.position, shootAnchorObject.transform.rotation); //lasergo.transform.localScale = transform.lossyScale; lasergo.name = gameObject.name + "'s Laser"; ArUcoDroneLaser laser = lasergo.GetComponentInChildren(); if(!laser) { Debug.LogError("projectilePrefab assigned to " + gameObject.name + " does not have an ArUcoDroneLaser component."); Destroy(lasergo); return; } laser.spawningDrone = this; if(audioSource && laserShootSound) { audioSource.PlayOneShot(laserShootSound); } } /// /// Spawns a projectile and advances the shootTimer until the cooldown period is over - then resets it, allowing another shot. /// /// private IEnumerator Shoot() { SpawnProjectile(); while(shootTimer < shootCooldownSeconds) { shootTimer += Time.deltaTime; yield return null; } shootTimer = 0f; } /// /// Reduces the drone's health, and destroys it if it goes below zero. /// Called by ArUcoDroneLaser when it hits this drone's collider. /// public void TakeDamage(float damage) { currentHealth -= damage; //print("Took " + damage + " damage. Now at " + currentHealth + " HP."); if (currentHealth <= 0) { Die(); } } /// /// Spawns an explosion and destroys this object. /// public void Die() { print(gameObject.name + " destroyed."); if(explosionPrefab) { Instantiate(explosionPrefab, transform.position, transform.rotation); } Destroy(gameObject); } }