上一篇,我们说的是ray射线碰撞方面的逻辑,在上一篇中,我们主要是解决了射线进入,射线离开,射线持续碰撞中的逻辑判断,有了这些逻辑,我们才能够在下一步中,有进入按钮,离开按钮,持续碰撞按钮的逻辑判断。既然有了这些判断之后,我们就可以在这些得到的逻辑判断中来进行按钮的点击操作,如按钮按下,按钮弹起,按钮持续按下中这些简单的操作,当然,这是在VR Unity 射线点击操作(3) 文章后我们才能够看到一套完整的逻辑过程。现在我们先梳理下手柄的输入操作,上一篇是射线的逻辑处理,这一篇说的是手柄的输入逻辑处理。先上代码来解释。代码是初期写的,磕磕碰碰后运行了一年多,按钮点击这块没出现过啥问题,当然还有很多设计的不足,看懂了,你们了解思路就好了。能学到的东西嘛,也就是一些基础,一些逻辑设计。这一篇是针对HTC 手柄来开发的,如果是普通的手柄,只要实现红线的判断就可以了,HTCHandleInputControl必须和SteamVR_TrackedObject放在同一个物体下
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
///
/// 手柄类型
///
public enum HandleType
{
None,
Left,
Right
}
///
/// HTC手柄输入控制,手柄输入逻辑在这里判断,必须和SteamVR_TrackedObject放在同一个物体下
/// 考虑到HTC手柄控制器有多个输入接口,所以我们再增加多为htc手柄的委托
///
public class HTCHandleInputControl : MyInputControl
{
///
/// 左边手柄
///
private SteamVR_TrackedObject _rightSteamVrTrackedObject;
///
/// 右手柄
///
public static HTCHandleInputControl HtcHandleInputControRight;
///
/// 左手柄
///
public static HTCHandleInputControl HtcHandleInputControlLeft;
///
/// 触摸按下的回调
///
public Action TouchDownCallBack;
///
/// 有触摸事件的回调
///
public Action TouchCallBack;
///
/// 圆盘触摸离开的回调
///
public Action TouchUpCallBack;
///
/// 触摸板按下的回调
///
public Action RoundelPressDownAxis0;
///
/// 圆盘物理弹起的回调
///
public Action RoundelPressUpAxis0;
///
/// 圆盘持续按下中
///
public Action RoundelPressAxis0;
///
/// 菜单键弹起回调
///
public Action ApplicationMenuPressUpCallBack;
///
/// 侧面键按下的回调
///
public Action GripPressDownCallBack;
///
/// 侧面键弹起的回调
///
public Action GripPressUpCallBack;
private Vector2 _currentVector2;
public HandleType HandleType = HandleType.None;
///
/// 手柄是否可用的回调,这里有可能是没电,然后不可用,或者是没信号了,不可用,很多不确定性,必须要通知到外面
/// 还需要进一步完善
///
public static Action 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();
}
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;
///
/// 鼠标或者蓝牙的按钮是否按下的状态,目前只设定支持一个
///
public enum PressState : int
{
///
/// 没有按键状态
///
None,
///
/// 按下状态
///
ButtonDown,
///
/// 弹起状态
///
ButtonUp,
///
/// 按下状态中
///
Buttoning
}
public interface InputControl
{
///
/// 监听器只监听这个属性就好
///
PressState State
{
get;
}
}
///
/// 游戏输入全局控制,输入监听在这里捕获并再进一步转化到状态信息给事件监听器
///
public class MyInputControl : MonoBehaviour, InputControl
{
///
/// 按下事件
///
public delegate void ButtonDown(bool isRight);
///
/// 弹起事件
///
public delegate void ButtonUp(bool isRight);
///
/// 正在按事件
///
public delegate void Buttoning();
public ButtonDown ButtonDownCallBack;
///
/// 按键弹起的回调
///
public static ButtonUp ButtonUpCallBack;
///
/// 按钮持续碰撞中的回调
///
public Buttoning ButtoningCallBack;
///
/// 鼠标或者蓝牙按键是否按下的状态
///
protected PressState PressState = PressState.None;
///
/// 设置静态,这样就不会产生垃圾了 ,我们需要它来处理最后的输入逻辑判断
///
protected static WaitForEndOfFrame _waitForEndOfFrame;
///
/// 鼠标或者蓝牙按键是否按下的状态
///
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());
}
///
/// 输入设置是在Update后检测
///
protected virtual void Update()
{
}
///
/// 整个工作循环结束后才执行的操作,这个方法在update,fixedUpdate,LateUpdate等操作完成之后才会执行,具体可查看unity的方法执行顺序
/// 也就是说,按钮点击后的事件,比手柄的扳机键按下还要晚,这样的话,我们就可以从扳机键是否按下的消息,来给点击后的逻辑来作为判断
///
///
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组件吧