Unity+SteamVR开发

您所在的位置:网站首页 vr手柄如何使用方法 Unity+SteamVR开发

Unity+SteamVR开发

2024-06-26 18:39:40| 来源: 网络整理| 查看: 265

一、前言

       本文使用两个工具为Unity2018.4.26和SteamVR2.6.1,SteamVR2.6.1相比之前的版本有了很大的改变,其中在交互上有了很大的提升,SteamVR2.6.1上给出的案例中提供了抛射物体、线性驱动、环形驱动以及复杂的射箭操作等。尽管给出了诸多的交互案例,但是在实际开发中依然会有新的交互情况出现,在SteamVR2.6.1中没有详细的使用说明下,本文首先大概介绍其各种交互案例,然后详细的介绍其交互的核心组件如Interactable和Hand等,最后结合我使用的案例实现如何动态添加各种交互。               

二、介绍

2.1、简单交互Simple Interactable

       如图1所示。,为实现的简单交互,手触碰到物体会有使物体呈现黄色轮廓框,然后按下扳机键既可以移动物体,但

图1

是在这里手的模型隐藏掉了。 该交互方式只需要添加核心交互组件Interactable到父物体上,子物体中有带碰撞体的就可以实现。

实现逻辑:

第一步:首先,Hand在激活的时候重复不断的调用UpdateHovering方法,该方法即处理手悬浮带Interactable组件的物体

protected virtual void OnEnable() { inputFocusAction.enabled = true; // Stagger updates between hands float hoverUpdateBegin = ((otherHand != null) && (otherHand.GetInstanceID() < GetInstanceID())) ? (0.5f * hoverUpdateInterval) : (0.0f); InvokeRepeating("UpdateHovering", hoverUpdateBegin, hoverUpdateInterval); InvokeRepeating("UpdateDebugText", hoverUpdateBegin, hoverUpdateInterval); }

 第二步:在UpdateHovering方法中判断哪个Interactable物体时和手最近的,判断的方法为CheckHoveringForTransform,在这个方法中会遍历子物体中的所有Collider,然后获取该Collider的父物体上的Interactable组件,如果不为Null,则比较与手的距离,找到最近的那一个,并将其Interactable的实例化对象赋值给Hand中的hoveringInteractable;

第三步:在Hand中hoveringInteractable为Interactable类型的属性,当该属性被赋值时会进行广播消息处理,所有继承了

public Interactable hoveringInteractable { get { return _hoveringInteractable; } set { if (_hoveringInteractable != value) { if (_hoveringInteractable != null) { if (spewDebugText) HandDebugLog("HoverEnd " + _hoveringInteractable.gameObject); _hoveringInteractable.SendMessage("OnHandHoverEnd", this, SendMessageOptions.DontRequireReceiver); //Note: The _hoveringInteractable can change after sending the OnHandHoverEnd message so we need to check it again before broadcasting this message if (_hoveringInteractable != null) { this.BroadcastMessage("OnParentHandHoverEnd", _hoveringInteractable, SendMessageOptions.DontRequireReceiver); // let objects attached to the hand know that a hover has ended } } _hoveringInteractable = value; if (_hoveringInteractable != null) { if (spewDebugText) HandDebugLog("HoverBegin " + _hoveringInteractable.gameObject); _hoveringInteractable.SendMessage("OnHandHoverBegin", this, SendMessageOptions.DontRequireReceiver); //Note: The _hoveringInteractable can change after sending the OnHandHoverBegin message so we need to check it again before broadcasting this message if (_hoveringInteractable != null) { this.BroadcastMessage("OnParentHandHoverBegin", _hoveringInteractable, SendMessageOptions.DontRequireReceiver); // let objects attached to the hand know that a hover has begun } } } } }

MonoBehaviour的脚本中定义了 OnHandHoverBegin和OnHandHoverEnd方法的都将被执行;

最后:在该案例的脚本InteractableExample中实现OnHandHoverBegin和OnHandHoverEnd方法。

2.2、抛射物体Throwable

        如图2所示为手抓取物体然后进行抛射的过程,当手抓取物体时会变换为手刚好握住物体的姿态且手指都为静态的,

图2

当手释放掉物体的时候又恢复到原来的状态,并且物体抛出去之后具有一定的速度。

 实现逻辑:

第一步:同样需要添加核心交互组件Interactable,并且需要添加SteamVR_Skeleton_Poser、Rigidbody以及Throwable(或子类);

第二步:编辑SteamVR_Skeleton_Poser中所需要的手部姿势

1)、如图3所示,点击Create创建一个新的姿势,所有的姿势都是SteamVR_Skeleton_Pose的ScriptableObject,保存后为.asset为后缀的文件,可以通过Resources.Load方法直接加载或者直接拖到面板上使用。

标题3

2)、 如图4所示,勾选Show Right Preview 即对手势进行编辑,此时可以看到在物体的附近有一个手的模型,如果想在模板的基础上编辑可以选择Reference Pose:选择之后手即可变成模板的样子。该手部编辑模型会作为子物体出现在,

图4

但是紧紧时在编辑模式下出现,作为编辑使用,编辑完之后需要取消 Show Right Preview的勾选方可正常显示和使用。

直接调整手的位置和关键,使其达到符合要求的握住物体的样子即可,然后勾选Show Left Preview此时下面的Copy Left pose to Right Hand和Copy Right pose to Right Hand会被激活。注意:因为刚刚编辑的时Right的,因此点右边下面的Copy Left pose to Right Hand按钮,将右边的镜像处理得到 左边的数据并覆盖当前左边的,点击之后即可看到两只手都以同样的姿势握住物体。这里一定要点对,不然前面的工作会被覆盖而需要重新做。最后,点击Save Pose 即可。    

第三步、Throwable编辑,在Throwable脚本中同样实现了OnHandHoverBegin和OnHandHoverEnd方法,处理握住物体的逻辑。并且还实现了HandHoverUpdate方法,在该方法中首先判断当前手的按键类型,只要不是抓取的按键触发就握

protected virtual void HandHoverUpdate( Hand hand ) { GrabTypes startingGrabType = hand.GetGrabStarting(); if (startingGrabType != GrabTypes.None) { hand.AttachObject( gameObject, startingGrabType, attachmentFlags, attachmentOffset ); hand.HideGrabHint(); } }

住物体,该方法在Hand的Update中被广播,另外,还有HandAttachedUpdate方法。

protected virtual void Update() { UpdateNoSteamVRFallback(); GameObject attachedObject = currentAttachedObject; if (attachedObject != null) { attachedObject.SendMessage("HandAttachedUpdate", this, SendMessageOptions.DontRequireReceiver); } if (hoveringInteractable) { hoveringInteractable.SendMessage("HandHoverUpdate", this, SendMessageOptions.DontRequireReceiver); } }

在Throwable中实现HandAttachedUpdate方法,代码如下:该方法每帧都执行,判断手的按键已经释放掉了该物体的时

protected virtual void HandAttachedUpdate(Hand hand) { if (hand.IsGrabEnding(this.gameObject)) { hand.DetachObject(gameObject, restoreOriginalParent); // Uncomment to detach ourselves late in the frame. // This is so that any vehicles the player is attached to // have a chance to finish updating themselves. // If we detach now, our position could be behind what it // will be at the end of the frame, and the object may appear // to teleport behind the hand when the player releases it. //StartCoroutine( LateDetach( hand ) ); } if (onHeldUpdate != null) onHeldUpdate.Invoke(hand); }

执行手放弃物体的操作 hand.DetachObject(gameObject, restoreOriginalParent);,然后Hand的DetachObject方法里调用广播函数广播OnDetachedFromHand,最终在Throwable脚本中的实现的OnDetachedFromHand方法里处理了最后被扔出去的逻辑。

public void DetachObject(GameObject objectToDetach, bool restoreOriginalParent = true { ... if (attachedObjects[index].attachedObject != null) { if (attachedObjects[index].interactable == null || (attachedObjects[index].interactable != null && attachedObjects[index].interactable.isDestroying == false)) attachedObjects[index].attachedObject.SetActive(true); attachedObjects[index].attachedObject.SendMessage("OnDetachedFromHand", this, SendMessageOptions.DontRequireReceiver); } ... }

2.3、 线性驱动LinearDrive

         如图5所示为线性驱动的效果示意图,手捂住操作的物体保持姿势不动,移动手柄,手捂住的物体跟随运动,但是

图5

只保持在横向的线性位置移动,手握住的物体不会超过该线性区域。

第一步 :核心组件Interactable当然比不可少,然后实现HandHoverUpdate和HandAttachedUpdate以及OnDetachedFromHand方法,编辑握住物体所需的手势;

第一步:获取手部捂住物体之后手移动的参数,计算方法为:获取手现在的位置和线性起点的位置组成的向量A和终点到起点的向量B,得到向量A和B的点积,然后将这个值作为线性插值的变化因子

protected virtual void HandAttachedUpdate(Hand hand) { UpdateLinearMapping(hand.transform); if (hand.IsGrabEnding(this.gameObject)) { hand.DetachObject(gameObject); } } protected void UpdateLinearMapping( Transform updateTransform ) { prevMapping = linearMapping.value; linearMapping.value = Mathf.Clamp01( initialMappingOffset + CalculateLinearMapping( updateTransform ) ); mappingChangeSamples[sampleCount % mappingChangeSamples.Length] = ( 1.0f / Time.deltaTime ) * ( linearMapping.value - prevMapping ); sampleCount++; if ( repositionGameObject ) { transform.position = Vector3.Lerp( startPosition.position, endPosition.position, linearMapping.value ); } } protected float CalculateLinearMapping( Transform updateTransform ) { Vector3 direction = endPosition.position - startPosition.position; float length = direction.magnitude; direction.Normalize(); Vector3 displacement = updateTransform.position - startPosition.position; return Vector3.Dot( displacement, direction ) / length; }

2.4、环形驱动CircularDrive

        如图6所示,其处理逻辑和线性驱动类似,只是在计算物体旋转上有所差别

 

图6

2.5、 悬浮按钮Hover Button

       如图6所示,手悬浮在按钮上,然后向下压物体可以实现物体按下效果。实现的逻辑和前面的简单交互类似。

图6

 2.6、射箭

        如图7所示为双手射箭的操作,这个交互应该是SteamVR2.6.1这个版本中最复杂的一部分。同样需要添加

图7

Interactable组件。重点是ItemPackageSpawner组件,该组件实现了手用弓箭的所有逻辑。

1)、ItemPackageSpawner实现了HandHoverUpdate方法,并且在面板上勾选了requireGrabActionToTake,因此在手触碰到弓并且按下抓取的扳机键的时候调用SpawnAndAttachObject生成一些列后续操作所需要的包并且这只手抓住弓

private void HandHoverUpdate( Hand hand ) { ... if ( requireGrabActionToTake ) { GrabTypes startingGrab = hand.GetGrabStarting(); if (startingGrab != GrabTypes.None) { SpawnAndAttachObject( hand, GrabTypes.Scripted); } } }

 SpawnAndAttachObject方法里先根据ItemPackageType类型来清空手上的东西,然后重新生成一个itemPackage里面的itemPrefab,然后让手抓住它,这个时候生成的物体为Longbow,是带握住手势的弓,如图8所示。如果itemPackage的

图8

 otherHandItemPrefab不为空的话也实例化该物体,并且用另外一只手抓住它。这里的otherHandItemPrefab为握住箭的手势ArrowHand,如图9所示,在ArrowHand中初始化只保留了一个握住箭的手势,箭的生成要在其内部

图9

 HandAttachedUpdate方法里实现

private GameObject InstantiateArrow() { GameObject arrow = Instantiate( arrowPrefab, arrowNockTransform.position, arrowNockTransform.rotation ) as GameObject; arrow.name = "Bow Arrow"; arrow.transform.parent = arrowNockTransform; Util.ResetTransform( arrow.transform ); arrowList.Add( arrow ); while ( arrowList.Count > maxArrowCount ) { GameObject oldArrow = arrowList[0]; arrowList.RemoveAt( 0 ); if ( oldArrow ) { Destroy( oldArrow ); } } return arrow; } //------------------------------------------------- private void HandAttachedUpdate( Hand hand ) { if ( allowArrowSpawn && ( currentArrow == null ) ) // If we're allowed to { currentArrow = InstantiateArrow(); arrowSpawnSound.Play(); } }

 2)、放箭的过程在ArrowHand的HandAttachedUpdate方法中实现,当弓被拉握住箭的手柄按键释放的时候,即射出箭

private void HandAttachedUpdate( Hand hand ) { ... // If arrow is nocked, and we release the trigger if ( nocked && hand.IsGrabbingWithType(nockedWithType) == false ) { if ( bow.pulled ) // If bow is pulled back far enough, fire arrow, otherwise reset arrow in arrowhand { FireArrow(); } else { arrowNockTransform.rotation = currentArrow.transform.rotation; currentArrow.transform.parent = arrowNockTransform; Util.ResetTransform( currentArrow.transform ); nocked = false; nockedWithType = GrabTypes.None; bow.ReleaseNock(); hand.HoverUnlock( GetComponent() ); allowTeleport.teleportAllowed = true; } bow.StartRotationLerp(); // Arrow is releasing from the bow, tell the bow to lerp back to controller rotation } }

2.7、远程控制

如图10a和10b所示为操作虚拟手柄远程控制物体的案例,这两个案例非常类似,只是处理的过程非常繁琐,这里不再详细展开了,唯一没有让我完全搞清楚的地方是,控制虚拟手柄的手部动作是如何做到动态的。跟前面手指静态的不同,

图10a

这里的手姿势控制未找到SteamVR_Skeleton_Poser的使用。

图10b

三、总结

3.1、交互的核心组件为Interactable,凡是涉及到用手进行交互都需要添加该组件,后面会讲射线与物体交互也会用到该组件;

3.2、手握住物体的姿势为SteamVR_Skeleton_Pose,是一个ScriptableObject的资源类,可以在编辑器中进行编辑并且保存为后缀.asset文件,该文件可以实现动态加载;

3.3、需要在获取手和物体的处理逻辑上一定要实现Hand中广播的方法;

3.4、远程操作的手握住虚拟手柄的姿势可以动手指,目前还不知道怎么编辑或设置。



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭