VR Unity 射线点击操作(2) 手柄输入篇

8 篇文章 2 订阅
1 篇文章 0 订阅

上一篇,我们说的是ray射线碰撞方面的逻辑,在上一篇中,我们主要是解决了射线进入,射线离开,射线持续碰撞中的逻辑判断,有了这些逻辑,我们才能够在下一步中,有进入按钮,离开按钮,持续碰撞按钮的逻辑判断。既然有了这些判断之后,我们就可以在这些得到的逻辑判断中来进行按钮的点击操作,如按钮按下,按钮弹起,按钮持续按下中这些简单的操作,当然,这是在VR Unity 射线点击操作(3) 文章后我们才能够看到一套完整的逻辑过程。现在我们先梳理下手柄的输入操作,上一篇是射线的逻辑处理,这一篇说的是手柄的输入逻辑处理。先上代码来解释。代码是初期写的,磕磕碰碰后运行了一年多,按钮点击这块没出现过啥问题,当然还有很多设计的不足,看懂了,你们了解思路就好了。能学到的东西嘛,也就是一些基础,一些逻辑设计。这一篇是针对HTC 手柄来开发的,如果是普通的手柄,只要实现红线的判断就可以了,HTCHandleInputControl必须和SteamVR_TrackedObject放在同一个物体下

 

 

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;


/// <summary>
/// 手柄类型
/// </summary>
public enum HandleType
{
    None,
    Left,
    Right
}
/// <summary>
/// HTC手柄输入控制,手柄输入逻辑在这里判断,必须和SteamVR_TrackedObject放在同一个物体下
/// 考虑到HTC手柄控制器有多个输入接口,所以我们再增加多为htc手柄的委托
/// </summary>
public class HTCHandleInputControl : MyInputControl
{
    /// <summary>
    /// 左边手柄
    /// </summary>
    private SteamVR_TrackedObject _rightSteamVrTrackedObject;
    /// <summary>
    /// 右手柄
    /// </summary>
    public static HTCHandleInputControl HtcHandleInputControRight;

    /// <summary>
    /// 左手柄
    /// </summary>
    public static HTCHandleInputControl HtcHandleInputControlLeft;


    /// <summary>
    /// 触摸按下的回调
    /// </summary>
    public Action<Vector2> TouchDownCallBack;
    /// <summary>
    /// 有触摸事件的回调
    /// </summary>
    public Action<Vector2> TouchCallBack;
    /// <summary>
    /// 圆盘触摸离开的回调
    /// </summary>
    public Action<Vector2> TouchUpCallBack;
    /// <summary>
    /// 触摸板按下的回调
    /// </summary>
    public Action<Vector2> RoundelPressDownAxis0;
    /// <summary>
    /// 圆盘物理弹起的回调
    /// </summary>
    public Action<Vector2> RoundelPressUpAxis0;
    /// <summary>
    /// 圆盘持续按下中
    /// </summary>
    public Action<Vector2> RoundelPressAxis0;
    /// <summary>
    /// 菜单键弹起回调
    /// </summary>
    public Action ApplicationMenuPressUpCallBack;
    /// <summary>
    /// 侧面键按下的回调
    /// </summary>
    public Action GripPressDownCallBack;
    /// <summary>
    /// 侧面键弹起的回调
    /// </summary>
    public Action<bool> GripPressUpCallBack;

    private Vector2 _currentVector2;

    public HandleType HandleType = HandleType.None;

    /// <summary>
    /// 手柄是否可用的回调,这里有可能是没电,然后不可用,或者是没信号了,不可用,很多不确定性,必须要通知到外面 
    /// 还需要进一步完善  
    /// </summary>
    public static Action<bool, HandleType> IsAvailableCallBack;

    public SteamVR_Controller.Device Device { get; private set; }

    private void Awake()
    {
        //Debug.Log("初始化手柄" + HandleType);
        if (HandleType == HandleType.None) throw new UnityException("没有指定手柄是左手还是右手");
        //判断该输入控制是左手柄还是右手柄
        if (HandleType == HandleType.Left)
        {
            if (HtcHandleInputControlLeft != null) throw new UnityException("已经初始化左手柄了,请检查是否有多个左手柄");
            HtcHandleInputControlLeft = this;
        }
        else
        {
            if (HtcHandleInputControRight != null) throw new UnityException("已经初始化右手柄了,请检查是否有多个右手柄");
            HtcHandleInputControRight = this;
        }
        _rightSteamVrTrackedObject = this.GetComponent<SteamVR_TrackedObject>();
    }


    protected override void OnDestroy()
    {
        HtcHandleInputControRight = null;

        HtcHandleInputControlLeft = null;
    }
    protected override void Start()
    {
        base.Start();
    }

    protected override void Update()
    {
        base.Update();

        Device = SteamVR_Controller.Input((int)_rightSteamVrTrackedObject.index);





        if (Device.GetPressDown(SteamVR_Controller.ButtonMask.Trigger))//判断手柄下方的扳机键按钮按下
        {
            if (ButtonDownCallBack != null)
                ButtonDownCallBack(HandleType != HandleType.Left);
            PressState = PressState.ButtonDown;


            //Debug.Log("HTC按下");
        }
        else if (Device.GetPressUp(SteamVR_Controller.ButtonMask.Trigger))//判断手柄下方的扳机键按钮弹起
        {
            if (ButtonUpCallBack != null)
            {
                ButtonUpCallBack(HandleType != HandleType.Left);
            }
            PressState = PressState.ButtonUp;
            // Debug.Log("HTC弹起");  
        }
        else if (Device.GetPress(SteamVR_Controller.ButtonMask.Trigger))//判断手柄下方的按钮按下状态中  
        {
            if (ButtoningCallBack != null)
                ButtoningCallBack();
            PressState = PressState.Buttoning;
            // Debug.Log("HTC正在按下中");
        }
        if (Device.GetTouch(SteamVR_Controller.ButtonMask.Touchpad))
        {
            // Debug.Log("触摸了 “Touchpad” “ ”");
            _currentVector2 = Device.GetAxis();
            if (TouchCallBack != null)
                TouchCallBack(_currentVector2);

            方法返回一个坐标 接触圆盘位置   
            //Vector2 cc = device.GetAxis();
            //Debug.Log(cc);

        }
        if (Device.GetTouchDown(SteamVR_Controller.ButtonMask.Touchpad))
        {
            // Debug.Log("触摸了 “Touchpad” “ ”");
            _currentVector2 = Device.GetAxis();
            if (TouchDownCallBack != null)
                TouchDownCallBack(_currentVector2);
        }
        if (Device.GetPressDown(SteamVR_Controller.ButtonMask.Axis0))//大圆盘的按下,而不是触摸
        {
            //Debug.Log("大圆盘的按下,而不是触摸");
            if (RoundelPressDownAxis0 != null)
                RoundelPressDownAxis0(_currentVector2);
        }
        if (Device.GetPress(SteamVR_Controller.ButtonMask.Axis0))//大圆盘的按下
        {
            if (RoundelPressAxis0 != null)
                RoundelPressAxis0(_currentVector2);
        }
        if (Device.GetPressUp(SteamVR_Controller.ButtonMask.Axis0))//大圆盘的弹起,而不是触摸离开
        {

            if (RoundelPressUpAxis0 != null)
                RoundelPressUpAxis0(_currentVector2);
        }

        if (Device.GetTouchUp(SteamVR_Controller.ButtonMask.Touchpad))//触摸离开大圆盘的时候触发
        {
            //Debug.Log("弹起了 “Touchpad” “ ”");
            if (TouchUpCallBack != null)
                TouchUpCallBack(_currentVector2);

            //  Debug.Log(_currentVector2);
        }
        if (Device.GetPressUp(SteamVR_Controller.ButtonMask.ApplicationMenu))
        {
            // Debug.Log("用press弹起了ApplicationMenu菜单键");
            if (ApplicationMenuPressUpCallBack != null)
                ApplicationMenuPressUpCallBack();
        }
        //Grip键 两侧的键 (vive雇佣兵游戏中的换弹键),每个手柄左右各一功能相同,同一手柄两个键是一个键。  

        if (Device.GetPressDown(SteamVR_Controller.ButtonMask.Grip))
        {
            //  Debug.Log("用press按下了 “Grip” “ ”");
            if (GripPressDownCallBack != null)
                GripPressDownCallBack();
        }
        if (Device.GetPressUp(SteamVR_Controller.ButtonMask.Grip))
        {
            //Debug.Log("用press弹起了 “Grip” “ ”");
            if (GripPressUpCallBack != null)
                GripPressUpCallBack(HandleType != HandleType.Left);
        }

      
    }

    protected override void OnEnable()
    {
        if (IsAvailableCallBack != null)
            IsAvailableCallBack(true, HandleType);
        base.OnEnable();

    }

    protected override void OnDisable()
    {
        if (IsAvailableCallBack != null)
            IsAvailableCallBack(false, HandleType);
        base.OnDisable();
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 鼠标或者蓝牙的按钮是否按下的状态,目前只设定支持一个
/// </summary>
public enum PressState : int
{
    /// <summary>
    /// 没有按键状态
    /// </summary>
    None,
    /// <summary>
    /// 按下状态
    /// </summary>
    ButtonDown,
    /// <summary>
    /// 弹起状态
    /// </summary>
    ButtonUp,
    /// <summary>
    /// 按下状态中
    /// </summary>
    Buttoning
}
public interface InputControl
{

    /// <summary>
    /// 监听器只监听这个属性就好
    /// </summary>
    PressState State
    {
        get;
    }

}

/// <summary>
/// 游戏输入全局控制,输入监听在这里捕获并再进一步转化到状态信息给事件监听器  
/// </summary>
public class MyInputControl : MonoBehaviour, InputControl
{
    /// <summary>
    /// 按下事件
    /// </summary>
    public delegate void ButtonDown(bool isRight);
    /// <summary>
    /// 弹起事件
    /// </summary>
    public delegate void ButtonUp(bool isRight);
    /// <summary>
    /// 正在按事件
    /// </summary>
    public delegate void Buttoning();



    public ButtonDown ButtonDownCallBack;
    /// <summary>
    /// 按键弹起的回调
    /// </summary>
    public static ButtonUp ButtonUpCallBack;
    /// <summary>
    /// 按钮持续碰撞中的回调
    /// </summary>
    public Buttoning ButtoningCallBack;

    /// <summary>
    /// 鼠标或者蓝牙按键是否按下的状态  
    /// </summary>
    protected PressState PressState = PressState.None;
    /// <summary>
    /// 设置静态,这样就不会产生垃圾了  ,我们需要它来处理最后的输入逻辑判断
    /// </summary>
    protected static WaitForEndOfFrame _waitForEndOfFrame;
    /// <summary>
    /// 鼠标或者蓝牙按键是否按下的状态
    /// </summary>
    public PressState State
    {
        get { return PressState; }
    }
    private Coroutine _coroutine;
    private void Awake()
    {
    }
    protected virtual void Start()
    {
    }
    protected virtual void OnEnable()
    {
        _waitForEndOfFrame = new WaitForEndOfFrame();
        if (_coroutine != null) StopCoroutine(_coroutine);
        _coroutine = StartCoroutine(WaitForEndOfFramed());
    }
    /// <summary>
    /// 输入设置是在Update后检测
    /// </summary>
    protected virtual void Update()
    {
    }
    /// <summary>
    /// 整个工作循环结束后才执行的操作,这个方法在update,fixedUpdate,LateUpdate等操作完成之后才会执行,具体可查看unity的方法执行顺序
    /// 也就是说,按钮点击后的事件,比手柄的扳机键按下还要晚,这样的话,我们就可以从扳机键是否按下的消息,来给点击后的逻辑来作为判断
    /// </summary>
    /// <returns></returns>
    protected IEnumerator WaitForEndOfFramed()
    {
        while (true)
        {
            yield return _waitForEndOfFrame;
            //延迟执行状态,等待其他update方法查询
            //一个脚本周期结束后如果状态不是按下,或者持续按下中,状态都要强制转换为空状态,其实排除了ButtonDown,Buttoning,就只有ButtonUp状态了buttonUp是标记着按键按下的标记
            if (PressState != PressState.ButtonDown && PressState != PressState.Buttoning)
                PressState = PressState.None;
        }
        // ReSharper disable once IteratorNeverReturns
    }
    protected virtual void OnDestroy()
    {
    }
    protected virtual void OnDisable()
    {
    }
}

到这里基本差不多了,下一章我们将写BaseListeningUI组件,相当于GUI中的button组件吧

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值