Interactable.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. //======= Copyright (c) Valve Corporation, All rights reserved. ===============
  2. //
  3. // Purpose: This object will get hover events and can be attached to the hands
  4. //
  5. //=============================================================================
  6. using UnityEngine;
  7. using UnityEngine.Events;
  8. using System.Collections;
  9. using System.Collections.Generic;
  10. namespace Valve.VR.InteractionSystem
  11. {
  12. //-------------------------------------------------------------------------
  13. public class Interactable : MonoBehaviour
  14. {
  15. [Tooltip("Activates an action set on attach and deactivates on detach")]
  16. public SteamVR_ActionSet activateActionSetOnAttach;
  17. [Tooltip("Hide the whole hand on attachment and show on detach")]
  18. public bool hideHandOnAttach = true;
  19. [Tooltip("Hide the skeleton part of the hand on attachment and show on detach")]
  20. public bool hideSkeletonOnAttach = false;
  21. [Tooltip("Hide the controller part of the hand on attachment and show on detach")]
  22. public bool hideControllerOnAttach = false;
  23. [Tooltip("The integer in the animator to trigger on pickup. 0 for none")]
  24. public int handAnimationOnPickup = 0;
  25. [Tooltip("The range of motion to set on the skeleton. None for no change.")]
  26. public SkeletalMotionRangeChange setRangeOfMotionOnPickup = SkeletalMotionRangeChange.None;
  27. public delegate void OnAttachedToHandDelegate(Hand hand);
  28. public delegate void OnDetachedFromHandDelegate(Hand hand);
  29. public event OnAttachedToHandDelegate onAttachedToHand;
  30. public event OnDetachedFromHandDelegate onDetachedFromHand;
  31. [Tooltip("Specify whether you want to snap to the hand's object attachment point, or just the raw hand")]
  32. public bool useHandObjectAttachmentPoint = true;
  33. public bool attachEaseIn = false;
  34. [HideInInspector]
  35. public AnimationCurve snapAttachEaseInCurve = AnimationCurve.EaseInOut(0.0f, 0.0f, 1.0f, 1.0f);
  36. public float snapAttachEaseInTime = 0.15f;
  37. public bool snapAttachEaseInCompleted = false;
  38. // [Tooltip("The skeleton pose to apply when grabbing. Can only set this or handFollowTransform.")]
  39. [HideInInspector]
  40. public SteamVR_Skeleton_Poser skeletonPoser;
  41. [Tooltip("Should the rendered hand lock on to and follow the object")]
  42. public bool handFollowTransform= true;
  43. [Tooltip("Set whether or not you want this interactible to highlight when hovering over it")]
  44. public bool highlightOnHover = true;
  45. protected MeshRenderer[] highlightRenderers;
  46. protected MeshRenderer[] existingRenderers;
  47. protected GameObject highlightHolder;
  48. protected SkinnedMeshRenderer[] highlightSkinnedRenderers;
  49. protected SkinnedMeshRenderer[] existingSkinnedRenderers;
  50. protected static Material highlightMat;
  51. [Tooltip("An array of child gameObjects to not render a highlight for. Things like transparent parts, vfx, etc.")]
  52. public GameObject[] hideHighlight;
  53. [Tooltip("Higher is better")]
  54. public int hoverPriority = 0;
  55. [System.NonSerialized]
  56. public Hand attachedToHand;
  57. [System.NonSerialized]
  58. public List<Hand> hoveringHands = new List<Hand>();
  59. public Hand hoveringHand
  60. {
  61. get
  62. {
  63. if (hoveringHands.Count > 0)
  64. return hoveringHands[0];
  65. return null;
  66. }
  67. }
  68. public bool isDestroying { get; protected set; }
  69. public bool isHovering { get; protected set; }
  70. public bool wasHovering { get; protected set; }
  71. private void Awake()
  72. {
  73. skeletonPoser = GetComponent<SteamVR_Skeleton_Poser>();
  74. }
  75. protected virtual void Start()
  76. {
  77. if (highlightMat == null)
  78. #if UNITY_URP
  79. highlightMat = (Material)Resources.Load("SteamVR_HoverHighlight_URP", typeof(Material));
  80. #else
  81. highlightMat = (Material)Resources.Load("SteamVR_HoverHighlight", typeof(Material));
  82. #endif
  83. if (highlightMat == null)
  84. Debug.LogError("<b>[SteamVR Interaction]</b> Hover Highlight Material is missing. Please create a material named 'SteamVR_HoverHighlight' and place it in a Resources folder", this);
  85. if (skeletonPoser != null)
  86. {
  87. if (useHandObjectAttachmentPoint)
  88. {
  89. //Debug.LogWarning("<b>[SteamVR Interaction]</b> SkeletonPose and useHandObjectAttachmentPoint both set at the same time. Ignoring useHandObjectAttachmentPoint.");
  90. useHandObjectAttachmentPoint = false;
  91. }
  92. }
  93. }
  94. protected virtual bool ShouldIgnoreHighlight(Component component)
  95. {
  96. return ShouldIgnore(component.gameObject);
  97. }
  98. protected virtual bool ShouldIgnore(GameObject check)
  99. {
  100. for (int ignoreIndex = 0; ignoreIndex < hideHighlight.Length; ignoreIndex++)
  101. {
  102. if (check == hideHighlight[ignoreIndex])
  103. return true;
  104. }
  105. return false;
  106. }
  107. protected virtual void CreateHighlightRenderers()
  108. {
  109. existingSkinnedRenderers = this.GetComponentsInChildren<SkinnedMeshRenderer>(true);
  110. highlightHolder = new GameObject("Highlighter");
  111. highlightSkinnedRenderers = new SkinnedMeshRenderer[existingSkinnedRenderers.Length];
  112. for (int skinnedIndex = 0; skinnedIndex < existingSkinnedRenderers.Length; skinnedIndex++)
  113. {
  114. SkinnedMeshRenderer existingSkinned = existingSkinnedRenderers[skinnedIndex];
  115. if (ShouldIgnoreHighlight(existingSkinned))
  116. continue;
  117. GameObject newSkinnedHolder = new GameObject("SkinnedHolder");
  118. newSkinnedHolder.transform.parent = highlightHolder.transform;
  119. SkinnedMeshRenderer newSkinned = newSkinnedHolder.AddComponent<SkinnedMeshRenderer>();
  120. Material[] materials = new Material[existingSkinned.sharedMaterials.Length];
  121. for (int materialIndex = 0; materialIndex < materials.Length; materialIndex++)
  122. {
  123. materials[materialIndex] = highlightMat;
  124. }
  125. newSkinned.sharedMaterials = materials;
  126. newSkinned.sharedMesh = existingSkinned.sharedMesh;
  127. newSkinned.rootBone = existingSkinned.rootBone;
  128. newSkinned.updateWhenOffscreen = existingSkinned.updateWhenOffscreen;
  129. newSkinned.bones = existingSkinned.bones;
  130. highlightSkinnedRenderers[skinnedIndex] = newSkinned;
  131. }
  132. MeshFilter[] existingFilters = this.GetComponentsInChildren<MeshFilter>(true);
  133. existingRenderers = new MeshRenderer[existingFilters.Length];
  134. highlightRenderers = new MeshRenderer[existingFilters.Length];
  135. for (int filterIndex = 0; filterIndex < existingFilters.Length; filterIndex++)
  136. {
  137. MeshFilter existingFilter = existingFilters[filterIndex];
  138. MeshRenderer existingRenderer = existingFilter.GetComponent<MeshRenderer>();
  139. if (existingFilter == null || existingRenderer == null || ShouldIgnoreHighlight(existingFilter))
  140. continue;
  141. GameObject newFilterHolder = new GameObject("FilterHolder");
  142. newFilterHolder.transform.parent = highlightHolder.transform;
  143. MeshFilter newFilter = newFilterHolder.AddComponent<MeshFilter>();
  144. newFilter.sharedMesh = existingFilter.sharedMesh;
  145. MeshRenderer newRenderer = newFilterHolder.AddComponent<MeshRenderer>();
  146. Material[] materials = new Material[existingRenderer.sharedMaterials.Length];
  147. for (int materialIndex = 0; materialIndex < materials.Length; materialIndex++)
  148. {
  149. materials[materialIndex] = highlightMat;
  150. }
  151. newRenderer.sharedMaterials = materials;
  152. highlightRenderers[filterIndex] = newRenderer;
  153. existingRenderers[filterIndex] = existingRenderer;
  154. }
  155. }
  156. protected virtual void UpdateHighlightRenderers()
  157. {
  158. if (highlightHolder == null)
  159. return;
  160. for (int skinnedIndex = 0; skinnedIndex < existingSkinnedRenderers.Length; skinnedIndex++)
  161. {
  162. SkinnedMeshRenderer existingSkinned = existingSkinnedRenderers[skinnedIndex];
  163. SkinnedMeshRenderer highlightSkinned = highlightSkinnedRenderers[skinnedIndex];
  164. if (existingSkinned != null && highlightSkinned != null && attachedToHand == false)
  165. {
  166. highlightSkinned.transform.position = existingSkinned.transform.position;
  167. highlightSkinned.transform.rotation = existingSkinned.transform.rotation;
  168. highlightSkinned.transform.localScale = existingSkinned.transform.lossyScale;
  169. highlightSkinned.localBounds = existingSkinned.localBounds;
  170. highlightSkinned.enabled = isHovering && existingSkinned.enabled && existingSkinned.gameObject.activeInHierarchy;
  171. int blendShapeCount = existingSkinned.sharedMesh.blendShapeCount;
  172. for (int blendShapeIndex = 0; blendShapeIndex < blendShapeCount; blendShapeIndex++)
  173. {
  174. highlightSkinned.SetBlendShapeWeight(blendShapeIndex, existingSkinned.GetBlendShapeWeight(blendShapeIndex));
  175. }
  176. }
  177. else if (highlightSkinned != null)
  178. highlightSkinned.enabled = false;
  179. }
  180. for (int rendererIndex = 0; rendererIndex < highlightRenderers.Length; rendererIndex++)
  181. {
  182. MeshRenderer existingRenderer = existingRenderers[rendererIndex];
  183. MeshRenderer highlightRenderer = highlightRenderers[rendererIndex];
  184. if (existingRenderer != null && highlightRenderer != null && attachedToHand == false)
  185. {
  186. highlightRenderer.transform.position = existingRenderer.transform.position;
  187. highlightRenderer.transform.rotation = existingRenderer.transform.rotation;
  188. highlightRenderer.transform.localScale = existingRenderer.transform.lossyScale;
  189. highlightRenderer.enabled = isHovering && existingRenderer.enabled && existingRenderer.gameObject.activeInHierarchy;
  190. }
  191. else if (highlightRenderer != null)
  192. highlightRenderer.enabled = false;
  193. }
  194. }
  195. /// <summary>
  196. /// Called when a Hand starts hovering over this object
  197. /// </summary>
  198. protected virtual void OnHandHoverBegin(Hand hand)
  199. {
  200. wasHovering = isHovering;
  201. isHovering = true;
  202. hoveringHands.Add(hand);
  203. if (highlightOnHover == true && wasHovering == false)
  204. {
  205. CreateHighlightRenderers();
  206. UpdateHighlightRenderers();
  207. }
  208. }
  209. /// <summary>
  210. /// Called when a Hand stops hovering over this object
  211. /// </summary>
  212. protected virtual void OnHandHoverEnd(Hand hand)
  213. {
  214. wasHovering = isHovering;
  215. hoveringHands.Remove(hand);
  216. if (hoveringHands.Count == 0)
  217. {
  218. isHovering = false;
  219. if (highlightOnHover && highlightHolder != null)
  220. Destroy(highlightHolder);
  221. }
  222. }
  223. protected virtual void Update()
  224. {
  225. if (highlightOnHover)
  226. {
  227. UpdateHighlightRenderers();
  228. if (isHovering == false && highlightHolder != null)
  229. Destroy(highlightHolder);
  230. }
  231. }
  232. protected float blendToPoseTime = 0.1f;
  233. protected float releasePoseBlendTime = 0.2f;
  234. protected virtual void OnAttachedToHand(Hand hand)
  235. {
  236. if (activateActionSetOnAttach != null)
  237. activateActionSetOnAttach.Activate(hand.handType);
  238. if (onAttachedToHand != null)
  239. {
  240. onAttachedToHand.Invoke(hand);
  241. }
  242. if (skeletonPoser != null && hand.skeleton != null)
  243. {
  244. hand.skeleton.BlendToPoser(skeletonPoser, blendToPoseTime);
  245. }
  246. attachedToHand = hand;
  247. }
  248. protected virtual void OnDetachedFromHand(Hand hand)
  249. {
  250. if (activateActionSetOnAttach != null)
  251. {
  252. if (hand.otherHand == null || hand.otherHand.currentAttachedObjectInfo.HasValue == false ||
  253. (hand.otherHand.currentAttachedObjectInfo.Value.interactable != null &&
  254. hand.otherHand.currentAttachedObjectInfo.Value.interactable.activateActionSetOnAttach != this.activateActionSetOnAttach))
  255. {
  256. activateActionSetOnAttach.Deactivate(hand.handType);
  257. }
  258. }
  259. if (onDetachedFromHand != null)
  260. {
  261. onDetachedFromHand.Invoke(hand);
  262. }
  263. if (skeletonPoser != null)
  264. {
  265. if (hand.skeleton != null)
  266. hand.skeleton.BlendToSkeleton(releasePoseBlendTime);
  267. }
  268. attachedToHand = null;
  269. }
  270. protected virtual void OnDestroy()
  271. {
  272. isDestroying = true;
  273. if (attachedToHand != null)
  274. {
  275. attachedToHand.DetachObject(this.gameObject, false);
  276. attachedToHand.skeleton.BlendToSkeleton(0.1f);
  277. }
  278. if (highlightHolder != null)
  279. Destroy(highlightHolder);
  280. }
  281. protected virtual void OnDisable()
  282. {
  283. isDestroying = true;
  284. if (attachedToHand != null)
  285. {
  286. attachedToHand.ForceHoverUnlock();
  287. }
  288. if (highlightHolder != null)
  289. Destroy(highlightHolder);
  290. }
  291. }
  292. }