4.SDK常用组件

此章节将详细介绍SDK常用的组件,如果您是初学者,建议您对照SDK中的demo场景,一一检查如下组件的设置以及说明。

如果您已经有相关开发经验,您可以在此章节中查找每个组件设置项的详细说明。

下面内容主要介绍组件的常规使用方法,没有写明具体API,如果您需要调用具体的API,可以查看 API文档

4.1 基础组件

SDK基础组件是指封装在 RhinoX-Unity.dll 中的组件 ,主要包括以下几种类型 :

ARCamera, TrackableIdentity, DynamicTarget, GroundPlane, RxController, RxInput, RxButtonEventTrigger, RxEventSystem, RxInputModule.

1.ARCamera

ARCamera介绍

ARCamera 代表头显。 用于控制和头显相关的功能, 包括位置跟踪,双目渲染。

创建方式: 在 Hierarchy 对象列表中使用右键弹出菜单创建 ARCamera ,如果场景里面已经有其他Camera,需要将其删除:

_images/context_ar_camera.png

创建完成后,ARCamera会自动生成在父对象 ARCameraRig下面,且ARCamera对象上会自动挂载ARCamera Script, ARCamera的相关配置都是通过此脚本控制。

注解

开发者不应该改变 ARCamera 和 ARCameraRig 的层级结构。 并且不应该移动 ARCamera 的位置。 如果需要改变 ARCamera 的起始位置,请移动 ARCameraRig 而不是 ARCamera.

在应用启动以后, ARCamera的实时位置代表了用户的头部位置。

ARCamera 的配置界面:

_images/arcamera.jpg

ARCamera属性

这里主要介绍GUI界面上的设置,以及您可能需要用到的一些API,详细API以及用法您可以查看 ARCameraAPI

TrackPosition : 是否随着用户的头部运动而更新位置。

TrackRotation : 是否随着用户的头部运动而更新朝向。

Tracking Profile : 挂载设置好的Tracking Profile,不能为空, 具体见下节 Tracking Profile 对象介绍。

Enable Reticle: 是否启动头瞄模式。 头瞄模式下, 用户通过瞄准准星以及头显上的 OK 键对UI进行交互。

Reticle Image : 头瞄准星的纹理贴图。

Reticle Interact Mask : 头瞄交互的对象层级。需要被头瞄交互的模型的Layer需要被包含在此层级里面,此处可选择多个层级。

SetObjecTrackingProfile(ObjectTrackingProfile newProfile): 使用此方法在运行时加载新的 ObjectTrackingProfile, 以此实现动态替换跟踪物体。 如果传入参数为Null,则此方法会清空当前已经加载的跟踪物体库。

LoadTrackingFile(string jsonFilePath): 使用此方法加载任意路径的跟踪数据文件,例如从互联网上下载保存到sdcard上的文件。

RecenterTracking(): 对ARCamera 做归中操作,调用后, ARCamera 的transform 的local position 和 local rotation 都会归零。

静态事件 : OnCreateStereoCameras : ARCamera在启动以后,会自动在transform层级下创建左右双目Camera, 如果要实现后处理特效, 需要通过 OnCreateStereoCameras 事件添加后处理组件.

代码示例: 在 ARCamera.OnCreateStereoCameras 添加后处理组件, (完整代码: https://gitee.com/PolyEngine_Ent/rhino-x-rendering-demo)

void Awake()
{
    //在 ARCamera.OnCreateStereoCameras 事件中,添加后处理脚本:
    //这个事件在Unity编辑器上不会触发,只有在RhinoX设备上运行的时候才会触发.
    ARCamera.OnCreateStereoCameras += ARCamera_OnCreateStereoCameras;
}

private void ARCamera_OnCreateStereoCameras(Camera left, Camera right)
{
    //创建左右眼上的后期效果:
    if (!isEffectScriptCreated)
    {
        leftEffectFx = left.gameObject.AddComponent<Bloom>();//添加Bloom后效组件
        rightEffectFx = right.gameObject.AddComponent<Bloom>();//添加Bloom后效组件
    }
}

代码示例: 等待 ARCamera 启动完成

private IEnumerator Start()
{
    if (Application.platform == RuntimePlatform.Android)
    {
        while (ARCamera.Instance == null || ARCamera.Instance.IsARBegan == false) //等待 ARCamera 启动完成.
        {
            yield return null;
        }
        Debug.Log("ARCamera is started");//此时,定位系统和渲染系统都已经启动。
    }
}

//从 v1.5 开始 , 可以通过 ARCamera.OnRhinoXSDKInitialized 事件获取 ARCamera 启动完成事件。
ARCamera.OnRhinoXSDKInitialized += ()=> {
    Debug.Log("ARCamera is started");//此时,定位系统和渲染系统都已经启动。
};

2.Tracking Profile

Tracking Profile主要是用来定义ARCamera需要跟踪的定位信标的配置组件,通常默认的Tracking Profile配置为可识别Beacon。 如果需要识别其他定位信标,需要新建Tracking Profile,并添加对应标定文件。

按照如下图路创建Tracking Profile

_images/trackingprofile.png

默认创建的Tracking Profile如下图,勾选了Track Beacons,表示此文件默认支持跟踪Beacon1,2,3。如果您只需要跟踪Beacon 则可以不用做任何修改,直接使用默认配置,将他挂载在ARCamera上即可

_images/TrackingProfileDefault.png

如果您已经购买了我们的其他定位信标,如地灯,或者侧发光灯等等,您需要勾选Additive Tracking,然后添加需要跟踪信标的 标定文件

_images/tracking_files.png

标定文件和标定数据 : 在 Assets/Plugins/Android/assets 下的json文件称为标定文件, dat 文件称为标定数据,两者均为 标准文件,不可修改。

json 文件定义了marker的ID和尺寸, 和引用的标定数据。

{
    "CARD_SINGLE":
    {
       "CalibFile": "CARD-500-40.dat",
       "Markers": [2, 6, 8, 11, 12, 13, 15, 16, 18, 19, 21, 22, 23, 24, 26, 64],
       "MarkersSize": [0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04]
    }
}

如果要实现对其他定位信标的跟踪,则需要添加额外的跟踪文件,只需要勾选 Additive tracking 选项,并将对应的 Json 文件拖动到GUI的添加区域上即可,如下图所示。

_images/add_tracking_file.png

注解

不需要添加额外的跟踪数据文件来跟踪控制器, 控制器跟踪文件是内置的。

如果没有对应定位信标的标定文件,请联系技术支持人员。

警告

被Tracking Profile引用的Json文件必须放置在 Assets/Plugins/Android/assets 目录下。

3.TrackableIdentity

_images/trackable_identity.png

Trackable identity 组件定义跟踪定位信标 的 ID 信息。 Dynamic TargetGround Plane 都需要 Trackable identity 组件,创建时会自动添加在他们上面。

Target 字段 定义定位信标的id , 内置了 Beacon 01/02/03 和 left / right 控制器的ID。除此之外的其他定位信标需要选择Custom, 通过Custom ID来设置定位信标的ID。

OnVisibilityChanged 事件 当定位信标出现/消失在跟踪区域的时候, 触发此事件。可以通过此事件触发定位信标跟踪或者消失 后的行为。

Tracking Log 如果为true,则在logcat中打印跟踪数据。

trackableIdentity.OnVisibilityChange.AddListener(
             (bool visible) => Debug.LogFormat("Marker becomes visible: {0}", visible));

4.Dynamic Target

_images/DynamicTarget.png

Dynamic Target 组件定义跟踪定位信标的跟踪行为:当定位信标进入Camera的跟踪区域时,Dynamic Target 对象会实时 更新他相对于 ARCamera 的Transform值。

手柄就是最典型的DynamicTarget对象,如果您需要跟踪的定位信标的使用方式与手柄类似,例如拿着一个定位信标在空间 中随意的移动,那么建议您将其创建为DynamicTarget对象。

注解

如果要让某一个模型跟随Dynamic Target对象移动,那么你需要将此模型添加在DynamicTarget下面,与其绑定在一起。 这也是他与接下来要讲的 Ground Plane 的根本区别,GroundPlane是控制整个场景的位置,不是某一个模型。

YawOnly:如果为true, 则Dynamic Target只更新transform 的Yaw轴朝向。 此模式适用于假设定位信标永远水平放置的情况。

Lost Tracking Behaviour: 如果是 Stay,当定位信标丢失跟踪的时候,会停留在最后一次出现的世界空间位置。 如果是 Follow,当定位信标丢失跟踪的时候,会停留在最后一次相对于头部空间的位置,并一直跟随头部运动。这个模式适用于代表用户的双手的控制器跟踪, 在丢失跟踪的情况下, 还可以使用陀螺仪控制其朝向。

DebugView:如果为true,渲染一个Gizmos。在调试阶段不确定是否跟踪到定位信标的情况下,可以勾选此设置。

_images/MarkerGizmos.png

5.Ground Plane

_images/GroundPlane.png

GroundPlane 如其名字所示,他的功能是做为世界坐标的中心点,实际应用中的作用是用来定位虚拟场景的原点位置。 GroundPlane有 WallGround 两种模式。通常我们将作为GroundPlane的定位信标平放在地上使用,即使用Ground模式

如果您需要将作为GroundPlane的定位信标贴在墙上或者竖直立着使用,则需要将其设置为Wall模式,前面提到的DynamicTarget因为 可以在空间中随意移动,所以没有这两种模式之分。

注解

由于GroundPlane的特殊作用,他只在Yaw方向上可以改变场景的朝向,在其他方向旋转不会生效

如果作为Ground模式使用,则只在贴着地面旋转时才改变场景的朝向,沿着其他方向不改变

如果作为Wall模式使用,则只在贴着墙面旋转时才改变场景的朝向,沿着其他方向不改变

Beacon就是最典型的GroundPlane,如果您需要跟踪的定位信标的使用方式与Beacon类似,则将其设置为GroundPlane对象

注解

GroundPlane的作用是控制整个场景的原点位置,只需要将他创建在工程的根目录下即可,不需要将任何模型或者场景绑定在他下面。

_images/ground_plane_anchor_head.gif

RecenterMode : 默认是Automate , 当作为Ground Plane的定位信标进入跟踪区域,并且满足跟踪配置的时候, 自动矫正头部位置。 如果是 Scripting, 代表开发者要自行控制矫正头部的时机, 可以调用静态方法 GroundPlane.ForceRecenter() 来实现程序化的头部矫正功能。

Placement : 定义定位信标的放置方式是水平还是竖直,设置为Wall模式时,定位信标可以贴在墙上(竖直放置)使用。

DecoupleVIO : 当GroundPlane矫正头部位置的时候,停用MR头显的VIO跟踪系统。这是只有进阶开发者才需要用到的功能。

Recenter Condition 参数配置说明:

UseBestConfidence : 只采用最高跟踪置信度时的数据来矫正头部的位置, 如果勾选此选项,会降低头部矫正的频率,提高矫正的精度。建议大部分开发者不要勾选此选项。

MinTrackAngle : 定位信标最小可跟踪角度。当定位信标和跟踪相机的夹角小于这个角度的时候, 不使用定位信标数据,即小于这个角度时 定位信标不起作用。建议保留默认值: 28˚

MinTrackDistance 和 MaxTrackDistance : 定位信标可跟踪距离范围。对于随箱Beacon,跟踪范围建议设置为0-1.5米. 如果使用主动 发光定位信标, 跟踪距离范围建议设置为 0 - 5米。

MaxRecenterAngularVelocity : 当用户的头部在快速转动,并且转动角速度大于此配置值的时候,不予矫正头部位置,因为可能造成用户晕眩。

MinTrackedConfidence : 最小跟踪置信度,当跟踪数据置信度小于这个值的时候, 不予矫正。建议保留默认值: 0.85

注解

置信度可以简单理解为跟踪数据的可信度,置信度越大,跟踪数据越稳定,但是会相应减小跟踪范围。置信度越小则更容易出现跟踪 抖动,但是可以包含更大的跟踪范围。

Min Error Head Distance 和 Min Error Head Diff Angle : 控制GroundPlane 归中时候的允许误差。当位置偏差超过这个值时 才会对头显进行校正,如果小于这个值,即使发生了偏移,也不会进行校正

注解

如果这两个数值太小, GroundPlane 会频繁归中ARCamera造成用户视觉上的瞬移感。

如果这两个数值太大, GroundPlane就会失去归中的功能。

对于Beacon我们建议用户保留默认的数值设置 :

MinTracked Distance = 0;

Max Tracked Distance = 1.5;

Head Error Distance = 0.12;

Head Error Diff Angle = 15;

对于主动发光板,建议采用如下数值:

MinTracked Distance = 0;

Max Tracked Distance = 5;

Head Error Distance = 0.2;

Head Error Diff Angle = 20;

OnFirstRecenter 事件 : 当GroundPlane对象第一次矫正用户头部的时候触发。 开发者可以使用此事件来实现“进入AR世界”的触发机制。

6.RxController

此组件代表控制器

_images/RxController.png

Index: 定义控制器的类型,包括左右手柄以及预留的ID Preserve03, 04。

MinTrackConfidenceUse 和 Min Track Confidence Recenter IMU : 代表控制器的最小可跟踪置信度。一般不需要自定义, 保留默认值。

Tracking Mode : 跟踪模式, 默认是6DOF跟踪, 如果是3DOF跟踪模式, 则只保留陀螺仪跟踪朝向,不采纳位置跟踪。

注解

在只需要使用3dof数据,且对数据稳定性要求较高时可使用此模式,例如使用虚拟键盘输入时。

ConnectionEvent : 控制器连接事件,控制器连接或者断开时上报。用户可以使用此事件,控制手柄模型的显示/隐藏的功能。

Raycasting 相关设置: 如果Physics-Raycasting 打开, 则RhinoX事件系统会每帧发射Raycasting实现物理检测。射线的起点是 RaycastOrigin 字段指向的 Transform。

Raycast Culling Mask 定义交互对象的层级。需要被交互的模型的Layer层级,必须包含在此层级里面才可以被控制器射线交互。

RaycastDistance : 交互射线的长度。

RenderRay : 是否渲染交互射线的 LineRenderer。

RayRenderer : 交互射线 LineRenderer。

Ray Hit Point : 交互射线的焦点。此对象应该是一个场景对象而不是一个资源引用。可以为空。

DebugMode : 如果为true,在logcat中打印按键和跟踪数据。

注解

当控制器和头显建立连接之后,SDK会自动设置RxController上的TrackableIdentity组件的Marker ID。

7.RxButtonEventTrigger

RxButtonEventTrigger 提供绑定按键响应事件的GUI配置界面。

_images/RxButtonEventTrigger.png

此事件与Unity的事件系统对接,通过RxButtonEventTrigger类可以将按键事件和对应相应方法做绑定。 使用方法请参照Unity事件系统,也可参考如下图:

_images/ButtonTrigger.png

SDK交互系统目前支持如下类型的按键事件: 点击 / 双击 / 长按 。

程序化注册按键事件代码示例:

private void RegisterRxButtonEvent()
{
    var eventTrigger = GetComponent<RXButtonEventTrigger>();
    eventTrigger.RegisterEvent(EventTriggerType.OnClickConfirmButton, (RhinoXButton button)) => {
        Debug.Log("Player is clicking on headset's confirm button !");

    };

}

8.RxInput 静态类

RxInput 静态类提供轮询式查询按键事件。 和 RxButtonEventTrigger 的响应式事件机制形成互补。

查询双击按键代码示例:

private void Update()
{
    if(RXInput.IsButton(RhinoXButton.ControllerTrigger, ControllerIndex.Controller_Right_Controller))
    {
        Debug.Log("User is pressing button : trigger"); //用户一直按着右手控制器的trigger按键,每帧触发。
    }

    if (RXInput.IsButtonTap(RhinoXButton.ControllerTrigger, ControllerIndex.Controller_Right_Controller))
    {
        Debug.Log("User clicks button : trigger"); //用户点击右手控制器的trigger按键, 只会在点击的时候触发一次。
    }

    if (RXInput.IsButtonLongHeldDown(RhinoXButton.ControllerTrigger, ControllerIndex.Controller_Right_Controller))
    {
        Debug.Log("User long hold button : trigger");  //用户长按右手控制器的trigger按键, 只会在松开按键之前触发一次。长按时间配置见 ProjectSetting/RhinoX Setting
    }
}

9.事件系统

事件系统组件包括:RxEventSystem、RxInputModule

这是RhinoX 的事件系统中枢组件。场景中必须存在这两个组件才能确保 RxController 的raycast射线可以和 Unity 的事件系统交互, RxController, RxInput 等交互组件才可以正常工作。

RxEventSystem 和 RxInputModule是重要的功能性接口方法, 它们的职责主要是事件调度,将RhinoX的事件和Unity的事件系统联动起来。

代码示例: 使用 unity event system 和 RxController 的射线系统交互:

using UnityEngine.EventSystems;

public class RecordButton : MonoBehaviour, IPointerEnterHandler  //当射线接触到某个可交互层级的物体时候, 该物体上的script会接收到 OnPointerEnter 事件
{
    public RXController leftController, rightController;
    void IPointerDownHandler.OnPointerEnter (PointerEventData eventData) {
         var raycasterTransform = eventData.pointerCurrentRaycast.module.transform;
         bool isRaycastFromLeftHand = raycasterTransform.IsChildOf(leftController.transform);//是否被左手控制器射线命中
         bool isRaycastFromRightHand = raycasterTransform.IsChildOf(rightController.transform); //是否被右手控制器射线命中
         Debug.LogFormat ("Is raycast hit by right hand : {0} , is raycast hit by left hand: {1}", isRaycastFromRightHand, isRaycastFromLeftHand);
    }
}


public class RecordButton : MonoBehaviour, IPointerExitHandler  //当射线离开某个可交互层级的物体时候, 该物体上的script会接收到 OnPointerExit 事件
{
    public RXController leftController, rightController;
    void IPointerDownHandler.OnPointerExit (PointerEventData eventData) {
         var raycasterTransform = eventData.pointerCurrentRaycast.module.transform;
         bool isRaycastFromLeftHand = raycasterTransform.IsChildOf(leftController.transform);//是否被左手控制器射线命中
         bool isRaycastFromRightHand = raycasterTransform.IsChildOf(rightController.transform); //是否被右手控制器射线命中
         Debug.LogFormat ("Is raycast exit by right hand : {0} , is raycast exit by left hand: {1}", isRaycastFromRightHand, isRaycastFromLeftHand);
    }
}

public class RecordButton : MonoBehaviour, IPointerClickHandler  //当射线停留在某个可交互层级的物体时候,用户按下trigger键, 该物体上的script会接收到 OnPointerClick 事件
{
    public RXController leftController, rightController;
    void IPointerDownHandler.OnPointerClick (PointerEventData eventData) {
         var raycasterTransform = eventData.pointerCurrentRaycast.module.transform;
         bool isRaycastFromLeftHand = raycasterTransform.IsChildOf(leftController.transform);//是否被左手控制器射线命中
         bool isRaycastFromRightHand = raycasterTransform.IsChildOf(rightController.transform); //是否被右手控制器射线命中
         Debug.LogFormat ("Is raycast click by right hand : {0} , is raycast click by left hand: {1}", isRaycastFromRightHand, isRaycastFromLeftHand);
    }
}

上面的内容主要是给大家介绍各组件的功能以及使用场景,具体需要调用的API,大家可以参考 API文档

4.2 交互组件

这里我们主要提供几种MR交互种最常用的几种交互功能组件

他们包括 : Touchable(触碰) / Grabable(抓取) / Throwable(抛掷) / Outline(勾边) / PlayerHand(用户的虚拟手部动画控制) / FrontDocker (UI 锚定眼前位置)。

交互组件是应用层脚本, 设计初衷是实现虚拟对象的物理交互功能,例如抓取物体, 抛掷物体等。

注解

交互组件是开源的C#代码, 放在在 SDK 的 OpenSource 目录中, 开发者可以按照自己的需求改动代码。

如何创建可交互的对象?

因为这些交互组件都是已脚本的形式提供给大家,如果您需要创建一个可交互的对象,您只需要在该对象的Inspector下面添加 对应的脚本即可。

_images/InterractionScript.png

1.Touchable & Touchable Unity Event

Touchable 代表一个可以被触摸的物体。 触摸是通过物理射线(也就是RxController上的raycaster)与 被交互模型上的碰撞体接触实现。所以 touchable 物体上需要有处于可交互层级的collider。

Touchable Unity Event 则将触碰的事件以GUI的形式展示出来,开发者可以调用这些事件实现需要的功能。

_images/touchable.png _images/touchable_demo.gif

LongTouchTime : 触发长时间触碰事件 / OnLongTouch() 的时间预值。

LongTouchTrigger : 触发OnLongTouch()事件的控制机制, 是否可以反复多次触发。如果为Once,则在射线进入-离开这个交互周期内, 只触发一次。

2.Grabable & Grabable Unity Event

Grabable 代表一个物体可以被抓取的行为。

_images/grabable.png

Grabable Unity Event 将 Grabable 的事件以GUI的形式展示出来,开发者可以调用这些事件实现需要的功能。

  • OnGrabBegin:当抓取发生的时候触发

  • OnGrabUpdate:正在抓取的过程中, 每帧触发一次

  • OnGrabEnd: 当抓取结束的时候触发

_images/grabable_demo.gif

GUI功能定义如下:

TriggerAction : 定义用户抓取物体的触发方式。

Button Down and Up - 在按下按键的时候抓取, 在松开按键的时候释放物体。

Touch - 在触碰到物体的时候自动抓取。在按下按键的时候释放物体。

ButtonClick - 在第一次点按(也就是按下-松开)的时候抓取,在第二次点按的时候释放。

AnchorTransform : 抓取锚点, 必须是 Grabable的子对象,此Transform定义当抓取的时候, grabable物件与手部对齐的位置锚点。

Tween To Player Hand 是否在抓取的时候, 控制Grabable 对象飞到用户的虚拟手上。

Tween Duration 如果Tween To Player Hand为true,控制飞到用户手上的时间长度。

Align Mode 控制物体被抓取的时候, 如何处理其朝向。 如果为 Full Align , 则朝向会和抓取锚点的朝向一致。 如果为 Dont Align ,则保持抓取前的朝向。

CanBeGrabByOtherHand 是否可以在被一只手抓取的时候,被另一只手交换抓取。

3.Throwable & Throwable Unity Event

Throwable 代表一个物体可以被抓取后抛出的行为。 Throwable 组件依赖于 Grabable和Rigidbody, 基于Unity的物理系统实现。

Throwable Unity Event 将 Throwable 的事件以GUI的形式暴露给开发者 : OnThrown 事件,当物体被抛出的时候触发。

_images/throwable.png
Throwable Behaviour Type 定义抛掷的行为模式。

Throw away: 是默认的模式,模拟用户抛掷物体的行为。

Eject: 是模拟射箭的模式, 用户不需要挥舞手部运动, 只需要按下按键,Throwable物体就会像箭一样发射。

UseGravity 定义当发射之后,是否对Rigidbody应用重力。

Eject Force 当 Throwable Behaviour Type 为 Eject 的时候有效, 定义发射的力度。

Disallow Grabable After Thrown 如果为true,当物体被抛掷之后, 禁用Grabable 组件。使用此功能模拟一些抛掷之后过一会就自动消失的,一次性的小物体,例如飞镖,小石块等。

Rigidbody 绑定的Rigidbody对象。

Speed Max 对抛掷之后限制的最大速度,使用此字段避免用户用力过大造成物体飞出太远的问题。

Multiplier 用户抛掷力的系数。

4.FrontDocker

FrontDocker 组件用于将Canvas锚定在用户眼前,如果您需要将某个UI固定在用户视野前方,跟随头显的转到而移动,您可以使用此组件。 您可以使用此组件强制某个UI一直显示在眼前,或者控制头显转动导致某个UI消失在视野之后,将UI再次自动定位到用户眼前。

_images/demo_frontdocker.gif _images/inspector_frontdocker.png

Z Depth UI与Camera的距离

Force EveryFrame 是否每帧更新位置。 如果为true, 则Canvas会一直锚定在用户眼前。

Angle Diff Error 当Force EveryFrame为false时,控制头部偏转一定的角度后,UI才重新锚定到用户眼前。

Dock Delay UI被重新锚定到眼前的延时。

Dock DampTime UI被重新锚定到眼前过程发生的时长。

Follow With Pitch 是否会随着用户俯仰角度的变化而变化。

Position Offset 在最终锚点上的位置偏移。