Using VR handle rays to trigger UI events in Unity

Create a ray test to determine the start and end points

public class LineController : SingletonMono<LineController>
{
    
    
    //属性
    [HideInInspector] public Vector3 pointStartPos, pointEndPos;
    [HideInInspector] public Vector3 lineDirection;
    [HideInInspector] public float lineRealLength;
    public Hand hand;
    public float rayLength = 20.0f;
    public LayerMask layerMask;

    //private UITriggerSystem currentTarget;
    private IEventHandle_VR currentTarget;
    /// <summary>
    /// 当前是否有触发对象
    /// </summary>
    public bool hasTarget
    {
    
    
        get {
    
     return currentTarget != null; }
    }

    //委托
    public Action OnEnterEvent;
    public Action OnExitEvent;
    public Action OnClickEvent;
    public Action<bool> OnActiveEvent;

    [HideInInspector] public bool active;
    private bool tempLineActive;

    public SteamVR_Action_Boolean menuAction = SteamVR_Input.GetAction<SteamVR_Action_Boolean>("Menu");
    public SteamVR_Action_Boolean teleportAction = SteamVR_Input.GetAction<SteamVR_Action_Boolean>("Teleport");

    private IEventHandle_VR currentObj;
    private IEventHandle_VR pressObj;
    void Start()
    {
    
    
        hand = Player.instance.rightHand;
    }

    void Update()
    {
    
    
        //判断是否触发圆盘按钮,如果按下就关闭射线检测
        if (teleportAction.GetStateDown(hand.handType))
        {
    
    
            tempLineActive = active;
            SetActive(false);
        }
        else if (teleportAction.GetStateUp(hand.handType))
        {
    
    
            if (tempLineActive) SetActive(true);
        }

        //按下菜单键,呼出射线
        if (menuAction.GetStateDown(hand.handType))
        {
    
    
            SetActive(!active);
        }

        if (!active) return;//未开启射线检测

        pointStartPos = hand.transform.position;
        lineDirection = hand.transform.forward;
        Ray ray = new Ray(pointStartPos, lineDirection);
        if (Physics.Raycast(ray, out RaycastHit hit, rayLength, layerMask))
        {
    
    
            if (hit.transform.TryGetComponent(out IEventHandle_VR uiTriggerSystem))
            {
    
    
                pointEndPos = hit.point;
                lineRealLength = hit.distance;

                onTrigger(uiTriggerSystem);

                //Debug.DrawLine(pointStartPos, lineDirection * rayLength, Color.green);
            }
            else
            {
    
    
                onTrigger(null);

                pointEndPos = pointStartPos + (lineDirection * 10000.0f - pointStartPos).normalized * rayLength;
                lineRealLength = rayLength;
            }
        }
        else
        {
    
    
            onTrigger(null);

            //pointEndPos = lineDirection * rayLength * 1000.0f;//有较大的误差,还不知道怎么解决,希望大佬看到可以留言解决,暂时用下面的方法。
            pointEndPos = pointStartPos + (lineDirection * 10000.0f - pointStartPos).normalized * rayLength;
            lineRealLength = rayLength;

            //Debug.DrawLine(pointStartPos, lineDirection * rayLength, Color.magenta);
        }


        if (hand.grabPinchAction.GetStateDown(hand.handType))
        {
    
    
            if (currentObj == null) return;
            pressObj = currentObj;
            onClick(pressObj);
            pressObj.OnHandDown(hand);
        }
        if (hand.grabPinchAction.GetStateUp(hand.handType))
        {
    
    
            if (pressObj == null) return;
            pressObj.OnHandUp(hand);
            pressObj = null;
        }
        if (hand.grabPinchAction.GetState(hand.handType))
        {
    
    
            currentObj?.OnHandStay(hand);
        }
    }

    private void onTrigger(IEventHandle_VR target)
    {
    
    
        currentObj = target;
        if (target == null)//离开
        {
    
    
            if (currentTarget != null)
            {
    
    
                currentTarget.OnHandExit(hand);
                onExit(currentTarget);
                //Debug.Log($"离开{currentTarget}");
            }
            currentTarget = null;
        }
        else
        {
    
    
            //第一次
            if (currentTarget == null)
            {
    
    
                target.OnHandEnter(hand);
                onEnter(target);
                //Debug.Log($"进入{target}");
                currentTarget = target;
            }
            else if (currentTarget != target)//第n次
            {
    
    
                currentTarget.OnHandExit(hand);
                target.OnHandEnter(hand);

                onExit(currentTarget);
                onEnter(target);
                //Debug.Log($"进入{target},离开{currentTarget}");
                currentTarget = target;
            }
        }
    }
    /// <summary>
    /// 射线进入
    /// </summary>
    private void onEnter(IEventHandle_VR target)
    {
    
    
        //Debug.Log("进入");
        OnEnterEvent?.Invoke();
    }
    /// <summary>
    /// 射线进入
    /// </summary>
    private void onExit(IEventHandle_VR target)
    {
    
    
        //Debug.Log("离开");
        OnExitEvent?.Invoke();
    }
    /// <summary>
    /// 手柄扣动扳机
    /// </summary>
    private void onClick(IEventHandle_VR target)
    {
    
    
        //Debug.Log("手柄点击");
        OnClickEvent?.Invoke();
    }
    /// <summary>
    /// 激活或者关闭射线检测
    /// </summary>
    public void SetActive(bool _active)
    {
    
    
        OnActiveEvent?.Invoke(_active);
        active = _active;

        if (!_active) onTrigger(null);//当手柄关闭射线检测
    }
}

draw ray

public class LineRenderer : MonoBehaviour
{
    
    
    private LineController lineController;
    public LineRenderer line;
    [SerializeField] GameObject targetPoint;
    void Start()
    {
    
    
        lineController = GetComponent<LineController>();

        createLine();
        createTargetPoint();

        lineController.OnEnterEvent += onEnter;
        lineController.OnExitEvent += onExit;
        lineController.OnActiveEvent += onActive;
    }

    // Update is called once per frame
    void Update()
    {
    
    
        line.SetPosition(0, lineController.pointStartPos);
        line.SetPosition(1, lineController.pointEndPos);

        targetPoint.SetActive(lineController.hasTarget);
        if (lineController.hasTarget)
        {
    
    
            targetPoint.transform.position = lineController.pointEndPos;
        }
    }
    void createLine()
    {
    
    
        line = Instantiate(line);
        line.useWorldSpace = true;
        line.startWidth = 0.0035f;
        line.endWidth = 0.0035f;
    }
    void createTargetPoint()
    {
    
    
        targetPoint = Instantiate(targetPoint);
    }
    private void onEnter()
    {
    
    
        line.material.SetFloat("_CenterIntensity", 10.0f);
    }
    private void onExit()
    {
    
    
        line.materials[0].SetFloat("_CenterIntensity", 2.0f);
    }
    private void onActive(bool active)
    {
    
    
        line.enabled = active;
    }
}

Trigger various states of UI controls, called when the handle ray is triggered

public class UIInputModule_VR : SingletonMono<UIInputModule_VR>
{
    
    
    private EventSystem eventSystem;
    private PointerEventData eventData;

    private GameObject currentObj;
    public HandTriggerType handTriggerType;
    void Start()
    {
    
    
        eventSystem = GetComponent<EventSystem>();
        eventData = new PointerEventData(eventSystem);
    }
    public void MouseEnter(GameObject obj)
    {
    
    
        currentObj = obj;
        eventData.pointerEnter = obj;
        ExecuteEvents.Execute(obj, eventData, ExecuteEvents.pointerEnterHandler);
    }
    public void MouseExit(GameObject obj)
    {
    
    
        currentObj = null;
        ExecuteEvents.Execute(eventData.pointerEnter, eventData, ExecuteEvents.pointerExitHandler);
    }
    public void MouseDown(GameObject obj)
    {
    
    
        eventData.pointerPress = obj;
        ExecuteEvents.Execute(obj, eventData, ExecuteEvents.pointerDownHandler);

        if(handTriggerType == HandTriggerType.Down)
        {
    
    
            ExecuteEvents.Execute(obj, eventData, ExecuteEvents.pointerClickHandler);
        }
    }
    public void MouseUp(GameObject obj)
    {
    
    
        ExecuteEvents.Execute(eventData.pointerPress, eventData, ExecuteEvents.pointerUpHandler);
        ExecuteEvents.Execute(eventData.pointerPress, eventData, ExecuteEvents.deselectHandler);

        if (currentObj == eventData.pointerPress && handTriggerType == HandTriggerType.Up)
        {
    
    
            ExecuteEvents.Execute(obj, eventData, ExecuteEvents.pointerClickHandler);
        }
    }

    public enum HandTriggerType
    {
    
    
        Up, Down
    }
}

public static class UIInputModuleExtension
{
    
    
    public static void MouseEnter(this GameObject obj)
    {
    
    
        UIInputModule_VR.Instance.MouseEnter(obj);
    }
    public static void MouseExit(this GameObject obj)
    {
    
    
        UIInputModule_VR.Instance.MouseExit(obj);
    }
    public static void MouseDown(this GameObject obj)
    {
    
    
        UIInputModule_VR.Instance.MouseDown(obj);
    }
    public static void MouseUp(this GameObject obj)
    {
    
    
        UIInputModule_VR.Instance.MouseUp(obj);
    }
}

Some other button-related code

public class UIComponent_VR : MonoBehaviour, IEventHandle_VR
{
    
    
    private CanvasRenderer canvasRenderer;
    private RectTransform rectTransform;
    private BoxCollider boxCollider;
    public bool triggerSizeEveryFrame = false;

    public Action OnHandEnterEvent;
    public Action OnHandExitEvent;

    public Action OnHandDownEvent;
    public Action OnHandStayEvent;
    public Action OnHandUpEvent;
    protected virtual void Start()
    {
    
    
        boxCollider = GetComponent<BoxCollider>();
        rectTransform = GetComponent<RectTransform>();
        canvasRenderer = GetComponent<CanvasRenderer>();

        boxCollider.isTrigger = true;
        boxCollider.size = new Vector3(rectTransform.rect.width, rectTransform.rect.height);
    }

    protected virtual void Update()
    {
    
    
        if (triggerSizeEveryFrame)
        {
    
    
            boxCollider.size = new Vector3(rectTransform.rect.width, rectTransform.rect.height);
        }
    }

    protected virtual void LateUpdate()
    {
    
    
        transform.localPosition = new Vector3(transform.localPosition.x, transform.localPosition.y, -canvasRenderer.absoluteDepth);
    }

    public virtual void OnHandEnter(Hand hand)
    {
    
    
        OnHandEnterEvent?.Invoke();
    }

    public virtual void OnHandExit(Hand hand)
    {
    
    
        OnHandExitEvent?.Invoke();
    }

    public virtual void OnHandDown(Hand hand)
    {
    
    
        OnHandDownEvent?.Invoke();
    }

    public virtual void OnHandStay(Hand hand)
    {
    
    
        OnHandStayEvent?.Invoke();
    }

    public virtual void OnHandUp(Hand hand)
    {
    
    
        OnHandUpEvent?.Invoke();
    }
}

public interface IEventHandle_VR
{
    
    
    /// <summary>
    /// 手柄进入触发
    /// </summary>
    public void OnHandEnter(Hand hand);
    /// <summary>
    /// 手柄离开触发
    /// </summary>
    public void OnHandExit(Hand hand);
    /// <summary>
    /// 手柄按下触发
    /// </summary>
    public void OnHandDown(Hand hand);
    /// <summary>
    /// 手柄停留触发
    /// </summary>
    public void OnHandStay(Hand hand);
    /// <summary>
    /// 手柄抬起触发
    /// </summary>
    public void OnHandUp(Hand hand);
}

Button code

public class Button_VR : UIComponent_VR
{
    
    

    public override void OnHandEnter(Hand hand)
    {
    
    
        base.OnHandEnter(hand);

        hand.TriggerHapticPulse(1000);

        gameObject.MouseEnter();
    }
    public override void OnHandExit(Hand hand)
    {
    
    
        base.OnHandExit(hand);

        gameObject.MouseExit();
    }

    public override void OnHandDown(Hand hand)
    {
    
    
        base.OnHandDown(hand);

        gameObject.MouseDown();
    }

    public override void OnHandUp(Hand hand)
    {
    
    
        base.OnHandUp(hand);

        gameObject.MouseUp();
    }
}

Guess you like

Origin blog.csdn.net/u010197227/article/details/129323638