如何使用眼动仪在UGUI中模拟按钮相关事件

最近在开发一款使用Tobii眼动仪控制的游戏,然后界面上的按钮都是使用UGUI,在前期用鼠标控制表现良好,但是最终需要用眼动仪控制按钮。而Unity默认的按钮是不支持眼动仪点击按钮的,并且Tobii眼动仪提供的API也只能拿到眼睛看到的坐标。

最后经过调查,发现Unity是通过StandaloneInputModule发送鼠标点击事件的,所以我利用已有的StandaloneInputModule源码重新写了一个GazeInputModle,最后实现了这个需求。目前眼动仪只要捕捉到眼光在按钮上停留两秒就会触发鼠标点击事件。

以下是主要的代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using Tobii.Gaming;

public class GazeInputModule : 
{
    private GameObject currentOverGameObject;
    private PointerEventData pointerEventData;
    private float durationTime;

    protected void HandlePointerExitAndEnter2(PointerEventData currentPointerData, GameObject newEnterTarget)
    {
        // if we have no target / pointerEnter has been deleted 
        // just send exit events to anything we are tracking 
        // then exit 
        if (newEnterTarget == null || currentPointerData.pointerEnter == null)
        {
            for (var i = 0; i < currentPointerData.hovered.Count; ++i)
                ExecuteEvents.Execute(currentPointerData.hovered[i], currentPointerData, ExecuteEvents.pointerExitHandler);
            currentPointerData.hovered.Clear();
            if (newEnterTarget == null)
            {
                currentPointerData.pointerEnter = newEnterTarget;
                return;
            }
        }
        // if we have not changed hover target 
        if (currentPointerData.pointerEnter == newEnterTarget && newEnterTarget)
            return;
        GameObject commonRoot = FindCommonRoot(currentPointerData.pointerEnter, newEnterTarget);
        // and we already an entered object from last time 
        if (currentPointerData.pointerEnter != null)
        {
            // send exit handler call to all elements in the chain 
            // until we reach the new target, or null! 
            Transform t = currentPointerData.pointerEnter.transform;
            while (t != null)
            {
                // if we reach the common root break out! 
                if (commonRoot != null && commonRoot.transform == t)
                    break;
                ExecuteEvents.Execute(t.gameObject, currentPointerData, ExecuteEvents.pointerExitHandler);
                currentPointerData.hovered.Remove(t.gameObject);
                t = t.parent;
            }
        }
        // now issue the enter call up to but not including the common root 
        currentPointerData.pointerEnter = newEnterTarget;
        if (newEnterTarget != null)
        {
            Transform t = newEnterTarget.transform;
            while (t != null && t.gameObject != commonRoot)
            {
                ExecuteEvents.Execute(t.gameObject, currentPointerData, ExecuteEvents.pointerEnterHandler);
                currentPointerData.hovered.Add(t.gameObject);
                t = t.parent;
            }
        }
    }

    public void ProcessPoint()
    {
        List<RaycastResult> results = new List<RaycastResult>();
        EventSystem.current.RaycastAll(pointerEventData, results);

        // Debug.Log("mousePosition=" + Input.mousePosition + ", count=" + results.Count);

        RaycastResult raycastResult = FindFirstRaycast(results);
        HandlePointerExitAndEnter2(pointerEventData, raycastResult.gameObject);

        if (raycastResult.gameObject == pointerEventData.pointerEnter && raycastResult.gameObject != null)
        {
            durationTime += Time.deltaTime;
        }
        else
        {
            durationTime = 0;
        }

        if (durationTime > Configuration.GetFloat("InputMode.GazeClickDurationTime", 2)) //看一个对象两秒触发点击事件,可以配置
        {
            Debug.Log("Send Click event, name=" + raycastResult.gameObject.name);
            ExecuteEvents.Execute(raycastResult.gameObject, pointerEventData, ExecuteEvents.pointerClickHandler);
            durationTime = 0;
        }
    }
    
    public void Update()
    {
        if (pointerEventData == null)
        {
            pointerEventData = new PointerEventData(gameObject.GetComponent<EventSystem>());
        }

        if (Configuration.GetBool("InputMode.TestMode", false) == false) //如果是测试模式则使用鼠标模拟眼动仪,便于测试
        {
            GazePoint gazePoint = TobiiAPI.GetGazePoint();
            if (gazePoint.IsValid)
            {
                pointerEventData.position = gazePoint.Screen;
                ProcessPoint();
            }
        }
        else
        {
            pointerEventData.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
            ProcessPoint();
        }
    }

    public override void Process()
    {
        //do nothing to avoid mouse event cover ours 
    }
    
    
}

  主要继承了StandaloneInputModule,然后在Update里检测点是否停留在按钮上。

  用的时候需要把这个脚本挂载在EventSystem对象上,同时需要disable对象上的StandaloneInputModule,否则在选中按钮时每个Module都会发送PointerEnter事件。

  目前只实现了PointerEnter、PointerExit、PointerClick事件,如果有需要可以增加其他事件。

  代码中Configuration相关方法是自己写一个配置类,用的时候需要修改下这些代码。

转载于:https://www.cnblogs.com/liu102401/p/10241435.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值