Search code examples
c#unity-game-engineuser-interfacevirtual-realitygaze-buttons

OnPointerEnter is not being called


I'm trying to animate the button while it is being gazed on. I have got the following code in which I Raycast from a sphere to find the button that it hits.

var eventDataCurrentPosition = new PointerEventData(EventSystem.current);
        eventDataCurrentPosition.position = screenPosition;

var results = new List<RaycastResult>();

EventSystem.current.RaycastAll(eventDataCurrentPosition, results);

foreach (var result in results)
{
     Debug.Log(result.gameObject.name);
}  

In order to animate the button, I'm adding the following code to the button. Unfortunately, the OnPointerEnter or ``OnPointerExit is never being called.

[RequireComponent(typeof(Button))]
public class InteractiveItem : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
    public Image  progressImage;
    public bool   isEntered = false;
    RectTransform rt;
    Button        _button;
    float         timeElapsed;
    Image         cursor;
    float         GazeActivationTime = 5;

// Use this for initialization
void Awake()
{
    _button = GetComponent<Button>();
    rt      = GetComponent<RectTransform>();
}


void Update()
{
    if (isEntered)
    {
        timeElapsed              += Time.deltaTime;
        progressImage.fillAmount =  Mathf.Clamp(timeElapsed / GazeActivationTime, 0, 1);
        if (timeElapsed >= GazeActivationTime)
        {
            timeElapsed = 0;
            _button.onClick.Invoke();
            progressImage.fillAmount = 0;
            isEntered                = false;
        }
    }
    else
    {
        timeElapsed = 0;
    }
}

#region IPointerEnterHandler implementation

public void OnPointerEnter(PointerEventData eventData)
{
    CodelabUtils._ShowAndroidToastMessage("entered");
    isEntered = true;
}

#endregion

#region IPointerExitHandler implementation

public void OnPointerExit(PointerEventData eventData)
{
    CodelabUtils._ShowAndroidToastMessage("exit");

    isEntered                = false;
    progressImage.fillAmount = 0;
}

#endregion
}

am I missing something ? or is there any other way of achieving this?


Solution

  • I guess your Raycast supposed to trigger the OnPointerEnter on all results.

    You will need to use ExecuteEvents.Execute like e.g.

    ExecuteEvents.Execute(result.gameObject, eventDataCurrentPosition, ExecuteEvents.pointerEnterHandler);
    

    And will also at some point have to invoke pointerExitHandler.

    I would therefore store a HashSet like

    using System.Linq;
    
    private HashSet<GameObject> previousEnters = new HashSet<GameObject>();
    
    ...
    
    foreach (var result in results.Select(r => r.gameObject))
    {
        Debug.Log(result.name);
        ExecuteEvents.Execute(result, eventDataCurrentPosition, ExecuteEvents.pointerEnterHandler);
        // Store the item so you can later invoke exit on them
        if(!previousEnters.Contains(result)) previousEnters.Add(result);
    }  
    
    // This uses Linq in order to get only those entries from previousEnters that are not in the results
    var exits = previousEnters.Except(results);
    foreach(var item in exits)
    {
        if(item) ExecuteEvents.Execute(item, eventDataCurrentPosition, ExecuteEvents.pointerExitHandler);
    }
    

    You might actually want to implement your own custom PointerInputModule.


    Alternative/Example

    As a starter you could also use my answer to Using Raycast instead of Gaze Pointer from a while ago where I created a script based on Steam's VR Laserpointer which allows to interact with 3D objects and UI elements.


    Note: Typed on smartphone but I hope the idea gets clear