VRTK插件教程案例解析_002_Controller_Events

代码中的类型名:
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()//获取是否有任意键被按下(可用作控制开始的触发事件)。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值