VR Cursor - Possible Unity 4.6 GUI bug or is it me?

I want to make a GUI using the 4.6 system with a 3D pointer in world space so I can hopefully use the same GUI and mouse motion in the Oculus Rift as would be used on a regular monitor. I’m having a problem though as shown here:

The project file is attached here. This is using Unity 5 RC1 and has not been tested on Unity 4.

I’m using two files here. The first is a modified version of Ralph Barbagallo’s code. I replaced his original SetTarget() function with PointerSubmit, PointerExit, and PointerEnter. For me it was easier to control this from a separate script attached to my 3D cursor which would call these functions from there using OnTriggerEnter and OnTriggerExit.

using UnityEngine;
using UnityEngine.EventSystems;

//modified version of Ralph Barbagallo's code here:
//https://gist.github.com/flarb/052467190b84657f10d2
//Referenced by his article here:
//http://ralphbarbagallo.com/2014/08/20/oculus-rift-world-space-cursors-for-world-space-canvases-in-unity-4-6/
//www.flarb.com
//www.ralphbarbagallo.com
//@flarb

public class VRInputModule : BaseInputModule
{

    public static GameObject targetObject;

    static VRInputModule _singleton;

    private int counter;

    private static bool mouseClicked;
    public static Vector3 cursorPosition;
  
    void Awake()
    {
        _singleton = this;
    }

    public override void Process()
    {
        if (targetObject == null)
        {
            mouseClicked = false;
            return;
        }

        //    //Original code now replaced by PointerSubmit() below.  Works the same as far as I can see:
        //if (mouseClicked)
        //{
        //    //BaseEventData data = GetBaseEventData();
        //    BaseEventData data = new BaseEventData(_singleton.eventSystem);
        //    data.selectedObject = targetObject;
        //    ExecuteEvents.Execute(targetObject, data, ExecuteEvents.submitHandler);
        //    print("clicked " + targetObject.name);
        //    mouseClicked = false;
        //}
        //}
    }

    //This function is not in the class anymore so can not be overridden:
    //public override bool IsPointerOverEventSystemObject(int pointerId)
    //{
    //    if (targetObject != null)
    //        return true;

    //    return false;
    //}

    public static void PointerSubmit(GameObject obj)
    {
        targetObject = obj;
        mouseClicked = true;
        if (mouseClicked)
        {
            //BaseEventData data = GetBaseEventData(); //Original from Process().  Can't be called here so is replaced by the next line:
            BaseEventData data = new BaseEventData(_singleton.eventSystem);
            data.selectedObject = targetObject;
            ExecuteEvents.Execute(targetObject, data, ExecuteEvents.submitHandler);
            print("clicked " + targetObject.name);
            mouseClicked = false;
          
        }

    }

    public static void PointerExit(GameObject obj)
    {
        print("PointerExit " + obj.name);
        PointerEventData pEvent = new PointerEventData(_singleton.eventSystem);
        ExecuteEvents.Execute(obj, pEvent, ExecuteEvents.pointerExitHandler);
    }

    public static void PointerEnter(GameObject obj)
    {
        print("PointerEnter " + obj.name);
        PointerEventData pEvent = new PointerEventData(_singleton.eventSystem);
        pEvent.pointerEnter = obj;
        pEvent.worldPosition = cursorPosition;
        ExecuteEvents.Execute(obj, pEvent, ExecuteEvents.pointerEnterHandler);
    }

  
}

Next comes my mouse cursor script:

using UnityEngine;
using System.Collections;

public class VRCursor : MonoBehaviour {

    public float xSens;
    public float ySens;

    private Collider currentCollider;

    // Update is called once per frame
    void Update ()
    {
        Vector3 thisPosition;
      
        thisPosition.x = Input.mousePosition.x * xSens;
        thisPosition.y = Input.mousePosition.y * ySens - 1;
        thisPosition.z = transform.position.z;

        transform.position = thisPosition;
      
        VRInputModule.cursorPosition = transform.position;

        if (Input.GetMouseButtonDown(0) && currentCollider)
        {
            VRInputModule.PointerSubmit(currentCollider.gameObject);
        }

    }

    void OnTriggerEnter(Collider other)
    {
        //print("OnTriggerEnter other " + other.gameObject);
        VRInputModule.PointerEnter(other.gameObject);
        currentCollider = other;
    }

    void OnTriggerExit(Collider other)
    {
        //print("OnTriggerExit other " + other.gameObject);
        VRInputModule.PointerExit(other.gameObject);
        currentCollider = null;
    }
}

How it works: Each button has a box collider. The mouse cursor is a sphere with a sphere collider. For now it’s just moved clumsily with the mouse to get the basic functionality working.

When the mouse cursor hits a button collider, OnTriggerEnter() is called which calls VRInputModule.PointerEnter(other.gameObject). That then calls ExecuteEvents.Execute(obj, pEvent, ExecuteEvents.pointerEnterHandler); This turns the button blue when you mouse over with the VRCursor. This works ok.

When the mouse cursor leaves a button collider, OnTriggerExit() is called which calls VRInputModule.PointerExit(other.gameObject). That then calls ExecuteEvents.Execute(obj, pEvent, ExecuteEvents.pointerExitHandler); This turns the button grey again.

When you click a button, VRInputModule.PointerSubmit(currentCollider.gameObject); Is called which does the actual button click via ExecuteEvents.Execute(targetObject, data, ExecuteEvents.submitHandler);. This works too.

As you can see in the video, everything is ok until a button is clicked and you move the mouse off of the button. Once that happens, it’s as though the ExecuteEvents.Execute(obj, pEvent, ExecuteEvents.pointerExitHandler) function no longer works. The function is being called just the same, it just doesn’t do anything or something else is going on that I don’t understand. The button just stays blue as though you still have your mouse over it.

On to the project files:
I have several scripts turned off. The canvas’s script “Graphic Raycaster” is deactivated. This seems to prevent the regular Windows mouse from conflicting with the events I’m trying to send. When that is turned on things really go haywire which is not a surprise. The buttons light up when neither the VRCursor or the Windows mouse pointer is anywhere near the buttons, etc… I don’t really care about that because I don’t want to use the Windows mouse pointer anyway. It’s probably just sending messages after mine.

The EventSystem object has “Standalone Input Module” and “Touch Input Module” turned off as well for similar reasons. I’ve tried all kinds of things and having these all turned off seems to get me the closest to the goal: Eventually having no Windows mouse pointer at all which will be replaced by the VRCursor.

The only hiccup I’m having are those buttons staying highlighted after moving the mouse off. It’s as though the pointerExitHandler doesn’t work. Probably more likely is some other event I’m unaware of is happening immediately after or something along those lines, but I’m at a loss as to what it could be or how to fix it.

Am I doing this all wrong or could there be a bug here on Unity’s side? I don’t want to bother the staff with a bug report if I’m really just bungling this whole thing up (entirely likely).

1956061–126530–RiftGUIWorldSpaceCanvas Unity 5 RC 1.zip (425 KB)

Fixed! :slight_smile:

Adding ExecuteEvents.Execute(obj, pEvent, ExecuteEvents.deselectHandler) fixed it. I assume by deselecting the button. Here is the new PointerExit function in VRInputModule:

    public static void PointerExit(GameObject obj)
    {
        print("PointerExit " + obj.name);
        PointerEventData pEvent = new PointerEventData(_singleton.eventSystem);
        ExecuteEvents.Execute(obj, pEvent, ExecuteEvents.pointerExitHandler);
        ExecuteEvents.Execute(obj, pEvent, ExecuteEvents.deselectHandler); //This fixes the problem
    }

Here we go, 4.6 GUI buttons in the Oculus Rift :slight_smile:

It’s looking like for my simple GUI I’ll be able to use the same GUI in both the Rift and on a regular monitor. Prior to this I’d written my own “look at” type of system. It seems to me that this mouse controlled type approach feels more natural and allows faster clicking on things that are closer together. That’s probably a discussion for the VR forum though. :stuck_out_tongue:

Thanks Todd for this !
I’ve been searching about this problem for a while.
Your solution makes for an interesting alternative to others.
Looks very nice!

Glad you like it. Here’s what I ended up with in the end: