代码中的类型名:
1:VRTK_ControllerEvents.buttonTwo
2:
3:
4:
5:
6:public virtual void OnControllerEnabled(ControllerInteractionEventArgs e)
7:
8:
9:
VRTK_ControllerEvents_ListenerExample.cs:监听控制器挂载的VRTK_ControllerEvents.cs脚本所触发的事件,并用控制台日志输出控制器所有操作。
绑定:
private void Start()
{
/*获取到当前Controller绑定的VRTK_ControllerEvents脚本,
为它的TriggerPressed绑定DoTriggerPressed方法,
在VRTK_ControllerEvents脚本中,
每一帧会检测trigger是否被按下,
如果按下,则发送事件*/
if (GetComponent<VRTK_ControllerEvents>() == null)
{
VRTK_Logger.Error
(
VRTK_Logger.GetCommonMessage
(
VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_GAMEOBJECT,
"VRTK_ControllerEvents_ListenerExample",
"VRTK_ControllerEvents",
"the same"
)
);
return;
}
// Setup controller event listeners
GetComponent<VRTK_ControllerEvents>().TriggerPressed +=
new ControllerInteractionEventHandler(DoTriggerPressed);
GetComponent<VRTK_ControllerEvents>().TriggerReleased +=
new ControllerInteractionEventHandler(DoTriggerReleased);
...
}
检测:
private void DoTriggerPressed(object sender, ControllerInteractionEventArgs e)
{
DebugLogger(VRTK_ControllerReference.GetRealIndex(e.controllerReference), "TRIGGER", "pressed", e);
}
VRTK_ControllerEvents.cs:
脚本参数设置:
ButtonAlias pointerToggleButton = ButtonAlias.TouchpadPress;//激光柱显示开关
ButtonAlias pointerSetButton = ButtonAlias.TouchpadPress;//激光停留的指向目标,触发目标的功能. 相当于鼠标点击
ButtonAlias grabToggleButton = ButtonAlias.GripPress;//抓住物体动作, 配合使用VRTK插件集成的抓取,投掷功能
ButtonAlias useToggleButton = ButtonAlias.TriggerPress;//触发目标事件, 比如把手柄放在门把手上,按下这个按键,触发门打开的功能,手柄放在电灯开关上,按下按键,触发电灯开发的功能。这个和pointerSetButton区别在于后者需要激光落在目标上,才能触发目标上的功能
ButtonAlias uiClickButton = ButtonAlias.TriggerPress;//触发UI Canvas 目标的事件
ButtonAlias menuToggleButton = ButtonAlias.ButtonTwoPress;//游戏目录显示开关
int axisFidelity = 1;//轴坐标拾取数值的变化量能够达到触发事件的灵敏度,可以理解为最小单位的大小,默认为1. 大于2的数字将会导致过于灵敏
float triggerClickThreshold = 1f;//按键按下的强度,完全按下是1,松开时是0, 现在只有Trigger扳机按键有此属性
float triggerForceZeroThreshold = 0.01f;//扳机要到多大程度,才能使用扳机的触发功能. 如果是0.5 ,则表示只要按下一半的程度就可以触发,如果是1,则必须完全按下才能触发
bool triggerAxisZeroOnUntouch = false;//trigger在触发松开事件(UnTouch)时是否归零
float gripClickThreshold = 1f;//在注册点击事件之前,控制器轴上要达到的水平度
float gripForceZeroThreshold = 0.01f;//在控制器轴归零之前,控制器轴要达到的水平度
bool gripAxisZeroOnUntouch = false;//grip在触发松开事件(UnTouch)时是否归零
脚本成员变量:
事件和bool状态变量有着对应的关系,通常一个bool状态变量会对应至少两个按钮事件
bool triggerPressed = false;//trigger按下一半左右
bool triggerTouched = false;//trigger按下一点点
bool triggerHairlinePressed = false;//trigger被按下的深度大于上一帧记录的按下深度
bool triggerClicked = false;//trigger被完全按下
bool triggerAxisChanged = false;//trigger被按下的深度有变化
bool gripPressed = false;//grip按下一半左右
bool gripTouched = false;//grip按下一点点
bool gripHairlinePressed = false; //grip被按下的深度大于上一帧记录的按下深度
bool gripClicked = false; //grip被完全按下
bool gripAxisChanged = false; //grip被按下的深度有变化
bool touchpadPressed = false; //touchpad被按下
bool touchpadTouched = false; //touchpad被触摸
bool touchpadAxisChanged = false; //touchpad被触摸的点坐标变化时
bool buttonOnePressed = false; //
bool buttonOneTouched = false; //
bool buttonTwoPressed = false; //
bool buttonTwoTouched = false; //
bool startMenuPressed = false; //
bool pointerPressed = false; //别名为pointer的按钮是否被按下
bool grabPressed = false; //别名为grab的按钮是否被按下
bool usePressed = false; //别名为use的按钮是否被按下
bool uiClickPressed = false; //别名为UI click的按钮是否被按下
bool menuPressed = false; //别名为menu的按钮是否被按下
bool controllerVisible = true; //控制器模型的显示状态
事件装载参数:
public struct ControllerInteractionEventArgs
{
[Obsolete("`ControllerInteractionEventArgs.controllerIndex` has been replaced with `ControllerInteractionEventArgs.controllerReference`. This parameter will be removed in a future version of VRTK.")]
public uint controllerIndex; //当前使用设备的索引
public VRTK_ControllerReference controllerReference; //
public float buttonPressure;//按钮的按压数值0f到1f
public Vector2 touchpadAxis; //touchpad被触摸的坐标(0,0)到右上角(1,1)
public float touchpadAngle; //touchpad触摸时滑动的角度, top为0, bottom为180,以此类推其他 . 0f 到 360f
}
委托类型:
声明一个委托类型,参数为object和ControllerInteractionEventArgs,绑定事件时一定要传入这两个参数,按钮被按下时会通过SetButtonEvent()方法来给ControllerInteractionEventArgs e分配值
public delegate void ControllerInteractionEventHandler(object sender, ControllerInteractionEventArgs e);
按钮别名:
这个工具类给Vive手柄一些常用的操作取一些别名,和实际的按钮建立映射
public enum ButtonAlias
{
Undefined,
TriggerHairline,
TriggerTouch,
TriggerPress,
TriggerClick,
GripHairline,
GripTouch,
GripPress,
GripClick,
TouchpadTouch,
TouchpadPress,
ButtonOneTouch,
ButtonOnePress,
ButtonTwoTouch,
ButtonTwoPress,
StartMenuPress
}
关联别名:
这个menuToggleButton与SteamVR中的SteamVR_Controller.ButtonMask.Touchpad对应,当这个按钮被按下时,别名按钮对应的事件(如果有绑定)也会发送和SteamVR相关的全局变量。例如:
public ButtonAlias pointerToggleButton = ButtonAlias.TouchpadPress;
其他变量:
protected Vector2 touchpadAxis = Vector2.zero;
protected Vector2 triggerAxis = Vector2.zero;
protected Vector2 gripAxis = Vector2.zero;
protected float hairTriggerDelta;
protected float hairGripDelta;
按钮事件发送:
事件和bool状态变量有着对应的关系,通常一个bool状态变量会对应至少两个按钮事件
//trigger被扣下一半左右时发送事件
public virtual void OnTriggerPressed(ControllerInteractionEventArgs e)
{
if (TriggerPressed != null)
{
//发送事件,通知绑定此事件的脚本,执行具体的逻辑,但是此处是真正最后调用的地方
TriggerPressed(this, e);
}
}
public virtual void OnTriggerReleased(e) :Trigger从按下一半左右的状态释放后发送事件
public virtual void OnTriggerTouchStart(e) :trigger被按下一点点时
public virtual void OnTriggerTouchEnd(e) :trigger完全没有被扣下时
public virtual void OnTriggerHairlineStart(e) :trigger按下的程度超过当前的hairline阈值时
public virtual void OnTriggerHairlineEnd(e) :trigger释放程度超过了当前的hairline阈值时
public virtual void OnTriggerClicked(e) :trigger在clicked之前扣下的过程中时
public virtual void OnTriggerUnclicked(e) :trigger不再一直处于clicked状态时
public virtual void OnTriggerAxisChanged(e) :trigger扣下的量发生变化时
public virtual void OnGripPressed(e) :grip被按下时
public virtual void OnGripReleased(e) :grip被释放时
public virtual void OnGripTouchStart(e) :grip被触摸时
public virtual void OnGripTouchEnd(e) :grip不再被触摸时
public virtual void OnGripHairlineStart(e) :grip程度超过当前的hairline阈值时
public virtual void OnGripHairlineEnd(e) :grip释放程度超过了当前的hairline阈值时
public virtual void OnGripClicked(e) :
public virtual void OnGripUnclicked(e) :
public virtual void OnGripAxisChanged(e) :
public virtual void OnTouchpadPressed(e) :touchpad被按下时(非触碰)
public virtual void OnTouchpadReleased(e) :touchpad从被按下(非触碰)的状态下释放时
public virtual void OnTouchpadTouchStart(e) :touchpad被触摸时
public virtual void OnTouchpadTouchEnd(e) :touchpad不再被触摸时
public virtual void OnTouchpadAxisChanged(e) :touchpad被触摸的点改变时
public virtual void OnButtonOneTouchStart(e) :
public virtual void OnButtonOneTouchEnd(e) :
public virtual void OnButtonOnePressed(e) :
public virtual void OnButtonOneReleased(e) :
public virtual void OnButtonTwoTouchStart(e) :
public virtual void OnButtonTwoTouchEnd(e) :
public virtual void OnButtonTwoPressed(e) :
public virtual void OnButtonTwoReleased(e) :
public virtual void OnStartMenuPressed(e) :
public virtual void OnStartMenuReleased(e) :
public virtual void OnAliasPointerOn(e) :pointer toggle(别名)被按下时
public virtual void OnAliasPointerOff(e) :pointer toggle(别名)被释放时
public virtual void OnAliasPointerSet(e) :pointer set(别名)被释放时
public virtual void OnAliasGrabOn(e) :grab toggle(别名)被按下时
public virtual void OnAliasGrabOff(e) :grab toggle(别名)被释放时
public virtual void OnAliasUseOn(e) :use toggle(别名)被按下时
public virtual void OnAliasUseOff(e) :use toggle(别名)被释放时
public virtual void OnAliasUIClickOn(e) :UI click(别名)被按下时
public virtual void OnAliasUIClickOff(e) :UI click(别名)被释放时
public virtual void OnAliasMenuOn(e) :menu toggle(别名)被按下时
public virtual void OnAliasMenuOff(e) :menu toggle(别名)被释放时
public virtual void OnControllerEnabled(e) :
public virtual void OnControllerDisabled(e) :
public virtual void OnControllerIndexChanged(e) :
public virtual void OnControllerVisible(e) :
public virtual void OnControllerHidden(e):
装载参数:
通过传入ref bool buttonBool,可以在对ControllerInteractionEventArgs进行装填的同时,把事件对应的按钮bool状态进行更新。例如TriggerPressed和TriggerReleased事件对应的按钮bool状态是triggerPressed,当发送TriggerPressed事件时要同时更新triggerPressed为true;发送TriggerReleased事件时要同时更新triggerPressed为false
public virtual ControllerInteractionEventArgs SetControllerEvent()
{
var nullBool = false;
return SetControllerEvent(ref nullBool);
}
public virtual ControllerInteractionEventArgs SetControllerEvent(ref bool buttonBool, bool value = false, float buttonPressure = 0f)
{
VRTK_ControllerReference controllerReference = VRTK_ControllerReference.GetControllerReference(gameObject);
buttonBool = value;
ControllerInteractionEventArgs e;
#pragma warning disable 0618
e.controllerIndex = VRTK_ControllerReference.GetRealIndex(controllerReference);
#pragma warning restore 0618
e.controllerReference = controllerReference;
e.buttonPressure = buttonPressure;
//调用SteamVR API获取当前的touchpad二维坐标
e.touchpadAxis = VRTK_SDK_Bridge.GetControllerAxis(SDK_BaseController.ButtonTypes.Touchpad, controllerReference);
//计算二维坐标在圆形表盘上对应的角度
e.touchpadAngle = CalculateTouchpadAxisAngle(e.touchpadAxis);
return e;
}
别名按钮事件发送:
根据type判断是哪个别名按钮,最后一个参数buttonBool对应的是非别名的按钮bool状态,例如这个pointerToggleButton,发送事件时要把touchpadPressed状态更新,而更新为true还是false要根据touchDown的值来判断,上面的OnAliasPointerOn等方法和OnTriggerPressed。值得注意的是,不同的别名对应的可能是相同的按钮,例如pointerToggleButton和pointerSetButton都是ButtonAlias.Touchpad_Press
#pragma warning disable 0618
protected virtual void EmitAlias(ButtonAlias type, bool touchDown, float buttonPressure, ref bool buttonBool)
{
if (pointerToggleButton == type)
{
if (touchDown)
{
pointerPressed = true;
OnAliasPointerOn(SetControllerEvent(ref buttonBool, true, buttonPressure));
}
else
{
pointerPressed = false;
OnAliasPointerOff(SetControllerEvent(ref buttonBool, false, buttonPressure));
}
}
if (pointerSetButton == type)
{
if (!touchDown)
{
OnAliasPointerSet(SetControllerEvent(ref buttonBool, false, buttonPressure));
}
}
…
}
#pragma warning restore 0618
禁用事件:
这个方法应该就是将所有事件对应的按钮bool状态置为false,同时保存touchpad和trigger上的坐标信息,但是为什么要重新获取一次device呢
protected virtual void OnDisable()
{
Invoke("DisableEvents", 0f); // 在0.1s内调用DisableEvents(),禁用所有事件发送
var actualController = VRTK_DeviceFinder.GetActualController(gameObject);
if (actualController)
{
var controllerTracker = actualController.GetComponent<VRTK_TrackedController>();
if (controllerTracker)
{
controllerTracker.ControllerEnabled -= TrackedControllerEnabled;
controllerTracker.ControllerDisabled -= TrackedControllerDisabled;
}
}
}
protected virtual void DisableEvents()
{
if (triggerPressed)
{
OnTriggerReleased(SetControllerEvent(ref triggerPressed, false, 0f));
EmitAlias(ButtonAlias.TriggerPress, false, 0f, ref triggerPressed);
}
if (triggerTouched)
{
OnTriggerTouchEnd(SetControllerEvent(ref triggerTouched, false, 0f));
EmitAlias(ButtonAlias.TriggerTouch, false, 0f, ref triggerTouched);
}
…
triggerAxisChanged = false;
gripAxisChanged = false;
touchpadAxisChanged = false;
VRTK_ControllerReference controllerReference = VRTK_ControllerReference.GetControllerReference(gameObject);
if (VRTK_ControllerReference.IsValid(controllerReference))
{
Vector2 currentTriggerAxis
= VRTK_SDK_Bridge.GetControllerAxis(SDK_BaseController.ButtonTypes.Trigger, controllerReference);
Vector2 currentGripAxis
= VRTK_SDK_Bridge.GetControllerAxis(SDK_BaseController.ButtonTypes.Grip, controllerReference);
Vector2 currentTouchpadAxis
= VRTK_SDK_Bridge.GetControllerAxis(SDK_BaseController.ButtonTypes.Touchpad, controllerReference);
// 保存当前的touchpad和trigger的设置
touchpadAxis = new Vector2(currentTouchpadAxis.x, currentTouchpadAxis.y);
triggerAxis = new Vector2(currentTriggerAxis.x, currentTriggerAxis.y);
gripAxis = new Vector2(currentGripAxis.x, currentGripAxis.y);
hairTriggerDelta
= VRTK_SDK_Bridge.GetControllerHairlineDelta(SDK_BaseController.ButtonTypes.TriggerHairline, controllerReference);
hairGripDelta
= VRTK_SDK_Bridge.GetControllerHairlineDelta(SDK_BaseController.ButtonTypes.GripHairline, controllerReference);
}
}
Update()方法:
protected virtual void Update()
{
VRTK_ControllerReference controllerReference = VRTK_ControllerReference.GetControllerReference(gameObject);
//Only continue if the controller reference is valid
if (!VRTK_ControllerReference.IsValid(controllerReference))
{
return;
}
Vector2 currentTriggerAxis
= VRTK_SDK_Bridge.GetControllerAxis(SDK_BaseController.ButtonTypes.Trigger, controllerReference);
Vector2 currentGripAxis
= VRTK_SDK_Bridge.GetControllerAxis(SDK_BaseController.ButtonTypes.Grip, controllerReference);
Vector2 currentTouchpadAxis
= VRTK_SDK_Bridge.GetControllerAxis(SDK_BaseController.ButtonTypes.Touchpad, controllerReference);
//Trigger Touched
if (VRTK_SDK_Bridge.GetControllerButtonState
(SDK_BaseController.ButtonTypes.Trigger,
SDK_BaseController.ButtonPressTypes.TouchDown, controllerReference)
)
{
// 发送事件,设TriggerTouch为true,同时发送triggerTouched对应的别名按钮事件
OnTriggerTouchStart(SetControllerEvent(ref triggerTouched, true, currentTriggerAxis.x));
EmitAlias(ButtonAlias.TriggerTouch, true, currentTriggerAxis.x, ref triggerTouched);
}
…
//Trigger Clicked
if (!triggerClicked && currentTriggerAxis.x >= triggerClickThreshold)
{
OnTriggerClicked(SetControllerEvent(ref triggerClicked, true, currentTriggerAxis.x));
EmitAlias(ButtonAlias.TriggerClick, true, currentTriggerAxis.x, ref triggerClicked);
}
else if (triggerClicked && currentTriggerAxis.x < triggerClickThreshold)
{
OnTriggerUnclicked(SetControllerEvent(ref triggerClicked, false, 0f));
EmitAlias(ButtonAlias.TriggerClick, false, 0f, ref triggerClicked);
}
…
//Trigger Axis
currentTriggerAxis.x = (
(!triggerTouched && triggerAxisZeroOnUntouch)
|| currentTriggerAxis.x < triggerForceZeroThreshold ? 0f : currentTriggerAxis.x
);
if (VRTK_SharedMethods.Vector2ShallowCompare(triggerAxis, currentTriggerAxis, axisFidelity))
{
triggerAxisChanged = false;
}
else
{
OnTriggerAxisChanged(SetControllerEvent(ref triggerAxisChanged, true, currentTriggerAxis.x));
}
…
// 保存当前trigger和touchpad状态
touchpadAxis = (touchpadAxisChanged ? new Vector2(currentTouchpadAxis.x, currentTouchpadAxis.y) : touchpadAxis);
triggerAxis = (triggerAxisChanged ? new Vector2(currentTriggerAxis.x, currentTriggerAxis.y) : triggerAxis);
gripAxis = (gripAxisChanged ? new Vector2(currentGripAxis.x, currentGripAxis.y) : gripAxis);
hairTriggerDelta
= VRTK_SDK_Bridge.GetControllerHairlineDelta(SDK_BaseController.ButtonTypes.TriggerHairline, controllerReference);
hairGripDelta
= VRTK_SDK_Bridge.GetControllerHairlineDelta(SDK_BaseController.ButtonTypes.GripHairline, controllerReference);
}
VRTK的API:
public virtual Vector3 GetVelocity()//获取控制器的在当前现实世界的速度。可以用来确定控制器被晃动的速度和方向
public virtual Vector3 GetAngularVelocity()//
public virtual Vector2 GetTouchpadAxis()//
public virtual float GetTouchpadAxisAngle()//获取圆盘触摸板上的角度,取值范围0f-360f,0f是朝上,90f是朝右。
public virtual float GetTriggerAxis()//获取扳机轴按下角度,取值范围0f-1f,0f是松开状态,1f是完全按下状态。
public virtual float GetGripAxis()//
public virtual float GetHairTriggerDelta()//按钮从初始位置到当前位置的力度变化值
public virtual float GetHairGripDelta()//
public virtual bool AnyButtonPressed()//获取是否有任意键被按下(可用作控制开始的触发事件)。