Unity+SteamVR开发——交互

一、前言

       本文使用两个工具为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 handsfloat 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 messageif (_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 messageif (_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 triggerif ( 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<Interactable>() );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、远程操作的手握住虚拟手柄的姿势可以动手指,目前还不知道怎么编辑或设置。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/29003.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity-SteamVR物体交互

谈到Unity VR开发&#xff0c;即使是初学者也会听过SteamVR或者HTC VIVE&#xff0c;这款设备是目前在国内能够方便买到且在使用上不错的一款设备。其实SteamVR是集成在Steam里的一个VR插件&#xff0c;而HTC是硬件设备&#xff0c;运行时用到SteamVR这个插件&#xff08;也是H…

Unity VR游戏开发干货教程:VR中的用户界面

在设计VR应用的用户界面时要考虑很多因素&#xff0c;而这些因素对于传统应用或游戏的界面设计可能都不是事儿。下面我们会看看作为一个VR开发者在设计用户界面时可能会遇到的一些问题&#xff0c;以及跟硬件相关的一些东西。UI分辨率和画质目前DK2的分辨率是1920*1080(单目是9…

【SR Works】使用HTC Vive Pro结合SteamVR在Unity中制作MR内容

HTC Vive Pro有两个前置RGB摄像头&#xff0c;官方出了一套SR Works SDK来借助这两个摄像头实现MR功能。 本篇的目标是&#xff0c;成功运行官方的MR示例&#xff0c;并结合SteamVR和SR Works SDK在Unity中实现MR项目的开发准备 1.下载SRWorks插件 下载地址&#xff1a; Late…

公司普通启动VR/AR软件开发虚拟场景游戏

随着元宇宙技术的不断革新&#xff0c;VR作为一种新兴技术&#xff0c;正在迅速普及。VR软件开发技术将用户沉浸到虚拟世界中&#xff0c;让人们可以身临其境地体验一些事物&#xff0c;如元宇宙游戏、商圈、展会、旅游等。而VR虚拟场景的开发&#xff0c;则是实现这一目标的重…

《SteamVR2.2.0交互系统(Interaction System)》(Yanlz+Unity+XR+VR+AR+MR+Valve+Teleport+Skeleton+立钻哥哥++ok++)

《SteamVR2.2.0快速入门》 《SteamVR2.2.0快速入门》 版本 作者 参与者 完成日期 备注 SteamVR2.2.0_Interaction_V01_1.0 严立钻 2019.04.11 ##《SteamVR2.2.0快速入门》发布说明&#xff1a; “SteamVR2.2.0快速入门”&#xff1a;是对“Stea…

怎么才能把短视频中文配音变成英文配音?

中文视频的类型 首先&#xff0c;将中文视频翻译成改为英文的视频&#xff0c;这是一个比较复杂的过程&#xff0c; 这里面还涉及到几种情况&#xff0c;下面简单罗列下&#xff1a; 第一种&#xff1a;带中文解说和中文字幕的视频&#xff1b; 第二种&#xff1a;只有中文解说…

Go Web下gin框架的模板渲染

〇、前言 Gin框架是一个用于构建Web应用程序的轻量级Web框架&#xff0c;使用Go语言开发。它具有高性能、低内存占用和快速路由匹配的特点&#xff0c;旨在提供简单、快速的方式来开发可扩展的Web应用程序。 Gin框架的设计目标是保持简单和易于使用&#xff0c;同时提供足够的…

【VINS-Fusion】学习笔记

配置文件-config\euroc\euroc_stereo_imu_config.yaml %YAML:1.0#common parameters #support: 1 imu 1 cam; 1 imu 2 cam: 2 cam; #1个mui&#xff0c;双目相机 imu: 1 num_of_cam: 2 imu_topic: "/imu0" image0_topic: "/cam0/image_raw" im…

Qt6教程之三(3) QtWedget自定义控件

在之前的博客中&#xff0c;我们使用的控件都是Qt官方提供的&#xff0c;对于控件的特性也只能被动地接受&#xff0c;为了打破这种束缚&#xff0c;可以按照自己的想法来定义控件。 不过自定义控件必须遵守Qt官方的一套自定义控件规则&#xff0c;在规则之下我们就可以定义属…

基于TextRank+Seq2Seq+Pyqt5文章摘要标题关键词辅助生成系统(含全部python工程源码)+训练数据集

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境TextRank环境TensorFlow环境PyQt5及Qt Designer运行环境 模块实现1. 数据预处理2. 抽取摘要3. 模型搭建与编译4. 模型训练与保存5. 图形化界面的开发6. 应用封装 系统测试1. 训练困惑度2. 测试效果3. 模型应用 工…

基于langchain-chatglm本地知识库得部署

项目的技术组成 LLM模型 大型语言模型&#xff08;LLM&#xff0c;是large language model&#xff09;是一种人工智能模型&#xff0c;旨在理解和生成人类语言。它们通过在大量文本数据上进行训练&#xff0c;能够执行多种任务&#xff0c;包括文本总结、翻译、情感分析等。L…

电信卡流量套餐超40G后,该如何解除网速限制?

在和朋友开视频的时候&#xff0c;朋友那边忽然网络中断了&#xff0c;过了会他给我发了一张图片过来&#xff0c;图片如下&#xff1a; 朋友的手机是电信的&#xff0c; 然后我们就在网上查各种解除限制的信息&#xff0c;首先我们按照短信上的提示发送SWFJ到10001&#xff0c…

流量控制与RateLimiter

一背景 如何提高系统的稳定性&#xff0c;简单来说除了加机器外就是服务降级、限流。加机器就是常说的分布式&#xff0c;从整个架构的稳定性角度看&#xff0c;一般SOA每个接口的所能提供的单位时间服务能力是有上限。假如超过服务能力&#xff0c;一般会造成整个接口服务停顿…

异常流量检测

项目目标是为了检测网络异常流量&#xff0c;防止网络攻击行为&#xff0c;本人参与了初期的工作&#xff0c;进行了文献阅读-数据处理-模型构建-参数优化工作。 网络攻击行为主要分为Dos、U2R、Probe等&#xff0c;其对应的流量异常特征为集合异常、点异常、上下文异常。 点异…

网络流量监控分析工具ntopng的安装与使用

我写这篇文章的起因&#xff1a;网上介绍ntopng这款工具的博客不算很多&#xff0c;而且安装方法基本没一个行得通&#xff08;可能是版本更新太快了&#xff09;&#xff0c;我在安装过程中遇到了一些问题&#xff0c;想跟大家分享下&#xff1b;其次&#xff0c;在讲使用方法…

流量异常检测

https://mp.weixin.qq.com/s/9h-hOt630W6k077Rupc9CA 流量异常检测主要有以下三个难点&#xff1a; 流量的大小会随着用户行为发生变化。对于大部分百度云上的业务&#xff0c;白天的访问流量较高&#xff0c;深夜的访问流量较低。这使得流量水位值存在上下文相关性&#xff0…

Android应用流量统计——NetworkStatsManager使用

在没有Root的情况下&#xff0c;Android应用流量统计在6.0之前一直没有太好的办法&#xff0c;官方虽然提供了TrafficStats&#xff0c;但其主要功能是设备启动以来流量的统计信息&#xff0c;和时间信息无法很好的配合。最近再看TrafficStats类时&#xff0c;发现说明中提到&a…

仿设置流量使用——细讲android获取流量使用情况,以及解决调用流量接口不准问题(非TrafficStats,而是NetworkStatsHistory)

不积跬步无以至千里 流量使用情况,好多软件都会带这个功能,比如360的流量监控,好多之类的,手机管家都会带上这个流量计算的功能,连系统应用设置里面也会带一个流量使用情况的查看功能,为什么呢?因为流量的使用关乎到用户使用流量的计费,当流量使用了很多,会给用…

vnstat流量统计(2.8版本)

vnstat流量统计&#xff08;2.8版本&#xff09; vnStat 是一个基于控制台的 Linux 和 BSD 网络流量监视器&#xff0c;它为所选接口保留网络流量日志。它使用内核提供的网络接口统计信息作为信息源。这意味着 vnStat 实际上不会嗅探任何流量&#xff0c;并且无论网络流量率如…

中职流量包分析attack/capture(1)

我希望网络安全的世界大家可以贡献自己的一部分&#xff0c;而不是拿来自私自利 作者拿到的流量包和题目是有出入的&#xff0c;但是因为出的题大同小异所以能分析出来 attack 1. 分 析 attack.pcapng 数 据 包 文 件 &#xff0c;通 过 分 析数据 包 attack.pcapng 找出恶意…