using UnityEngine; using System.Collections; [RequireComponent(typeof(SteamVR_TrackedObject))] public class Hand : MonoBehaviour { /// /// The point at which to attach the object we pick up on the hands. /// public Rigidbody AttachPoint; /// /// When true, the object will be picked up based on where the hand /// touched the object. When false, the object will attach to the contact point /// assigned. /// public bool IgnoreContactPoint = false; /// /// The thickness of the teleporting laser. /// public float LaserThickness = 0.002f; /// /// The color of the laser when the touchpad is touched. /// public Color TeleportStartLaserColor = Color.blue; /// /// The color of the laser when the touchpad is pressed. /// public Color TeleportLaserColor = Color.red; /// /// The maximum distance the player can teleport. /// public float MaxTeleportDistance = 100.0f; /// /// Manages the state of the hand. /// private enum State { EMPTY, TOUCHING, HOLDING }; /// /// The hands current state. /// private State mHandState = State.EMPTY; /// /// Our actual controller device. This is used to handle button presses, velocities, etc. /// private SteamVR_Controller.Device mControllerDevice; /// /// The tracked object representing our controller. This is used to get the /// controller device. /// private SteamVR_TrackedObject mTrackedObj; /// /// The object currently being held. /// private Rigidbody mHeldObject; /// /// A temporary joint connect the object we are holding to our hand. /// private FixedJoint mTempJoint; /// /// The laser's physical object (just a block). /// private GameObject mLaser; /// /// Initializes a hand's starting properties. /// void Start () { mHandState = State.EMPTY; mTrackedObj = GetComponent(); mControllerDevice = SteamVR_Controller.Input((int)mTrackedObj.index); mLaser = GameObject.CreatePrimitive(PrimitiveType.Cube); mLaser.transform.parent = this.transform; mLaser.transform.localScale = new Vector3(0f, 0f, 0f); mLaser.transform.localPosition = new Vector3(0.0f, 0.0f, 0f); mLaser.GetComponent().material = new Material(Shader.Find("Unlit/Color")); mLaser.GetComponent().material.SetColor("_Color", TeleportStartLaserColor); mLaser.GetComponent().enabled = false; } /// /// Update called in sync with physics. /// void FixedUpdate () { updateHand(); updateLaser(); } /// /// Throws the object the hand was holding. /// private void throwObject() { var origin = mTrackedObj.origin ? mTrackedObj.origin : mTrackedObj.transform.parent; if (origin != null) { mHeldObject.velocity = origin.TransformVector(mControllerDevice.velocity); mHeldObject.angularVelocity = origin.TransformVector(mControllerDevice.angularVelocity); } else { mHeldObject.velocity = mControllerDevice.velocity; mHeldObject.angularVelocity = mControllerDevice.angularVelocity; } mHeldObject.maxAngularVelocity = mHeldObject.angularVelocity.magnitude; } /// /// Updates the hand based on its state. /// private void updateHand() { switch (mHandState) { case State.TOUCHING: if (mTempJoint == null && mControllerDevice.GetPress(SteamVR_Controller.ButtonMask.Grip)) { mHeldObject.velocity = Vector3.zero; mTempJoint = mHeldObject.gameObject.AddComponent(); if (IgnoreContactPoint) { mHeldObject.transform.position = AttachPoint.transform.position; } mTempJoint.connectedBody = AttachPoint; mHandState = State.HOLDING; } break; case State.HOLDING: if (mTempJoint != null && mControllerDevice.GetPressUp(SteamVR_Controller.ButtonMask.Grip)) { Object.DestroyImmediate(mTempJoint); mTempJoint = null; throwObject(); mHandState = State.EMPTY; } break; } } /// /// Updates the teleport laser in each hand. /// private void updateLaser() { if (mControllerDevice != null) { Ray raycast = new Ray(transform.position, transform.forward); RaycastHit hitInfo; bool hit = Physics.Raycast(raycast, out hitInfo, MaxTeleportDistance); float distance = hit ? hitInfo.distance : MaxTeleportDistance; if (mControllerDevice.GetTouch(SteamVR_Controller.ButtonMask.Touchpad)) { mLaser.transform.localScale = new Vector3(LaserThickness, LaserThickness, distance); mLaser.transform.localPosition = new Vector3(0f, 0f, distance / 2f); } else { mLaser.transform.localScale = new Vector3(0f, 0f, 0f); mLaser.transform.localPosition = new Vector3(0f, 0f, 0f); } if (mControllerDevice.GetPress(SteamVR_Controller.ButtonMask.Touchpad)) { if (hit && hitInfo.collider.gameObject.layer == LayerMask.NameToLayer("teleportable")) { mLaser.GetComponent().material.SetColor("_Color", TeleportLaserColor); } else { mLaser.GetComponent().material.SetColor("_Color", TeleportStartLaserColor); } } if (mControllerDevice.GetPressUp(SteamVR_Controller.ButtonMask.Touchpad)) { if (hit && hitInfo.collider.gameObject.layer == LayerMask.NameToLayer("teleportable")) { float originalY = transform.parent.position.y; transform.parent.position = transform.parent.position + raycast.direction * distance; transform.parent.position = new Vector3(transform.parent.position.x, originalY, transform.parent.position.z); } mLaser.GetComponent().material.SetColor("_Color", TeleportStartLaserColor); } } } /// /// Checks for a collision with the hand and /// changes the state based on it, also storing /// the object we touched. /// /// The object we touched. void OnTriggerEnter(Collider collider) { if (mHandState == State.EMPTY) { GameObject temp = collider.gameObject; while (temp.GetComponent() == null && temp.transform.parent != null) { temp = temp.transform.parent.gameObject; } if (temp != null && temp.layer == LayerMask.NameToLayer("grabbable") && temp.GetComponent() != null) { mHeldObject = temp.GetComponent(); mHandState = State.TOUCHING; mControllerDevice.TriggerHapticPulse(2000); } } } /// /// When we are no longer touching an object or holding /// and object, clean up the current state and remove the object /// we let go of/stopped touching. /// /// The object we are no longer touching. void OnTriggerExit(Collider collider) { if (mHandState != State.HOLDING) { if (collider.gameObject.layer == LayerMask.NameToLayer("grabbable")) { mHeldObject = null; mHandState = State.EMPTY; } } } }