Assignment for RMIT Mixed Reality in 2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1195 lines
50 KiB

  1. // Snap Drop Zone|Prefabs|0080
  2. namespace VRTK
  3. {
  4. using UnityEngine;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. using Highlighters;
  8. /// <summary>
  9. /// Event Payload
  10. /// </summary>
  11. /// <param name="snappedObject">The interactable object that is dealing with the snap drop zone.</param>
  12. public struct SnapDropZoneEventArgs
  13. {
  14. public GameObject snappedObject;
  15. }
  16. /// <summary>
  17. /// Event Payload
  18. /// </summary>
  19. /// <param name="sender">this object</param>
  20. /// <param name="e"><see cref="SnapDropZoneEventArgs"/></param>
  21. public delegate void SnapDropZoneEventHandler(object sender, SnapDropZoneEventArgs e);
  22. /// <summary>
  23. /// Provides a predefined zone where a valid interactable object can be dropped and upon dropping it snaps to the set snap drop zone transform position, rotation and scale.
  24. /// </summary>
  25. /// <remarks>
  26. /// **Prefab Usage:**
  27. /// * Place the `VRTK/Prefabs/SnapDropZone/SnapDropZone` prefab into the scene hierarchy.
  28. /// * Provide the SnapDropZone with an optional `Highlight Object Prefab` to generate an object outline in the scene that determines the final position, rotation and scale of the snapped object.
  29. /// * If no `VRTK_BaseHighlighter` derivative is applied to the SnapDropZone then the default MaterialColorSwap Highlighter will be used.
  30. /// * The collision zone that activates the SnapDropZone is a `Sphere Collider` by default but can be amended or replaced on the SnapDropZone GameObject.
  31. /// * If the `Use Joint` Snap Type is selected then a custom Joint component is required to be added to the `SnapDropZone` Game Object and upon release the interactable object's rigidbody will be linked to this joint as the `Connected Body`.
  32. /// </remarks>
  33. /// <example>
  34. /// `VRTK/Examples/041_Controller_ObjectSnappingToDropZones` uses the `VRTK_SnapDropZone` prefab to set up pre-determined snap zones for a range of objects and demonstrates how only objects of certain types can be snapped into certain areas.
  35. /// </example>
  36. [ExecuteInEditMode]
  37. public class VRTK_SnapDropZone : MonoBehaviour
  38. {
  39. /// <summary>
  40. /// The types of snap on release available.
  41. /// </summary>
  42. public enum SnapTypes
  43. {
  44. /// <summary>
  45. /// Will set the interactable object rigidbody to `isKinematic = true`.
  46. /// </summary>
  47. UseKinematic,
  48. /// <summary>
  49. /// Will attach the interactable object's rigidbody to the provided joint as it's `Connected Body`.
  50. /// </summary>
  51. UseJoint,
  52. /// <summary>
  53. /// Will set the SnapDropZone as the interactable object's parent and set it's rigidbody to `isKinematic = true`.
  54. /// </summary>
  55. UseParenting
  56. }
  57. [Tooltip("A game object that is used to draw the highlighted destination for within the drop zone. This object will also be created in the Editor for easy placement.")]
  58. public GameObject highlightObjectPrefab;
  59. [Tooltip("The Snap Type to apply when a valid interactable object is dropped within the snap zone.")]
  60. public SnapTypes snapType = SnapTypes.UseKinematic;
  61. [Tooltip("The amount of time it takes for the object being snapped to move into the new snapped position, rotation and scale.")]
  62. public float snapDuration = 0f;
  63. [Tooltip("If this is checked then the scaled size of the snap drop zone will be applied to the object that is snapped to it.")]
  64. public bool applyScalingOnSnap = false;
  65. [Tooltip("If this is checked then when the snapped object is unsnapped from the drop zone, a clone of the unsnapped object will be snapped back into the drop zone.")]
  66. public bool cloneNewOnUnsnap = false;
  67. [Tooltip("The colour to use when showing the snap zone is active. This is used as the highlight colour when no object is hovering but `Highlight Always Active` is true.")]
  68. public Color highlightColor = Color.clear;
  69. [Tooltip("The colour to use when showing the snap zone is active and a valid object is hovering. If this is `Color.clear` then the `Highlight Color` will be used.")]
  70. public Color validHighlightColor = Color.clear;
  71. [Tooltip("The highlight object will always be displayed when the snap drop zone is available even if a valid item isn't being hovered over.")]
  72. public bool highlightAlwaysActive = false;
  73. [Tooltip("A specified VRTK_PolicyList to use to determine which interactable objects will be snapped to the snap drop zone on release.")]
  74. public VRTK_PolicyList validObjectListPolicy;
  75. [Tooltip("If this is checked then the drop zone highlight section will be displayed in the scene editor window.")]
  76. public bool displayDropZoneInEditor = true;
  77. [Tooltip("The Interactable Object to snap into the dropzone when the drop zone is enabled. The Interactable Object must be valid in any given policy list to snap.")]
  78. public VRTK_InteractableObject defaultSnappedInteractableObject;
  79. [Header("Obsolete Settings")]
  80. [System.Obsolete("`VRTK_SnapDropZone.defaultSnappedObject` has been replaced with the `VRTK_SnapDropZone.defaultSnappedInteractableObject`. This parameter will be removed in a future version of VRTK.")]
  81. [ObsoleteInspector]
  82. public GameObject defaultSnappedObject;
  83. /// <summary>
  84. /// Emitted when a valid interactable object enters the snap drop zone trigger collider.
  85. /// </summary>
  86. public event SnapDropZoneEventHandler ObjectEnteredSnapDropZone;
  87. /// <summary>
  88. /// Emitted when a valid interactable object exists the snap drop zone trigger collider.
  89. /// </summary>
  90. public event SnapDropZoneEventHandler ObjectExitedSnapDropZone;
  91. /// <summary>
  92. /// Emitted when an interactable object is successfully snapped into a drop zone.
  93. /// </summary>
  94. public event SnapDropZoneEventHandler ObjectSnappedToDropZone;
  95. /// <summary>
  96. /// Emitted when an interactable object is removed from a snapped drop zone.
  97. /// </summary>
  98. public event SnapDropZoneEventHandler ObjectUnsnappedFromDropZone;
  99. protected GameObject previousPrefab;
  100. protected GameObject highlightContainer;
  101. protected GameObject highlightObject;
  102. protected GameObject highlightEditorObject = null;
  103. protected List<VRTK_InteractableObject> currentValidSnapInteractableObjects = new List<VRTK_InteractableObject>();
  104. protected VRTK_InteractableObject currentSnappedObject = null;
  105. protected GameObject objectToClone = null;
  106. protected bool[] clonedObjectColliderStates = new bool[0];
  107. protected bool willSnap = false;
  108. protected bool isSnapped = false;
  109. protected bool wasSnapped = false;
  110. protected bool isHighlighted = false;
  111. protected VRTK_BaseHighlighter objectHighlighter;
  112. protected Coroutine transitionInPlaceRoutine;
  113. protected Coroutine attemptTransitionAtEndOfFrameRoutine;
  114. protected Coroutine checkCanSnapRoutine;
  115. protected bool originalJointCollisionState = false;
  116. protected Coroutine overridePreviousStateAtEndOfFrameRoutine;
  117. protected const string HIGHLIGHT_CONTAINER_NAME = "HighlightContainer";
  118. protected const string HIGHLIGHT_OBJECT_NAME = "HighlightObject";
  119. protected const string HIGHLIGHT_EDITOR_OBJECT_NAME = "EditorHighlightObject";
  120. public virtual void OnObjectEnteredSnapDropZone(SnapDropZoneEventArgs e)
  121. {
  122. if (ObjectEnteredSnapDropZone != null)
  123. {
  124. ObjectEnteredSnapDropZone(this, e);
  125. }
  126. }
  127. public virtual void OnObjectExitedSnapDropZone(SnapDropZoneEventArgs e)
  128. {
  129. if (ObjectExitedSnapDropZone != null)
  130. {
  131. ObjectExitedSnapDropZone(this, e);
  132. }
  133. }
  134. public virtual void OnObjectSnappedToDropZone(SnapDropZoneEventArgs e)
  135. {
  136. if (ObjectSnappedToDropZone != null)
  137. {
  138. ObjectSnappedToDropZone(this, e);
  139. }
  140. }
  141. public virtual void OnObjectUnsnappedFromDropZone(SnapDropZoneEventArgs e)
  142. {
  143. UnsnapObject();
  144. if (ObjectUnsnappedFromDropZone != null)
  145. {
  146. ObjectUnsnappedFromDropZone(this, e);
  147. }
  148. }
  149. public virtual SnapDropZoneEventArgs SetSnapDropZoneEvent(GameObject interactableObject)
  150. {
  151. SnapDropZoneEventArgs e;
  152. e.snappedObject = interactableObject;
  153. return e;
  154. }
  155. /// <summary>
  156. /// The InitaliseHighlightObject method sets up the highlight object based on the given Highlight Object Prefab.
  157. /// </summary>
  158. /// <param name="removeOldObject">If this is set to true then it attempts to delete the old highlight object if it exists. Defaults to `false`</param>
  159. public virtual void InitaliseHighlightObject(bool removeOldObject = false)
  160. {
  161. //force delete previous created highlight object
  162. if (removeOldObject)
  163. {
  164. DeleteHighlightObject();
  165. }
  166. //Always remove editor highlight object at runtime
  167. ChooseDestroyType(transform.Find(ObjectPath(HIGHLIGHT_EDITOR_OBJECT_NAME)));
  168. highlightEditorObject = null;
  169. GenerateObjects();
  170. }
  171. /// <summary>
  172. /// the ForceSnap method attempts to automatically attach a valid GameObject to the snap drop zone.
  173. /// </summary>
  174. /// <param name="objectToSnap">The GameObject to attempt to snap.</param>
  175. public virtual void ForceSnap(GameObject objectToSnap)
  176. {
  177. ForceSnap(objectToSnap.GetComponentInParent<VRTK_InteractableObject>());
  178. }
  179. /// <summary>
  180. /// the ForceSnap method attempts to automatically attach a valid Interactable Object to the snap drop zone.
  181. /// </summary>
  182. /// <param name="objectToSnap">The Interactable Object to attempt to snap.</param>
  183. protected virtual void ForceSnap(VRTK_InteractableObject interactableObjectToSnap)
  184. {
  185. if (interactableObjectToSnap != null)
  186. {
  187. if (attemptTransitionAtEndOfFrameRoutine != null)
  188. {
  189. StopCoroutine(attemptTransitionAtEndOfFrameRoutine);
  190. }
  191. if (checkCanSnapRoutine != null)
  192. {
  193. StopCoroutine(checkCanSnapRoutine);
  194. }
  195. if (interactableObjectToSnap.IsGrabbed())
  196. {
  197. interactableObjectToSnap.ForceStopInteracting();
  198. }
  199. if (gameObject.activeInHierarchy)
  200. {
  201. attemptTransitionAtEndOfFrameRoutine = StartCoroutine(AttemptForceSnapAtEndOfFrame(interactableObjectToSnap));
  202. }
  203. }
  204. }
  205. /// <summary>
  206. /// The ForceUnsnap method attempts to automatically remove the current snapped game object from the snap drop zone.
  207. /// </summary>
  208. public virtual void ForceUnsnap()
  209. {
  210. if (isSnapped && ValidSnapObject(currentSnappedObject, false))
  211. {
  212. currentSnappedObject.ToggleSnapDropZone(this, false);
  213. }
  214. }
  215. /// <summary>
  216. /// The ValidSnappableObjectIsHovering method determines if any valid objects are currently hovering in the snap drop zone area.
  217. /// </summary>
  218. /// <returns>Returns true if a valid object is currently in the snap drop zone area.</returns>
  219. public virtual bool ValidSnappableObjectIsHovering()
  220. {
  221. for (int i = 0; i < currentValidSnapInteractableObjects.Count; i++)
  222. {
  223. if (currentValidSnapInteractableObjects[i].IsGrabbed())
  224. {
  225. return true;
  226. }
  227. }
  228. return false;
  229. }
  230. /// <summary>
  231. /// The IsObjectHovering method determines if the given GameObject is currently howvering (but not snapped) in the snap drop zone area.
  232. /// </summary>
  233. /// <param name="checkObject">The GameObject to check to see if it's hovering in the snap drop zone area.</param>
  234. /// <returns>Returns true if the given GameObject is hovering (but not snapped) in the snap drop zone area.</returns>
  235. public virtual bool IsObjectHovering(GameObject checkObject)
  236. {
  237. VRTK_InteractableObject interactableObjectToCheck = checkObject.GetComponentInParent<VRTK_InteractableObject>();
  238. return (interactableObjectToCheck != null ? currentValidSnapInteractableObjects.Contains(interactableObjectToCheck) : false);
  239. }
  240. /// <summary>
  241. /// The IsInteractableObjectHovering method determines if the given Interactable Object script is currently howvering (but not snapped) in the snap drop zone area.
  242. /// </summary>
  243. /// <param name="checkObject">The Interactable Object script to check to see if it's hovering in the snap drop zone area.</param>
  244. /// <returns>Returns true if the given Interactable Object script is hovering (but not snapped) in the snap drop zone area.</returns>
  245. public virtual bool IsInteractableObjectHovering(VRTK_InteractableObject checkObject)
  246. {
  247. return (checkObject != null ? currentValidSnapInteractableObjects.Contains(checkObject) : false);
  248. }
  249. /// <summary>
  250. /// The GetHoveringObjects method returns a List of valid GameObjects that are currently hovering (but not snapped) in the snap drop zone area.
  251. /// </summary>
  252. /// <returns>The List of valid GameObjects that are hovering (but not snapped) in the snap drop zone area.</returns>
  253. public virtual List<GameObject> GetHoveringObjects()
  254. {
  255. List<GameObject> returnList = new List<GameObject>();
  256. for (int i = 0; i < currentValidSnapInteractableObjects.Count; i++)
  257. {
  258. VRTK_SharedMethods.AddListValue(returnList, currentValidSnapInteractableObjects[i].gameObject);
  259. }
  260. return returnList;
  261. }
  262. /// <summary>
  263. /// The GetHoveringInteractableObjects method returns a List of valid Interactable Object scripts that are currently hovering (but not snapped) in the snap drop zone area.
  264. /// </summary>
  265. /// <returns>The List of valid Interactable Object scripts that are hovering (but not snapped) in the snap drop zone area.</returns>
  266. public virtual List<VRTK_InteractableObject> GetHoveringInteractableObjects()
  267. {
  268. return currentValidSnapInteractableObjects;
  269. }
  270. /// <summary>
  271. /// The GetCurrentSnappedObejct method returns the GameObject that is currently snapped in the snap drop zone area.
  272. /// </summary>
  273. /// <returns>The GameObject that is currently snapped in the snap drop zone area.</returns>
  274. public virtual GameObject GetCurrentSnappedObject()
  275. {
  276. return (currentSnappedObject != null ? currentSnappedObject.gameObject : null);
  277. }
  278. /// <summary>
  279. /// The GetCurrentSnappedInteractableObject method returns the Interactable Object script that is currently snapped in the snap drop zone area.
  280. /// </summary>
  281. /// <returns>The Interactable Object script that is currently snapped in the snap drop zone area.</returns>
  282. public virtual VRTK_InteractableObject GetCurrentSnappedInteractableObject()
  283. {
  284. return currentSnappedObject;
  285. }
  286. /// <summary>
  287. /// The Clone method returns the GameObject of the cloned snap drop zone
  288. /// </summary>
  289. /// <param name="position">Position of the cloned GameObject</param>
  290. /// <returns>The GameObject of the clone</returns>
  291. public virtual GameObject Clone(Vector3 position)
  292. {
  293. VRTK_SnapDropZone cloneSDZ = Instantiate(gameObject, position, transform.rotation).GetComponent<VRTK_SnapDropZone>();
  294. for (int childID = 0; childID < cloneSDZ.transform.childCount; childID++)
  295. {
  296. Transform child = cloneSDZ.transform.GetChild(childID);
  297. if (child.GetComponent<VRTK_InteractableObject>() != null)
  298. {
  299. Destroy(child.gameObject);
  300. }
  301. }
  302. if (isSnapped)
  303. {
  304. VRTK_InteractableObject currObject = currentSnappedObject;
  305. //Get copy of Objects original state
  306. Transform previousParent;
  307. bool previousKinematic;
  308. bool previousGrabbable;
  309. currObject.GetPreviousState(out previousParent, out previousKinematic, out previousGrabbable);
  310. GameObject clonedObject = null;
  311. if (cloneNewOnUnsnap)
  312. {
  313. clonedObject = Instantiate(objectToClone);
  314. clonedObject.SetActive(true);
  315. }
  316. else
  317. {
  318. clonedObject = Instantiate(currObject.gameObject);
  319. }
  320. clonedObject.transform.position = cloneSDZ.transform.position;
  321. cloneSDZ.ForceSnap(clonedObject);
  322. overridePreviousStateAtEndOfFrameRoutine = StartCoroutine(OverridePreviousStateAtEndOfFrame(clonedObject.GetComponent<VRTK_InteractableObject>(), previousParent, previousKinematic, previousGrabbable));
  323. }
  324. return cloneSDZ.gameObject;
  325. }
  326. /// <summary>
  327. /// The Clone method returns the GameObject of the cloned snap drop zone
  328. /// </summary>
  329. /// <returns>The GameObject of the clone</returns>
  330. public virtual GameObject Clone()
  331. {
  332. return Clone(Vector3.zero);
  333. }
  334. protected virtual void Awake()
  335. {
  336. if (Application.isPlaying)
  337. {
  338. InitaliseHighlightObject();
  339. }
  340. }
  341. protected virtual void OnApplicationQuit()
  342. {
  343. if (objectHighlighter != null)
  344. {
  345. objectHighlighter.Unhighlight();
  346. }
  347. }
  348. protected virtual void OnEnable()
  349. {
  350. currentValidSnapInteractableObjects.Clear();
  351. currentSnappedObject = null;
  352. objectToClone = null;
  353. clonedObjectColliderStates = new bool[0];
  354. willSnap = false;
  355. isSnapped = false;
  356. wasSnapped = false;
  357. isHighlighted = false;
  358. #pragma warning disable 618
  359. if (defaultSnappedObject != null && defaultSnappedInteractableObject == null)
  360. {
  361. defaultSnappedInteractableObject = defaultSnappedObject.GetComponentInParent<VRTK_InteractableObject>();
  362. }
  363. #pragma warning restore 618
  364. DisableHighlightShadows();
  365. if (!VRTK_SharedMethods.IsEditTime() && Application.isPlaying && defaultSnappedInteractableObject != null)
  366. {
  367. ForceSnap(defaultSnappedInteractableObject);
  368. }
  369. }
  370. protected virtual void OnDisable()
  371. {
  372. if (transitionInPlaceRoutine != null)
  373. {
  374. StopCoroutine(transitionInPlaceRoutine);
  375. }
  376. if (attemptTransitionAtEndOfFrameRoutine != null)
  377. {
  378. StopCoroutine(attemptTransitionAtEndOfFrameRoutine);
  379. }
  380. if (checkCanSnapRoutine != null)
  381. {
  382. StopCoroutine(checkCanSnapRoutine);
  383. }
  384. if(overridePreviousStateAtEndOfFrameRoutine != null)
  385. {
  386. StopCoroutine(overridePreviousStateAtEndOfFrameRoutine);
  387. }
  388. ForceUnsnap();
  389. SetHighlightObjectActive(false);
  390. UnregisterAllUngrabEvents();
  391. }
  392. protected virtual void Update()
  393. {
  394. CheckSnappedItemExists();
  395. CheckPrefabUpdate();
  396. CreateHighlightersInEditor();
  397. CheckCurrentValidSnapObjectStillValid();
  398. previousPrefab = highlightObjectPrefab;
  399. SetObjectHighlight();
  400. }
  401. protected virtual void OnTriggerEnter(Collider collider)
  402. {
  403. CheckCanSnap(collider.GetComponentInParent<VRTK_InteractableObject>());
  404. }
  405. protected virtual void OnTriggerExit(Collider collider)
  406. {
  407. CheckCanUnsnap(collider.GetComponentInParent<VRTK_InteractableObject>());
  408. }
  409. protected virtual void CheckCanSnap(VRTK_InteractableObject interactableObjectCheck)
  410. {
  411. if (interactableObjectCheck != null && ValidSnapObject(interactableObjectCheck, true))
  412. {
  413. AddCurrentValidSnapObject(interactableObjectCheck);
  414. if (!isSnapped)
  415. {
  416. ToggleHighlight(interactableObjectCheck, true);
  417. interactableObjectCheck.SetSnapDropZoneHover(this, true);
  418. if (!willSnap)
  419. {
  420. OnObjectEnteredSnapDropZone(SetSnapDropZoneEvent(interactableObjectCheck.gameObject));
  421. }
  422. willSnap = true;
  423. ToggleHighlightColor();
  424. }
  425. }
  426. }
  427. protected virtual void CheckCanUnsnap(VRTK_InteractableObject interactableObjectCheck)
  428. {
  429. if (interactableObjectCheck != null && currentValidSnapInteractableObjects.Contains(interactableObjectCheck) && ValidUnsnap(interactableObjectCheck))
  430. {
  431. if (isSnapped && currentSnappedObject == interactableObjectCheck)
  432. {
  433. ForceUnsnap();
  434. }
  435. RemoveCurrentValidSnapObject(interactableObjectCheck);
  436. if (!ValidSnappableObjectIsHovering())
  437. {
  438. ToggleHighlight(interactableObjectCheck, false);
  439. willSnap = false;
  440. }
  441. interactableObjectCheck.SetSnapDropZoneHover(this, false);
  442. if (ValidSnapObject(interactableObjectCheck, true))
  443. {
  444. ToggleHighlightColor();
  445. OnObjectExitedSnapDropZone(SetSnapDropZoneEvent(interactableObjectCheck.gameObject));
  446. }
  447. }
  448. }
  449. protected virtual bool ValidUnsnap(VRTK_InteractableObject interactableObjectCheck)
  450. {
  451. return (interactableObjectCheck.IsGrabbed() || ((snapType != SnapTypes.UseJoint || !float.IsInfinity(GetComponent<Joint>().breakForce)) && interactableObjectCheck.validDrop == VRTK_InteractableObject.ValidDropTypes.DropAnywhere));
  452. }
  453. protected virtual void SnapObjectToZone(VRTK_InteractableObject objectToSnap)
  454. {
  455. if (!isSnapped && ValidSnapObject(objectToSnap, false))
  456. {
  457. SnapObject(objectToSnap);
  458. }
  459. }
  460. protected virtual void UnregisterAllUngrabEvents()
  461. {
  462. for (int i = 0; i < currentValidSnapInteractableObjects.Count; i++)
  463. {
  464. currentValidSnapInteractableObjects[i].InteractableObjectGrabbed -= InteractableObjectGrabbed;
  465. currentValidSnapInteractableObjects[i].InteractableObjectUngrabbed -= InteractableObjectUngrabbed;
  466. }
  467. }
  468. protected virtual bool ValidSnapObject(VRTK_InteractableObject interactableObjectCheck, bool grabState, bool checkGrabState = true)
  469. {
  470. return (interactableObjectCheck != null && (!checkGrabState || interactableObjectCheck.IsGrabbed() == grabState) && !VRTK_PolicyList.Check(interactableObjectCheck.gameObject, validObjectListPolicy));
  471. }
  472. protected virtual string ObjectPath(string name)
  473. {
  474. return HIGHLIGHT_CONTAINER_NAME + "/" + name;
  475. }
  476. protected virtual void CheckSnappedItemExists()
  477. {
  478. if (isSnapped && (currentSnappedObject == null || !currentSnappedObject.gameObject.activeInHierarchy))
  479. {
  480. ForceUnsnap();
  481. OnObjectUnsnappedFromDropZone(SetSnapDropZoneEvent((currentSnappedObject != null ? currentSnappedObject.gameObject : null)));
  482. }
  483. }
  484. protected virtual void CheckPrefabUpdate()
  485. {
  486. //If the highlightObjectPrefab has changed then delete the highlight object in preparation to create a new one
  487. if (previousPrefab != null && previousPrefab != highlightObjectPrefab)
  488. {
  489. DeleteHighlightObject();
  490. }
  491. }
  492. protected virtual void SetObjectHighlight()
  493. {
  494. if (highlightAlwaysActive && !isSnapped && !isHighlighted)
  495. {
  496. SetHighlightObjectActive(true);
  497. ToggleHighlightColor();
  498. }
  499. if (!highlightAlwaysActive && isHighlighted && !ValidSnappableObjectIsHovering())
  500. {
  501. SetHighlightObjectActive(false);
  502. }
  503. }
  504. protected virtual void ToggleHighlightColor()
  505. {
  506. if (Application.isPlaying && highlightAlwaysActive && !isSnapped && objectHighlighter != null)
  507. {
  508. objectHighlighter.Highlight((willSnap && validHighlightColor != Color.clear ? validHighlightColor : highlightColor));
  509. }
  510. }
  511. protected virtual void CreateHighlightersInEditor()
  512. {
  513. if (VRTK_SharedMethods.IsEditTime())
  514. {
  515. GenerateHighlightObject();
  516. if (snapType == SnapTypes.UseJoint && GetComponent<Joint>() == null)
  517. {
  518. VRTK_Logger.Warn(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_GAMEOBJECT, "SnapDropZone:" + name, "Joint", "the same", " because the `Snap Type` is set to `Use Joint`"));
  519. }
  520. GenerateEditorHighlightObject();
  521. ForceSetObjects();
  522. if (highlightEditorObject != null)
  523. {
  524. highlightEditorObject.SetActive(displayDropZoneInEditor);
  525. }
  526. }
  527. }
  528. protected virtual void CheckCurrentValidSnapObjectStillValid()
  529. {
  530. for (int i = 0; i < currentValidSnapInteractableObjects.Count; i++)
  531. {
  532. VRTK_InteractableObject interactableObjectCheck = currentValidSnapInteractableObjects[i];
  533. //if the interactable object associated with it has been snapped to another zone, then unset the current valid snap object
  534. if (interactableObjectCheck != null && interactableObjectCheck.GetStoredSnapDropZone() != null && interactableObjectCheck.GetStoredSnapDropZone() != this)
  535. {
  536. RemoveCurrentValidSnapObject(interactableObjectCheck);
  537. if (isHighlighted && highlightObject != null && !highlightAlwaysActive)
  538. {
  539. SetHighlightObjectActive(false);
  540. }
  541. }
  542. }
  543. }
  544. protected virtual void ForceSetObjects()
  545. {
  546. if (highlightEditorObject == null)
  547. {
  548. Transform forceFindHighlightEditorObject = transform.Find(ObjectPath(HIGHLIGHT_EDITOR_OBJECT_NAME));
  549. highlightEditorObject = (forceFindHighlightEditorObject ? forceFindHighlightEditorObject.gameObject : null);
  550. }
  551. if (highlightObject == null)
  552. {
  553. Transform forceFindHighlightObject = transform.Find(ObjectPath(HIGHLIGHT_OBJECT_NAME));
  554. highlightObject = (forceFindHighlightObject ? forceFindHighlightObject.gameObject : null);
  555. }
  556. if (highlightContainer == null)
  557. {
  558. Transform forceFindHighlightContainer = transform.Find(HIGHLIGHT_CONTAINER_NAME);
  559. highlightContainer = (forceFindHighlightContainer ? forceFindHighlightContainer.gameObject : null);
  560. }
  561. }
  562. protected virtual void GenerateContainer()
  563. {
  564. if (highlightContainer == null || transform.Find(HIGHLIGHT_CONTAINER_NAME) == null)
  565. {
  566. highlightContainer = new GameObject(HIGHLIGHT_CONTAINER_NAME);
  567. highlightContainer.transform.SetParent(transform);
  568. highlightContainer.transform.localPosition = Vector3.zero;
  569. highlightContainer.transform.localRotation = Quaternion.identity;
  570. highlightContainer.transform.localScale = Vector3.one;
  571. }
  572. }
  573. protected virtual void DisableHighlightShadows()
  574. {
  575. if (highlightObject != null)
  576. {
  577. Renderer[] foundRenderers = highlightObject.GetComponentsInChildren<Renderer>(true);
  578. for (int i = 0; i < foundRenderers.Length; i++)
  579. {
  580. foundRenderers[i].receiveShadows = false;
  581. foundRenderers[i].shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
  582. }
  583. }
  584. }
  585. protected virtual void SetContainer()
  586. {
  587. Transform findContainer = transform.Find(HIGHLIGHT_CONTAINER_NAME);
  588. if (findContainer != null)
  589. {
  590. highlightContainer = findContainer.gameObject;
  591. }
  592. }
  593. protected virtual void GenerateObjects()
  594. {
  595. GenerateHighlightObject();
  596. if (highlightObject != null && objectHighlighter == null)
  597. {
  598. InitialiseHighlighter();
  599. }
  600. }
  601. protected virtual void SnapObject(VRTK_InteractableObject interactableObjectCheck)
  602. {
  603. //If the item is in a snappable position and this drop zone isn't snapped and the collider is a valid interactable object
  604. if (willSnap && !isSnapped && ValidSnapObject(interactableObjectCheck, false))
  605. {
  606. //Only snap it to the drop zone if it's not already in a drop zone
  607. if (!interactableObjectCheck.IsInSnapDropZone())
  608. {
  609. if (highlightObject != null)
  610. {
  611. //Turn off the drop zone highlighter
  612. SetHighlightObjectActive(false);
  613. }
  614. Vector3 newLocalScale = GetNewLocalScale(interactableObjectCheck);
  615. if (transitionInPlaceRoutine != null)
  616. {
  617. StopCoroutine(transitionInPlaceRoutine);
  618. }
  619. isSnapped = true;
  620. currentSnappedObject = interactableObjectCheck;
  621. if (cloneNewOnUnsnap)
  622. {
  623. CreatePermanentClone();
  624. }
  625. if (gameObject.activeInHierarchy)
  626. {
  627. transitionInPlaceRoutine = StartCoroutine(UpdateTransformDimensions(interactableObjectCheck, highlightContainer, newLocalScale, snapDuration));
  628. }
  629. interactableObjectCheck.ToggleSnapDropZone(this, true);
  630. }
  631. }
  632. //Force reset isSnapped if the item is grabbed but isSnapped is still true
  633. isSnapped = (isSnapped && interactableObjectCheck != null && interactableObjectCheck.IsGrabbed() ? false : isSnapped);
  634. willSnap = !isSnapped;
  635. wasSnapped = false;
  636. }
  637. protected virtual void CreatePermanentClone()
  638. {
  639. VRTK_BaseHighlighter currentSnappedObjectHighlighter = currentSnappedObject.GetComponent<VRTK_BaseHighlighter>();
  640. if (currentSnappedObjectHighlighter != null)
  641. {
  642. currentSnappedObjectHighlighter.Unhighlight();
  643. }
  644. objectToClone = Instantiate(currentSnappedObject.gameObject);
  645. objectToClone.transform.position = highlightContainer.transform.position;
  646. objectToClone.transform.rotation = highlightContainer.transform.rotation;
  647. Collider[] clonedObjectStates = currentSnappedObject.GetComponentsInChildren<Collider>();
  648. clonedObjectColliderStates = new bool[clonedObjectStates.Length];
  649. for (int i = 0; i < clonedObjectStates.Length; i++)
  650. {
  651. Collider clonedObjectColliderState = clonedObjectStates[i];
  652. clonedObjectColliderStates[i] = clonedObjectColliderState.isTrigger;
  653. clonedObjectColliderState.isTrigger = true;
  654. }
  655. objectToClone.SetActive(false);
  656. }
  657. protected virtual void ResetPermanentCloneColliders(GameObject objectToReset)
  658. {
  659. if (objectToReset != null && clonedObjectColliderStates.Length > 0)
  660. {
  661. Collider[] clonedObjectStates = objectToReset.GetComponentsInChildren<Collider>();
  662. for (int i = 0; i < clonedObjectStates.Length; i++)
  663. {
  664. Collider clonedObjectColliderState = clonedObjectStates[i];
  665. if (clonedObjectColliderStates.Length > i)
  666. {
  667. clonedObjectColliderState.isTrigger = clonedObjectColliderStates[i];
  668. }
  669. }
  670. }
  671. }
  672. protected virtual void ResnapPermanentClone()
  673. {
  674. if (objectToClone != null)
  675. {
  676. float savedSnapDuration = snapDuration;
  677. snapDuration = 0f;
  678. objectToClone.SetActive(true);
  679. ResetPermanentCloneColliders(objectToClone);
  680. ForceSnap(objectToClone);
  681. snapDuration = savedSnapDuration;
  682. }
  683. }
  684. protected virtual void UnsnapObject()
  685. {
  686. if (currentSnappedObject != null)
  687. {
  688. ResetPermanentCloneColliders(currentSnappedObject.gameObject);
  689. RemoveCurrentValidSnapObject(currentSnappedObject);
  690. }
  691. isSnapped = false;
  692. wasSnapped = true;
  693. VRTK_InteractableObject checkCanSnapObject = currentSnappedObject;
  694. currentSnappedObject = null;
  695. ResetSnapDropZoneJoint();
  696. if (transitionInPlaceRoutine != null)
  697. {
  698. StopCoroutine(transitionInPlaceRoutine);
  699. }
  700. if (cloneNewOnUnsnap)
  701. {
  702. ResnapPermanentClone();
  703. }
  704. if (checkCanSnapRoutine != null)
  705. {
  706. StopCoroutine(checkCanSnapRoutine);
  707. }
  708. if (gameObject.activeInHierarchy)
  709. {
  710. checkCanSnapRoutine = StartCoroutine(CheckCanSnapObjectAtEndOfFrame(checkCanSnapObject));
  711. }
  712. checkCanSnapObject = null;
  713. }
  714. protected virtual Vector3 GetNewLocalScale(VRTK_InteractableObject checkObject)
  715. {
  716. // If apply scaling is checked then use the drop zone scale to resize the object
  717. Vector3 newLocalScale = checkObject.transform.localScale;
  718. if (applyScalingOnSnap)
  719. {
  720. checkObject.StoreLocalScale();
  721. newLocalScale = Vector3.Scale(checkObject.transform.localScale, transform.localScale);
  722. }
  723. return newLocalScale;
  724. }
  725. protected virtual IEnumerator CheckCanSnapObjectAtEndOfFrame(VRTK_InteractableObject interactableObjectCheck)
  726. {
  727. yield return new WaitForEndOfFrame();
  728. CheckCanSnap(interactableObjectCheck);
  729. }
  730. protected virtual IEnumerator UpdateTransformDimensions(VRTK_InteractableObject ioCheck, GameObject endSettings, Vector3 endScale, float duration)
  731. {
  732. float elapsedTime = 0f;
  733. Transform ioTransform = ioCheck.transform;
  734. Vector3 startPosition = ioTransform.position;
  735. Quaternion startRotation = ioTransform.rotation;
  736. Vector3 startScale = ioTransform.localScale;
  737. bool storedKinematicState = ioCheck.isKinematic;
  738. ioCheck.isKinematic = true;
  739. while (elapsedTime <= duration)
  740. {
  741. elapsedTime += Time.deltaTime;
  742. if (ioTransform != null && endSettings != null)
  743. {
  744. ioTransform.position = Vector3.Lerp(startPosition, endSettings.transform.position, (elapsedTime / duration));
  745. ioTransform.rotation = Quaternion.Lerp(startRotation, endSettings.transform.rotation, (elapsedTime / duration));
  746. ioTransform.localScale = Vector3.Lerp(startScale, endScale, (elapsedTime / duration));
  747. }
  748. yield return null;
  749. }
  750. if (ioTransform != null && endSettings != null)
  751. {
  752. //Force all to the last setting in case anything has moved during the transition
  753. ioTransform.position = endSettings.transform.position;
  754. ioTransform.rotation = endSettings.transform.rotation;
  755. ioTransform.localScale = endScale;
  756. }
  757. ioCheck.isKinematic = storedKinematicState;
  758. SetDropSnapType(ioCheck);
  759. }
  760. protected virtual void SetDropSnapType(VRTK_InteractableObject ioCheck)
  761. {
  762. switch (snapType)
  763. {
  764. case SnapTypes.UseKinematic:
  765. ioCheck.SaveCurrentState();
  766. ioCheck.isKinematic = true;
  767. break;
  768. case SnapTypes.UseParenting:
  769. ioCheck.SaveCurrentState();
  770. ioCheck.isKinematic = true;
  771. ioCheck.transform.SetParent(transform);
  772. break;
  773. case SnapTypes.UseJoint:
  774. SetSnapDropZoneJoint(ioCheck.GetComponent<Rigidbody>());
  775. break;
  776. }
  777. OnObjectSnappedToDropZone(SetSnapDropZoneEvent(ioCheck.gameObject));
  778. }
  779. protected virtual void SetSnapDropZoneJoint(Rigidbody snapTo)
  780. {
  781. Joint snapDropZoneJoint = GetComponent<Joint>();
  782. if (snapDropZoneJoint == null)
  783. {
  784. VRTK_Logger.Error(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_GAMEOBJECT, "SnapDropZone:" + name, "Joint", "the same", " because the `Snap Type` is set to `Use Joint`"));
  785. return;
  786. }
  787. if (snapTo == null)
  788. {
  789. VRTK_Logger.Error(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_GAMEOBJECT, "VRTK_SnapDropZone", "Rigidbody", "the `VRTK_InteractableObject`"));
  790. return;
  791. }
  792. snapDropZoneJoint.connectedBody = snapTo;
  793. originalJointCollisionState = snapDropZoneJoint.enableCollision;
  794. //need to set this to true otherwise highlighting doesn't work again on grab
  795. snapDropZoneJoint.enableCollision = true;
  796. }
  797. protected virtual void ResetSnapDropZoneJoint()
  798. {
  799. Joint snapDropZoneJoint = GetComponent<Joint>();
  800. if (snapDropZoneJoint != null)
  801. {
  802. snapDropZoneJoint.enableCollision = originalJointCollisionState;
  803. }
  804. }
  805. protected virtual void AddCurrentValidSnapObject(VRTK_InteractableObject givenObject)
  806. {
  807. if (givenObject != null)
  808. {
  809. if (VRTK_SharedMethods.AddListValue(currentValidSnapInteractableObjects, givenObject, true))
  810. {
  811. givenObject.InteractableObjectGrabbed += InteractableObjectGrabbed;
  812. givenObject.InteractableObjectUngrabbed += InteractableObjectUngrabbed;
  813. }
  814. }
  815. }
  816. protected virtual void RemoveCurrentValidSnapObject(VRTK_InteractableObject givenObject)
  817. {
  818. if (givenObject != null)
  819. {
  820. if (currentValidSnapInteractableObjects.Remove(givenObject))
  821. {
  822. givenObject.InteractableObjectGrabbed -= InteractableObjectGrabbed;
  823. givenObject.InteractableObjectUngrabbed -= InteractableObjectUngrabbed;
  824. }
  825. }
  826. }
  827. protected virtual void InteractableObjectGrabbed(object sender, InteractableObjectEventArgs e)
  828. {
  829. VRTK_InteractableObject grabbedInteractableObject = sender as VRTK_InteractableObject;
  830. if (!grabbedInteractableObject.IsInSnapDropZone())
  831. {
  832. CheckCanSnap(grabbedInteractableObject);
  833. }
  834. }
  835. protected virtual void InteractableObjectUngrabbed(object sender, InteractableObjectEventArgs e)
  836. {
  837. VRTK_InteractableObject releasedInteractableObject = sender as VRTK_InteractableObject;
  838. if (attemptTransitionAtEndOfFrameRoutine != null)
  839. {
  840. StopCoroutine(attemptTransitionAtEndOfFrameRoutine);
  841. }
  842. attemptTransitionAtEndOfFrameRoutine = StartCoroutine(AttemptForceSnapAtEndOfFrame(releasedInteractableObject));
  843. }
  844. protected virtual void AttemptForceSnap(VRTK_InteractableObject objectToSnap)
  845. {
  846. //force snap settings on
  847. willSnap = true;
  848. //Force touch one of the object's colliders on this trigger collider
  849. SnapObjectToZone(objectToSnap);
  850. }
  851. protected virtual IEnumerator AttemptForceSnapAtEndOfFrame(VRTK_InteractableObject objectToSnap)
  852. {
  853. yield return new WaitForEndOfFrame();
  854. objectToSnap.SaveCurrentState();
  855. AttemptForceSnap(objectToSnap);
  856. }
  857. protected virtual void ToggleHighlight(VRTK_InteractableObject checkObject, bool state)
  858. {
  859. if (highlightObject != null && ValidSnapObject(checkObject, true, state))
  860. {
  861. //Toggle the highlighter state
  862. SetHighlightObjectActive(state);
  863. }
  864. }
  865. protected virtual void CopyObject(GameObject objectBlueprint, ref GameObject clonedObject, string givenName)
  866. {
  867. GenerateContainer();
  868. Vector3 saveScale = transform.localScale;
  869. transform.localScale = Vector3.one;
  870. clonedObject = Instantiate(objectBlueprint, highlightContainer.transform) as GameObject;
  871. clonedObject.name = givenName;
  872. //default position of new highlight object
  873. clonedObject.transform.localPosition = Vector3.zero;
  874. clonedObject.transform.localRotation = Quaternion.identity;
  875. transform.localScale = saveScale;
  876. CleanHighlightObject(clonedObject);
  877. }
  878. protected virtual void GenerateHighlightObject()
  879. {
  880. //If there is a given highlight prefab and no existing highlight object then create a new highlight object
  881. if (highlightObjectPrefab != null && highlightObject == null && transform.Find(ObjectPath(HIGHLIGHT_OBJECT_NAME)) == null)
  882. {
  883. CopyObject(highlightObjectPrefab, ref highlightObject, HIGHLIGHT_OBJECT_NAME);
  884. }
  885. //if highlight object exists but not in the variable then force grab it
  886. Transform checkForChild = transform.Find(ObjectPath(HIGHLIGHT_OBJECT_NAME));
  887. if (checkForChild != null && highlightObject == null)
  888. {
  889. highlightObject = checkForChild.gameObject;
  890. }
  891. //if no highlight object prefab is set but a highlight object is found then destroy the highlight object
  892. if (highlightObjectPrefab == null && highlightObject != null)
  893. {
  894. DeleteHighlightObject();
  895. }
  896. DisableHighlightShadows();
  897. SetHighlightObjectActive(false);
  898. SetContainer();
  899. }
  900. protected virtual void SetHighlightObjectActive(bool state)
  901. {
  902. if (highlightObject != null)
  903. {
  904. highlightObject.SetActive(state);
  905. isHighlighted = state;
  906. }
  907. }
  908. protected virtual void DeleteHighlightObject()
  909. {
  910. ChooseDestroyType(transform.Find(HIGHLIGHT_CONTAINER_NAME));
  911. highlightContainer = null;
  912. highlightObject = null;
  913. objectHighlighter = null;
  914. }
  915. protected virtual void GenerateEditorHighlightObject()
  916. {
  917. if (highlightObject != null && highlightEditorObject == null && transform.Find(ObjectPath(HIGHLIGHT_EDITOR_OBJECT_NAME)) == null)
  918. {
  919. CopyObject(highlightObject, ref highlightEditorObject, HIGHLIGHT_EDITOR_OBJECT_NAME);
  920. Renderer[] renderers = highlightEditorObject.GetComponentsInChildren<Renderer>();
  921. for (int i = 0; i < renderers.Length; i++)
  922. {
  923. renderers[i].material = Resources.Load("SnapDropZoneEditorObject") as Material;
  924. }
  925. highlightEditorObject.SetActive(true);
  926. }
  927. }
  928. protected virtual void CleanHighlightObject(GameObject objectToClean)
  929. {
  930. //If the highlight object has any child snap zones, then force delete these
  931. VRTK_SnapDropZone[] deleteSnapZones = objectToClean.GetComponentsInChildren<VRTK_SnapDropZone>(true);
  932. for (int i = 0; i < deleteSnapZones.Length; i++)
  933. {
  934. ChooseDestroyType(deleteSnapZones[i].gameObject);
  935. }
  936. //determine components that shouldn't be deleted from highlight object
  937. string[] validComponents = new string[] { "Transform", "MeshFilter", "MeshRenderer", "SkinnedMeshRenderer", "VRTK_GameObjectLinker" };
  938. //First clean out the joints cause RigidBodys depends on them.
  939. Joint[] joints = objectToClean.GetComponentsInChildren<Joint>(true);
  940. for (int i = 0; i < joints.Length; i++)
  941. {
  942. ChooseDestroyType(joints[i]);
  943. }
  944. //Go through all of the components on the highlighted object and delete any components that aren't in the valid component list
  945. Component[] components = objectToClean.GetComponentsInChildren<Component>(true);
  946. for (int i = 0; i < components.Length; i++)
  947. {
  948. Component component = components[i];
  949. bool valid = false;
  950. //Loop through each valid component and check to see if this component is valid
  951. for (int j = 0; j < validComponents.Length; j++)
  952. {
  953. //if it's a valid component then break the check
  954. if (component.GetType().ToString().Contains("." + validComponents[j]))
  955. {
  956. valid = true;
  957. break;
  958. }
  959. }
  960. //if this is a valid component then just continue to the next component
  961. if (valid)
  962. {
  963. continue;
  964. }
  965. //If not a valid component then delete it
  966. ChooseDestroyType(component);
  967. }
  968. }
  969. protected virtual void InitialiseHighlighter()
  970. {
  971. VRTK_BaseHighlighter existingHighlighter = VRTK_BaseHighlighter.GetActiveHighlighter(gameObject);
  972. //If no highlighter is found on the GameObject then create the default one
  973. if (existingHighlighter == null)
  974. {
  975. highlightObject.AddComponent<VRTK_MaterialColorSwapHighlighter>();
  976. }
  977. else
  978. {
  979. VRTK_SharedMethods.CloneComponent(existingHighlighter, highlightObject);
  980. }
  981. //Initialise highlighter and set highlight colour
  982. objectHighlighter = highlightObject.GetComponent<VRTK_BaseHighlighter>();
  983. objectHighlighter.unhighlightOnDisable = false;
  984. objectHighlighter.Initialise(highlightColor);
  985. objectHighlighter.Highlight(highlightColor);
  986. //if the object highlighter is using a cloned object then disable the created highlight object's renderers
  987. if (objectHighlighter.UsesClonedObject())
  988. {
  989. Renderer[] renderers = GetComponentsInChildren<Renderer>(true);
  990. for (int i = 0; i < renderers.Length; i++)
  991. {
  992. if (!VRTK_PlayerObject.IsPlayerObject(renderers[i].gameObject, VRTK_PlayerObject.ObjectTypes.Highlighter))
  993. {
  994. renderers[i].enabled = false;
  995. }
  996. }
  997. }
  998. }
  999. protected virtual void ChooseDestroyType(Transform deleteTransform)
  1000. {
  1001. if (deleteTransform != null)
  1002. {
  1003. ChooseDestroyType(deleteTransform.gameObject);
  1004. }
  1005. }
  1006. protected virtual void ChooseDestroyType(GameObject deleteObject)
  1007. {
  1008. if (VRTK_SharedMethods.IsEditTime())
  1009. {
  1010. if (deleteObject != null)
  1011. {
  1012. DestroyImmediate(deleteObject);
  1013. }
  1014. }
  1015. else
  1016. {
  1017. if (deleteObject != null)
  1018. {
  1019. Destroy(deleteObject);
  1020. }
  1021. }
  1022. }
  1023. protected virtual void ChooseDestroyType(Component deleteComponent)
  1024. {
  1025. if (VRTK_SharedMethods.IsEditTime())
  1026. {
  1027. if (deleteComponent != null)
  1028. {
  1029. DestroyImmediate(deleteComponent);
  1030. }
  1031. }
  1032. else
  1033. {
  1034. if (deleteComponent != null)
  1035. {
  1036. Destroy(deleteComponent);
  1037. }
  1038. }
  1039. }
  1040. protected virtual void OnDrawGizmosSelected()
  1041. {
  1042. if (highlightObject != null && !displayDropZoneInEditor)
  1043. {
  1044. Vector3 boxSize = VRTK_SharedMethods.GetBounds(highlightObject.transform).size * 1.05f;
  1045. Gizmos.color = Color.red;
  1046. Gizmos.DrawWireCube(highlightObject.transform.position, boxSize);
  1047. }
  1048. }
  1049. protected virtual IEnumerator OverridePreviousStateAtEndOfFrame(VRTK_InteractableObject io, Transform parent, bool kinematic, bool grabbable)
  1050. {
  1051. yield return new WaitForEndOfFrame();
  1052. io.OverridePreviousState(parent, kinematic, grabbable);
  1053. }
  1054. }
  1055. }