本文节选自视频《HTC VIVE 交互开发实例教程》的第18课时。
在配置VRTK一篇中我们对瞬移做了初步的实现,但是作为一款优秀的工具集,VRTK还为我们提供了很多可以自行控制的功能,涉及到日常开发中经常遇到的问题,比如自定义射线的外观、让指定的物体不参与到瞬移中、自适应高度的瞬移(楼梯)等。
为什么在VR中使用瞬移?好好走路不行吗?
这个问题我们在《VRDC笔记——VR交互原型制作的50条最佳实践》中也涉及过这种交互原则。你可能在VR内容中习惯了使用瞬移,但是按照自然交互原则,理应是像现实一样自由行走才对,为什么需要使用瞬移呢?这主要是由当前硬件限制以及人的生理结构决定的。这需要从人的前庭系统说起。人眼获取信息,反馈给前庭系统,当眼睛与前庭系统不一致的时候,人会感到不舒服,比如当体验VR过山车游戏的时候,多数情况下会感觉到不适,我们的眼睛知道哪里加速、减速、坠落,但是前庭系统并没有相应的感受到加速、减速、坠落,于是就引起不适,这跟晕车晕船一样的道理。所以,在VR中的移动,尽量避免加速、减速,甚至是匀速。由于屏幕刷新率的问题,如果制作的VR内容帧率过低,则容易引起晕动症,这种感觉试一下Cardboard类型的眼镜就可以体会到,比如暴风魔镜等。
表现形式及原理
VR中的瞬移过程,是在体验者决定移动之前,通过手柄选择目标位置,选定之后,视野呈现短暂黑屏(时间相当于一次眨眼),过后将体验者位置设定为选定位置。
Unity VR中的瞬移,选择的过程基于碰撞体,即,默认情况下,有碰撞体的物体,都可以作为移动以后的目标点,故可以通过设定碰撞体的范围对可移动范围进行简单限制,比如,房间漫游中,可以将地面的碰撞体沿地面边界缩进一定的距离,来防止用户穿墙。关于更多防穿墙的体验优化,可参考《VRTK使用心得(六):优化用户体验——防穿墙及自动坠落》一节。
具体实现
- 我们假定已经将VRTK初始配置完毕,即保证LeftController和RightController挂载VRTK_Pointer指针脚本,并确定其Enable Teleport属性勾选。
-
如下图,在本场景以地面为瞬移范围,为其添加一个BoxCollider。
-
在VRTK配置结构下,添加一个空物体,可以命名为PlayArea,为其添加VRTK_BasicTeleport组件。
该组件的属性:
- Blink To Color:闪屏的颜色。前面说到黑屏,还可以设置任意颜色。
- Blink Transition Speed:闪屏持续时间,建议设置在1秒以内。
- Distance Blink Delay:数值在0-32之间,此参数用闪屏消失的延时,来体现瞬移过程的远近程度,新目标点与原目标点越远,闪屏褪去延时越长。
- Headset Position Compensation:头盔位置补偿。因为VIVE的特性,体验者是可以在小范围内移动的,所以,头盔与游玩区域(Play Area)会有一个相对位置,如果此选项被选中,新的位置会考虑头盔与游玩区域(Play Area)的相对位置,如果不选中,则只将游玩区域的中心位置设定到新的位置。可参考本组件的源代码片段如下:
newX = (headsetPositionCompensation ? (tipPosition.x - (headset.position.x - playArea.position.x)) : tipPosition.x);
newY = playArea.position.y;
newZ = (headsetPositionCompensation ? (tipPosition.z - (headset.position.z - playArea.position.z)) : tipPosition.z);
- Target List Policy: 一个限定参与瞬移的配置。前面我们说到,默认情况下,瞬移范围由碰撞体的范围来决定,Target List Policy则指定了一个更加灵活的规则,用来确定哪些物体是可以参与瞬移的,这个我们会在下篇进行说明。
- Nav Mesh Limit Distance: VRTK可以使用Nav Mesh来确定瞬移范围,注意还是需要保证有Collider,在Bake好Nav Mesh信息以后,此属性设定的值为Nav Mesh 边界以外多少米的范围可以参与瞬移。结合Nav Mesh进行瞬移的优点是可以将不想参与瞬移的物体,比如障碍物隔离在Nav Mesh以外。
另外两种瞬移:
在VR场景中移动,有时会遇到移动到一个相对高的位置,这时候使用基本的瞬移,只能是移动到与之前位置高度相同的位置,即Y坐标不变,这时候就容易出现移动到物体内部的情况,比如爬楼梯、爬山等,这时候就需要使用到一个能够自适应高度的瞬移脚本,叫VRTK_HeightAdjustTeleport组件,该组件能够实现在移动到一个相对高度的位置,同时改变体验者PlayArea的高度。使用方法与Basic Teleport相同,挂载到PlayArea上即可。如下图:
通过上图观察,该组件大部分属性与VRTK_BasicTeleport相同,通过源代码也可得到验证,它继承了VRTK_BasicTeleport类,实现自适应高度的过程,是添加了一个GetTeleportY的函数,代码片段如下:
float newY = playArea.position.y;
float heightOffset = 0.1f;
//Check to see if the tip is on top of an object
Vector3 rayStartPositionOffset = Vector3.up * heightOffset;
Ray ray = new Ray(tipPosition + rayStartPositionOffset, -playArea.up);
RaycastHit rayCollidedWith;
#pragma warning disable 0618
if (target != null && VRTK_CustomRaycast.Raycast(customRaycast, ray, out rayCollidedWith, layersToIgnore, Mathf.Infinity, QueryTriggerInteraction.Ignore))
#pragma warning restore 0618
{
newY = (tipPosition.y - rayCollidedWith.distance) + heightOffset;
}
return newY;
除了闪屏瞬移,还有一种瞬移形式,即,伴随着闪屏,体验者能够感受到移动的过程,但是这个过程极快。在VRTK中,使用的是VRTK_DashTeleport,同样,这个脚本继承了前面VRTK_HeightAdjustTeleport组件——能够实现基本瞬移,能够自适应高度,扩展的选项可以自定义这个过程的相关属性,比如时间、速度等。
下篇中,我们将详细介绍VRTK限定瞬移区域的三种方法。