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
}


