using System.Collections; using System.Collections.Generic; using UnityEngine; #if UNITY_2019_3_OR_NEWER using UnityEngine.XR; #endif /// /// Fires a laser when the user issues a command. /// If there is a ZEDControllerTracker in the same object, and the Oculus Integration or SteamVR plugins are installed, /// it'll automatically check them for inputs. /// [RequireComponent(typeof(AudioSource))] public class LaserGun : MonoBehaviour { /// /// What we spawn when the trigger is pulled. /// [Tooltip("What we spawn when the trigger is pulled.")] public GameObject laserShotPrefab; /// /// Anchor object of the PointerBead, which works as a crosshair. /// [Tooltip("Anchor object of the PointerBead, which works as a crosshair.")] public GameObject laserPointerBeadHolder; /// /// Anchor point where the laser spawns. /// [Tooltip("Anchor point where the laser spawns.")] public Transform laserSpawnLocation; /// /// Reference to the scene's primary ZEDManager component. Used for placing the crosshair. /// [Tooltip("Reference to the scene's primary ZEDManager component. Used for placing the crosshair.")] public ZEDManager zedManager = null; /// /// Reference to the object that will be placed at the end of the laser. /// private GameObject pointerbead; /// /// Reference to the audio source /// private AudioSource audiosource; /// /// The gameobject's controller for tracking and input. /// private ZEDControllerTracker_DemoInputs objecttracker; IEnumerator Start() { audiosource = GetComponent(); if (zedManager == null) { zedManager = FindObjectOfType(); if (ZEDManager.GetInstances().Count > 1) { Debug.Log("Warning: " + gameObject + " ZEDManager reference not set, but there are multiple ZEDManagers in the scene. " + "Setting to first available ZEDManager, which may cause undesirable crosshair positions."); } } if (laserPointerBeadHolder != null) { //Get the laser bead from the parent/achor object. pointerbead = laserPointerBeadHolder.transform.GetChild(0).gameObject; //Disable the laser bead to wait for the ZED to initialize. pointerbead.SetActive(false); } //Wait for VR Controllers to initilize yield return new WaitForSeconds(1f); objecttracker = GetComponent(); if (objecttracker != null) { #if ZED_STEAM_VR if (objecttracker.index >= 0) yield break; #endif #if ZED_OCULUS if (OVRInput.GetConnectedControllers().ToString().ToLower().Contains("touch")) yield break; #endif // If it got here then there's no VR Controller connected int children = transform.childCount; for (int i = 0; i < children; ++i) transform.GetChild(i).gameObject.SetActive(false); //this.enabled = false; } else { //If its not attached to an object tracker var otherObjectTracker = FindObjectsOfType(); if (otherObjectTracker != null) { int children = transform.childCount; #if ZED_STEAM_VR foreach (ZEDControllerTracker trackers in otherObjectTracker) { if (trackers.index >= 0) { for (int i = 0; i < children; ++i) transform.GetChild(i).gameObject.SetActive(false); this.enabled = false; yield break; } } #endif #if ZED_OCULUS if (OVRManager.isHmdPresent) { if (OVRInput.GetConnectedControllers().ToString().ToLower().Contains("touch")) { for (int i = 0; i < children; ++i) transform.GetChild(i).gameObject.SetActive(false); //this.enabled = false; yield break; } } #endif } } } // Update is called once per frame void Update() { //Do we have a Pointer Bead to position in the world? if (laserPointerBeadHolder != null) { Vector3 crosshairpoint; Vector3 crosshairnormal; //Point the bead at the closest thing in front of the camera. if (FindCrosshairPosition(out crosshairpoint, out crosshairnormal)) { //We hit something. Make sure the bead is active. pointerbead.SetActive(true); //Position the bead a the collision point, and make it face you. pointerbead.transform.position = crosshairpoint; if (crosshairnormal.magnitude > 0.0001f) pointerbead.transform.rotation = Quaternion.LookRotation(crosshairnormal); } else { //We didn't hit anything. Disable the bead object. pointerbead.SetActive(false); } } //Check to see if any valid fire keys are triggered. //This is more complex than often necessary so as to work out-of-the-box for a variety of configurations. //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. bool buttondown = false; //Check for keys present on all systems //Use objecttracker to know if this controller was intended to be on a VR controller. if (objecttracker == null && (Input.GetKeyDown(KeyCode.Mouse0) || Input.GetKeyDown(KeyCode.Space))) { buttondown = true; } #if ZED_OCULUS //Update whether the Touch controllers are active. int children = transform.childCount; if (OVRManager.isHmdPresent) { if (OVRInput.GetConnectedControllers().ToString().ToLower().Contains("touch")) { for (int i = 0; i < children; ++i) transform.GetChild(i).gameObject.SetActive(true); } else { for (int i = 0; i < children; ++i) transform.GetChild(i).gameObject.SetActive(false); } } //We're controlling the fire Rate. OVRInput doesn't have a GetDown function for the IndexTrigger. Only an axis output. if (objecttracker != null) { buttondown = objecttracker.CheckFireButton(ControllerButtonState.Down); //OVRInput.Update(); } #endif #if ZED_STEAM_VR //Looks for any input from this controller through SteamVR if (objecttracker != null) { buttondown = objecttracker.CheckFireButton(ControllerButtonState.Down); } #endif if (buttondown) { Fire(); } } /// /// Tests the depth of both the real and virtual in the center of the screen, and returns the world position of the closest one. /// /// Where the crosshair should be rendered. /// The normal vector of the surface aimed at, for rotating the crosshair accordingly if desired. /// False if there is no valid object, real or virtual, on which to place the crosshair. private bool FindCrosshairPosition(out Vector3 crosshairpoint, out Vector3 collisionnormal) { //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. Vector3 realpoint = Vector3.zero; float realdistance = 20f; //Arbitrary distance to put the crosshair if it hits nothing at all. Chosen by ZED's max range. bool foundrealdistance = false; if (ZEDSupportFunctions.HitTestOnRay(zedManager.zedCamera, zedManager.GetMainCamera(), laserPointerBeadHolder.transform.position, laserPointerBeadHolder.transform.rotation, 5f, 0.01f, out realpoint)) { realdistance = Vector3.Distance(laserPointerBeadHolder.transform.position, realpoint); foundrealdistance = true; } //Find the distance to the virtual. The bool will be false if there are no colliders ahead of you. RaycastHit hitinfo; bool foundvirtualdistance = Physics.Raycast(laserPointerBeadHolder.transform.position, laserPointerBeadHolder.transform.rotation * Vector3.forward, out hitinfo); //If we didn't find either, return false so the laser and bead can be disabled. if (!foundrealdistance && !foundvirtualdistance) { crosshairpoint = Vector3.zero; collisionnormal = Vector3.zero; return false; } //Decide if we use the real or virtual distance if (!foundvirtualdistance || realdistance < hitinfo.distance) { //The real world is closer. Give the position of the real world pixel and return true. crosshairpoint = realpoint; ZEDSupportFunctions.GetNormalAtWorldLocation(zedManager.zedCamera, realpoint, sl.REFERENCE_FRAME.WORLD, zedManager.GetMainCamera(), out collisionnormal); return true; } else { //The virtual world is closer, or they're tied. Return the world posiiton where the raycast hit the virtual collider. crosshairpoint = hitinfo.point; collisionnormal = hitinfo.normal; return true; } } /// /// Spawns the laser prefab at the spawn anchor. /// void Fire() { //Create the shot and position/rotate it accordingly. GameObject blastershot = Instantiate(laserShotPrefab); blastershot.transform.position = laserSpawnLocation != null ? laserSpawnLocation.transform.position : transform.position; blastershot.transform.rotation = laserSpawnLocation != null ? laserSpawnLocation.transform.rotation : transform.rotation; if (laserPointerBeadHolder != null && pointerbead.transform.localPosition != Vector3.zero) { blastershot.transform.LookAt(pointerbead.transform.position); } //Play a sound if (audiosource) { audiosource.Play(); } } #if ZED_OCULUS /// /// Returns if this script is bound to an Oculus Touch controller that is currently not connected. /// For example, if it's a Right Controller but only the left is connected, it returns false. /// If not bound to a controller, returns true. /// /// private bool IsConnectedController() { if (!objecttracker) return true; //Not attached to a tracker. Return true since it doesn't depend on a controller to be alive. if (objecttracker.deviceToTrack != ZEDControllerTracker.Devices.LeftController && objecttracker.deviceToTrack != ZEDControllerTracker.Devices.RightController) return true; //Not bound to a left or right controller, so let it live. string connectedcontrollers = OVRInput.GetConnectedControllers().ToString().ToLower(); if (connectedcontrollers == "touch") return true; //Both controllers are connected, so if (objecttracker.deviceToTrack == ZEDControllerTracker.Devices.LeftController && connectedcontrollers == "ltouch") return true; //Left controller only. if (objecttracker.deviceToTrack == ZEDControllerTracker.Devices.RightController && connectedcontrollers == "rtouch") return true; //Right controller only. return false; } #endif }