cancel
Showing results for 
Search instead for 
Did you mean: 

New Unity UI + OVR Look-Based Input HOWTO

Anonymous
Not applicable
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
105 REPLIES 105

ezRocket
Explorer
Cool, gonna try this out later.

Thanks for the writeup.

knup
Honored Guest
Thanks for sharing! Good to see some custom input module code.

Anonymous
Not applicable
Awesome, I was just about to tackle this with 0.4.3... thanks for posting!

virror
Explorer
Great post! Thanx : )

ndaviden
Honored Guest
Finally. I've been scouring the internet all night looking for something that works. Thanks for putting this all together with such clear instructions. It's such a relief.

One problem I'm currently facing now is that I can't actually enter any keyboard input into the InputField UI. Do you have any idea how I might go about doing that while still being able to keep what you've made? I've noticed that unity can still read when I press "space" to bring up the VR menu, so I know the keyboard is not disabled completely.

Anonymous
Not applicable
Sorry I haven't tried an input field yet. I'll try that in a minute. There may be something special that needs to happen with keyboard input focus that my basic example doesn't do. I'm only using Submit and Select events in the example code. There are many others to experiment with.

Maybe add the standalone input module back to the Event System object and re-enable? They should be able to work together.

BTW - if you use "Fire1" as the submit button and you haven't changed the Unity default input settings, both mouse click and gamepad button 0 (A on xbox controller) will click buttons, select sliders, etc.

ccs

Anonymous
Not applicable
I fixed a minor bug in the code I posted. It has been corrected in the original post:

Was:

if (newPressed != null) {
eventSystem.SetSelectedGameObject(go);
}


Is now:

if (newPressed != null) {
eventSystem.SetSelectedGameObject(newPressed);
}


I can't seem to get the InputField to work either. It responds to the submit action and the cursor starts blinking so I know it is activating, but the keyboard input is not getting picked up to change the text.

virror
Explorer
Did you try posting in the Unity 4.6 beta forum about this?

Anonymous
Not applicable
I figured it out by looking at the StandaloneInputModule code here:

https://gist.github.com/stramit/ce455682b7944bdff0e7

What I was missing was this procedure that handles the update events that need to get passed to the currently selected UI object:

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


I fixed it in the original post. I make a call to this function right at the beginning of the Process() function. This is what passes on the keyboard input events to the selected object.

Keep in mind, this doesn't disable all the key bindings made elsewhere in code, like in the OVR code. You will have to find a way to disable those other scripts from trying to also use the keystroke. OVR SDK has a lot of keyboard shortcuts.