可交互对象 (VRTK_InteractableObject)
发表于2016-11-02
概述
这个脚本可以绑定在任何需要交互的游戏对象上。
直接把该脚本绑定在游戏对象上,手柄就可以与它进行简单的交互,如触碰,抓取,使用等。但是推荐为不同类型和功用的物体写单独的类,继承该类,个性化添加功能。
Inspector可见参数
触碰交互
· Highlight On Touch: 如果勾选,那么物体只有在手柄碰到它的时候才高亮。
· Touch Highlight Color: 物体被碰到高亮时的颜色. 这个颜色会覆盖任何其他的颜色设置 (例如VRTK_InteractTouch 脚本)。
· Rumble On Touch: 当手柄碰到物体时触发触觉反馈, x表示持续时间, y表示脉冲强度。 (在编辑器里可以修改其值)
· Allowed Touch Controllers: 决定哪个手柄可以触碰物体.可用选项有:
· Both 两个手柄都可以
· Left_Only 只有左边手柄可以
· Right_Only 只有右边手柄可以
· Hide Controller On Touch: 是否覆盖手柄相关设置 (触碰物体时隐藏手柄):
· Default 使用手柄的设置
· Override Hide 无论手柄设置为何,隐藏
· Override Dont Hide 无论手柄设置为何,不隐藏
抓取交互
· Is Grabbable: 物体是否可被抓取。
· Is Droppable: 使用grab按钮是否可以把已被抓取的物体放下。如果未勾选此项那么一旦物体被抓取就不能被放下。但是当很大的力施加在连接处的时候,连接断裂,物体也会放下。为了避免这种情况最好使用child Of Controller方式。
· Is Swappable: 物体是否可以在两个手柄之间传递。如果未勾选此项,那么物体被另一个手柄抓取之前必须先从当前手柄上放下。
· Hold Button To Grab: 如果此项被勾选,那么要一直按着按钮才能保持物体抓取,松开按钮物体会掉落。如果未勾选此项那么按一下抓取按钮物体会被抓取且在按第二下之前它不会掉落。
· Rumble On Grab: 当手柄抓取物体时震动反馈,x表示持续时间, y表示脉冲强度。 (在编辑器里可以修改其值)。
· Allowed Grab Controllers: 决定哪个手柄可以抓取物体.可用选项有:
· Both 两个手柄都可以
· Left_Only 只有左边手柄可以
· Right_Only 只有右边手柄可以
· Precision_Snap: 如果此项勾选那么当手柄抓取物体的时候, 它会在手柄触碰点精确地抓取物体。物体被抓取后与手柄触碰物体时两者的相对位置保持一致,不勾选时,物体的中心会与指定连接点重合
· Right Snap Handle: 一个空物体的Transform,它必须是被抓取物体的子对象,当右侧手柄抓取该物体时,就会抓取这个Transform。如果没有提供Right Snap Handle但是提供了Left Snap Handle,那么就代而使用Left Snap Handle。如果没有提供任何Snap Handle那么该物体被抓取的位置就是它的中心点。
· Left Snap Handle: 一个空物体的Transform,它必须是被抓取物体的子对象,当右侧手柄抓取该物体时,就会抓取这个Transform。如果没有提供Left Snap Handle但是提供了Right Snap Handle,那么就代而使用Right Snap Handle。如果没有提供任何Snap Handle那么该物体被抓取的位置就是它的中心点。
· Hide Controller On Grab: 是否覆盖手柄相关设置 (抓取物体时隐藏手柄):
· Default 使用手柄的设置
· Override Hide 无论手柄设置为何,隐藏
· Override Dont Hide 无论手柄设置为何,不隐藏
抓取方式
· Grab Attach Type: 当物体被抓取的时候,以什么样的形式附着在手柄上面
· Fixed Joint: 物体和手柄以固定关节连接,抓取时为物体添加FixedJoint组件,连接至手柄上的刚体组件
· Spring Joint: 物体和手柄以弹簧关节连接,抓取时为物体添加SpringJoint组件,连接至手柄上的刚体组件,让它们像被弹簧连接着一样联动
· Track Object: 物体不会吸附在手柄上, 但是它会随着手柄的方向移动, 铰链关节的物体可以使用这种
· Rotator Track: 跟随手柄的动作旋转. 例如手柄控制门的开关
· Child Of Controller 让该物体直接变成手柄的一个子对象
· Climbable 用来攀爬的静态物体,不随手柄运动
· Detach Threshold: 把物体和手柄分离时需要的力的大小。如果手柄意图给物体施加一个比这个值要大的力,那么物体与手柄之间的连接就会断开,这样物体便不再被抓取.如果物体是以Track Object方式被抓取,没有吸附在手柄上的话,那就把这个参数作为一个最大距离,来判断是否分离。
· Spring Joint Strength: 物体与手柄连接弹簧的弹力。数值越小弹簧越松,那么需要更大的力才能移动物体,数值越大弹簧越紧,一点点力就会让物体移动。
· Spring Joint Damper: 弹簧的阻尼,当弹簧激活时减少弹簧强度
· Throw Multiplier: 当物体被手柄释放时可以随着手柄的运动有一个初速度,实现抛出的效果,此参数为速度乘上一个值,控制抛出的初速度。
· On Grab Collision Delay: 当物体第一次被抓取时,给碰撞效果一个延时。在物体被手柄抓取的过程中取消它的碰撞,等到被抓取完成一段时间以后才恢复碰撞效果,以防抓取时发生不必要的碰撞。
使用交互
· Is Usable: 物体是否可以被使用
· Use Only If Grabbed: 如果此项勾选那么物体使用之前必须先被抓取
· Hold Button To Use: 如果勾选此项那么要一直按着使用按钮物体才能被持续使用.如果此项未勾选那么在按第二次按钮之前物体会持续使用
· Pointer Activates Use Action: 如果勾选此项,那么手柄发出指示射线击中该物体时,也可以触发物体的使用逻辑,且此时World Pointer 中会判断,不执行传送的逻辑
· Rumble On Use: 当手柄使用物体时触发触觉反馈, x表示持续时间, y表示脉冲强度. (在编辑器里可以修改其值).
· Allowed Use Controllers: 决定哪个手柄可以使用物体.可用选项有:
· Both 两个手柄都可以.
· Left_Only 只有左边手柄可以.
· Right_Only 只有右边手柄可以.
· Hide Controller On Use: 是否覆盖手柄相关设置 (使用物体时隐藏手柄):
· Default 使用手柄的设置.
· Override Hide 无论手柄设置为何,隐藏.
· Override Dont Hide 无论手柄设置为何,不隐藏.
事件类
· InteractableObjectTouched - 其他物体(例如手柄)触碰当前物体时发送事件.
· InteractableObjectUntouched - 其他物体(例如手柄)停止触碰当前物体时发送事件.
· InteractableObjectGrabbed - 其他物体(例如手柄)抓取当前物体时发送事件.
· InteractableObjectUngrabbed - 其他物体(例如手柄)停止抓取当前物体时发送事件.
· InteractableObjectUsed - 其他物体(例如手柄)使用当前物体时发送事件.
· InteractableObjectUnused - 其他物体(例如手柄)停止使用当前物体时发送事件.
事件参数
· GameObject interactingObject - 发起交互行为的游戏对象(例如手柄)
内置参数
· rb: 物体上的刚体组件
· touchingObject: 正在触碰物体的游戏对象(例如手柄)
· grabbingObject: 正在抓取物体的游戏对象(例如手柄)
· usingObject: 正在使用物体的游戏对象(例如手柄)
· usingState: 手柄对物体按下使用按钮的次数,如果物体不勾选holdButtonToUse,一个使用周期内第二次按下使用按钮,即usingState>=2,物体才会停止使用
· originalObjectColours: 物体默认的材质颜色
· grabbedSnapHandle: 被当前手柄抓取的部位,根据自己的设置和手柄的处理动态生成
· trackPoint: 追踪连接的连接点,当物体勾选了Precision_Snap,并且以追踪连接方式被抓取,那么就会在手柄对象下生成一个连接点,位置和旋转与物体一致
· customTrackPoint: 如果trackPoint就是手柄默认的那个连接点子对象,就为false,否则为true
· originalControllerAttachPoint: 物体的一个子对象,记录手柄在抓取物体前与物体的接触点
· previousParent: 物体初始的父对象
· previousKinematicState: 物体初始的Kinematic状态
· previousIsGrabbable: 物体初始的可抓取属性
· forcedDropped: 默认为false,在强制停止物体的方法执行后会置其为true,如果强制停止物体交互时脚本被禁用,那么下次脚本启用的时候看见这个参数为true就知道应该加载脚本被禁用之前的状态
公有方法
CheckHideMode/2
1 | public bool CheckHideMode( bool defaultMode, ControllerHideMode overrideMode) |
· Parameters
· bool defaultMode - 手柄上的默认设置(true=隐藏, false=不隐藏).
· ControllerHideMode overrideMode - 物体的设置,将覆盖手柄默认设置.
· Default 使用手柄的设置.(returns overrideMode).
· OverrideHide 无论手柄设置为何,隐藏.(even if defaultModeis true).
· OverrideDontHide 无论手柄设置为何,不隐藏.(even if defaultModeisfalse).
· Returns
· bool - 返回true,如果综合defaultMode和overrideMode考虑后依旧要隐藏手柄 .
CheckHideMode方法是一个供其他脚本(例如InteractTouch InteractGrab InteractUse)使用的简单的方法,它通过同时考虑手柄默认设置和可交互物体的设置(物体的设置优先级更高),用来得到最终决定手柄是否隐藏
IsTouched/0
1 | public bool IsTouched() |
· Parameters
· none
· Returns
· bool - 如果物体正在被触碰返回true
IsTouched方法用于检查物体当前是不是被触碰。
IsGrabbed/0
1 | public bool IsGrabbed() |
· Parameters
· none
· Returns
· bool - 如果物体正在被抓取返回true
IsGrabbed方法用于检查物体当前是不是被抓取。
IsUsing/0
1 | public bool IsUsing() |
· Parameters
· none
· Returns
· bool - 如果物体正在被使用返回true
IsUsing方法用于检查物体当前是不是被使用。
StartTouching/1
1 | public virtual void StartTouching(GameObject currentTouchingObject) |
· Parameters
· GameObject currentTouchingObject - 正在触碰该物体的游戏对象(例如手柄).
· Returns
· none
StartTouching方法会在当物体开始被触碰时被调用,调用处是手柄绑定的VRTK_InteractTouch脚本的OntriggerStay(),当手柄的碰撞器进入物体的trigger范围就会触发该方法。它是一个虚方法,在此类中只是做简单的状态参数修改,可以被子类重写,根据需要实现更多功能。
StopTouching/1
1 | public virtual void StopTouching(GameObject previousTouchingObject) |
· Parameters
· GameObject previousTouchingObject - 先前触及此物体的游戏对象(例如手柄).
· Returns
· none
StopTouching方法会在物体停止被触碰时被调用,调用处是手柄绑定的VRTK_InteractTouch脚本的StopTouching(),然后在OnTriggerExit()处最后调用,当手柄的碰撞器离开物体的trigger范围就会触发该方法。它是一个虚方法,在此类中只是做简单的状态参数修改,可以被子类重写,根据需要实现更多功能。
Grabbed/1
1 | public virtual void Grabbed(GameObject currentGrabbingObject) |
· Parameters
· GameObject currentGrabbingObject - 正在抓取当前物体的游戏对象(例如手柄).
· Returns
· none
Grabbed方法会在物体开始被抓取时被调用。它是一个虚方法,在此类中,它需要做如下几件事
1、发送事件将currentGrabbingObject作为参数,回调委托给InteractableObjectGrabbed的方法
2、如果之前有别的手柄抓取该物体,要强行释放抓取
3、移除原来的追踪连接点
4、设置新的追踪连接点
5、如果物体设置了isSwappable=false,假设现在物体是被手柄1抓取的,此时的isGrabbable为true,说明物体本身是可以被抓取的,但是由于不能交换,所以我们保存物体本身的isGrabbable到previousIsGrabbable中,并且暂时把物体的isGrabbable设为false,这样手柄2就不能直接从手柄1中接过物体抓取了。
同样可以被子类重写,根据需要实现更多功能。
Ungrabbed/1
1 | public virtual void Ungrabbed() |
· Parameters
· GameObject - 先前抓取此物体的游戏对象(例如手柄)
· Returns
· none
UnGrabbed方法会在物体停止被抓取时被调用。它会
1、发送事件将传入的previousGrabbingObject作为参数,回调委托给InteractableObjectUngrabbed的方法
2、将一些参数置为null,并且加载物体的原始参数,如颜色等
3、同时,要停止手柄对物体的使用。
同样可以被子类重写,根据需要实现更多功能。
StartUsing/1
1 | public virtual void StartUsing() |
· Parameters
· GameObject - 正在使用当前物体的游戏对象(例如手柄).
· Returns
· none
StartUsing方法会在物体开始被使用时被调用,它会
1、发送事件将传入的currentGrabbingObject作为参数,回调委托给InteractableObjectUsed的方法
2、设置当前使用物体的usingObject为传入的游戏对象
同样可以被子类重写,根据需要实现更多功能。
StopUsing/1
1 | public virtual void StopUsing(GameObject) |
· Parameters
· GameObject - 先前使用此物体的游戏对象(例如手柄)
· Returns
· none
StopUsing方法会在物体停止被使用时被调用,它会
1、发送事件将传入的previousUsingObject作为参数,回调委托给InteractableObjectUnused的方法
2、设置当前使用物体的usingObject为null
同样可以被子类重写,根据需要实现更多功能。
ToggleHighlight/1
1 | public virtual void ToggleHighlight() |
· Parameters
· bool - 为true时启用高亮,为false时禁用高亮
· Returns
· none
ToggleHighlight/1方法是一个关闭高亮的快捷方法,它的方法签名和实际控制的高亮开关方法一样。它传入的参数必须永远是false(意义何在啊?),并且调用ToggleHighlight(toggle,Color.clear)实现关闭高亮。
ToggleHighlight/2
1 | public virtual void ToggleHighlight() |
· Parameters
· bool - 为true时启用高亮,为false时禁用高亮.
· Color - 高亮物体时使用的颜色.
· Returns
· none
ToggleHighlight/2方法用于开启/关闭物体的高亮。根据传入的颜色和手柄的设置来创建一个颜色字典,传入ChangeColor方法中来改变物体渲染的颜色,实现高亮的效果。
PauseCollisions/0
1 | public void PauseCollisions() |
· Parameters
· none
· Returns
· none
PauseCollisions方法在物体被抓取的时候通过移除物体上刚体组件的检测碰撞功能
rb.detectCollisions = false;
来暂停物体上的所有碰撞。这个方法可以在手柄第一次抓取物体的时候给一个反应时间,避免手柄还没抓稳物体,就把物体弹开。在一定时间后,再重新启用碰撞检测,
1 | Invoke( "UnpauseCollisions" , onGrabCollisionDelay); |
AttachIsTrackObject/0
1 | public bool AttachIsTrackObject() |
· Parameters
· none
· Returns
· bool - 当抓取方式为追踪连接时为真,例如Track Object或者Rotator Track.
AttachIsTrackObject方法用于确认物体是否使用了追踪连接的方式被抓取.
AttachIsClimbObject/0
1 | public bool AttachIsClimbObject() |
· Parameters
· none
· Returns
· bool - 抓取方式为Climbable时为真.
AttachIsClimbObject方法用于确认物体是否使用了Climbable的抓取连接方式.
AttachIsStaticObject/0
1 | public bool AttachIsStaticObject() |
· Parameters
· none
· Returns
· bool - 抓取方式为类似Climbable的静态连接方式时为真.
AttachIsStaticObject方法用于确认物体是否使用了静态的抓取连接方式。目前工具包里只有Climbable这一种静态连接方式。
ZeroVelocity/0
1 | public void ZeroVelocity() |
· Parameters
· none
· Returns
· none
ZeroVelocity方法重置物体的刚体组件的速度和角速度都为0.
SaveCurrentState/0
1 | public void SaveCurrentState() |
· Parameters
· none
· Returns
· none
SaveCurrentState方法将物体当前的父对象以及刚体运动学设置保存下来,放在previous打头的参数里面。
ToggleKinematic/1
1 | public void ToggleKinematic() |
· Parameters
· bool - 物体的刚体运动学状态.
· Returns
· none
ToggleKinematic方法用于设置物体的刚体运动学状态.
GetGrabbingObject/0
1 | public GameObject GetGrabbingObject() |
· Parameters
· none
· Returns
· GameObject - 正在抓取当前物体的游戏对象.
GetGrabbingObject方法用于找到正在抓取当前物体的游戏对象.
IsValidInteractableController/2
1 | public bool IsValidInteractableController() |
· Parameters
· GameObject - 接受检测的手柄游戏对象.
· AllowedController - 哪个手柄被允许与当前物体交互.
· Returns
· bool - 如果此手柄被允许与当前物体交互.
IsValidInteractableController方法由于检测一个手柄游戏对象是否被允许与当前物体交互,因为有的情况下(根据使用需求)手柄是禁止与物体交互的.
ForceStopInteracting/0
1 | public void ForceStopInteracting() |
· Parameters
· none
· Returns
· none
ForceStopInteracting方法强行停止物体的交互行为,手柄会放下物体并停止触碰它.当手柄要和另外的物体交互时这个方法很有用。
1 | StartCoroutine(ForceStopInteractingAtEndOfFrame()); |
SetGrabbedSnapHandle/1
1 | public void SetGrabbedSnapHandle() |
· Parameters
· Transform - 物体被抓取的时候,抓取的位置,例如一个杯子,可以设置把手为SnapHandle
· Returns
· none
SetGrabbedSnapHandle方法用于在运行的时候直接设置物体抓取的位置grabbedSnapHandle,无需考虑rightSnapHandle或者leftSnapHandle的设置。
RegisterTeleporters/0
1 | public void RegisterTeleporters() |
· Parameters
· none
· Returns
· none
RegisterTeleporters方法用于找到绑定了传送脚本的游戏对象,把OnTeleported委托给传送脚本里的Teleported事件。这个方法可以让物体随着传送移动。
私有/保护方法
LoadPreviousState/0
1 | protected virtual void LoadPreviousState() |
· Parameters
· none
· Returns
· none
LoadPreviousState方法会恢复物体被抓取之前的一些重要状态。特别是当物体的抓取方式是作为手柄的子对象时,这个方法非常重要,手柄释放物体后,物体的父对象必须要还原为初始的状态。
ForceReleaseGrab/0
1 | private void ForceReleaseGrab() |
· Parameters
· none
· Returns
· none
ForceReleaseGrab方法强制当前手柄停止对物体的抓取,
1 | grabbingObject.GetComponent().ForceRelease(); |
UnpauseCollisions/0
1 | private void UnpauseCollisions() |
· Parameters
· none
· Returns
· none
UnpauseCollisions方法恢复物体(包括子对象上的)刚体组件的碰撞检测
1 | rb.detectCollisions = true ; |
GetRendererArray/0
1 | private Renderer[] GetRendererArray() |
· Parameters
· none
· Returns
· Renderer[] - 渲染器数组,包含游戏对象内所有渲染器
GetRendererArray方法返回物体(包括子对象)上所有的Renderer,以数组形式返回
1 | return (GetComponents().Length > 0 ? GetComponents() : GetComponentsInChildren()); |
StoreOriginalColors/0
1 | private Dictionary< string , color[]= "" > StoreOriginalColors() string ,> |
· Parameters
· none
· Returns
· Dictionary - 反映渲染器与材质颜色映射的字典
StoreOriginalColors方法返回一个存有游戏对象材质颜色的数据字典。在一个游戏对象中,可能有不同的渲染器,而渲染器的下面有很多的材质,把这种映射以字典的形式转化为数据结构,以渲染器的名字为key,value为一个颜色数组,代表该渲染器下所有的材质颜色,数组长度为该渲染器材质的个数。
1 | colors[renderer.gameObject.name][i] = material.color; |
BuildHighlightColorArray/1
1 2 | private Dictionary< string , color[]= "" > BuildHighlightColorArray()
string ,> |
· Parameters
· Color - 高亮颜色
· Returns
· Dictionary - 反映渲染器与材质颜色映射的字典
BuildHighlightColorArray方法和StoreOriginalColors一样,都会返回一个以渲染器名字为key,材质颜色数组为value的数据字典,区别是StoreOriginalColors的材质颜色数组中,颜色是按照实际情况中物体的颜色来赋值的,而BuildHighlightColorArray方法中,材质的颜色全部为传入的参数Color color。
ChangeColor/1
1 | private void ChangeColor() |
· Parameters
· Dictionary - 修改物体的材质颜色映射为该目标映射
· Returns
· none
ChangeColor方法用来修改游戏对象的材质颜色,目标材质颜色映射由传入的参数指定,本类中通常指BuildHighlightColorArray方法返回的高亮颜色字典。
CheckBreakDistance/0
1 | private void CheckBreakDistance() |
· Parameters
· none
· Returns
· none
CheckBreakDistance方法会在Update方法中调用,每帧检查物体(追踪连接方式)与追踪点之间的距离,如果大于设定值就要强制断开抓取连接。
SetTrackPoint/1
1 | private void SetTrackPoint(GameObject point) |
· Parameters
· GameObject
· Returns
· none
SetTrackPoint方法用来设置追踪点,即为脚本中的参数trackPoint和originalControllerAttachPoint赋值,其中前者是手柄的子对象,后者是物体的子对象,物体和手柄通过这两个点来相互追踪。
RemoveTrackPoint/0
1 | private void RemoveTrackPoint() |
· Parameters
· none
· Returns
· none
RemoveTrackPoint方法用来移除手柄和物体上的追踪点,对于物体上的追踪点直接销毁即可。在移除手柄上的追踪点时,要注意
1、如果追踪点是新生成的游戏对象,那么销毁
Destroy(trackPoint.gameObject);
2、如果追踪点是引用的手柄中默认的子对象,那么不能销毁,只要切断引用就可以
1 | trackPoint = null ; |
FixedUpdateRotatorTrack/0
1 | private void FixedUpdateRotatorTrack() |
· Parameters
· none
· Returns
· none
FixedUpdateRotatorTrack方法用于在FixedUpdate中,固定更新旋转追踪方式连接手柄的物体,物体始终在其追踪点有一个指向手柄追踪点的力,这种方式连接的物体,效果和弹簧连接很像。
FixedUpdateTrackObject/0
1 | private void FixedUpdateTrackObject() |
· Parameters
· none
· Returns
· none
FixedUpdateTrackObject方法用于在FixedUpdate中,固定更新追踪连接方式连接手柄的物体,感觉和Rotator_Track没什么区别,但是好像贴的更紧一点。
OnTeleported/2
1 | private void OnTeleported() |
· Parameters
· object
· Returns
· DestinationMarkerEventArgs
OnTeleported方法会监听传送脚本上的Teleported事件,当传送完成时,将物体的位置更新为正抓取物体的手柄的位置,保证传送后,抓取连接不会断开。
StopUsingOnControllerChange/1
1 | private IEnumerator StopUsingOnControllerChange() |
· Parameters
· GameObject - 要停止使用的手柄对象
· Returns
· none
StopUsingOnControllerChange方法用于在协程中等待一帧后强制停止手柄对物体的使用
ForceStopInteractingAtEndOfFrame/0
1 | private IEnumerator ForceStopInteractingAtEndOfFrame() |
· Parameters
· none
· Returns
· none
ForceStopInteractingAtEndOfFrame方法用于在协程中等待一帧后停止所有手柄与物体的交互(触碰、抓取、使用),并设forcedDropped = true;
Monobehaviour 方法
Awake
获取当前物体对象上的刚体组件,如果不是静态类型对象,且物体上没有刚体组件时,为它添加一个并且设置
1 | rb.isKinematic = true ; |
设置forcedDropped默认值
1 | forcedDropped = false ; |
Start
把物体的所有材质颜色以字典映射的形式Dictionary存储下来
1 | originalObjectColours = StoreOriginalColors(); |
Update
主要做两个检测,
1、当物体不可用,强制停止一切交互。
2、当物体是以追踪方式连接手柄,每帧更新两者间的距离,如果距离大于阈值,则强制手柄释放物体。
FixedUpdate
如果物体是追踪连接的方式,且追踪连接点不为null,则根据具体的连接分类,调用各自的方法,更新物体的位置和旋转,如果是关节连接等,Unity自己就会处理,不用另外自己写了。
OnEnable
1、监听Teleported事件,将OnTeleported委托给它调用。
2、如果脚本曾经被禁用过,加载还原禁用前物体的状态。
OnDisable
1、停止事件监听。
2、强制停止物体一切交互,注意脚本被禁用后forcedDropped会被设为true。
OnJointBreak
物体上的关节断开时,强制手柄释放物体。
1 | ForceReleaseGrab(); |
Example
SteamVR_Unity_Toolkit/Examples/005_Controller_BasicObjectGrabbing 将 VRTK_InteractTouch 和VRTK_InteractGrab脚本绑定在手柄对象上,演示了如何触碰、抓取、扔掷可交互的物体。
腾讯GAD游戏程序交流群:484290331