ArUcoDrone.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. /// <summary>
  5. /// Behavior for drone objects in the ZED ArUco Drone Wars sample.
  6. /// When other drones are in front of this drone and within range, will fire projectiles until it's dead.
  7. /// </summary>
  8. [RequireComponent(typeof(Collider))]
  9. public class ArUcoDrone : MonoBehaviour
  10. {
  11. /// <summary>
  12. /// How close other drones have to be for this drone to start firing. Must also be directly in front of the drone.
  13. /// </summary>
  14. [Tooltip("How close other drones have to be for this drone to start firing. Must also be directly in front of the drone.")]
  15. public float shootRangeMeters = 0.25f;
  16. /// <summary>
  17. /// Time between each shot when firing.
  18. /// </summary>
  19. [Tooltip("Time between each shot when firing.")]
  20. public float shootCooldownSeconds = 0.33f;
  21. /// <summary>
  22. /// How much damage this drone can take before dying.
  23. /// </summary>
  24. [Tooltip("How much damage this drone can take before dying.")]
  25. public float maxHealth = 10;
  26. /// <summary>
  27. /// How much health the drone currently has. Goes down when a ArUcoDroneLaser hits it and calls TakeDamage().
  28. /// </summary>
  29. private float currentHealth;
  30. /// <summary>
  31. /// Time since last shot. Set to zero after it exceeds shootCooldownSeconds.
  32. /// </summary>
  33. private float shootTimer = 0f;
  34. /// <summary>
  35. /// Whether the drone can shoot right now. Simply checks if shootTimer is 0, which the Shoot() coroutine will set it to when finished.
  36. /// </summary>
  37. private bool canShoot
  38. {
  39. get
  40. {
  41. return shootTimer <= 0f;
  42. }
  43. }
  44. /// <summary>
  45. /// Prefab of the projectile object the drone shoots. Should have an ArUcoDroneLaser component on it.
  46. /// </summary>
  47. [Tooltip("Prefab of the projectile object the drone shoots. Should have an ArUcoDroneLaser component on it.")]
  48. public GameObject projectilePrefab;
  49. /// <summary>
  50. /// Transform where laser shots are spawned, and from where the drone's laser range is tested.
  51. /// </summary>
  52. [Tooltip("Transform where laser shots are spawned, and from where the drone's laser range is tested.")]
  53. public Transform shootAnchorObject;
  54. /// <summary>
  55. /// Object spawned when the drone dies.
  56. /// </summary>
  57. [Tooltip("Object spawned when the drone dies.")]
  58. public GameObject explosionPrefab;
  59. private Collider col;
  60. /// <summary>
  61. /// Sound played every time the drone fires a laser. Requires an AudioSource attached to this GameObject.
  62. /// </summary>
  63. [Tooltip("Sound played every time the drone fires a laser. Requires an AudioSource attached to this GameObject.")]
  64. public AudioClip laserShootSound;
  65. private AudioSource audioSource;
  66. /// <summary>
  67. /// If true, will use the ZED's depth to check if there's a real-world object between this drone and its target.
  68. /// </summary>
  69. [Tooltip("If true, will use the ZED's depth to check if there's a real-world object between this drone and its target.")]
  70. public bool checkRealWorldObstaclesBeforeShooting = false;
  71. // Use this for initialization
  72. void Start ()
  73. {
  74. col = GetComponent<Collider>();
  75. currentHealth = maxHealth;
  76. audioSource = GetComponent<AudioSource>();
  77. }
  78. private void OnEnable()
  79. {
  80. shootTimer = 0f; //If the drone was disabled from being hidden, this prevents laser not being able to fire.
  81. }
  82. // Update is called once per frame
  83. void Update ()
  84. {
  85. if(canShoot == true && IsAimingAtTarget() == true)
  86. {
  87. StartCoroutine(Shoot());
  88. }
  89. }
  90. /// <summary>
  91. /// Checks if another ArUcoDrone is in front of the drone and within range.
  92. /// Will also check if there's a real object in the way, if checkRealWorldObstaclesBeforeShooting is true.
  93. /// </summary>
  94. /// <returns>True if there's a valid target in front of the drone.</returns>
  95. private bool IsAimingAtTarget()
  96. {
  97. //First make sure there's a valid virtual target in front of the drone.
  98. Ray ray = new Ray(shootAnchorObject.position, shootAnchorObject.rotation * Vector3.forward);
  99. RaycastHit[] hits = Physics.RaycastAll(ray, shootRangeMeters);
  100. bool foundvirtualtarget = false;
  101. float nearestdist = Mathf.Infinity;
  102. foreach(RaycastHit hit in hits)
  103. {
  104. ArUcoDrone otherdrone = hit.transform.GetComponent<ArUcoDrone>();
  105. if(otherdrone != null && otherdrone != this)
  106. {
  107. foundvirtualtarget = true;
  108. if (hit.distance < nearestdist) nearestdist = hit.distance;
  109. }
  110. }
  111. if (!foundvirtualtarget) return false;
  112. if (checkRealWorldObstaclesBeforeShooting) //Make sure there's not a real-world obstacle in the way of the target.
  113. {
  114. //If there is one, check to make sure there's not a real-world object in the way.
  115. Vector3 collisionpoint; //Not used but required for HitTestOnRay function.
  116. foreach (ZEDManager manager in ZEDManager.GetInstances())
  117. {
  118. bool hitreal = ZEDSupportFunctions.HitTestOnRay(manager.zedCamera, manager.GetLeftCamera(), shootAnchorObject.transform.position, shootAnchorObject.transform.rotation,
  119. nearestdist, 0.01f, out collisionpoint, false, 0.1f);
  120. if (hitreal) return false;
  121. }
  122. return true;
  123. }
  124. else return true; //We're not checking against the real world, and we already found a virtual object, so fire.
  125. }
  126. /// <summary>
  127. /// Instantiate a projectilePrefab at the anchor point.
  128. /// </summary>
  129. private void SpawnProjectile()
  130. {
  131. //SHOOT STUFF.
  132. if (!projectilePrefab) return;
  133. GameObject lasergo = Instantiate(projectilePrefab, shootAnchorObject.transform.position, shootAnchorObject.transform.rotation);
  134. //lasergo.transform.localScale = transform.lossyScale;
  135. lasergo.name = gameObject.name + "'s Laser";
  136. ArUcoDroneLaser laser = lasergo.GetComponentInChildren<ArUcoDroneLaser>();
  137. if(!laser)
  138. {
  139. Debug.LogError("projectilePrefab assigned to " + gameObject.name + " does not have an ArUcoDroneLaser component.");
  140. Destroy(lasergo);
  141. return;
  142. }
  143. laser.spawningDrone = this;
  144. if(audioSource && laserShootSound)
  145. {
  146. audioSource.PlayOneShot(laserShootSound);
  147. }
  148. }
  149. /// <summary>
  150. /// Spawns a projectile and advances the shootTimer until the cooldown period is over - then resets it, allowing another shot.
  151. /// </summary>
  152. /// <returns></returns>
  153. private IEnumerator Shoot()
  154. {
  155. SpawnProjectile();
  156. while(shootTimer < shootCooldownSeconds)
  157. {
  158. shootTimer += Time.deltaTime;
  159. yield return null;
  160. }
  161. shootTimer = 0f;
  162. }
  163. /// <summary>
  164. /// Reduces the drone's health, and destroys it if it goes below zero.
  165. /// Called by ArUcoDroneLaser when it hits this drone's collider.
  166. /// </summary>
  167. public void TakeDamage(float damage)
  168. {
  169. currentHealth -= damage;
  170. //print("Took " + damage + " damage. Now at " + currentHealth + " HP.");
  171. if (currentHealth <= 0)
  172. {
  173. Die();
  174. }
  175. }
  176. /// <summary>
  177. /// Spawns an explosion and destroys this object.
  178. /// </summary>
  179. public void Die()
  180. {
  181. print(gameObject.name + " destroyed.");
  182. if(explosionPrefab)
  183. {
  184. Instantiate(explosionPrefab, transform.position, transform.rotation);
  185. }
  186. Destroy(gameObject);
  187. }
  188. }