player.cs
Interable.cs
加上可以进行交互。创建高亮。
InterableHoverEvents.cs
交互后的Event
Throw.cs
加上可以被捡起。
下边文章都是代码,自行复制。后几篇,更新 传送Teleport.cs TelelportPoint等脚本。
疑问: 不知道Hand使用了什么方法对物体检测,应该也是碰撞检测,做了个测试 ,禁用BoxColider后 ,就拿不起来。
但是Hand.cs中没有找到OnTrrigerEnter碰撞函数。也许放到其他脚本中。
这里我对脚本进行了注释,后期完善
如果多的话 ,可以复制脚本去查看。
还有一个问题,启动查看实例 后,会发现 Player 为不销毁的,
最后找了半天,这个逻辑DontDestroyOnLoad 写在了这个脚本中。勾选即可
这里是检测,给可以建起来的物品添加脚本,拾取 就可以看到输出。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Valve.VR.InteractionSystem;
public class VREvent : MonoBehaviour
{
///
/// 当手悬浮开始
///
///
protected virtual void OnHandHoverBegin(Hand hand)
{
TimorDebug.LogBold("当手悬浮开始");
}
///
/// 当手悬浮更新
///
///
protected virtual void HandHoverUpdate(Hand hand)
{
TimorDebug.LogBold("当手悬浮更新");
}
///
/// 当手悬浮结束
///
///
protected virtual void OnHandHoverEnd(Hand hand)
{
TimorDebug.LogBold("当手悬浮结束");
}
///
/// 当被添加到手
///
///
protected virtual void OnAttachedToHand(Hand hand)
{
TimorDebug.LogBold("当被添加到手");
}
///
/// 当被添加到手每帧
///
///
protected virtual void HandAttachedUpdate(Hand hand)
{
TimorDebug.LogBold("当被添加到手每帧");
}
///
/// 当从手中分离
///
///
protected virtual void OnDetachedFromHand(Hand hand)
{
TimorDebug.LogBold("当从手中分离");
}
///
/// 当手焦点获取
///
///
protected virtual void OnHandFocusAcquired(Hand hand)
{
TimorDebug.LogBold("当手焦点获取");
}
///
/// 当手焦点丢失
///
///
protected virtual void OnHandFocusLost(Hand hand)
{
TimorDebug.LogBold("当手焦点丢失");
}
}
场景预览
这个有拾取 传送区域 传送点 射箭 操纵小车 人物 交互UI等
名字:Interactions_Example
路径:
用途:持有2个手,以及头显
//======= Copyright (c) Valve Corporation, All rights reserved. ===============
//
// Purpose: Player interface used to query HMD transforms and VR hands
// 目的:用于查询头显位置和vr手
//=============================================================================
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace Valve.VR.InteractionSystem
{
//-------------------------------------------------------------------------
// Singleton representing the local VR player/user, with methods for getting
// the player's hands, head, tracking origin, and guesses for various properties.
//表示本地虚拟现实播放器/用户,其方法是
//玩家的手、头、追踪原点,以及对各种属性的猜测
//-------------------------------------------------------------------------
///
/// 玩家
///
public class Player : MonoBehaviour
{
[Tooltip( "Virtual transform corresponding to the meatspace tracking origin. Devices are tracked relative to this." )]
public Transform trackingOriginTransform;
[Tooltip( "List of possible transforms for the head/HMD, including the no-SteamVR fallback camera." )]
public Transform[] hmdTransforms;
[Tooltip( "List of possible Hands, including no-SteamVR fallback Hands." )]
public Hand[] hands;
[Tooltip( "Reference to the physics collider that follows the player's HMD position." )]
public Collider headCollider;
[Tooltip( "These objects are enabled when SteamVR is available" )]
public GameObject rigSteamVR;
[Tooltip( "These objects are enabled when SteamVR is not available, or when the user toggles out of VR" )]
public GameObject rig2DFallback;
[Tooltip( "The audio listener for this player" )]
public Transform audioListener;
[Tooltip("This action lets you know when the player has placed the headset on their head")]
public SteamVR_Action_Boolean headsetOnHead = SteamVR_Input.GetBooleanAction("HeadsetOnHead");
public bool allowToggleTo2D = true;
#region 玩家单例
//-------------------------------------------------
// Singleton instance of the Player. Only one can exist at a time.
//-------------------------------------------------
private static Player _instance;
public static Player instance
{
get
{
if ( _instance == null )
{
_instance = FindObjectOfType();
}
return _instance;
}
}
#endregion
//-------------------------------------------------
// Get the number of active Hands.
//-------------------------------------------------
///
/// 获取活动手的数量
///
public int handCount
{
get
{
int count = 0;
for ( int i = 0; i < hands.Length; i++ )
{
if ( hands[i].gameObject.activeInHierarchy )
{
count++;
}
}
return count;
}
}
//-------------------------------------------------
// Get the i-th active Hand.
//
// i - Zero-based index of the active Hand to get
//-------------------------------------------------
///
/// 获取活动的手
///
///
///
public Hand GetHand( int i )
{
for ( int j = 0; j < hands.Length; j++ )
{
if ( !hands[j].gameObject.activeInHierarchy )
{
continue;
}
if ( i > 0 )
{
i--;
continue;
}
return hands[j];
}
return null;
}
public Hand leftHand
{
get
{
for ( int j = 0; j < hands.Length; j++ )
{
if ( !hands[j].gameObject.activeInHierarchy )
{
continue;
}
if ( hands[j].handType != SteamVR_Input_Sources.LeftHand)
{
continue;
}
return hands[j];
}
return null;
}
}
//-------------------------------------------------
public Hand rightHand
{
get
{
for ( int j = 0; j < hands.Length; j++ )
{
if ( !hands[j].gameObject.activeInHierarchy )
{
continue;
}
if ( hands[j].handType != SteamVR_Input_Sources.RightHand)
{
continue;
}
return hands[j];
}
return null;
}
}
//-------------------------------------------------
// Get Player scale. Assumes it is scaled equally on all axes.
//-------------------------------------------------
///
/// 获取玩家缩放。假设它在所有轴上的比例相等。
///
public float scale
{
get
{
return transform.lossyScale.x;
}
}
//-------------------------------------------------
// Get the HMD transform. This might return the fallback camera transform if SteamVR is unavailable or disabled.
//-------------------------------------------------
public Transform hmdTransform
{
get
{
if (hmdTransforms != null)
{
for (int i = 0; i < hmdTransforms.Length; i++)
{
if (hmdTransforms[i].gameObject.activeInHierarchy)
return hmdTransforms[i];
}
}
return null;
}
}
//-------------------------------------------------
// Height of the eyes above the ground - useful for estimating player height.
//-------------------------------------------------
///
/// 眼睛离地面的高度-用于估计玩家的高度。
///
public float eyeHeight
{
get
{
Transform hmd = hmdTransform;
if ( hmd )
{
Vector3 eyeOffset = Vector3.Project( hmd.position - trackingOriginTransform.position, trackingOriginTransform.up );
return eyeOffset.magnitude / trackingOriginTransform.lossyScale.x;
}
return 0.0f;
}
}
//-------------------------------------------------
// Guess for the world-space position of the player's feet, directly beneath the HMD.
//-------------------------------------------------
///
/// 猜测脚的的位置
///
public Vector3 feetPositionGuess
{
get
{
Transform hmd = hmdTransform;
if ( hmd )
{
return trackingOriginTransform.position + Vector3.ProjectOnPlane( hmd.position - trackingOriginTransform.position, trackingOriginTransform.up );
}
return trackingOriginTransform.position;
}
}
//-------------------------------------------------
// Guess for the world-space direction of the player's hips/torso. This is effectively just the gaze direction projected onto the floor plane.
//-------------------------------------------------
public Vector3 bodyDirectionGuess
{
get
{
Transform hmd = hmdTransform;
if ( hmd )
{
Vector3 direction = Vector3.ProjectOnPlane( hmd.forward, trackingOriginTransform.up );
if ( Vector3.Dot( hmd.up, trackingOriginTransform.up ) < 0.0f )
{
// The HMD is upside-down. Either
// -The player is bending over backwards
// -The player is bent over looking through their legs
direction = -direction;
}
return direction;
}
return trackingOriginTransform.forward;
}
}
//-------------------------------------------------
private void Awake()
{
if ( trackingOriginTransform == null )
{
trackingOriginTransform = this.transform;
}
}
//-------------------------------------------------
private IEnumerator Start()
{
_instance = this;
while (SteamVR.initializedState == SteamVR.InitializedStates.None || SteamVR.initializedState == SteamVR.InitializedStates.Initializing)
yield return null;
if ( SteamVR.instance != null )
{
ActivateRig( rigSteamVR );
}
else
{
#if !HIDE_DEBUG_UI
ActivateRig( rig2DFallback );
#endif
}
}
protected virtual void Update()
{
if (SteamVR.initializedState != SteamVR.InitializedStates.InitializeSuccess)
return;
if (headsetOnHead != null)
{
if (headsetOnHead.GetStateDown(SteamVR_Input_Sources.Head))
{
Debug.Log("SteamVR Interaction System Headset placed on head");
}
else if (headsetOnHead.GetStateUp(SteamVR_Input_Sources.Head))
{
Debug.Log("SteamVR Interaction System Headset removed");
}
}
}
//-------------------------------------------------
void OnDrawGizmos()
{
if ( this != instance )
{
return;
}
//NOTE: These gizmo icons don't work in the plugin since the icons need to exist in a specific "Gizmos"
// folder in your Asset tree. These icons are included under Core/Icons. Moving them into a
// "Gizmos" folder should make them work again.
Gizmos.color = Color.white;
Gizmos.DrawIcon( feetPositionGuess, "vr_interaction_system_feet.png" );
Gizmos.color = Color.cyan;
Gizmos.DrawLine( feetPositionGuess, feetPositionGuess + trackingOriginTransform.up * eyeHeight );
// Body direction arrow
Gizmos.color = Color.blue;
Vector3 bodyDirection = bodyDirectionGuess;
Vector3 bodyDirectionTangent = Vector3.Cross( trackingOriginTransform.up, bodyDirection );
Vector3 startForward = feetPositionGuess + trackingOriginTransform.up * eyeHeight * 0.75f;
Vector3 endForward = startForward + bodyDirection * 0.33f;
Gizmos.DrawLine( startForward, endForward );
Gizmos.DrawLine( endForward, endForward - 0.033f * ( bodyDirection + bodyDirectionTangent ) );
Gizmos.DrawLine( endForward, endForward - 0.033f * ( bodyDirection - bodyDirectionTangent ) );
Gizmos.color = Color.red;
int count = handCount;
for ( int i = 0; i < count; i++ )
{
Hand hand = GetHand( i );
if ( hand.handType == SteamVR_Input_Sources.LeftHand)
{
Gizmos.DrawIcon( hand.transform.position, "vr_interaction_system_left_hand.png" );
}
else if ( hand.handType == SteamVR_Input_Sources.RightHand)
{
Gizmos.DrawIcon( hand.transform.position, "vr_interaction_system_right_hand.png" );
}
else
{
/*
Hand.HandType guessHandType = hand.currentHandType;
if ( guessHandType == Hand.HandType.Left )
{
Gizmos.DrawIcon( hand.transform.position, "vr_interaction_system_left_hand_question.png" );
}
else if ( guessHandType == Hand.HandType.Right )
{
Gizmos.DrawIcon( hand.transform.position, "vr_interaction_system_right_hand_question.png" );
}
else
{
Gizmos.DrawIcon( hand.transform.position, "vr_interaction_system_unknown_hand.png" );
}
*/
}
}
}
//-------------------------------------------------
///
/// 绘制2D调试
///
public void Draw2DDebug()
{
if ( !allowToggleTo2D )
return;
if ( !SteamVR.active )
return;
int width = 100;
int height = 25;
int left = Screen.width / 2 - width / 2;
int top = Screen.height - height - 10;
string text = ( rigSteamVR.activeSelf ) ? "2D Debug" : "VR";
if ( GUI.Button( new Rect( left, top, width, height ), text ) )
{
if ( rigSteamVR.activeSelf )
{
ActivateRig( rig2DFallback );
}
else
{
ActivateRig( rigSteamVR );
}
}
}
//-------------------------------------------------
private void ActivateRig( GameObject rig )
{
rigSteamVR.SetActive( rig == rigSteamVR );
rig2DFallback.SetActive( rig == rig2DFallback );
if ( audioListener )
{
audioListener.transform.parent = hmdTransform;
audioListener.transform.localPosition = Vector3.zero;
audioListener.transform.localRotation = Quaternion.identity;
}
}
//-------------------------------------------------
public void PlayerShotSelf()
{
//Do something appropriate here
}
}
}
vr手柄按键检测 ,拾取物品,需要 Throw.cs Interable.cs等脚本
在处理,拾取物品时遇到一些问题,
Steamvr案例给的是,扳机、侧键、鼠标左键。按下拾取物品,松开丢弃。
然后我更改为,扳机点击、鼠标点击拾取物品,空格、侧键丢弃物品。
,自行查看逻辑。 中文都注释好了。
//======= Copyright (c) Valve Corporation, All rights reserved. ===============
//
// Purpose: The hands used by the player in the vr interaction system
//
//=============================================================================
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using UnityEngine.Events;
using System.Threading;
namespace Valve.VR.InteractionSystem
{
//-------------------------------------------------------------------------
// Links with an appropriate SteamVR controller and facilitates
// interactions with objects in the virtual world.
//-------------------------------------------------------------------------
public class Hand : MonoBehaviour
{
public KeyCode DetachedKey = KeyCode.Space;
// The flags used to determine how an object is attached to the hand.
///
/// 用于确定对象如何附加到手的标志。
///
[Flags]
public enum AttachmentFlags
{
///
/// 该对象应卡到手上指定附着点的位置。
///
SnapOnAttach = 1 << 0, // The object should snap to the position of the specified attachment point on the hand.
///
/// 其他附着在这只手上的物体将被分离
///
DetachOthers = 1 << 1, // Other objects attached to this hand will be detached.
///
/// 这个物体将与另一只手分开
///
DetachFromOtherHand = 1 << 2, // This object will be detached from the other hand.
///
/// 对象将由手作为父对象
///
ParentToHand = 1 << 3, // The object will be parented to the hand.
///
/// 对象将尝试移动以匹配手的位置和旋转
///
VelocityMovement = 1 << 4, // The object will attempt to move to match the position and rotation of the hand.
///
/// 物体不会对外部物理产生反应
///
TurnOnKinematic = 1 << 5, // The object will not respond to external physics.
TurnOffGravity = 1 << 6, // The object will not respond to external physics.
///
/// 该物体能够从夹持器切换到夹持器。降低好投掷的可能性,同时也降低意外下降的可能性
///
AllowSidegrade = 1 << 7, // The object is able to switch from a pinch grab to a grip grab. Decreases likelyhood of a good throw but also decreases likelyhood of accidental drop
};
///
/// 默认附加标识
///
public const AttachmentFlags defaultAttachmentFlags = AttachmentFlags.ParentToHand |
AttachmentFlags.DetachOthers |
AttachmentFlags.DetachFromOtherHand |
AttachmentFlags.TurnOnKinematic |
AttachmentFlags.SnapOnAttach;
///
/// 另一只手
///
public Hand otherHand;
public SteamVR_Input_Sources handType;
///
/// 追踪对象
///
public SteamVR_Behaviour_Pose trackedObject;
///
/// 扳机
///
public SteamVR_Action_Boolean grabPinchAction = SteamVR_Input.GetAction("GrabPinch");
///
/// 侧键
///
public SteamVR_Action_Boolean grabGripAction = SteamVR_Input.GetAction("GrabGrip");
public SteamVR_Action_Vibration hapticAction = SteamVR_Input.GetAction("Haptic");
public SteamVR_Action_Boolean uiInteractAction = SteamVR_Input.GetAction("InteractUI");
///
/// 是否使用悬浮球体
///
public bool useHoverSphere = true;
///
/// 悬浮球体变换
///
public Transform hoverSphereTransform;
///
/// 悬浮球体半径
///
public float hoverSphereRadius = 0.05f;
public LayerMask hoverLayerMask = -1;
///
/// /悬浮更新间隔
///
public float hoverUpdateInterval = 0.1f;
///
/// 是否使用控制器悬停组件
///
public bool useControllerHoverComponent = true;
///
/// 控制器悬停组件
///
public string controllerHoverComponent = "tip";
///
/// 控制器悬停组件半径
///
public float controllerHoverRadius = 0.075f;
///
/// 是否使用手指关节悬停
///
public bool useFingerJointHover = true;
public SteamVR_Skeleton_JointIndexEnum fingerJointHover = SteamVR_Skeleton_JointIndexEnum.indexTip;
///
/// 手指关节悬停半径
///
public float fingerJointHoverRadius = 0.025f;
///
/// 对象附加点
///
[Tooltip("A transform on the hand to center attached objects on")]
public Transform objectAttachmentPoint;
///
/// 无SteamVR备用相机
///
public Camera noSteamVRFallbackCamera;
///
/// 无SteamVR备用最大距离无项目
///
public float noSteamVRFallbackMaxDistanceNoItem = 10.0f;
///
/// 无SteamVR备用最大距离有项目
///
public float noSteamVRFallbackMaxDistanceWithItem = 0.5f;
///
/// 无SteamVR备用交互距离
///
private float noSteamVRFallbackInteractorDistance = -1.0f;
///
/// 渲染预制件
///
public GameObject renderModelPrefab;
protected List renderModels = new List();
///
/// 主要渲染模型
///
protected RenderModel mainRenderModel;
///
/// 高亮渲染模型
///
protected RenderModel hoverhighlightRenderModel;
///
/// 是否显示调试文本
///
public bool showDebugText = false;
public bool spewDebugText = false;
///
/// 是否调试交互
///
public bool showDebugInteractables = false;
///
/// 附加对象
///
public struct AttachedObject
{
///
/// 附加对象
///
public GameObject attachedObject;
///
/// 交互
///
public Interactable interactable;
///
/// 附加刚体
///
public Rigidbody attachedRigidbody;
///
/// 碰撞检测模式
///
public CollisionDetectionMode collisionDetectionMode;
///
/// 是否附近刚提为运动学
///
public bool attachedRigidbodyWasKinematic;
///
/// 是否附近刚体使用重力
///
public bool attachedRigidbodyUsedGravity;
///
/// 原来父物体
///
public GameObject originalParent;
///
/// 是否父物体到手
///
public bool isParentedToHand;
///
/// 抓取类型
///
public GrabTypes grabbedWithType;
///
/// 附加标志
///
public AttachmentFlags attachmentFlags;
///
/// 初始化位置偏移
///
public Vector3 initialPositionalOffset;
///
/// 初始化旋转便宜
///
public Quaternion initialRotationalOffset;
///
/// 附加偏移位置
///
public Transform attachedOffsetTransform;
public Transform handAttachmentPointTransform;
public Vector3 easeSourcePosition;
public Quaternion easeSourceRotation;
///
/// 附加时间
///
public float attachTime;
///
/// 是否有附加标志
///
///
///
public bool HasAttachFlag(AttachmentFlags flag)
{
return (attachmentFlags & flag) == flag;
}
}
///
/// 附加对象List
///
private List attachedObjects = new List();
public ReadOnlyCollection AttachedObjects
{
get { return attachedObjects.AsReadOnly(); }
}
///
/// 是否悬浮锁定
///
public bool hoverLocked { get; private set; }
///
/// 悬浮中交互
///
private Interactable _hoveringInteractable;
///
/// 调试文本
///
private TextMesh debugText;
private int prevOverlappingColliders = 0;
///
/// 碰撞器数组大小
///
private const int ColliderArraySize = 16;
///
/// 重叠碰撞器
///
private Collider[] overlappingColliders;
///
/// 角色单例
///
private Player playerInstance;
///
/// 应用程序丢失焦点对象
///
private GameObject applicationLostFocusObject;
///
/// 输入焦点对象
///
private SteamVR_Events.Action inputFocusAction;
///
/// 是否活动
///
public bool isActive
{
get
{
if (trackedObject != null)
return trackedObject.isActive;
return this.gameObject.activeInHierarchy;
}
}
///
/// 是否Pose有效
///
public bool isPoseValid
{
get
{
return trackedObject.isValid;
}
}
//-------------------------------------------------
// The Interactable object this Hand is currently hovering over
//-------------------------------------------------
///
/// 悬浮中交互
///
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
}
}
}
}
}
//-------------------------------------------------
// Active GameObject attached to this Hand
// 与此手相连的活动游戏对象
//-------------------------------------------------
///
/// 当前附加对象
///
public GameObject currentAttachedObject
{
get
{
CleanUpAttachedObjectStack();
if (attachedObjects.Count > 0)
{
return attachedObjects[attachedObjects.Count - 1].attachedObject;
}
return null;
}
}
///
/// 当前附加对象信息
///
public AttachedObject? currentAttachedObjectInfo
{
get
{
CleanUpAttachedObjectStack();
if (attachedObjects.Count > 0)
{
return attachedObjects[attachedObjects.Count - 1];
}
return null;
}
}
///
/// 骨骼
///
public SteamVR_Behaviour_Skeleton skeleton
{
get
{
if (mainRenderModel != null)
return mainRenderModel.GetSkeleton();
return null;
}
}
///
/// 显示控制器
///
///
public void ShowController(bool permanent = false)
{
if (mainRenderModel != null)
mainRenderModel.SetControllerVisibility(true, permanent);
if (hoverhighlightRenderModel != null)
hoverhighlightRenderModel.SetControllerVisibility(true, permanent);
}
///
/// 隐藏控制器
///
///
public void HideController(bool permanent = false)
{
if (mainRenderModel != null)
mainRenderModel.SetControllerVisibility(false, permanent);
if (hoverhighlightRenderModel != null)
hoverhighlightRenderModel.SetControllerVisibility(false, permanent);
}
///
/// 显示骨骼
///
///
public void ShowSkeleton(bool permanent = false)
{
if (mainRenderModel != null)
mainRenderModel.SetHandVisibility(true, permanent);
if (hoverhighlightRenderModel != null)
hoverhighlightRenderModel.SetHandVisibility(true, permanent);
}
///
/// 隐藏骨骼
///
///
public void HideSkeleton(bool permanent = false)
{
if (mainRenderModel != null)
mainRenderModel.SetHandVisibility(false, permanent);
if (hoverhighlightRenderModel != null)
hoverhighlightRenderModel.SetHandVisibility(false, permanent);
}
///
/// 是否有骨骼
///
///
public bool HasSkeleton()
{
return mainRenderModel != null && mainRenderModel.GetSkeleton() != null;
}
public void Show()
{
SetVisibility(true);
}
public void Hide()
{
SetVisibility(false);
}
///
/// 设置可见性
///
///
public void SetVisibility(bool visible)
{
if (mainRenderModel != null)
mainRenderModel.SetVisibility(visible);
}
///
/// 设置骨架运动范围
///
/// 新的运动范围
/// 几秒钟后混合
public void SetSkeletonRangeOfMotion(EVRSkeletalMotionRange newRangeOfMotion, float blendOverSeconds = 0.1f)
{
for (int renderModelIndex = 0; renderModelIndex < renderModels.Count; renderModelIndex++)
{
renderModels[renderModelIndex].SetSkeletonRangeOfMotion(newRangeOfMotion, blendOverSeconds);
}
}
///
/// 设置临时骨架运动范围
///
///
/// 几秒钟后混合
public void SetTemporarySkeletonRangeOfMotion(SkeletalMotionRangeChange temporaryRangeOfMotionChange, float blendOverSeconds = 0.1f)
{
for (int renderModelIndex = 0; renderModelIndex < renderModels.Count; renderModelIndex++)
{
renderModels[renderModelIndex].SetTemporarySkeletonRangeOfMotion(temporaryRangeOfMotionChange, blendOverSeconds);
}
}
///
/// 重置骨架运动范围
///
/// 几秒钟后混合
public void ResetTemporarySkeletonRangeOfMotion(float blendOverSeconds = 0.1f)
{
for (int renderModelIndex = 0; renderModelIndex < renderModels.Count; renderModelIndex++)
{
renderModels[renderModelIndex].ResetTemporarySkeletonRangeOfMotion(blendOverSeconds);
}
}
///
/// 设置动画状态
///
/// 状态值
public void SetAnimationState(int stateValue)
{
for (int renderModelIndex = 0; renderModelIndex < renderModels.Count; renderModelIndex++)
{
renderModels[renderModelIndex].SetAnimationState(stateValue);
}
}
///
/// 停止动画
///
public void StopAnimation()
{
for (int renderModelIndex = 0; renderModelIndex < renderModels.Count; renderModelIndex++)
{
renderModels[renderModelIndex].StopAnimation();
}
}
//-------------------------------------------------
// Attach a GameObject to this GameObject
//
// objectToAttach - The GameObject to attach
// flags - The flags to use for attaching the object
// attachmentPoint - Name of the GameObject in the hierarchy of this Hand which should act as the attachment point for this GameObject
//-------------------------------------------------
///
/// 附加对象
///
/// 要附加的对象
/// 抓取类型
/// 附加标志
/// 附加偏移
public void AttachObject(GameObject objectToAttach, GrabTypes grabbedWithType, AttachmentFlags flags = defaultAttachmentFlags, Transform attachmentOffset = null)
{
AttachedObject attachedObject = new AttachedObject();
attachedObject.attachmentFlags = flags;
attachedObject.attachedOffsetTransform = attachmentOffset;
attachedObject.attachTime = Time.time;
if (flags == 0)
{
flags = defaultAttachmentFlags;
}
//Make sure top object on stack is non-null
CleanUpAttachedObjectStack();
//Detach the object if it is already attached so that it can get re-attached at the top of the stack
if(ObjectIsAttached(objectToAttach))
DetachObject(objectToAttach);
//Detach from the other hand if requested
if (attachedObject.HasAttachFlag(AttachmentFlags.DetachFromOtherHand))
{
if (otherHand != null)
otherHand.DetachObject(objectToAttach);
}
if (attachedObject.HasAttachFlag(AttachmentFlags.DetachOthers))
{
//Detach all the objects from the stack
while (attachedObjects.Count > 0)
{
DetachObject(attachedObjects[0].attachedObject);
}
}
if (currentAttachedObject)
{
currentAttachedObject.SendMessage("OnHandFocusLost", this, SendMessageOptions.DontRequireReceiver);
}
attachedObject.attachedObject = objectToAttach;
attachedObject.interactable = objectToAttach.GetComponent();
attachedObject.handAttachmentPointTransform = this.transform;
if (attachedObject.interactable != null)
{
if (attachedObject.interactable.attachEaseIn)
{
attachedObject.easeSourcePosition = attachedObject.attachedObject.transform.position;
attachedObject.easeSourceRotation = attachedObject.attachedObject.transform.rotation;
attachedObject.interactable.snapAttachEaseInCompleted = false;
}
if (attachedObject.interactable.useHandObjectAttachmentPoint)
attachedObject.handAttachmentPointTransform = objectAttachmentPoint;
if (attachedObject.interactable.hideHandOnAttach)
Hide();
if (attachedObject.interactable.hideSkeletonOnAttach && mainRenderModel != null && mainRenderModel.displayHandByDefault)
HideSkeleton();
if (attachedObject.interactable.hideControllerOnAttach && mainRenderModel != null && mainRenderModel.displayControllerByDefault)
HideController();
if (attachedObject.interactable.handAnimationOnPickup != 0)
SetAnimationState(attachedObject.interactable.handAnimationOnPickup);
if (attachedObject.interactable.setRangeOfMotionOnPickup != SkeletalMotionRangeChange.None)
SetTemporarySkeletonRangeOfMotion(attachedObject.interactable.setRangeOfMotionOnPickup);
}
attachedObject.originalParent = objectToAttach.transform.parent != null ? objectToAttach.transform.parent.gameObject : null;
attachedObject.attachedRigidbody = objectToAttach.GetComponent();
if (attachedObject.attachedRigidbody != null)
{
if (attachedObject.interactable.attachedToHand != null) //already attached to another hand
{
//if it was attached to another hand, get the flags from that hand
for (int attachedIndex = 0; attachedIndex < attachedObject.interactable.attachedToHand.attachedObjects.Count; attachedIndex++)
{
AttachedObject attachedObjectInList = attachedObject.interactable.attachedToHand.attachedObjects[attachedIndex];
if (attachedObjectInList.interactable == attachedObject.interactable)
{
attachedObject.attachedRigidbodyWasKinematic = attachedObjectInList.attachedRigidbodyWasKinematic;
attachedObject.attachedRigidbodyUsedGravity = attachedObjectInList.attachedRigidbodyUsedGravity;
attachedObject.originalParent = attachedObjectInList.originalParent;
}
}
}
else
{
attachedObject.attachedRigidbodyWasKinematic = attachedObject.attachedRigidbody.isKinematic;
attachedObject.attachedRigidbodyUsedGravity = attachedObject.attachedRigidbody.useGravity;
}
}
attachedObject.grabbedWithType = grabbedWithType;
if (attachedObject.HasAttachFlag(AttachmentFlags.ParentToHand))
{
//Parent the object to the hand
objectToAttach.transform.parent = this.transform;
attachedObject.isParentedToHand = true;
}
else
{
attachedObject.isParentedToHand = false;
}
if (attachedObject.HasAttachFlag(AttachmentFlags.SnapOnAttach))
{
if (attachedObject.interactable != null && attachedObject.interactable.skeletonPoser != null && HasSkeleton())
{
SteamVR_Skeleton_PoseSnapshot pose = attachedObject.interactable.skeletonPoser.GetBlendedPose(skeleton);
//snap the object to the center of the attach point
objectToAttach.transform.position = this.transform.TransformPoint(pose.position);
objectToAttach.transform.rotation = this.transform.rotation * pose.rotation;
attachedObject.initialPositionalOffset = attachedObject.handAttachmentPointTransform.InverseTransformPoint(objectToAttach.transform.position);
attachedObject.initialRotationalOffset = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * objectToAttach.transform.rotation;
}
else
{
if (attachmentOffset != null)
{
//offset the object from the hand by the positional and rotational difference between the offset transform and the attached object
Quaternion rotDiff = Quaternion.Inverse(attachmentOffset.transform.rotation) * objectToAttach.transform.rotation;
objectToAttach.transform.rotation = attachedObject.handAttachmentPointTransform.rotation * rotDiff;
Vector3 posDiff = objectToAttach.transform.position - attachmentOffset.transform.position;
objectToAttach.transform.position = attachedObject.handAttachmentPointTransform.position + posDiff;
}
else
{
//snap the object to the center of the attach point
objectToAttach.transform.rotation = attachedObject.handAttachmentPointTransform.rotation;
objectToAttach.transform.position = attachedObject.handAttachmentPointTransform.position;
}
Transform followPoint = objectToAttach.transform;
attachedObject.initialPositionalOffset = attachedObject.handAttachmentPointTransform.InverseTransformPoint(followPoint.position);
attachedObject.initialRotationalOffset = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * followPoint.rotation;
}
}
else
{
if (attachedObject.interactable != null && attachedObject.interactable.skeletonPoser != null && HasSkeleton())
{
attachedObject.initialPositionalOffset = attachedObject.handAttachmentPointTransform.InverseTransformPoint(objectToAttach.transform.position);
attachedObject.initialRotationalOffset = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * objectToAttach.transform.rotation;
}
else
{
if (attachmentOffset != null)
{
//get the initial positional and rotational offsets between the hand and the offset transform
Quaternion rotDiff = Quaternion.Inverse(attachmentOffset.transform.rotation) * objectToAttach.transform.rotation;
Quaternion targetRotation = attachedObject.handAttachmentPointTransform.rotation * rotDiff;
Quaternion rotationPositionBy = targetRotation * Quaternion.Inverse(objectToAttach.transform.rotation);
Vector3 posDiff = (rotationPositionBy * objectToAttach.transform.position) - (rotationPositionBy * attachmentOffset.transform.position);
attachedObject.initialPositionalOffset = attachedObject.handAttachmentPointTransform.InverseTransformPoint(attachedObject.handAttachmentPointTransform.position + posDiff);
attachedObject.initialRotationalOffset = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * (attachedObject.handAttachmentPointTransform.rotation * rotDiff);
}
else
{
attachedObject.initialPositionalOffset = attachedObject.handAttachmentPointTransform.InverseTransformPoint(objectToAttach.transform.position);
attachedObject.initialRotationalOffset = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * objectToAttach.transform.rotation;
}
}
}
if (attachedObject.HasAttachFlag(AttachmentFlags.TurnOnKinematic))
{
if (attachedObject.attachedRigidbody != null)
{
attachedObject.collisionDetectionMode = attachedObject.attachedRigidbody.collisionDetectionMode;
if (attachedObject.collisionDetectionMode == CollisionDetectionMode.Continuous)
attachedObject.attachedRigidbody.collisionDetectionMode = CollisionDetectionMode.Discrete;
attachedObject.attachedRigidbody.isKinematic = true;
}
}
if (attachedObject.HasAttachFlag(AttachmentFlags.TurnOffGravity))
{
if (attachedObject.attachedRigidbody != null)
{
attachedObject.attachedRigidbody.useGravity = false;
}
}
if (attachedObject.interactable != null && attachedObject.interactable.attachEaseIn)
{
attachedObject.attachedObject.transform.position = attachedObject.easeSourcePosition;
attachedObject.attachedObject.transform.rotation = attachedObject.easeSourceRotation;
}
attachedObjects.Add(attachedObject);
UpdateHovering();
if (spewDebugText)
HandDebugLog("AttachObject " + objectToAttach);
objectToAttach.SendMessage("OnAttachedToHand", this, SendMessageOptions.DontRequireReceiver);
}
///
/// 是否对象被附加
///
/// 要检测的对象
///
public bool ObjectIsAttached(GameObject go)
{
for (int attachedIndex = 0; attachedIndex < attachedObjects.Count; attachedIndex++)
{
if (attachedObjects[attachedIndex].attachedObject == go)
return true;
}
return false;
}
///
/// 强制悬停解锁
///
public void ForceHoverUnlock()
{
hoverLocked = false;
}
//-------------------------------------------------
// Detach this GameObject from the attached object stack of this Hand
//
// objectToDetach - The GameObject to detach from this Hand
//-------------------------------------------------
///
/// 分离对象
///
/// 要分离的对象
/// 是否还原原始父级,默认还原
public void DetachObject(GameObject objectToDetach, bool restoreOriginalParent = true)
{
int index = attachedObjects.FindIndex(l => l.attachedObject == objectToDetach);
if (index != -1)
{
if (spewDebugText)
HandDebugLog("DetachObject " + objectToDetach);
GameObject prevTopObject = currentAttachedObject;
if (attachedObjects[index].interactable != null)
{
if (attachedObjects[index].interactable.hideHandOnAttach)
Show();
if (attachedObjects[index].interactable.hideSkeletonOnAttach && mainRenderModel != null && mainRenderModel.displayHandByDefault)
ShowSkeleton();
if (attachedObjects[index].interactable.hideControllerOnAttach && mainRenderModel != null && mainRenderModel.displayControllerByDefault)
ShowController();
if (attachedObjects[index].interactable.handAnimationOnPickup != 0)
StopAnimation();
if (attachedObjects[index].interactable.setRangeOfMotionOnPickup != SkeletalMotionRangeChange.None)
ResetTemporarySkeletonRangeOfMotion();
}
Transform parentTransform = null;
if (attachedObjects[index].isParentedToHand)
{
if (restoreOriginalParent && (attachedObjects[index].originalParent != null))
{
parentTransform = attachedObjects[index].originalParent.transform;
}
if (attachedObjects[index].attachedObject != null)
{
attachedObjects[index].attachedObject.transform.parent = parentTransform;
}
}
if (attachedObjects[index].HasAttachFlag(AttachmentFlags.TurnOnKinematic))
{
if (attachedObjects[index].attachedRigidbody != null)
{
attachedObjects[index].attachedRigidbody.isKinematic = attachedObjects[index].attachedRigidbodyWasKinematic;
attachedObjects[index].attachedRigidbody.collisionDetectionMode = attachedObjects[index].collisionDetectionMode;
}
}
if (attachedObjects[index].HasAttachFlag(AttachmentFlags.TurnOffGravity))
{
if (attachedObjects[index].attachedObject != null)
{
if (attachedObjects[index].attachedRigidbody != null)
attachedObjects[index].attachedRigidbody.useGravity = attachedObjects[index].attachedRigidbodyUsedGravity;
}
}
if (attachedObjects[index].interactable != null && attachedObjects[index].interactable.handFollowTransform && HasSkeleton())
{
skeleton.transform.localPosition = Vector3.zero;
skeleton.transform.localRotation = Quaternion.identity;
}
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);
}
attachedObjects.RemoveAt(index);
CleanUpAttachedObjectStack();
GameObject newTopObject = currentAttachedObject;
hoverLocked = false;
//Give focus to the top most object on the stack if it changed
if (newTopObject != null && newTopObject != prevTopObject)
{
newTopObject.SetActive(true);
newTopObject.SendMessage("OnHandFocusAcquired", this, SendMessageOptions.DontRequireReceiver);
}
}
CleanUpAttachedObjectStack();
if (mainRenderModel != null)
mainRenderModel.MatchHandToTransform(mainRenderModel.transform);
if (hoverhighlightRenderModel != null)
hoverhighlightRenderModel.MatchHandToTransform(hoverhighlightRenderModel.transform);
}
//-------------------------------------------------
// Get the world velocity of the VR Hand.
//-------------------------------------------------
///
/// 获取跟踪对象速度
///
/// 时间偏移
///
public Vector3 GetTrackedObjectVelocity(float timeOffset = 0)
{
if (trackedObject == null)
{
Vector3 velocityTarget, angularTarget;
GetUpdatedAttachedVelocities(currentAttachedObjectInfo.Value, out velocityTarget, out angularTarget);
return velocityTarget;
}
if (isActive)
{
if (timeOffset == 0)
return Player.instance.trackingOriginTransform.TransformVector(trackedObject.GetVelocity());
else
{
Vector3 velocity;
Vector3 angularVelocity;
trackedObject.GetVelocitiesAtTimeOffset(timeOffset, out velocity, out angularVelocity);
return Player.instance.trackingOriginTransform.TransformVector(velocity);
}
}
return Vector3.zero;
}
//-------------------------------------------------
// Get the world space angular velocity of the VR Hand.
//-------------------------------------------------
///
/// 获取追踪对象角速度
///
/// 时间偏移
///
public Vector3 GetTrackedObjectAngularVelocity(float timeOffset = 0)
{
if (trackedObject == null)
{
Vector3 velocityTarget, angularTarget;
GetUpdatedAttachedVelocities(currentAttachedObjectInfo.Value, out velocityTarget, out angularTarget);
return angularTarget;
}
if (isActive)
{
if (timeOffset == 0)
return Player.instance.trackingOriginTransform.TransformDirection(trackedObject.GetAngularVelocity());
else
{
Vector3 velocity;
Vector3 angularVelocity;
trackedObject.GetVelocitiesAtTimeOffset(timeOffset, out velocity, out angularVelocity);
return Player.instance.trackingOriginTransform.TransformDirection(angularVelocity);
}
}
return Vector3.zero;
}
///
/// 获取估计的峰值速度
///
/// 速度
/// 角速度
public void GetEstimatedPeakVelocities(out Vector3 velocity, out Vector3 angularVelocity)
{
trackedObject.GetEstimatedPeakVelocities(out velocity, out angularVelocity);
velocity = Player.instance.trackingOriginTransform.TransformVector(velocity);
angularVelocity = Player.instance.trackingOriginTransform.TransformDirection(angularVelocity);
}
//-------------------------------------------------
///
/// 清理附加的对象堆栈
///
private void CleanUpAttachedObjectStack()
{
attachedObjects.RemoveAll(l => l.attachedObject == null);
}
//-------------------------------------------------
protected virtual void Awake()
{
inputFocusAction = SteamVR_Events.InputFocusAction(OnInputFocus);
if (hoverSphereTransform == null)
hoverSphereTransform = this.transform;
if (objectAttachmentPoint == null)
objectAttachmentPoint = this.transform;
applicationLostFocusObject = new GameObject("_application_lost_focus");
applicationLostFocusObject.transform.parent = transform;
applicationLostFocusObject.SetActive(false);
if (trackedObject == null)
{
trackedObject = this.gameObject.GetComponent();
if (trackedObject != null)
trackedObject.onTransformUpdatedEvent += OnTransformUpdated;
}
}
protected virtual void OnDestroy()
{
if (trackedObject != null)
{
trackedObject.onTransformUpdatedEvent -= OnTransformUpdated;
}
}
///
/// 当变换更新
///
/// 更新姿势
/// 更新源
protected virtual void OnTransformUpdated(SteamVR_Behaviour_Pose updatedPose, SteamVR_Input_Sources updatedSource)
{
HandFollowUpdate();
}
//-------------------------------------------------
///
/// IEnumerator Start
///
///
protected virtual IEnumerator Start()
{
// save off player instance
playerInstance = Player.instance;
if (!playerInstance)
{
Debug.LogError("[SteamVR Interaction] No player instance found in Hand Start()");
}
// allocate array for colliders
overlappingColliders = new Collider[ColliderArraySize];
// We are a "no SteamVR fallback hand" if we have this camera set
// we'll use the right mouse to look around and left mouse to interact
// - don't need to find the device
if (noSteamVRFallbackCamera)
{
yield break;
}
//Debug.Log( "[SteamVR Interaction] Hand - initializing connection routine" );
while (true)
{
if (isPoseValid)
{
InitController();
break;
}
yield return null;
}
}
//-------------------------------------------------
///
/// 更新悬浮
///
protected virtual void UpdateHovering()
{
if ((noSteamVRFallbackCamera == null) && (isActive == false))
{
return;
}
if (hoverLocked)
return;
if (applicationLostFocusObject.activeSelf)
return;
float closestDistance = float.MaxValue;
Interactable closestInteractable = null;
if (useHoverSphere)
{
float scaledHoverRadius = hoverSphereRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(hoverSphereTransform));
CheckHoveringForTransform(hoverSphereTransform.position, scaledHoverRadius, ref closestDistance, ref closestInteractable, Color.green);
}
if (useControllerHoverComponent && mainRenderModel != null && mainRenderModel.IsControllerVisibile())
{
float scaledHoverRadius = controllerHoverRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(this.transform));
CheckHoveringForTransform(mainRenderModel.GetControllerPosition(controllerHoverComponent), scaledHoverRadius / 2f, ref closestDistance, ref closestInteractable, Color.blue);
}
if (useFingerJointHover && mainRenderModel != null && mainRenderModel.IsHandVisibile())
{
float scaledHoverRadius = fingerJointHoverRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(this.transform));
CheckHoveringForTransform(mainRenderModel.GetBonePosition((int)fingerJointHover), scaledHoverRadius / 2f, ref closestDistance, ref closestInteractable, Color.yellow);
}
// Hover on this one
hoveringInteractable = closestInteractable;
}
///
/// 检查悬浮从位置
///
/// 悬浮位置
/// 悬浮半径
/// 最近距离
/// 最近交互
/// 调试颜色
///
protected virtual bool CheckHoveringForTransform(Vector3 hoverPosition, float hoverRadius, ref float closestDistance, ref Interactable closestInteractable, Color debugColor)
{
bool foundCloser = false;
// null out old vals
for (int i = 0; i < overlappingColliders.Length; ++i)
{
overlappingColliders[i] = null;
}
int numColliding = Physics.OverlapSphereNonAlloc(hoverPosition, hoverRadius, overlappingColliders, hoverLayerMask.value);
if (numColliding == ColliderArraySize)
Debug.LogWarning("[SteamVR Interaction] This hand is overlapping the max number of colliders: " + ColliderArraySize + ". Some collisions may be missed. Increase ColliderArraySize on Hand.cs");
// DebugVar
int iActualColliderCount = 0;
// Pick the closest hovering
for (int colliderIndex = 0; colliderIndex < overlappingColliders.Length; colliderIndex++)
{
Collider collider = overlappingColliders[colliderIndex];
if (collider == null)
continue;
Interactable contacting = collider.GetComponentInParent();
// Yeah, it's null, skip
if (contacting == null)
continue;
// Ignore this collider for hovering
IgnoreHovering ignore = collider.GetComponent();
if (ignore != null)
{
if (ignore.onlyIgnoreHand == null || ignore.onlyIgnoreHand == this)
{
continue;
}
}
// Can't hover over the object if it's attached
bool hoveringOverAttached = false;
for (int attachedIndex = 0; attachedIndex < attachedObjects.Count; attachedIndex++)
{
if (attachedObjects[attachedIndex].attachedObject == contacting.gameObject)
{
hoveringOverAttached = true;
break;
}
}
if (hoveringOverAttached)
continue;
// Occupied by another hand, so we can't touch it
if (otherHand && otherHand.hoveringInteractable == contacting)
continue;
// Best candidate so far...
float distance = Vector3.Distance(contacting.transform.position, hoverPosition);
if (distance < closestDistance)
{
closestDistance = distance;
closestInteractable = contacting;
foundCloser = true;
}
iActualColliderCount++;
}
if (showDebugInteractables && foundCloser)
{
Debug.DrawLine(hoverPosition, closestInteractable.transform.position, debugColor, .05f, false);
}
if (iActualColliderCount > 0 && iActualColliderCount != prevOverlappingColliders)
{
prevOverlappingColliders = iActualColliderCount;
if (spewDebugText)
HandDebugLog("Found " + iActualColliderCount + " overlapping colliders.");
}
return foundCloser;
}
//-------------------------------------------------
///
/// 更新无SteamVR备用
///
protected virtual void UpdateNoSteamVRFallback()
{
if (noSteamVRFallbackCamera)
{
Ray ray = noSteamVRFallbackCamera.ScreenPointToRay(Input.mousePosition);
if (attachedObjects.Count > 0)
{
// Holding down the mouse:
// move around a fixed distance from the camera
transform.position = ray.origin + noSteamVRFallbackInteractorDistance * ray.direction;
}
else
{
// Not holding down the mouse:
// cast out a ray to see what we should mouse over
// Don't want to hit the hand and anything underneath it
// So move it back behind the camera when we do the raycast
Vector3 oldPosition = transform.position;
transform.position = noSteamVRFallbackCamera.transform.forward * (-1000.0f);
RaycastHit raycastHit;
if (Physics.Raycast(ray, out raycastHit, noSteamVRFallbackMaxDistanceNoItem))
{
transform.position = raycastHit.point;
// Remember this distance in case we click and drag the mouse
noSteamVRFallbackInteractorDistance = Mathf.Min(noSteamVRFallbackMaxDistanceNoItem, raycastHit.distance);
}
else if (noSteamVRFallbackInteractorDistance > 0.0f)
{
// Move it around at the distance we last had a hit
transform.position = ray.origin + Mathf.Min(noSteamVRFallbackMaxDistanceNoItem, noSteamVRFallbackInteractorDistance) * ray.direction;
}
else
{
// Didn't hit, just leave it where it was
transform.position = oldPosition;
}
}
}
}
//-------------------------------------------------
///
/// 更新调试文本
///
private void UpdateDebugText()
{
if (showDebugText)
{
if (debugText == null)
{
debugText = new GameObject("_debug_text").AddComponent();
debugText.fontSize = 120;
debugText.characterSize = 0.001f;
debugText.transform.parent = transform;
debugText.transform.localRotation = Quaternion.Euler(90.0f, 0.0f, 0.0f);
}
if (handType == SteamVR_Input_Sources.RightHand)
{
debugText.transform.localPosition = new Vector3(-0.05f, 0.0f, 0.0f);
debugText.alignment = TextAlignment.Right;
debugText.anchor = TextAnchor.UpperRight;
}
else
{
debugText.transform.localPosition = new Vector3(0.05f, 0.0f, 0.0f);
debugText.alignment = TextAlignment.Left;
debugText.anchor = TextAnchor.UpperLeft;
}
debugText.text = string.Format(
"Hovering: {0}\n" +
"Hover Lock: {1}\n" +
"Attached: {2}\n" +
"Total Attached: {3}\n" +
"Type: {4}\n",
(hoveringInteractable ? hoveringInteractable.gameObject.name : "null"),
hoverLocked,
(currentAttachedObject ? currentAttachedObject.name : "null"),
attachedObjects.Count,
handType.ToString());
}
else
{
if (debugText != null)
{
Destroy(debugText.gameObject);
}
}
}
//-------------------------------------------------
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);
}
//-------------------------------------------------
protected virtual void OnDisable()
{
inputFocusAction.enabled = false;
CancelInvoke();
}
//-------------------------------------------------
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);
}
}
///
/// 当手当前悬停在可交互传入的
/// Returns true when the hand is currently hovering over the interactable passed in
///
public bool IsStillHovering(Interactable interactable)
{
return hoveringInteractable == interactable;
}
///
/// 手跟踪更新
///
protected virtual void HandFollowUpdate()
{
GameObject attachedObject = currentAttachedObject;
if (attachedObject != null)
{
if (currentAttachedObjectInfo.Value.interactable != null)
{
SteamVR_Skeleton_PoseSnapshot pose = null;
if (currentAttachedObjectInfo.Value.interactable.skeletonPoser != null && HasSkeleton())
{
pose = currentAttachedObjectInfo.Value.interactable.skeletonPoser.GetBlendedPose(skeleton);
}
if (currentAttachedObjectInfo.Value.interactable.handFollowTransform)
{
Quaternion targetHandRotation;
Vector3 targetHandPosition;
if (pose == null)
{
Quaternion offset = Quaternion.Inverse(this.transform.rotation) * currentAttachedObjectInfo.Value.handAttachmentPointTransform.rotation;
targetHandRotation = currentAttachedObjectInfo.Value.interactable.transform.rotation * Quaternion.Inverse(offset);
Vector3 worldOffset = (this.transform.position - currentAttachedObjectInfo.Value.handAttachmentPointTransform.position);
Quaternion rotationDiff = mainRenderModel.GetHandRotation() * Quaternion.Inverse(this.transform.rotation);
Vector3 localOffset = rotationDiff * worldOffset;
targetHandPosition = currentAttachedObjectInfo.Value.interactable.transform.position + localOffset;
}
else
{
Transform objectT = currentAttachedObjectInfo.Value.attachedObject.transform;
Vector3 oldItemPos = objectT.position;
Quaternion oldItemRot = objectT.transform.rotation;
objectT.position = TargetItemPosition(currentAttachedObjectInfo.Value);
objectT.rotation = TargetItemRotation(currentAttachedObjectInfo.Value);
Vector3 localSkelePos = objectT.InverseTransformPoint(transform.position);
Quaternion localSkeleRot = Quaternion.Inverse(objectT.rotation) * transform.rotation;
objectT.position = oldItemPos;
objectT.rotation = oldItemRot;
targetHandPosition = objectT.TransformPoint(localSkelePos);
targetHandRotation = objectT.rotation * localSkeleRot;
}
if (mainRenderModel != null)
mainRenderModel.SetHandRotation(targetHandRotation);
if (hoverhighlightRenderModel != null)
hoverhighlightRenderModel.SetHandRotation(targetHandRotation);
if (mainRenderModel != null)
mainRenderModel.SetHandPosition(targetHandPosition);
if (hoverhighlightRenderModel != null)
hoverhighlightRenderModel.SetHandPosition(targetHandPosition);
}
}
}
}
protected virtual void FixedUpdate()
{
if (currentAttachedObject != null)
{
AttachedObject attachedInfo = currentAttachedObjectInfo.Value;
if (attachedInfo.attachedObject != null)
{
if (attachedInfo.HasAttachFlag(AttachmentFlags.VelocityMovement))
{
if(attachedInfo.interactable.attachEaseIn == false || attachedInfo.interactable.snapAttachEaseInCompleted)
UpdateAttachedVelocity(attachedInfo);
/*if (attachedInfo.interactable.handFollowTransformPosition)
{
skeleton.transform.position = TargetSkeletonPosition(attachedInfo);
skeleton.transform.rotation = attachedInfo.attachedObject.transform.rotation * attachedInfo.skeletonLockRotation;
}*/
}else
{
if (attachedInfo.HasAttachFlag(AttachmentFlags.ParentToHand))
{
attachedInfo.attachedObject.transform.position = TargetItemPosition(attachedInfo);
attachedInfo.attachedObject.transform.rotation = TargetItemRotation(attachedInfo);
}
}
if (attachedInfo.interactable.attachEaseIn)
{
float t = Util.RemapNumberClamped(Time.time, attachedInfo.attachTime, attachedInfo.attachTime + attachedInfo.interactable.snapAttachEaseInTime, 0.0f, 1.0f);
if (t < 1.0f)
{
if (attachedInfo.HasAttachFlag(AttachmentFlags.VelocityMovement))
{
attachedInfo.attachedRigidbody.velocity = Vector3.zero;
attachedInfo.attachedRigidbody.angularVelocity = Vector3.zero;
}
t = attachedInfo.interactable.snapAttachEaseInCurve.Evaluate(t);
attachedInfo.attachedObject.transform.position = Vector3.Lerp(attachedInfo.easeSourcePosition, TargetItemPosition(attachedInfo), t);
attachedInfo.attachedObject.transform.rotation = Quaternion.Lerp(attachedInfo.easeSourceRotation, TargetItemRotation(attachedInfo), t);
}
else if (!attachedInfo.interactable.snapAttachEaseInCompleted)
{
attachedInfo.interactable.gameObject.SendMessage("OnThrowableAttachEaseInCompleted", this, SendMessageOptions.DontRequireReceiver);
attachedInfo.interactable.snapAttachEaseInCompleted = true;
}
}
}
}
}
///
/// 最大速度变化
///
protected const float MaxVelocityChange = 10f;
///
/// 速度Magic
///
protected const float VelocityMagic = 6000f;
///
/// 角速度Magic
///
protected const float AngularVelocityMagic = 50f;
///
/// 最大角速度变化
///
protected const float MaxAngularVelocityChange = 20f;
///
/// 更新附加的速度
///
/// 附加物体信息
protected void UpdateAttachedVelocity(AttachedObject attachedObjectInfo)
{
Vector3 velocityTarget, angularTarget;
bool success = GetUpdatedAttachedVelocities(attachedObjectInfo, out velocityTarget, out angularTarget);
if (success)
{
float scale = SteamVR_Utils.GetLossyScale(currentAttachedObjectInfo.Value.handAttachmentPointTransform);
float maxAngularVelocityChange = MaxAngularVelocityChange * scale;
float maxVelocityChange = MaxVelocityChange * scale;
attachedObjectInfo.attachedRigidbody.velocity = Vector3.MoveTowards(attachedObjectInfo.attachedRigidbody.velocity, velocityTarget, maxVelocityChange);
attachedObjectInfo.attachedRigidbody.angularVelocity = Vector3.MoveTowards(attachedObjectInfo.attachedRigidbody.angularVelocity, angularTarget, maxAngularVelocityChange);
}
}
///
/// /目标项目位置
///
///
///
protected Vector3 TargetItemPosition(AttachedObject attachedObject)
{
if (attachedObject.interactable != null && attachedObject.interactable.skeletonPoser != null && HasSkeleton())
{
Vector3 tp = attachedObject.handAttachmentPointTransform.InverseTransformPoint(transform.TransformPoint(attachedObject.interactable.skeletonPoser.GetBlendedPose(skeleton).position));
//tp.x *= -1;
return currentAttachedObjectInfo.Value.handAttachmentPointTransform.TransformPoint(tp);
}
else
{
return currentAttachedObjectInfo.Value.handAttachmentPointTransform.TransformPoint(attachedObject.initialPositionalOffset);
}
}
///
/// 目标项目旋转
///
///
///
protected Quaternion TargetItemRotation(AttachedObject attachedObject)
{
if (attachedObject.interactable != null && attachedObject.interactable.skeletonPoser != null && HasSkeleton())
{
Quaternion tr = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * (transform.rotation * attachedObject.interactable.skeletonPoser.GetBlendedPose(skeleton).rotation);
return currentAttachedObjectInfo.Value.handAttachmentPointTransform.rotation * tr;
}
else
{
return currentAttachedObjectInfo.Value.handAttachmentPointTransform.rotation * attachedObject.initialRotationalOffset;
}
}
///
/// 是否获取更新附加的速度
///
/// 附加对象信息
/// 目标速度
/// 目标角度
///
protected bool GetUpdatedAttachedVelocities(AttachedObject attachedObjectInfo, out Vector3 velocityTarget, out Vector3 angularTarget)
{
bool realNumbers = false;
float velocityMagic = VelocityMagic;
float angularVelocityMagic = AngularVelocityMagic;
Vector3 targetItemPosition = TargetItemPosition(attachedObjectInfo);
Vector3 positionDelta = (targetItemPosition - attachedObjectInfo.attachedRigidbody.position);
velocityTarget = (positionDelta * velocityMagic * Time.deltaTime);
if (float.IsNaN(velocityTarget.x) == false && float.IsInfinity(velocityTarget.x) == false)
{
if (noSteamVRFallbackCamera)
velocityTarget /= 10; //hacky fix for fallback
realNumbers = true;
}
else
velocityTarget = Vector3.zero;
Quaternion targetItemRotation = TargetItemRotation(attachedObjectInfo);
Quaternion rotationDelta = targetItemRotation * Quaternion.Inverse(attachedObjectInfo.attachedObject.transform.rotation);
float angle;
Vector3 axis;
rotationDelta.ToAngleAxis(out angle, out axis);
if (angle > 180)
angle -= 360;
if (angle != 0 && float.IsNaN(axis.x) == false && float.IsInfinity(axis.x) == false)
{
angularTarget = angle * axis * angularVelocityMagic * Time.deltaTime;
if (noSteamVRFallbackCamera)
angularTarget /= 10; //hacky fix for fallback
realNumbers &= true;
}
else
angularTarget = Vector3.zero;
return realNumbers;
}
//-------------------------------------------------
///
/// 当输入焦点
///
/// 是否有焦点
protected virtual void OnInputFocus(bool hasFocus)
{
if (hasFocus)
{
DetachObject(applicationLostFocusObject, true);
applicationLostFocusObject.SetActive(false);
UpdateHovering();
BroadcastMessage("OnParentHandInputFocusAcquired", SendMessageOptions.DontRequireReceiver);
}
else
{
applicationLostFocusObject.SetActive(true);
AttachObject(applicationLostFocusObject, GrabTypes.Scripted, AttachmentFlags.ParentToHand);
BroadcastMessage("OnParentHandInputFocusLost", SendMessageOptions.DontRequireReceiver);
}
}
//-------------------------------------------------
protected virtual void OnDrawGizmos()
{
if (useHoverSphere && hoverSphereTransform != null)
{
Gizmos.color = Color.green;
float scaledHoverRadius = hoverSphereRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(hoverSphereTransform));
Gizmos.DrawWireSphere(hoverSphereTransform.position, scaledHoverRadius/2);
}
if (useControllerHoverComponent && mainRenderModel != null && mainRenderModel.IsControllerVisibile())
{
Gizmos.color = Color.blue;
float scaledHoverRadius = controllerHoverRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(this.transform));
Gizmos.DrawWireSphere(mainRenderModel.GetControllerPosition(controllerHoverComponent), scaledHoverRadius/2);
}
if (useFingerJointHover && mainRenderModel != null && mainRenderModel.IsHandVisibile())
{
Gizmos.color = Color.yellow;
float scaledHoverRadius = fingerJointHoverRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(this.transform));
Gizmos.DrawWireSphere(mainRenderModel.GetBonePosition((int)fingerJointHover), scaledHoverRadius/2);
}
}
//-------------------------------------------------
///
/// 调试日志
///
/// 消息
private void HandDebugLog(string msg)
{
if (spewDebugText)
{
Debug.Log("[SteamVR Interaction] Hand (" + this.name + "): " + msg);
}
}
//-------------------------------------------------
// Continue to hover over this object indefinitely, whether or not the Hand moves out of its interaction trigger volume.
//
// interactable - The Interactable to hover over indefinitely.
//-------------------------------------------------
///
/// 悬浮锁定
///
/// 交互
public void HoverLock(Interactable interactable)
{
if (spewDebugText)
HandDebugLog("HoverLock " + interactable);
hoverLocked = true;
hoveringInteractable = interactable;
}
//-------------------------------------------------
// Stop hovering over this object indefinitely.
//
// interactable - The hover-locked Interactable to stop hovering over indefinitely.
//-------------------------------------------------
///
/// 悬浮解锁
///
///
public void HoverUnlock(Interactable interactable)
{
if (spewDebugText)
HandDebugLog("HoverUnlock " + interactable);
if (hoveringInteractable == interactable)
{
hoverLocked = false;
}
}
///
///
///
///
public void TriggerHapticPulse(ushort microSecondsDuration)
{
float seconds = (float)microSecondsDuration / 1000000f;
hapticAction.Execute(0, seconds, 1f / seconds, 1, handType);
}
///
/// 触发触觉脉冲,或许是震动
///
/// 持续时间
/// 频率
/// 振幅
public void TriggerHapticPulse(float duration, float frequency, float amplitude)
{
hapticAction.Execute(0, duration, frequency, amplitude, handType);
}
///
/// 显示抓取提示
///
public void ShowGrabHint()
{
ControllerButtonHints.ShowButtonHint(this, grabGripAction); //todo: assess
}
///
/// 隐藏抓取信息
///
public void HideGrabHint()
{
ControllerButtonHints.HideButtonHint(this, grabGripAction); //todo: assess
}
///
/// 显示抓取提示
///
/// 文本
public void ShowGrabHint(string text)
{
ControllerButtonHints.ShowTextHint(this, grabGripAction, text);
}
///
/// 获取抓取开始
///
///
///
public GrabTypes GetGrabStarting(GrabTypes explicitType = GrabTypes.None)
{
if (explicitType != GrabTypes.None)
{
if (noSteamVRFallbackCamera)
{
if (Input.GetMouseButtonDown(0))
return explicitType;
}
if (explicitType == GrabTypes.Pinch && grabPinchAction.GetStateDown(handType))
return GrabTypes.Pinch;
// if (explicitType == GrabTypes.Grip && grabGripAction.GetStateDown(handType))
// return GrabTypes.Grip;
}
else
{
if (noSteamVRFallbackCamera)
{
if (Input.GetMouseButtonDown(0))
return GrabTypes.Grip;
}
if (grabPinchAction.GetStateDown(handType))
return GrabTypes.Pinch;
// if (grabGripAction.GetStateDown(handType))
// return GrabTypes.Grip;
}
return GrabTypes.None;
}
///
/// 获取抓取结束
///
///
///
public GrabTypes GetGrabEnding(GrabTypes explicitType = GrabTypes.None)
{
if (explicitType != GrabTypes.None)
{
if (noSteamVRFallbackCamera)
{
// if (Input.GetMouseButtonUp(0))
// return explicitType;
if (Input.GetKeyUp(DetachedKey))
{
return explicitType;
}
}
//if (explicitType == GrabTypes.Pinch && grabPinchAction.GetStateUp(handType))
// return GrabTypes.Pinch;
if (explicitType == GrabTypes.Grip && grabGripAction.GetStateUp(handType))
return GrabTypes.Grip;
}
else
{
if (noSteamVRFallbackCamera)
{
//if (Input.GetMouseButtonUp(0))
// return GrabTypes.Grip;
if (Input.GetKeyUp(DetachedKey))
{
return explicitType;
}
}
// if (grabPinchAction.GetStateUp(handType))
// return GrabTypes.Pinch;
if (grabGripAction.GetStateUp(handType))
return GrabTypes.Grip;
}
return GrabTypes.None;
}
///
/// 物体是否抓取结束
///
/// 要附加的物体
///
public bool IsGrabEnding(GameObject attachedObject)
{
for (int attachedObjectIndex = 0; attachedObjectIndex < attachedObjects.Count; attachedObjectIndex++)
{
if (attachedObjects[attachedObjectIndex].attachedObject == attachedObject)
{
return IsGrabbingWithType(attachedObjects[attachedObjectIndex].grabbedWithType) == false;
}
}
return false;
}
///
/// 是否正在抓取
///
/// 类型
///
public bool IsGrabbingWithType(GrabTypes type)
{
if (noSteamVRFallbackCamera)
{
if (Input.GetKeyUp(DetachedKey))
{
return false;
}
}
if (grabGripAction.GetStateUp(handType))
{
return false;
}
return true;
//if (noSteamVRFallbackCamera)
//{
// if (Input.GetMouseButton(0))
// return true;
//}
//switch (type)
//{
// case GrabTypes.Pinch:
// return grabPinchAction.GetState(handType);
// case GrabTypes.Grip:
// return grabGripAction.GetState(handType);
// default:
// return false;
//}
}
///
/// 正在用相反的类型抓取
///
///
///
public bool IsGrabbingWithOppositeType(GrabTypes type)
{
if (noSteamVRFallbackCamera)
{
if (Input.GetMouseButton(0))
return true;
}
switch (type)
{
case GrabTypes.Pinch:
return grabGripAction.GetState(handType);
case GrabTypes.Grip:
return grabPinchAction.GetState(handType);
default:
return false;
}
}
///
/// 获得做好的抓取类型???
///
///
public GrabTypes GetBestGrabbingType()
{
return GetBestGrabbingType(GrabTypes.None);
}
///
/// 获取最好的抓取类型
///
/// 首选
/// 是否强制偏好
///
public GrabTypes GetBestGrabbingType(GrabTypes preferred, bool forcePreference = false)
{
if (noSteamVRFallbackCamera)
{
if (Input.GetMouseButton(0))
return preferred;
}
if (preferred == GrabTypes.Pinch)
{
if (grabPinchAction.GetState(handType))
return GrabTypes.Pinch;
else if (forcePreference)
return GrabTypes.None;
}
if (preferred == GrabTypes.Grip)
{
if (grabGripAction.GetState(handType))
return GrabTypes.Grip;
else if (forcePreference)
return GrabTypes.None;
}
if (grabPinchAction.GetState(handType))
return GrabTypes.Pinch;
if (grabGripAction.GetState(handType))
return GrabTypes.Grip;
return GrabTypes.None;
}
//-------------------------------------------------
///
/// 初始化控制器
///
private void InitController()
{
if (spewDebugText)
HandDebugLog("Hand " + name + " connected with type " + handType.ToString());
bool hadOldRendermodel = mainRenderModel != null;
EVRSkeletalMotionRange oldRM_rom = EVRSkeletalMotionRange.WithController;
if(hadOldRendermodel)
oldRM_rom = mainRenderModel.GetSkeletonRangeOfMotion;
foreach (RenderModel r in renderModels)
{
if (r != null)
Destroy(r.gameObject);
}
renderModels.Clear();
GameObject renderModelInstance = GameObject.Instantiate(renderModelPrefab);
renderModelInstance.layer = gameObject.layer;
renderModelInstance.tag = gameObject.tag;
renderModelInstance.transform.parent = this.transform;
renderModelInstance.transform.localPosition = Vector3.zero;
renderModelInstance.transform.localRotation = Quaternion.identity;
renderModelInstance.transform.localScale = renderModelPrefab.transform.localScale;
//TriggerHapticPulse(800); //pulse on controller init
int deviceIndex = trackedObject.GetDeviceIndex();
mainRenderModel = renderModelInstance.GetComponent();
renderModels.Add(mainRenderModel);
if (hadOldRendermodel)
mainRenderModel.SetSkeletonRangeOfMotion(oldRM_rom);
this.BroadcastMessage("SetInputSource", handType, SendMessageOptions.DontRequireReceiver); // let child objects know we've initialized
this.BroadcastMessage("OnHandInitialized", deviceIndex, SendMessageOptions.DontRequireReceiver); // let child objects know we've initialized
}
///
/// 设置模型
///
///
public void SetRenderModel(GameObject prefab)
{
renderModelPrefab = prefab;
if (mainRenderModel != null && isPoseValid)
InitController();
}
///
/// 设置悬浮模型
///
///
public void SetHoverRenderModel(RenderModel hoverRenderModel)
{
hoverhighlightRenderModel = hoverRenderModel;
renderModels.Add(hoverRenderModel);
}
///
/// 得到设备索引
///
///
public int GetDeviceIndex()
{
return trackedObject.GetDeviceIndex();
}
}
[System.Serializable]
public class HandEvent : UnityEvent { }
#if UNITY_EDITOR
//-------------------------------------------------------------------------
[UnityEditor.CustomEditor(typeof(Hand))]
public class HandEditor : UnityEditor.Editor
{
//-------------------------------------------------
// Custom Inspector GUI allows us to click from within the UI
//-------------------------------------------------
public override void OnInspectorGUI()
{
DrawDefaultInspector();
/*
Hand hand = (Hand)target;
if (hand.otherHand)
{
if (hand.otherHand.otherHand != hand)
{
UnityEditor.EditorGUILayout.HelpBox("The otherHand of this Hand's otherHand is not this Hand.", UnityEditor.MessageType.Warning);
}
if (hand.handType == SteamVR_Input_Sources.LeftHand && hand.otherHand && hand.otherHand.handType != SteamVR_Input_Sources.RightHand)
{
UnityEditor.EditorGUILayout.HelpBox("This is a left Hand but otherHand is not a right Hand.", UnityEditor.MessageType.Warning);
}
if (hand.handType == SteamVR_Input_Sources.RightHand && hand.otherHand && hand.otherHand.handType != SteamVR_Input_Sources.LeftHand)
{
UnityEditor.EditorGUILayout.HelpBox("This is a right Hand but otherHand is not a left Hand.", UnityEditor.MessageType.Warning);
}
if (hand.handType == SteamVR_Input_Sources.Any && hand.otherHand && hand.otherHand.handType != SteamVR_Input_Sources.Any)
{
UnityEditor.EditorGUILayout.HelpBox("This is an any-handed Hand but otherHand is not an any-handed Hand.", UnityEditor.MessageType.Warning);
}
}
*/ //removing for now because it conflicts with other input sources (trackers and such)
} //暂时删除,因为它与其他输入源(跟踪程序等)冲突
}
#endif
}
//======= Copyright (c) Valve Corporation, All rights reserved. ===============
//
// Purpose: Basic throwable object
//
//=============================================================================
using UnityEngine;
using UnityEngine.Events;
using System.Collections;
using UnityEditor;
namespace Valve.VR.InteractionSystem
{
//-------------------------------------------------------------------------
[RequireComponent( typeof( Interactable ) )]
[RequireComponent( typeof( Rigidbody ) )]
[RequireComponent( typeof(VelocityEstimator))]
public class Throwable : MonoBehaviour
{
///
/// 附加标志
///
[EnumFlags]
[Tooltip("The flags used to attach this object to the hand. 用于将此对象附加到手的标志")]
public Hand.AttachmentFlags attachmentFlags = Hand.AttachmentFlags.ParentToHand | Hand.AttachmentFlags.DetachFromOtherHand | Hand.AttachmentFlags.TurnOnKinematic;
///
/// 附加偏移
/// 局部点,在保持时起位置偏移和旋转偏移的作用
///
[Tooltip("The local point which acts as a positional and rotational offset to use while held 局部点,在保持时起位置偏移和旋转偏移的作用")]
public Transform attachmentOffset;
///
/// 捕捉速度阈值
///
[Tooltip("How fast must this object be moving to attach due to a trigger hold instead of a trigger press? (-1 to disable)由于触发器保持而不是触发器按下,该对象移动到附加时的速度必须有多快?(1禁用)")]
public float catchingSpeedThreshold = -1;
///
/// 释放类型
///
public ReleaseStyle releaseVelocityStyle = ReleaseStyle.GetFromHand;
///
/// 释放速度时间偏移
///
[Tooltip("The time offset used when releasing the object with the RawFromHand option释放带有Raw From Hand选项的对象时使用的时间偏移量")]
public float releaseVelocityTimeOffset = -0.011f;
///
/// 缩放
///
public float scaleReleaseVelocity = 1.1f;
///
/// 是否恢复原来的父
/// 当分离对象时,它应该返回到原来的父对象吗
///
[Tooltip("When detaching the object, should it return to its original parent? 当分离对象时,它应该返回到原来的父对象吗")]
public bool restoreOriginalParent = false;
protected VelocityEstimator velocityEstimator;
///
/// 是否附加
///
protected bool attached = false;
///
/// 附加时间
///
protected float attachTime;
///
/// 附加位置
///
protected Vector3 attachPosition;
///
/// 附加旋转
///
protected Quaternion attachRotation;
///
/// 附加变换
///
protected Transform attachEaseInTransform;
///
/// 当捡起
///
public UnityEvent onPickUp;
///
/// 当从手分离
///
public UnityEvent onDetachFromHand;
///
/// 在进行更新?
///
public UnityEvent onHeldUpdate;
protected RigidbodyInterpolation hadInterpolation = RigidbodyInterpolation.None;
///
/// 刚体
///
protected new Rigidbody rigidbody;
///
/// 交互
///
[HideInInspector]
public Interactable interactable;
//-------------------------------------------------
protected virtual void Awake()
{
velocityEstimator = GetComponent();
interactable = GetComponent();
rigidbody = GetComponent();
//最大角速度
rigidbody.maxAngularVelocity = 50.0f;
if(attachmentOffset != null)
{
// remove?
//interactable.handFollowTransform = attachmentOffset;
}
}
//-------------------------------------------------
///
/// 当手悬浮开始
///
///
protected virtual void OnHandHoverBegin( Hand hand )
{
bool showHint = false;
// "Catch" the throwable by holding down the interaction button instead of pressing it.
// Only do this if the throwable is moving faster than the prescribed threshold speed,
// and if it isn't attached to another hand
if ( !attached && catchingSpeedThreshold != -1)
{
float catchingThreshold = catchingSpeedThreshold * SteamVR_Utils.GetLossyScale(Player.instance.trackingOriginTransform);
GrabTypes bestGrabType = hand.GetBestGrabbingType();
if ( bestGrabType != GrabTypes.None )
{
if (rigidbody.velocity.magnitude >= catchingThreshold)
{
hand.AttachObject( gameObject, bestGrabType, attachmentFlags );
showHint = false;
}
}
}
if ( showHint )
{
hand.ShowGrabHint();
}
}
//-------------------------------------------------
///
/// 当手悬浮结束
///
///
protected virtual void OnHandHoverEnd( Hand hand )
{
hand.HideGrabHint();
}
//-------------------------------------------------
///
/// 当手悬浮更新
///
///
protected virtual void HandHoverUpdate( Hand hand )
{
GrabTypes startingGrabType = hand.GetGrabStarting();
if (startingGrabType != GrabTypes.None)
{
hand.AttachObject( gameObject, startingGrabType, attachmentFlags, attachmentOffset );
hand.HideGrabHint();
}
}
//-------------------------------------------------
///
/// 当被添加到手
///
///
protected virtual void OnAttachedToHand( Hand hand )
{
//Debug.Log("[SteamVR Interaction] Pickup: " + hand.GetGrabStarting().ToString());
hadInterpolation = this.rigidbody.interpolation;
attached = true;
onPickUp.Invoke();
hand.HoverLock( null );
rigidbody.interpolation = RigidbodyInterpolation.None;
velocityEstimator.BeginEstimatingVelocity();
attachTime = Time.time;
attachPosition = transform.position;
attachRotation = transform.rotation;
}
//-------------------------------------------------
///
/// 当从手中分离
///
///
protected virtual void OnDetachedFromHand(Hand hand)
{
attached = false;
onDetachFromHand.Invoke();
hand.HoverUnlock(null);
rigidbody.interpolation = hadInterpolation;
Vector3 velocity;
Vector3 angularVelocity;
GetReleaseVelocities(hand, out velocity, out angularVelocity);
rigidbody.velocity = velocity;
rigidbody.angularVelocity = angularVelocity;
}
///
/// 得到释放速度
///
/// 手
/// 速度
/// 角速度
public virtual void GetReleaseVelocities(Hand hand, out Vector3 velocity, out Vector3 angularVelocity)
{
if (hand.noSteamVRFallbackCamera && releaseVelocityStyle != ReleaseStyle.NoChange)
releaseVelocityStyle = ReleaseStyle.ShortEstimation; // only type that works with fallback hand is short estimation.
switch (releaseVelocityStyle)
{
case ReleaseStyle.ShortEstimation:
velocityEstimator.FinishEstimatingVelocity();
velocity = velocityEstimator.GetVelocityEstimate();
angularVelocity = velocityEstimator.GetAngularVelocityEstimate();
break;
case ReleaseStyle.AdvancedEstimation:
hand.GetEstimatedPeakVelocities(out velocity, out angularVelocity);
break;
case ReleaseStyle.GetFromHand:
velocity = hand.GetTrackedObjectVelocity(releaseVelocityTimeOffset);
angularVelocity = hand.GetTrackedObjectAngularVelocity(releaseVelocityTimeOffset);
break;
default:
case ReleaseStyle.NoChange:
velocity = rigidbody.velocity;
angularVelocity = rigidbody.angularVelocity;
break;
}
if (releaseVelocityStyle != ReleaseStyle.NoChange)
velocity *= scaleReleaseVelocity;
}
//-------------------------------------------------
///
/// 当被添加到手每帧
///
///
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 ) );
//取消注释,以便在稍后的帧中分离我们自己。
//这就是玩家所依附的任何车辆
//有机会完成自我更新。
//如果我们现在脱离,我们的位置可能会落后于形势
//将位于帧的末尾,对象可能会出现
//当玩家松开手的时候,在手的后面传送。
//StartCoroutine(LateDetach(hand));
}
if (onHeldUpdate != null)
onHeldUpdate.Invoke(hand);
}
//-------------------------------------------------
///
/// 延迟分离
///
///
///
protected virtual IEnumerator LateDetach( Hand hand )
{
yield return new WaitForEndOfFrame();
hand.DetachObject( gameObject, restoreOriginalParent );
}
//-------------------------------------------------
///
/// 当手焦点获取
///
///
protected virtual void OnHandFocusAcquired( Hand hand )
{
gameObject.SetActive( true );
velocityEstimator.BeginEstimatingVelocity();
}
//-------------------------------------------------
///
/// 当手焦点丢失
///
///
protected virtual void OnHandFocusLost( Hand hand )
{
gameObject.SetActive( false );
velocityEstimator.FinishEstimatingVelocity();
}
}
///
/// 释放类型
///
public enum ReleaseStyle
{
///
/// 无变化
///
NoChange,
///
/// 得到来自手
///
GetFromHand,
ShortEstimation,
AdvancedEstimation,
}
}
//======= Copyright (c) Valve Corporation, All rights reserved. ===============
//
// Purpose: This object will get hover events and can be attached to the hands
//目的:该对象将获得悬停事件,并可以附加到指针上
//=============================================================================
using UnityEngine;
using UnityEngine.Events;
using System.Collections;
using System.Collections.Generic;
namespace Valve.VR.InteractionSystem
{
//-------------------------------------------------------------------------
public class Interactable : MonoBehaviour
{
[Tooltip("Activates an action set on attach and deactivates on detach 在连接时激活一个动作集,在分离时禁用该动作集")]
public SteamVR_ActionSet activateActionSetOnAttach;
///
/// 是否隐藏手当被附加时
///
[Tooltip("Hide the whole hand on attachment and show on detach")]
public bool hideHandOnAttach = true;
///
/// 将手的骨骼部分隐藏在附件中,并在分离时显示出来
///
[Tooltip("Hide the skeleton part of the hand on attachment and show on detach 将手的骨骼部分隐藏在附件中,并在分离时显示出来")]
public bool hideSkeletonOnAttach = false;
///
/// 将手的控制器部分隐藏在附件中,并在分离时显示出来。
///
[Tooltip("Hide the controller part of the hand on attachment and show on detach 将手的控制器部分隐藏在附件中,并在分离时显示出来。")]
public bool hideControllerOnAttach = false;
///
/// 要在拾取时触发的动画器中的整数。0没有
///
[Tooltip("The integer in the animator to trigger on pickup. 0 for none 要在拾取时触发的动画器中的整数。0没有”)")]
public int handAnimationOnPickup = 0;
///
/// 在骨架上设定的运动范围。没有不改变的
///
[Tooltip("The range of motion to set on the skeleton. None for no change.在骨架上设定的运动范围。没有不改变的")]
public SkeletalMotionRangeChange setRangeOfMotionOnPickup = SkeletalMotionRangeChange.None;
public delegate void OnAttachedToHandDelegate(Hand hand);
public delegate void OnDetachedFromHandDelegate(Hand hand);
///
/// 当被附加到手
///
public event OnAttachedToHandDelegate onAttachedToHand;
///
/// 当从手中分离
///
public event OnDetachedFromHandDelegate onDetachedFromHand;
///
/// 指定是要对齐到指针的对象附着点,还是只对齐到原始指针
///
[Tooltip("Specify whether you want to snap to the hand's object attachment point, or just the raw hand 指定是要对齐到指针的对象附着点,还是只对齐到原始指针")]
public bool useHandObjectAttachmentPoint = true;
///
/// 附加在
///
public bool attachEaseIn = false;
[HideInInspector]
public AnimationCurve snapAttachEaseInCurve = AnimationCurve.EaseInOut(0.0f, 0.0f, 1.0f, 1.0f);
public float snapAttachEaseInTime = 0.15f;
///
/// 完成后,快速安装
///
public bool snapAttachEaseInCompleted = false;
//抓取时的骨架姿势。只能设置这个或handFollowTransform
// [Tooltip("The skeleton pose to apply when grabbing. Can only set this or handFollowTransform.")]
[HideInInspector]
public SteamVR_Skeleton_Poser skeletonPoser;
///
/// 呈现的手应该锁定并跟随对象吗
///
[Tooltip("Should the rendered hand lock on to and follow the object")]
public bool handFollowTransform= true;
///
/// 设置是否要在悬停时突出显示此可交互项
///
[Tooltip("Set whether or not you want this interactible to highlight when hovering over it 设置是否要在悬停时突出显示此可交互项")]
public bool highlightOnHover = true;
///
/// 高亮渲染
///
protected MeshRenderer[] highlightRenderers;
///
/// 当前渲染
///
protected MeshRenderer[] existingRenderers;
///
/// 高亮持有者
///
protected GameObject highlightHolder;
///
/// 蒙皮网格渲染
///
protected SkinnedMeshRenderer[] highlightSkinnedRenderers;
///
/// 当前蒙皮网格渲染
///
protected SkinnedMeshRenderer[] existingSkinnedRenderers;
protected static Material highlightMat;
[Tooltip("An array of child gameObjects to not render a highlight for. Things like transparent parts, vfx, etc.一组子游戏对象,不呈现突出显示。比如透明的部分,特效等等。")]
public GameObject[] hideHighlight;
[System.NonSerialized]
public Hand attachedToHand;
[System.NonSerialized]
public Hand hoveringHand;
///
/// 是否销毁
///
public bool isDestroying { get; protected set; }
///
/// 是否悬浮
///
public bool isHovering { get; protected set; }
public bool wasHovering { get; protected set; }
private void Awake()
{
skeletonPoser = GetComponent();
}
protected virtual void Start()
{
highlightMat = (Material)Resources.Load("SteamVR_HoverHighlight", typeof(Material));
if (highlightMat == null)
Debug.LogError("[SteamVR Interaction] Hover Highlight Material is missing. Please create a material named 'SteamVR_HoverHighlight' and place it in a Resources folder");
if (skeletonPoser != null)
{
if (useHandObjectAttachmentPoint)
{
//Debug.LogWarning("[SteamVR Interaction] SkeletonPose and useHandObjectAttachmentPoint both set at the same time. Ignoring useHandObjectAttachmentPoint.");
useHandObjectAttachmentPoint = false;
}
}
}
protected virtual void OnDestroy()
{
isDestroying = true;
if (attachedToHand != null)
{
attachedToHand.DetachObject(this.gameObject, false);
attachedToHand.skeleton.BlendToSkeleton(0.1f);
}
if (highlightHolder != null)
Destroy(highlightHolder);
}
protected virtual void OnDisable()
{
isDestroying = true;
if (attachedToHand != null)
{
attachedToHand.ForceHoverUnlock();
}
if (highlightHolder != null)
Destroy(highlightHolder);
}
///
/// 应该忽略高亮
///
///
///
protected virtual bool ShouldIgnoreHighlight(Component component)
{
return ShouldIgnore(component.gameObject);
}
protected virtual bool ShouldIgnore(GameObject check)
{
for (int ignoreIndex = 0; ignoreIndex < hideHighlight.Length; ignoreIndex++)
{
if (check == hideHighlight[ignoreIndex])
return true;
}
return false;
}
///
/// 创建高亮
///
protected virtual void CreateHighlightRenderers()
{
existingSkinnedRenderers = this.GetComponentsInChildren(true);
highlightHolder = new GameObject("Highlighter -- 高亮");
highlightSkinnedRenderers = new SkinnedMeshRenderer[existingSkinnedRenderers.Length];
for (int skinnedIndex = 0; skinnedIndex < existingSkinnedRenderers.Length; skinnedIndex++)
{
SkinnedMeshRenderer existingSkinned = existingSkinnedRenderers[skinnedIndex];
if (ShouldIgnoreHighlight(existingSkinned))
continue;
GameObject newSkinnedHolder = new GameObject("SkinnedHolder");
newSkinnedHolder.transform.parent = highlightHolder.transform;
SkinnedMeshRenderer newSkinned = newSkinnedHolder.AddComponent();
Material[] materials = new Material[existingSkinned.sharedMaterials.Length];
for (int materialIndex = 0; materialIndex < materials.Length; materialIndex++)
{
materials[materialIndex] = highlightMat;
}
newSkinned.sharedMaterials = materials;
newSkinned.sharedMesh = existingSkinned.sharedMesh;
newSkinned.rootBone = existingSkinned.rootBone;
newSkinned.updateWhenOffscreen = existingSkinned.updateWhenOffscreen;
newSkinned.bones = existingSkinned.bones;
highlightSkinnedRenderers[skinnedIndex] = newSkinned;
}
MeshFilter[] existingFilters = this.GetComponentsInChildren(true);
existingRenderers = new MeshRenderer[existingFilters.Length];
highlightRenderers = new MeshRenderer[existingFilters.Length];
for (int filterIndex = 0; filterIndex < existingFilters.Length; filterIndex++)
{
MeshFilter existingFilter = existingFilters[filterIndex];
MeshRenderer existingRenderer = existingFilter.GetComponent();
if (existingFilter == null || existingRenderer == null || ShouldIgnoreHighlight(existingFilter))
continue;
GameObject newFilterHolder = new GameObject("FilterHolder");
newFilterHolder.transform.parent = highlightHolder.transform;
MeshFilter newFilter = newFilterHolder.AddComponent();
newFilter.sharedMesh = existingFilter.sharedMesh;
MeshRenderer newRenderer = newFilterHolder.AddComponent();
Material[] materials = new Material[existingRenderer.sharedMaterials.Length];
for (int materialIndex = 0; materialIndex < materials.Length; materialIndex++)
{
materials[materialIndex] = highlightMat;
}
newRenderer.sharedMaterials = materials;
highlightRenderers[filterIndex] = newRenderer;
existingRenderers[filterIndex] = existingRenderer;
}
}
///
/// 更新高亮渲染
///
protected virtual void UpdateHighlightRenderers()
{
if (highlightHolder == null)
return;
for (int skinnedIndex = 0; skinnedIndex < existingSkinnedRenderers.Length; skinnedIndex++)
{
SkinnedMeshRenderer existingSkinned = existingSkinnedRenderers[skinnedIndex];
SkinnedMeshRenderer highlightSkinned = highlightSkinnedRenderers[skinnedIndex];
if (existingSkinned != null && highlightSkinned != null && attachedToHand == false)
{
highlightSkinned.transform.position = existingSkinned.transform.position;
highlightSkinned.transform.rotation = existingSkinned.transform.rotation;
highlightSkinned.transform.localScale = existingSkinned.transform.lossyScale;
highlightSkinned.localBounds = existingSkinned.localBounds;
highlightSkinned.enabled = isHovering && existingSkinned.enabled && existingSkinned.gameObject.activeInHierarchy;
int blendShapeCount = existingSkinned.sharedMesh.blendShapeCount;
for (int blendShapeIndex = 0; blendShapeIndex < blendShapeCount; blendShapeIndex++)
{
highlightSkinned.SetBlendShapeWeight(blendShapeIndex, existingSkinned.GetBlendShapeWeight(blendShapeIndex));
}
}
else if (highlightSkinned != null)
highlightSkinned.enabled = false;
}
for (int rendererIndex = 0; rendererIndex < highlightRenderers.Length; rendererIndex++)
{
MeshRenderer existingRenderer = existingRenderers[rendererIndex];
MeshRenderer highlightRenderer = highlightRenderers[rendererIndex];
if (existingRenderer != null && highlightRenderer != null && attachedToHand == false)
{
highlightRenderer.transform.position = existingRenderer.transform.position;
highlightRenderer.transform.rotation = existingRenderer.transform.rotation;
highlightRenderer.transform.localScale = existingRenderer.transform.lossyScale;
highlightRenderer.enabled = isHovering && existingRenderer.enabled && existingRenderer.gameObject.activeInHierarchy;
}
else if (highlightRenderer != null)
highlightRenderer.enabled = false;
}
}
///
/// 当一只手开始悬停在该对象上时调用
/// Called when a Hand starts hovering over this object
///
protected virtual void OnHandHoverBegin(Hand hand)
{
wasHovering = isHovering;
isHovering = true;
hoveringHand = hand;
if (highlightOnHover == true)
{
CreateHighlightRenderers();
UpdateHighlightRenderers();
}
}
///
/// 当一只手停止悬停在该对象上时调用
/// Called when a Hand stops hovering over this object
///
private void OnHandHoverEnd(Hand hand)
{
wasHovering = isHovering;
isHovering = false;
if (highlightOnHover && highlightHolder != null)
Destroy(highlightHolder);
}
protected virtual void Update()
{
if (highlightOnHover)
{
UpdateHighlightRenderers();
if (isHovering == false && highlightHolder != null)
Destroy(highlightHolder);
}
}
protected float blendToPoseTime = 0.1f;
protected float releasePoseBlendTime = 0.2f;
///
/// 当被附加到手
///
///
protected virtual void OnAttachedToHand(Hand hand)
{
if (activateActionSetOnAttach != null)
activateActionSetOnAttach.Activate(hand.handType);
if (onAttachedToHand != null)
{
onAttachedToHand.Invoke(hand);
}
if (skeletonPoser != null && hand.skeleton != null)
{
hand.skeleton.BlendToPoser(skeletonPoser, blendToPoseTime);
}
attachedToHand = hand;
}
///
/// 当从手分离
///
///
protected virtual void OnDetachedFromHand(Hand hand)
{
if (activateActionSetOnAttach != null)
{
if (hand.otherHand == null || hand.otherHand.currentAttachedObjectInfo.HasValue == false ||
(hand.otherHand.currentAttachedObjectInfo.Value.interactable != null &&
hand.otherHand.currentAttachedObjectInfo.Value.interactable.activateActionSetOnAttach != this.activateActionSetOnAttach))
{
activateActionSetOnAttach.Deactivate(hand.handType);
}
}
if (onDetachedFromHand != null)
{
onDetachedFromHand.Invoke(hand);
}
if (skeletonPoser != null)
{
if (hand.skeleton != null)
hand.skeleton.BlendToSkeleton(releasePoseBlendTime);
}
attachedToHand = null;
}
}
}
跟我之前写的检测 事件脚本同理。也是使用sendmessage,由Hand.cs发送,这些脚本接收
不过可以绑定,这个脚本使用了UnityEvent,虽然我还不明白
//======= Copyright (c) Valve Corporation, All rights reserved. ===============
//
// Purpose: Sends UnityEvents for basic hand interactions
//
//=============================================================================
using UnityEngine;
using UnityEngine.Events;
using System.Collections;
namespace Valve.VR.InteractionSystem
{
//-------------------------------------------------------------------------
[RequireComponent( typeof( Interactable ) )]
public class InteractableHoverEvents : MonoBehaviour
{
public UnityEvent onHandHoverBegin;
public UnityEvent onHandHoverEnd;
public UnityEvent onAttachedToHand;
public UnityEvent onDetachedFromHand;
//-------------------------------------------------
private void OnHandHoverBegin()
{
onHandHoverBegin.Invoke();
}
//-------------------------------------------------
private void OnHandHoverEnd()
{
onHandHoverEnd.Invoke();
}
//-------------------------------------------------
private void OnAttachedToHand( Hand hand )
{
onAttachedToHand.Invoke();
}
//-------------------------------------------------
private void OnDetachedFromHand( Hand hand )
{
onDetachedFromHand.Invoke();
}
}
}