Skip to main content
November 4, 2014

New Unity UI + OVR Look-Based Input HOWTO

LookInputModule.jpg

Edit6: version 5 of example project/code:
https://www.dropbox.com/s/g8ptl7w9xdewp ... 5.zip?dl=0
Version removed old Oculus integration and uses Native VR support + 0.1.2 utils so it works with latest runtimes (0.7 or 0.8).

Edit5: version 4 of example project/code (fixed for and tested with Unity 4.6.4f1):
https://www.dropbox.com/s/3bpz5rgimbxdk ... 4.zip?dl=0
This version also adds support for a cursor that scales size with distance so it will always appear the same size but be at the right depth.

Edit4: version 3 of example project/code:
[removed - see updated example above]

Edit3: New version of example project and code posted:
[removed - see updated example above]

Edit2: If you don't have time to read all of this thread and just want the code and a sample project, I uploaded one here:
[removed - see updated example above]

Edit: fixed some issues with the code to handle InputField and also was selecting wrong game object in some cases.

I have been messing around with the new Unity GUI stuff in 4.6 release, and I figured I would share a basic solution to get up and running quickly with a look-based input system. Here is how to get a basic look UI working:

1. Add some kind of UI element to your scene. This will also add a Canvas object and an EventSystem object if you don't already have them in your scene. All UI elements must be parented by a Canvas.

2. On the UI Canvas object, set the Render Mode to World Space

3. Arrange your Canvas in world space and add more UI elements as you would like. To get started, I would just add some buttons.

4. Under the OVRCameraRig->CenterEyeAnchor object, add a regular Unity Camera at position and rotation 0,0,0, and set this camera's Culling Mask to "Nothing" so it won't actually bother rendering anything and waste CPU/GPU cycles. I named it "Look Camera". This camera is just for use by the UI event system. By putting it under the CenterEyeAnchor object, it tracks perfectly with the head. The OVR cameras don't seem to work with the UI system, probably because they utilize render textures and not screen space rendering. This dummy look camera solves that problem.

5. For every UI Canvas object you have in the scene, drag this "Look Camera" object into the "Event Camera" field.

6. create a new script called BasicLookInputModule.cs. Copy in this code:

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using System.Collections;

public class BasicLookInputModule : BaseInputModule {

public const int kLookId = -3;
public string submitButtonName = "Fire1";
public string controlAxisName = "Horizontal";
private PointerEventData lookData;

// use screen midpoint as locked pointer location, enabling look location to be the "mouse"
private PointerEventData GetLookPointerEventData() {
Vector2 lookPosition;
lookPosition.x = Screen.width/2;
lookPosition.y = Screen.height/2;
if (lookData == null) {
lookData = new PointerEventData(eventSystem);
}
lookData.Reset();
lookData.delta = Vector2.zero;
lookData.position = lookPosition;
lookData.scrollDelta = Vector2.zero;
eventSystem.RaycastAll(lookData, m_RaycastResultCache);
lookData.pointerCurrentRaycast = FindFirstRaycast(m_RaycastResultCache);
m_RaycastResultCache.Clear();
return lookData;
}

private bool SendUpdateEventToSelectedObject() {
if (eventSystem.currentSelectedGameObject == null)
return false;
BaseEventData data = GetBaseEventData ();
ExecuteEvents.Execute (eventSystem.currentSelectedGameObject, data, ExecuteEvents.updateSelectedHandler);
return data.used;
}

public override void Process() {
// send update events if there is a selected object - this is important for InputField to receive keyboard events
SendUpdateEventToSelectedObject();
PointerEventData lookData = GetLookPointerEventData();
// use built-in enter/exit highlight handler
HandlePointerExitAndEnter(lookData,lookData.pointerCurrentRaycast.gameObject);
if (Input.GetButtonDown (submitButtonName)) {
eventSystem.SetSelectedGameObject(null);
if (lookData.pointerCurrentRaycast.gameObject != null) {
GameObject go = lookData.pointerCurrentRaycast.gameObject;
GameObject newPressed = ExecuteEvents.ExecuteHierarchy (go, lookData, ExecuteEvents.submitHandler);
if (newPressed == null) {
// submit handler not found, try select handler instead
newPressed = ExecuteEvents.ExecuteHierarchy (go, lookData, ExecuteEvents.selectHandler);
}
if (newPressed != null) {
eventSystem.SetSelectedGameObject(newPressed);
}
}
}
if (eventSystem.currentSelectedGameObject && controlAxisName != null && controlAxisName != "") {
float newVal = Input.GetAxis (controlAxisName);
if (newVal > 0.01f || newVal < -0.01f) {
AxisEventData axisData = GetAxisEventData(newVal,0.0f,0.0f);
ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, axisData, ExecuteEvents.moveHandler);
}
}
}
}



7. Drag this script onto the EventSystem object in the scene.

8. fix Submit Button Name and Control Axis Name to match what you use in the Input Settings if you don't use Unity defaults.

9. disable or remove Standalone Input Module and Touch Input Module on the Event System object.

That's it. Push play. Look around with headset and you should see it highlight the buttons/sliders/etc. as you are looking around. When you hit the submit button it will trigger that UI element (buttons On Value Changed operations will be called if you set any of those up). Sliders get selected when you click them and can then be manipulated with the axis that was chosen on the BasicLookInputModule component.

I hope someone finds this useful since there isn't much out there on how to do this yet.

ccs
    This topic has been closed for replies.

    105 replies

    Up and Coming
    August 26, 2015
    First, thanks for the great script ccs, saved me a lot of headaches!

    Just a quick thing to watch out for when using the cursor:
    if you are using a cursor, add a CanvasGroup element to it and make it non interactable and not blocking the raycasts, otherwise your elements will only be selected briefly before the raycast to the actual element is blocked by the freshly placed cursor.
    Up and Coming
    August 27, 2015
    Hello
    I tried this script and it works fine. Iam using samsung gear VR with the touchpad in the device.
    I have a panel with a scrollrect and inside a horizontal grid layout with images dynamically placed in them.

    Now for the scroll rect, if i try to scroll, it works only when i touch on the touchpad and rotate my head to do the scroll.

    If i do a swipe on the touchpad, it does not scroll
    Can you please help me to fix this.

    Thanks
    August 31, 2015
    "OculusDev" wrote:

    I have an issue that I'm trying to solve with the UI button. If I look at the button and click on it then the button continues to be rendered in a "clicked" state even when I look away from it. I have to click on a non-UI element to deselect the button.


    Sorry for my slow response on some of these. Been busy with day job + summer vacation + VR development.

    Instead of BasicLookInputModule.cs, look at the other example I posted, LookInputModule.cs. It includes button up code to handle what you are describing:

    		// have to handle button up even if looking away
    if (Input.GetButtonUp(submitButtonName)) {
    if (currentDragging) {
    ExecuteEvents.Execute(currentDragging,lookData,ExecuteEvents.endDragHandler);
    if (currentLook != null) {
    ExecuteEvents.ExecuteHierarchy(currentLook,lookData,ExecuteEvents.dropHandler);
    }
    lookData.pointerDrag = null;
    currentDragging = null;
    }
    if (currentPressed) {
    ExecuteEvents.Execute(currentPressed,lookData,ExecuteEvents.pointerUpHandler);
    lookData.rawPointerPress = null;
    lookData.pointerPress = null;
    currentPressed = null;
    }
    }
    August 31, 2015
    "JagaHee" wrote:
    Hello ccs
    Thank you for posting this code. Very useful. I have a question about BasicLookInputModule. As you wrote in instruction, i've attached the script to EventSystem object. And disabled touch input module and standalone. Then i need to deactivate lookInput and activate touch and standalone. But when i do this strage thing happens. Touch works only when by a second finger and Input system says it is the first finger. Do i need to clean something or stop any Input system somewhere?
    p.s. sorry for my english)


    I don't use touch input or standalone module at all, but my guess is the EventSystem calls Process function on ALL input modules, even if they are deactivated, and the BasicLookInputModule is grabbing some of the events so they don't appear to the TouchInputModule. In the BasicLookInputModule.cs code Process function, you could test this theory by adding an "if enabled" around all the code that is in that function now:


    public override void Process() {
    if (enabled) {
    ...put all original contents of this function inside here...
    }
    }


    Another thing to test would be to remove BasicLookInputModule.cs (instead of deactivate) to see if the problem goes away. If that doesn't fix it, it points to some other issue not related to BasicLookInputModule at all.

    ccs
    August 31, 2015
    "leftbigtoe" wrote:
    First, thanks for the great script ccs, saved me a lot of headaches!

    Just a quick thing to watch out for when using the cursor:
    if you are using a cursor, add a CanvasGroup element to it and make it non interactable and not blocking the raycasts, otherwise your elements will only be selected briefly before the raycast to the actual element is blocked by the freshly placed cursor.


    There is a simpler way. The cursor should already be on its own Canvas as in the example I posted, and every Canvas has a Graphic Raycaster component that goes with it. Simply disable the Graphic Raycaster and the cursor won't block anything.

    DisableCursorRaycast.PNG
    August 31, 2015
    "muraliv" wrote:
    Hello
    I tried this script and it works fine. Iam using samsung gear VR with the touchpad in the device.
    I have a panel with a scrollrect and inside a horizontal grid layout with images dynamically placed in them.

    Now for the scroll rect, if i try to scroll, it works only when i touch on the touchpad and rotate my head to do the scroll.

    If i do a swipe on the touchpad, it does not scroll
    Can you please help me to fix this.

    Thanks


    It would probably be a bit of work to get the touchpad working. GearVR touchpad is handled throuh the Input.*mouse* interface. You would need to capture mouse point each frame, and then look at the change in y or x (depending on how you want to scroll). Here is some quick untested code to try to point you in right direction (should go in the Process() function):

    Vector2 mousePoint = Input.mousePosition;
    Vector2 changeMouse = mousePoint - lastMoustPoint;
    float someFactor = 0.002f;

    if (Input.GetMouseButtonDown(0)) {
    // touch pad is being pressed
    // send scroll event to current selected
    Scrollbar sb = eventSystem.currentSelectedGameObject.GetComponent<Scrollbar>();
    if (sb != null) {
    sb.value += changeMouse.y * someFactor;
    }
    }
    September 1, 2015
    Hi,

    I've been reading through these posts and know that these fixes are mostly for Unity 4, but I'm working in Unity 5. I was wondering if anyone has been able to get the GUI to work in Unity 5 so far, since I'm having issue with a simple GUI text not showing up when in play mode. It looks as it should in the game view; however, once its in play mode the GUI disappears. I've tried checking if its obscured by objects by deleting them in play mode, but it's just not there.
    New Participant
    September 1, 2015
    "uhcacinorev" wrote:
    Hi,

    I've been reading through these posts and know that these fixes are mostly for Unity 4, but I'm working in Unity 5. I was wondering if anyone has been able to get the GUI to work in Unity 5 so far, since I'm having issue with a simple GUI text not showing up when in play mode. It looks as it should in the game view; however, once its in play mode the GUI disappears. I've tried checking if its obscured by objects by deleting them in play mode, but it's just not there.


    Hi uhcacinorev,

    Are you sure you have your canvas set to "World Space"?
    September 1, 2015
    The demo package I posted on 1st page works fine in Unity 5. Unity 5 is all I am using these days.
    September 1, 2015
    Just to be sure, I confirmed the zip file I posted in the 1st post works with Unity 5.1.2p2 (and probably other Unity 5 versions) and OVR runtime 0.6.0.1. It does require 1 small fix to OVRMainMenu.cs to be compliant to new API:

    Change line 283 to this:
    #if UNITY_5_0 || UNITY_5_1 || UNITY_5_2

    Then it should work as posted. Keep in mind this is using old legacy-integration and not new built-in VR mode that is now available in Unity. I'll see if I can find time to create and post an updated version that uses new native integration.

    I haven't tried running 0.7.0.0 runtime yet either. I'm in the process of moving to that now for my other projects, and I'll try this one out just to make sure it still works.