using UnityEngine; using System.Collections; public class OVRGazePointer : MonoBehaviour { [Tooltip("Should the pointer be hidden when not over interactive objects.")] public bool hideByDefault = true; [Tooltip("Time after leaving interactive object before pointer fades.")] public float showTimeoutPeriod = 1; [Tooltip("Time after mouse pointer becoming inactive before pointer unfades.")] public float hideTimeoutPeriod = 0.1f; [Tooltip("Keep a faint version of the pointer visible while using a mouse")] public bool dimOnHideRequest = true; [Tooltip("Angular scale of pointer")] public float depthScaleMultiplier = 0.03f; [Tooltip("Used for positioning pointer in scene")] public OVRCameraRig cameraRig; /// <summary> /// Is gaze pointer current visible /// </summary> public bool hidden { get; private set; } /// <summary> /// Current scale applied to pointer /// </summary> public float currentScale { get; private set; } /// <summary> /// Current depth of pointer from camera /// </summary> private float depth; /// <summary> /// How many times position has been set this frame. Used to detect when there are no position sets in a frame. /// </summary> private int positionSetsThisFrame = 0; /// <summary> /// Position last frame. /// </summary> private Vector3 lastPosition; /// <summary> /// Last time code requested the pointer be shown. Usually when pointer passes over interactive elements. /// </summary> private float lastShowRequestTime; /// <summary> /// Last time pointer was requested to be hidden. Usually mouse pointer activity. /// </summary> private float lastHideRequestTime; // How much the gaze pointer moved in the last frame private Vector3 _positionDelta; public Vector3 positionDelta { get { return _positionDelta; } } private static OVRGazePointer _instance; public static OVRGazePointer instance { // If there's no GazePointer already in the scene, instanciate one now. get { if (_instance == null) { Debug.Log(string.Format("Instanciating GazePointer", 0)); _instance = (OVRGazePointer)GameObject.Instantiate((OVRGazePointer)Resources.Load("Prefabs/GazePointerRing", typeof(OVRGazePointer))); } return _instance; } } public float visibilityStrength { get { float strengthFromShowRequest; if (hideByDefault) { strengthFromShowRequest = Mathf.Clamp01(1 - (Time.time - lastShowRequestTime) / showTimeoutPeriod); } else { strengthFromShowRequest = 1; } // Now consider factors requesting pointer to be hidden float strengthFromHideRequest; if (dimOnHideRequest) { strengthFromHideRequest = (lastHideRequestTime + hideTimeoutPeriod > Time.time) ? 0.1f : 1; } else { strengthFromHideRequest = (lastHideRequestTime + hideTimeoutPeriod > Time.time) ? 0 : 1; } // Hide requests take priority return Mathf.Min(strengthFromShowRequest, strengthFromHideRequest); } } private void Awake() { currentScale = 1; // Only allow one instance at runtime. if (_instance != null && _instance != this) { enabled = false; DestroyImmediate(this); return; } _instance = this; } // Update is called once per frame void Update () { // Even if this runs after SetPosition, it will work out to be the same position // Keep pointer at same distance from camera rig transform.position = cameraRig.centerEyeAnchor.transform.position + cameraRig.centerEyeAnchor.transform.forward * depth; if (visibilityStrength == 0 && !hidden) { Hide(); } else if (visibilityStrength > 0 && hidden) { Show(); } } /// <summary> /// Set position and orientation of pointer /// </summary> /// <param name="pos"></param> /// <param name="normal"></param> public void SetPosition(Vector3 pos, Vector3 normal) { transform.position = pos; // Set the rotation to match the normal of the surface it's on. For the other degree of freedom use // the direction of movement so that trail effects etc are easier Quaternion newRot = transform.rotation; newRot.SetLookRotation(normal, (lastPosition - transform.position).normalized); transform.rotation = newRot; // record depth so that distance doesn't pop when pointer leaves an object depth = (cameraRig.centerEyeAnchor.transform.position - pos).magnitude; //set scale based on depth currentScale = depth * depthScaleMultiplier; transform.localScale = new Vector3(currentScale, currentScale, currentScale); positionSetsThisFrame++; } /// <summary> /// SetPosition overload without normal. Just makes cursor face user /// </summary> /// <param name="pos"></param> public void SetPosition(Vector3 pos) { SetPosition(pos, cameraRig.centerEyeAnchor.transform.forward); } void LateUpdate() { // This happens after all Updates so we know nothing set the position this frame if (positionSetsThisFrame == 0) { // No geometry intersections, so gazing into space. Make the cursor face directly at the camera Quaternion newRot = transform.rotation; newRot.SetLookRotation(cameraRig.centerEyeAnchor.transform.forward, (lastPosition - transform.position).normalized); transform.rotation = newRot; } // Keep track of cursor movement direction _positionDelta = transform.position - lastPosition; lastPosition = transform.position; positionSetsThisFrame = 0; } /// <summary> /// Request the pointer be hidden /// </summary> public void RequestHide() { if (!dimOnHideRequest) { Hide(); } lastHideRequestTime = Time.time; } /// <summary> /// Request the pointer be shown. Hide requests take priority /// </summary> public void RequestShow() { Show(); lastShowRequestTime = Time.time; } private void Hide() { foreach (Transform child in transform) { child.gameObject.SetActive(false); } if (GetComponent<Renderer>()) GetComponent<Renderer>().enabled = false; hidden = true; } private void Show() { foreach (Transform child in transform) { child.gameObject.SetActive(true); } if (GetComponent<Renderer>()) GetComponent<Renderer>().enabled = true; hidden = false; } }
using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; using System.Collections; using System.Collections.Generic; [AddComponentMenu("Radial Menu Framework/RMF Core Script")] public class RMF_RadialMenu : MonoBehaviour { [HideInInspector] public RectTransform rt; //public RectTransform baseCircleRT; //public Image selectionFollowerImage; [Tooltip("Adjusts the radial menu for use with a gamepad or joystick. You might need to edit this script if you're not using the default horizontal and vertical input axes.")] public bool useGamepad = false; [Tooltip("With lazy selection, you only have to point your mouse (or joystick) in the direction of an element to select it, rather than be moused over the element entirely.")] public bool useLazySelection = true; [Tooltip("If set to true, a pointer with a graphic of your choosing will aim in the direction of your mouse. You will need to specify the container for the selection follower.")] public bool useSelectionFollower = true; [Tooltip("If using the selection follower, this must point to the rect transform of the selection follower's container.")] public RectTransform selectionFollowerContainer; [Tooltip("This is the text object that will display the labels of the radial elements when they are being hovered over. If you don't want a label, leave this blank.")] public Text textLabel; [Tooltip("This is the list of radial menu elements. This is order-dependent. The first element in the list will be the first element created, and so on.")] public List<RMF_RadialMenuElement> elements = new List<RMF_RadialMenuElement>(); [Tooltip("Controls the total angle offset for all elements. For example, if set to 45, all elements will be shifted +45 degrees. Good values are generally 45, 90, or 180")] public float globalOffset = 0f; [HideInInspector] public float currentAngle = 0f; //Our current angle from the center of the radial menu. [HideInInspector] public int index = 0; //The current index of the element we're pointing at. private int elementCount; private float angleOffset; //The base offset. For example, if there are 4 elements, then our offset is 360/4 = 90 private int previousActiveIndex = 0; //Used to determine which buttons to unhighlight in lazy selection. private PointerEventData pointer; void Awake() { pointer = new PointerEventData(EventSystem.current); rt = GetComponent<RectTransform>(); if (rt == null) Debug.LogError("Radial Menu: Rect Transform for radial menu " + gameObject.name + " could not be found. Please ensure this is an object parented to a canvas."); if (useSelectionFollower && selectionFollowerContainer == null) Debug.LogError("Radial Menu: Selection follower container is unassigned on " + gameObject.name + ", which has the selection follower enabled."); elementCount = elements.Count; angleOffset = (360f / (float)elementCount); //Loop through and set up the elements. for (int i = 0; i < elementCount; i++) { if (elements[i] == null) { Debug.LogError("Radial Menu: element " + i.ToString() + " in the radial menu " + gameObject.name + " is null!"); continue; } elements[i].parentRM = this; elements[i].setAllAngles((angleOffset * i) + globalOffset, angleOffset); elements[i].assignedIndex = i; } } void Start() { if (useGamepad) { EventSystem.current.SetSelectedGameObject(gameObject, null); //We'll make this the active object when we start it. Comment this line to set it manually from another script. if (useSelectionFollower && selectionFollowerContainer != null) selectionFollowerContainer.localRotation = Quaternion.Euler(0, 0, -globalOffset); //Point the selection follower at the first element. } } // Update is called once per frame void Update() { //If your gamepad uses different horizontal and vertical joystick inputs, change them here! //============================================================================================== bool joystickMoved = Input.GetAxis("Horizontal") != 0.0 || Input.GetAxis("Vertical") != 0.0; //============================================================================================== float rawAngle; if (!useGamepad) rawAngle = Mathf.Atan2(Input.mousePosition.y - rt.position.y, Input.mousePosition.x - rt.position.x) * Mathf.Rad2Deg; else rawAngle = Mathf.Atan2(Input.GetAxis("Vertical"), Input.GetAxis("Horizontal")) * Mathf.Rad2Deg; //If no gamepad, update the angle always. Otherwise, only update it if we've moved the joystick. if (!useGamepad) currentAngle = normalizeAngle(-rawAngle + 90 - globalOffset + (angleOffset / 2f)); else if (joystickMoved) currentAngle = normalizeAngle(-rawAngle + 90 - globalOffset + (angleOffset / 2f)); //Handles lazy selection. Checks the current angle, matches it to the index of an element, and then highlights that element. if (angleOffset != 0 && useLazySelection) { //Current element index we're pointing at. index = (int)(currentAngle / angleOffset); if (elements[index] != null) { //Select it. selectButton(index); //If we click or press a "submit" button (Button on joystick, enter, or spacebar), then we'll execut the OnClick() function for the button. if (Input.GetMouseButtonDown(0) || Input.GetButtonDown("Submit")) { ExecuteEvents.Execute(elements[index].button.gameObject, pointer, ExecuteEvents.submitHandler); } } } //Updates the selection follower if we're using one. if (useSelectionFollower && selectionFollowerContainer != null) { if (!useGamepad || joystickMoved) selectionFollowerContainer.localRotation = Quaternion.Euler(0, 0, rawAngle + 270); } } //Selects the button with the specified index. private void selectButton(int i) { if (elements[i].active == false) { elements[i].highlightThisElement(pointer); //Select this one if (previousActiveIndex != i) elements[previousActiveIndex].unHighlightThisElement(pointer); //Deselect the last one. } previousActiveIndex = i; } //Keeps angles between 0 and 360. private float normalizeAngle(float angle) { angle = angle % 360f; if (angle < 0) angle += 360; return angle; } }
https://youtu.be/ifW6HMAVM4w
Unity VR Editor Mouse Look Script
using UnityEngine; using UnityEngine.VR; public class VRMouseLook : MonoBehaviour { #if UNITY_EDITOR public bool enableYaw = true; public bool autoRecenterPitch = true; public bool autoRecenterRoll = true; public KeyCode HorizontalAndVerticalKey = KeyCode.LeftAlt; public KeyCode RollKey = KeyCode.LeftControl; Transform vrCameraTransform; Transform rotationTransform; Transform forwardTransform; private float mouseX = 0; private float mouseY = 0; private float mouseZ = 0; void Awake() { // get the vr camera so we can align our forward with it Camera vrCamera = gameObject.GetComponentInChildren<Camera>(); vrCameraTransform = vrCamera.transform; // create a hierarchy to enable us to additionally rotate the vr camera rotationTransform = new GameObject("VR Mouse Look (Rotation)").GetComponent<Transform>(); forwardTransform = new GameObject("VR Mouse Look (Forward)").GetComponent<Transform>(); rotationTransform.SetParent(transform.parent, false); forwardTransform.SetParent(rotationTransform, false); transform.SetParent(forwardTransform, false); } void Update () { bool rolled = false; bool pitched = false; if (Input.GetKey(HorizontalAndVerticalKey)) { pitched = true; if (enableYaw) { mouseX += Input.GetAxis("Mouse X") * 5; if (mouseX <= -180) { mouseX += 360; } else if (mouseX > 180) { mouseX -= 360; } } mouseY -= Input.GetAxis("Mouse Y") * 2.4f; mouseY = Mathf.Clamp(mouseY, -85, 85); } else if (Input.GetKey(RollKey)) { rolled = true; mouseZ += Input.GetAxis("Mouse X") * 5; mouseZ = Mathf.Clamp(mouseZ, -85, 85); } if (!rolled && autoRecenterRoll) { // People don't usually leave their heads tilted to one side for long. mouseZ = Mathf.Lerp(mouseZ, 0, Time.deltaTime / (Time.deltaTime + 0.1f)); } if (!pitched && autoRecenterPitch) { // People don't usually leave their heads tilted to one side for long. mouseY = Mathf.Lerp(mouseY, 0, Time.deltaTime / (Time.deltaTime + 0.1f)); } forwardTransform.localRotation = Quaternion.Inverse(Quaternion.Euler(0.0f, vrCameraTransform.localRotation.eulerAngles.y, 0.0f)); rotationTransform.localRotation = Quaternion.Euler(0, vrCameraTransform.localRotation.eulerAngles.y, 0.0f) * Quaternion.Euler(mouseY, mouseX, mouseZ); } #endif }