帮助文档很丰富,github上示例也很多,但开发起来还是有很多问题 T T
本次开发使用的是A-Frame 1.2.0版本,使用的是Oculus Rift S头盔,FireFox版本为78.10.0(高版本的可能有问题),记录的是已经解决的一些问题的思路,
1、组件传参
a-frame的整体是通过“实体-组件-系统”的概念实现的,js代码都是给实体添加组件,在组件的钩子函数中进行各种操作。但是a-frame未提供相应的数据共享方法,因此,可以通过修改组件的参数来实现传值。
例如entity1的A组件要给entity2的B组件传递一个值,那么需要先在B组件的schema中定义B组件接收的参数X,然后A组件通过entity2.setAttribute的方法定义entity2的B组件的参数X的值,然后B组件在使用的时候就可以获取到更新过的参数X。如果需要大量传参,还可以定义一个数据共享的组件,专门用于各组件之间传值。
2、显示中文
a-frame默认的字体无法显示中文,可以在这个网站上选择字体以及需要显示的文字,生成对应字体的json文件与png文件才可以使用。使用的过程中发现标签的许多属性例如align属性不起作用,换成的写法之后才可以起作用。
3、点击事件
a-frame中的点击事件和web中的点击事件有所区别,只设置模型被点击时候的事件是不行的,必须设定射线投射器(raycaster),射线和模型表明相交时,才表明模型被点击了。对于需要求交的模型,设置其class为x,而将左右手柄的射线发射器raycaster的属性值设置为object: .x,这样才可以触发射线与模型相交的事件。
4、相机相对位置
由于菜单栏、手柄、信息框、属性图片都是显示在用户面前,即场景的相机面前的,每次当相机位置改变的时候,都需要调整手柄的位置,当菜单栏、信息框和属性图片显示的时候,都需要计算相机的位置再显示,这样是十分麻烦的。因此,可以根据相机建立一个局部坐标系,每次当相机移动的时候,菜单栏、手机、信息框和属性图片都一同移动。建立局部坐标系的方式为
外部id为rig的entity就是一个局部坐标系,这样当相机移动的时候,其实是rig的position将变化,camera的rotation变化,这样menu、hand、infoBox、infoImage的position就变为了相对于rig的position了,这样更方便位置的控制。
5、文字排版
a-frame中显示文字背景、实现hover效果和web端的实现思路有所差别,a-frame无法直接显示文字背景,而是在文字的后面显示一个plane的geometry来当做文字背景,且font的大小没有size等参数控制,只能通过scale等比缩放font,而hover效果必须通过鼠标的mouseenter、mouseleave实现,所以需要设置每个plane的class为对应的射线投射器的名字。并且,a-frame中没有div文字溢出、滚轮等方法,所以必须提前给每一个文字、横线、平面设定好大小和尺寸,动态控制它们scale或visible实现。
6、选择
当菜单栏、模型等可以通过射线投射器选择的entity,当设置其为visible为false时候,仅仅是让其不显示,但是射线投射器还是可以选择,例如当菜单栏隐藏的时候,右手还算可以选择对应菜单栏的某一项回到某位置,此时只是菜单栏没有显示,但是还是位于对应的位置,还是可以计算射线与entity是否相交,因此此时通过设置visible属性是存在一定问题的,最好的是同时设置scale属性,例如初始状态设置scale为0.0001 0.0001 0.0001,此时scale过小,大多数情况下射线与entity是无法相交的,而当entity应该显示是实话,设置scale为1 1 1,这样entity就可以正常显示、正常被选择了。
7、计算位置
计算菜单栏、信息框显示的时候,需要计算它们相对于用户当时的position,以及rotation。a-frame的position坐标系和rotation坐标系如下所示,是右手坐标系。rotation采用的欧拉角,默认的旋转顺序为x、y、z,因此x和z的取值范围为[-PI,PI],y的取值范围为[-PI/2,PI/2](这篇帖子有证明)
由于菜单栏、信息框相对于用户移动的位置已经通过上面第三点解决了,此时只需要计算菜单栏、信息框相对于camera的位置。例如,当菜单栏初始位置为0 0 -1的时候,就是在用户初始视角正前方1个单位的位置,而当camera的rotation变为0 -90 0,即为右手边的方向时,此时菜单栏的位置应该是0 1 0。
(1)菜单栏只需要绕y轴旋转camera旋转的角度即可,但是,在开发过程中发现,由于y的取值范围为[-PI/2,PI/2],共180°,因此无法辨别绕y轴旋转360°不同的方向,所以,必须修改rotation的默认顺序,可以把XYZ变为YZX,这样y轴的取值范围就为[-PI,PI],符合要求。由于没有找到a-frame或three.js提供的响应接口,就直接修改源码。
所以rotation设置为0 theta 0即可,theta的值为camera.rotation.y。
(2)确定了菜单栏的rotation之后,还需要确定其位置,y值即为显示的高度,设置为camera的position.y即可,而x和z主要是根据theta以及菜单栏与camera的距离确定。设菜单栏的初始坐标为0 0 -r,然后相机旋转了theta角度,根据r和theta即可求得菜单栏的x值和z值。俯视图如下所示,三角形最小锐角对应的方向为视线或显示方向:
还需要注意计算时候的符号,由于在a-frame中上述的theta是负值,所以计算过程为: