LaserGun.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. #if UNITY_2019_3_OR_NEWER
  5. using UnityEngine.XR;
  6. #endif
  7. /// <summary>
  8. /// Fires a laser when the user issues a command.
  9. /// If there is a ZEDControllerTracker in the same object, and the Oculus Integration or SteamVR plugins are installed,
  10. /// it'll automatically check them for inputs.
  11. /// </summary>
  12. [RequireComponent(typeof(AudioSource))]
  13. public class LaserGun : MonoBehaviour
  14. {
  15. /// <summary>
  16. /// What we spawn when the trigger is pulled.
  17. /// </summary>
  18. [Tooltip("What we spawn when the trigger is pulled.")]
  19. public GameObject laserShotPrefab;
  20. /// <summary>
  21. /// Anchor object of the PointerBead, which works as a crosshair.
  22. /// </summary>
  23. [Tooltip("Anchor object of the PointerBead, which works as a crosshair.")]
  24. public GameObject laserPointerBeadHolder;
  25. /// <summary>
  26. /// Anchor point where the laser spawns.
  27. /// </summary>
  28. [Tooltip("Anchor point where the laser spawns.")]
  29. public Transform laserSpawnLocation;
  30. /// <summary>
  31. /// Reference to the scene's primary ZEDManager component. Used for placing the crosshair.
  32. /// </summary>
  33. [Tooltip("Reference to the scene's primary ZEDManager component. Used for placing the crosshair.")]
  34. public ZEDManager zedManager = null;
  35. /// <summary>
  36. /// Reference to the object that will be placed at the end of the laser.
  37. /// </summary>
  38. private GameObject pointerbead;
  39. /// <summary>
  40. /// Reference to the audio source
  41. /// </summary>
  42. private AudioSource audiosource;
  43. /// <summary>
  44. /// The gameobject's controller for tracking and input.
  45. /// </summary>
  46. private ZEDControllerTracker_DemoInputs objecttracker;
  47. IEnumerator Start()
  48. {
  49. audiosource = GetComponent<AudioSource>();
  50. if (zedManager == null)
  51. {
  52. zedManager = FindObjectOfType<ZEDManager>();
  53. if (ZEDManager.GetInstances().Count > 1)
  54. {
  55. Debug.Log("Warning: " + gameObject + " ZEDManager reference not set, but there are multiple ZEDManagers in the scene. " +
  56. "Setting to first available ZEDManager, which may cause undesirable crosshair positions.");
  57. }
  58. }
  59. if (laserPointerBeadHolder != null)
  60. {
  61. //Get the laser bead from the parent/achor object.
  62. pointerbead = laserPointerBeadHolder.transform.GetChild(0).gameObject;
  63. //Disable the laser bead to wait for the ZED to initialize.
  64. pointerbead.SetActive(false);
  65. }
  66. //Wait for VR Controllers to initilize
  67. yield return new WaitForSeconds(1f);
  68. objecttracker = GetComponent<ZEDControllerTracker_DemoInputs>();
  69. if (objecttracker != null)
  70. {
  71. #if ZED_STEAM_VR
  72. if (objecttracker.index >= 0)
  73. yield break;
  74. #endif
  75. #if ZED_OCULUS
  76. if (OVRInput.GetConnectedControllers().ToString().ToLower().Contains("touch"))
  77. yield break;
  78. #endif
  79. // If it got here then there's no VR Controller connected
  80. int children = transform.childCount;
  81. for (int i = 0; i < children; ++i)
  82. transform.GetChild(i).gameObject.SetActive(false);
  83. //this.enabled = false;
  84. }
  85. else
  86. {
  87. //If its not attached to an object tracker
  88. var otherObjectTracker = FindObjectsOfType<ZEDControllerTracker>();
  89. if (otherObjectTracker != null)
  90. {
  91. int children = transform.childCount;
  92. #if ZED_STEAM_VR
  93. foreach (ZEDControllerTracker trackers in otherObjectTracker)
  94. {
  95. if (trackers.index >= 0)
  96. {
  97. for (int i = 0; i < children; ++i)
  98. transform.GetChild(i).gameObject.SetActive(false);
  99. this.enabled = false;
  100. yield break;
  101. }
  102. }
  103. #endif
  104. #if ZED_OCULUS
  105. if (OVRManager.isHmdPresent)
  106. {
  107. if (OVRInput.GetConnectedControllers().ToString().ToLower().Contains("touch"))
  108. {
  109. for (int i = 0; i < children; ++i)
  110. transform.GetChild(i).gameObject.SetActive(false);
  111. //this.enabled = false;
  112. yield break;
  113. }
  114. }
  115. #endif
  116. }
  117. }
  118. }
  119. // Update is called once per frame
  120. void Update()
  121. {
  122. //Do we have a Pointer Bead to position in the world?
  123. if (laserPointerBeadHolder != null)
  124. {
  125. Vector3 crosshairpoint;
  126. Vector3 crosshairnormal;
  127. //Point the bead at the closest thing in front of the camera.
  128. if (FindCrosshairPosition(out crosshairpoint, out crosshairnormal))
  129. {
  130. //We hit something. Make sure the bead is active.
  131. pointerbead.SetActive(true);
  132. //Position the bead a the collision point, and make it face you.
  133. pointerbead.transform.position = crosshairpoint;
  134. if (crosshairnormal.magnitude > 0.0001f)
  135. pointerbead.transform.rotation = Quaternion.LookRotation(crosshairnormal);
  136. }
  137. else
  138. {
  139. //We didn't hit anything. Disable the bead object.
  140. pointerbead.SetActive(false);
  141. }
  142. }
  143. //Check to see if any valid fire keys are triggered.
  144. //This is more complex than often necessary so as to work out-of-the-box for a variety of configurations.
  145. //If using/extending this script for your own project, it's recommended to use Input.GetButtonDown() with a custom Input, use Unity.XR, or interface with a VR SDK.
  146. bool buttondown = false;
  147. //Check for keys present on all systems
  148. //Use objecttracker to know if this controller was intended to be on a VR controller.
  149. if (objecttracker == null && (Input.GetKeyDown(KeyCode.Mouse0) || Input.GetKeyDown(KeyCode.Space)))
  150. {
  151. buttondown = true;
  152. }
  153. #if ZED_OCULUS
  154. //Update whether the Touch controllers are active.
  155. int children = transform.childCount;
  156. if (OVRManager.isHmdPresent)
  157. {
  158. if (OVRInput.GetConnectedControllers().ToString().ToLower().Contains("touch"))
  159. {
  160. for (int i = 0; i < children; ++i)
  161. transform.GetChild(i).gameObject.SetActive(true);
  162. }
  163. else
  164. {
  165. for (int i = 0; i < children; ++i)
  166. transform.GetChild(i).gameObject.SetActive(false);
  167. }
  168. }
  169. //We're controlling the fire Rate. OVRInput doesn't have a GetDown function for the IndexTrigger. Only an axis output.
  170. if (objecttracker != null)
  171. {
  172. buttondown = objecttracker.CheckFireButton(ControllerButtonState.Down);
  173. //OVRInput.Update();
  174. }
  175. #endif
  176. #if ZED_STEAM_VR
  177. //Looks for any input from this controller through SteamVR
  178. if (objecttracker != null)
  179. {
  180. buttondown = objecttracker.CheckFireButton(ControllerButtonState.Down);
  181. }
  182. #endif
  183. if (buttondown)
  184. {
  185. Fire();
  186. }
  187. }
  188. /// <summary>
  189. /// Tests the depth of both the real and virtual in the center of the screen, and returns the world position of the closest one.
  190. /// </summary>
  191. /// <param name="crosshairpoint">Where the crosshair should be rendered.</param>
  192. /// <param name="collisionnormal">The normal vector of the surface aimed at, for rotating the crosshair accordingly if desired.</param>
  193. /// <returns>False if there is no valid object, real or virtual, on which to place the crosshair. </returns>
  194. private bool FindCrosshairPosition(out Vector3 crosshairpoint, out Vector3 collisionnormal)
  195. {
  196. //Find the distance to the real world. The bool will be false if there is an error reading the depth at the center of the screen.
  197. Vector3 realpoint = Vector3.zero;
  198. float realdistance = 20f; //Arbitrary distance to put the crosshair if it hits nothing at all. Chosen by ZED's max range.
  199. bool foundrealdistance = false;
  200. if (ZEDSupportFunctions.HitTestOnRay(zedManager.zedCamera, zedManager.GetMainCamera(), laserPointerBeadHolder.transform.position,
  201. laserPointerBeadHolder.transform.rotation, 5f, 0.01f, out realpoint))
  202. {
  203. realdistance = Vector3.Distance(laserPointerBeadHolder.transform.position, realpoint);
  204. foundrealdistance = true;
  205. }
  206. //Find the distance to the virtual. The bool will be false if there are no colliders ahead of you.
  207. RaycastHit hitinfo;
  208. bool foundvirtualdistance = Physics.Raycast(laserPointerBeadHolder.transform.position, laserPointerBeadHolder.transform.rotation * Vector3.forward, out hitinfo);
  209. //If we didn't find either, return false so the laser and bead can be disabled.
  210. if (!foundrealdistance && !foundvirtualdistance)
  211. {
  212. crosshairpoint = Vector3.zero;
  213. collisionnormal = Vector3.zero;
  214. return false;
  215. }
  216. //Decide if we use the real or virtual distance
  217. if (!foundvirtualdistance || realdistance < hitinfo.distance)
  218. {
  219. //The real world is closer. Give the position of the real world pixel and return true.
  220. crosshairpoint = realpoint;
  221. ZEDSupportFunctions.GetNormalAtWorldLocation(zedManager.zedCamera, realpoint, sl.REFERENCE_FRAME.WORLD, zedManager.GetMainCamera(), out collisionnormal);
  222. return true;
  223. }
  224. else
  225. {
  226. //The virtual world is closer, or they're tied. Return the world posiiton where the raycast hit the virtual collider.
  227. crosshairpoint = hitinfo.point;
  228. collisionnormal = hitinfo.normal;
  229. return true;
  230. }
  231. }
  232. /// <summary>
  233. /// Spawns the laser prefab at the spawn anchor.
  234. /// </summary>
  235. void Fire()
  236. {
  237. //Create the shot and position/rotate it accordingly.
  238. GameObject blastershot = Instantiate(laserShotPrefab);
  239. blastershot.transform.position = laserSpawnLocation != null ? laserSpawnLocation.transform.position : transform.position;
  240. blastershot.transform.rotation = laserSpawnLocation != null ? laserSpawnLocation.transform.rotation : transform.rotation;
  241. if (laserPointerBeadHolder != null && pointerbead.transform.localPosition != Vector3.zero)
  242. {
  243. blastershot.transform.LookAt(pointerbead.transform.position);
  244. }
  245. //Play a sound
  246. if (audiosource)
  247. {
  248. audiosource.Play();
  249. }
  250. }
  251. #if ZED_OCULUS
  252. /// <summary>
  253. /// Returns if this script is bound to an Oculus Touch controller that is currently not connected.
  254. /// For example, if it's a Right Controller but only the left is connected, it returns false.
  255. /// If not bound to a controller, returns true.
  256. /// </summary>
  257. /// <returns></returns>
  258. private bool IsConnectedController()
  259. {
  260. if (!objecttracker) return true; //Not attached to a tracker. Return true since it doesn't depend on a controller to be alive.
  261. if (objecttracker.deviceToTrack != ZEDControllerTracker.Devices.LeftController && objecttracker.deviceToTrack != ZEDControllerTracker.Devices.RightController)
  262. return true; //Not bound to a left or right controller, so let it live.
  263. string connectedcontrollers = OVRInput.GetConnectedControllers().ToString().ToLower();
  264. if (connectedcontrollers == "touch") return true; //Both controllers are connected, so
  265. if (objecttracker.deviceToTrack == ZEDControllerTracker.Devices.LeftController && connectedcontrollers == "ltouch") return true; //Left controller only.
  266. if (objecttracker.deviceToTrack == ZEDControllerTracker.Devices.RightController && connectedcontrollers == "rtouch") return true; //Right controller only.
  267. return false;
  268. }
  269. #endif
  270. }