devNotes 6-18-16 UI GearVR, devPipeline, radial menus

unityblog3

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
}

ad913ff0-6980-4f4a-a690-c6f365f6bddf_scaled