Gaze Input in Google Cardboard App

I wrote these scripts to use gaze input with Google Cardboard SDK. Please feel free to use it and let me know if there is any issue with this.

  1. Add InteractiveItem.cs script to every Selectable/Clickable object (Button, Toggle, Scroll bar etc).

InteractiveItem.cs

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

[RequireComponent(typeof(Selectable))]
public class InteractiveItem : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler 
{
    public bool isEntered = false;
    public Selectable _selectable;
    public float GazeActivationTime = 3f;

    BaseInputModule input;

    float timeElapsed;


    void Start ()
    {
        input = FindObjectOfType<BaseInputModule>();
        _selectable = GetComponent<Selectable>();
    }

    void Update ()
    {
        if (!_selectable.IsInteractable())
        {
            ReticlePointer.Instance.SetFillAmount(0);
            return;
        }
        if(isEntered)
        {
            timeElapsed += Time.deltaTime;

            ReticlePointer.Instance.SetFillAmount(Mathf.Clamp(timeElapsed/GazeActivationTime,0,1));

            if(timeElapsed >= GazeActivationTime)
            {
                timeElapsed = 0;

                ((GazeInputModule)input).HandleTriggerManually();

                ReticlePointer.Instance.SetFillAmount(0);

                isEntered = false;
            }

            ReticlePointer.Instance.Show();

        }
        else
        {
            timeElapsed = 0;
        }
    }

    void OnDisable()
    {
        isEntered = false;
        if (ReticlePointer.Instance!= null)
        {
            ReticlePointer.Instance.SetFillAmount(0);
        }
    }

#region IPointerEnterHandler implementation

    public void OnPointerEnter (PointerEventData eventData)
    {
        if (_selectable.IsInteractable())
        {
            Invoke("SetEnteredTrue",0.3f); // start timer after 0.3 seconds of gaze. You can call SetEnteredTrue() directly.
        }
    }

    void SetEnteredTrue()
    {
        isEntered = true;
    }

#endregion

#region IPointerExitHandler implementation

    public void OnPointerExit (PointerEventData eventData)
    {
        if (!_selectable.IsInteractable())
            return;
        try
        {
            CancelInvoke("SetEnteredTrue");
            isEntered = false;
            ReticlePointer.Instance.SetFillAmount(0);
        }
        catch (System.Exception ex)
        {
            Debug.LogError(ex.Message);
        }
    }
#endregion

#region IPointerClickHandler implementation

    public void OnPointerClick (PointerEventData eventData)
    {
        isEntered = false;
        timeElapsed = 0;
        ReticlePointer.Instance.SetFillAmount(0);

    }
#endregion

}
  1. Then this is how you place your pointer in hierarchy (Note: I am using legacy prefab GVRMain here. But you can add selected gameObject under Main Camera in whatever your prefab is):

2847759--207978--reticleHierarchy.PNG

  1. GazePointerRing is should be empty gameObject, attach ReticlePointer.cs to this object.
  2. Pointer and Fill_Image arering images in a canvas. scale them accordingly and assign them in ReticlePointer’s Inspector as reference as instructed. Fill_Image should have Image type “Fill”.

ReticlePointer.cs

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

public class ReticlePointer : MonoBehaviour
{
    public bool hideByDefault = true;
    public Image image;  // assign Fill_Image here.
    public float hideTimeoutPeriod = 3;
    public GameObject pointerObject; // assign Pointer Image here.

    public static ReticlePointer Instance;

    void Awake()
    {
        Instance = this;
        image.fillAmount = 0;
    }

    public void SetFillAmount(float amount)
    {
        image.fillAmount = amount;
    }

    void Update()
    {
        if (hideByDefault)
        {
            if (EventSystem.current.IsPointerOverGameObject())
            {
                CancelInvoke("Hide");
                Show();
            }
            else
            {
                SetFillAmount(0);
               Invoke("Hide",hideTimeoutPeriod);
            }
        }
        else
        {
            Show();
        }
    }

    public void Show()
    {
        pointerObject.SetActive(true);
    }

    public void Hide()
    {
        pointerObject.SetActive(false);
    }
}
  1. And finally add following lines of code to GazeInputModule.cs which comes within SDK.
public void HandleTriggerManually()
{
    HandleTrigger();
}

You are good to go.

You don’t need to use GVRReticle from cardboard SDK.

1 Like

Thank you for your script and for sharing it with us.
Assets/InteractiveItem.cs(many lines): error CS0103: The name `ReticlePointer’ does not exist in the current context
I guess new SDK messed up really bad with this script :frowning:

ReticlePointer should be name of above class it is a custom script (not from SDK what so ever). Can you please explain how exactly you are trying to use it?