WebVR手柄交互


前言

现在说webVr可能不太合适,正确应该叫webXR API,不过下面的内容确实是跟VR设备相关,以Threejs为例,事实上webxr是个标准,跟具体3D引擎库没关系

一、扳机键和抓取键

对于threejs来说,当前能直接使用接口获取手柄交互的有两个,分别是扳机键和抓取键,下面的代码表示扳机键和抓取键按下和松开的事件,这两个按钮在大部分情况下足够了。其实事件参数event里面是有变量表示圆盘(摇杆)键的数值的,但是从这里获取就意味着必须按一次扳机/抓取键才能获取,不靠谱

this.controller1 = this.renderer.xr.getController(0)
this.controller1.addEventListener('selectstart', onSelectStart) //select--扳机键
this.controller1.addEventListener('selectend', onSelectEnd)
this.controller1.addEventListener('squeezestart', onSqueezeStart)//squeeze--抓取键
this.controller1.addEventListener('squeezeend', onSqueezeEnd)

function onSelectStart(event) {
   console.log('onSelectStart', event)
}

function onSelectEnd(event) {
   console.log('onSelectEnd', event)
}

function onSqueezeStart(event) {
   console.log('onSqueezeStart', event)
}

function onSqueezeEnd(event) {
  console.log('onSqueezeEnd', event)
}

二、其他按键

经过一番搜索,发现其实是能获取到输入源(inputSources)列表的,如下面的代码,可以在手柄接入时自动调用,获取手柄列表,inputSource里有个属性gamepad可获取手柄的绝大多数按键,比如圆盘/摇杆键,扳机键,抓取键,一体机手柄的A、B键等等(事实上一体机还能获取到两个菜单按键,但这两个按键在浏览器已经被使用了),对于这些按键的监听我使用的是每帧循环检测(应该有更高效的方式,奈何本人太菜)

session.addEventListener('inputsourceschange', event => {
 	console.log(event)
	var inputSourceList = event.session.inputSources
	//可以通过回调函数将inputSourceList传递到其他脚本
})

这里的代码没有检测扳机键和抓取键,这两个按键感觉用上面的事件监听更高效,具体也可看下代码里的注释。这个函数需要每帧调用

/**
  * 处理手柄事件
  */
handleEvent() {
  if (this.vrInputSource && this.vrInputSource.length > 0) {
   this.vrInputSource.forEach(xrInputSource => {
     if (xrInputSource.gamepad) {
       //通过navigator.userAgentData.platform == 'Windows'属性来判断是一体机还是主机式设备
       //我项目用的是HTC,所以这里是HTC的检测方式,其他设备类似
       if (this.isWindows) {
       // htc圆盘键按下时除了需要检测axes外还需要检测第三个按钮是否按下(第一个按钮是扳机键,第二个是抓取键)
         if (xrInputSource.gamepad.buttons.length != 3) {
           console.log('不是HTC设备!')
         } else {
         if (!xrInputSource.lastAxes) {
            xrInputSource.offset = 0.8
            xrInputSource.lastAxes = new Vector2(0, 0)
            xrInputSource.buttonStates = false
         }

         if (
            xrInputSource.gamepad.axes[0] > xrInputSource.offset &&
            xrInputSource.lastAxes.x <= xrInputSource.offset &&
            xrInputSource.gamepad.buttons[2].pressed &&
            !xrInputSource.buttonStates
            ) {
                xrInputSource.lastAxes.x = xrInputSource.gamepad.axes[0]
                console.log('按下右键')
              } else if (
                xrInputSource.gamepad.axes[0] < -xrInputSource.offset &&
                xrInputSource.lastAxes.x >= -xrInputSource.offset &&
                xrInputSource.gamepad.buttons[2].pressed &&
                !xrInputSource.buttonStates
              ) {
                xrInputSource.lastAxes.x = xrInputSource.gamepad.axes[0]
                console.log('按下左键')
              }
              if (
                xrInputSource.gamepad.axes[1] > xrInputSource.offset &&
                xrInputSource.lastAxes.y <= xrInputSource.offset &&
                xrInputSource.gamepad.buttons[2].pressed &&
                !xrInputSource.buttonStates
              ) {
                xrInputSource.lastAxes.y = xrInputSource.gamepad.axes[1]
                console.log('按下下键')
              } else if (
                xrInputSource.gamepad.axes[1] < -xrInputSource.offset &&
                xrInputSource.lastAxes.y >= -xrInputSource.offset &&
                xrInputSource.gamepad.buttons[2].pressed &&
                !xrInputSource.buttonStates
              ) {
                xrInputSource.lastAxes.y = xrInputSource.gamepad.axes[1]
                console.log('按下上键')
              }
              xrInputSource.buttonStates = xrInputSource.gamepad.buttons[2].pressed
              // 没有按下时重置轴记录 令其可连续点击
              if (!xrInputSource.buttonStates) {
                xrInputSource.lastAxes.x = 0
                xrInputSource.lastAxes.y = 0
              }
            }
          }
          // pico 或其他3Dof设备(只测试了pico )
          else {
            if (!xrInputSource.lastAxes) {
              xrInputSource.offset = 0.8
              xrInputSource.lastAxes = new Vector2(0, 0)
              xrInputSource.buttonStates = {}
            }
            // pico 的axes有4个元素,但只有后两个有效
            if (xrInputSource.gamepad.axes[2] > xrInputSource.offset && xrInputSource.lastAxes.x <= xrInputSource.offset) {
              //console.log('按下左键')
              //makeSystemTip函数是自己写的在一体机里弹出一个面板,显示文字信息
              //xrInputSource.handedness表示了手柄的左右之分(HTC的是先接入的是右手(惯用手))
              this.makeSystemTip(xrInputSource.handedness + '按下右键', 1000)
            }
            if (xrInputSource.gamepad.axes[2] < -xrInputSource.offset && xrInputSource.lastAxes.x >= -xrInputSource.offset) {
              //console.log('按下右键')
              this.makeSystemTip(xrInputSource.handedness + '按下左键', 1000)
            }
            if (xrInputSource.gamepad.axes[3] > xrInputSource.offset && xrInputSource.lastAxes.y <= xrInputSource.offset) {
              //console.log('按下上键')
              this.makeSystemTip(xrInputSource.handedness + '按下下键', 1000)
            }
            if (xrInputSource.gamepad.axes[3] < -xrInputSource.offset && xrInputSource.lastAxes.y >= -xrInputSource.offset) {
              //console.log('按下下键')
              this.makeSystemTip(xrInputSource.handedness + '按下上键', 1000)
            }
            xrInputSource.lastAxes.x = xrInputSource.gamepad.axes[2]
            xrInputSource.lastAxes.y = xrInputSource.gamepad.axes[3]

            // pico 的按钮有6个,第一二个是扳机键和抓取键,第五六个是A、B键,中间两个是菜单键,已经被设备托管了,不能使用
            if (xrInputSource.gamepad.buttons.length == 6) {
              if (xrInputSource.gamepad.buttons[4].pressed && !xrInputSource.buttonStates['4']) {
                //console.log('按下B键')
                this.makeSystemTip(xrInputSource.handedness + '按下A键', 1000)
              }
              if (xrInputSource.gamepad.buttons[5].pressed && !xrInputSource.buttonStates['5']) {
                //console.log('按下A键')
                this.makeSystemTip(xrInputSource.handedness + '按下B键', 1000)
              }
              xrInputSource.buttonStates['4'] = xrInputSource.gamepad.buttons[4].pressed
              xrInputSource.buttonStates['5'] = xrInputSource.gamepad.buttons[5].pressed
            }
          }
        }
      })
    }
  }

参考

https://developer.mozilla.org/zh-CN/docs/Web/API/WebXR_Device_API
https://developer.mozilla.org/en-US/docs/Web/API/XRInputSource
https://developer-cn.pico-interactive.com/document/web/development-platform/#7e7428a1

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值