|
|
- // Snap Drop Zone|Prefabs|0080
- namespace VRTK
- {
- using UnityEngine;
- using System.Collections;
- using System.Collections.Generic;
- using Highlighters;
-
- /// <summary>
- /// Event Payload
- /// </summary>
- /// <param name="snappedObject">The interactable object that is dealing with the snap drop zone.</param>
- public struct SnapDropZoneEventArgs
- {
- public GameObject snappedObject;
- }
-
- /// <summary>
- /// Event Payload
- /// </summary>
- /// <param name="sender">this object</param>
- /// <param name="e"><see cref="SnapDropZoneEventArgs"/></param>
- public delegate void SnapDropZoneEventHandler(object sender, SnapDropZoneEventArgs e);
-
- /// <summary>
- /// 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.
- /// </summary>
- /// <remarks>
- /// **Prefab Usage:**
- /// * Place the `VRTK/Prefabs/SnapDropZone/SnapDropZone` prefab into the scene hierarchy.
- /// * 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.
- /// * If no `VRTK_BaseHighlighter` derivative is applied to the SnapDropZone then the default MaterialColorSwap Highlighter will be used.
- /// * The collision zone that activates the SnapDropZone is a `Sphere Collider` by default but can be amended or replaced on the SnapDropZone GameObject.
- /// * 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`.
- /// </remarks>
- /// <example>
- /// `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.
- /// </example>
- [ExecuteInEditMode]
- public class VRTK_SnapDropZone : MonoBehaviour
- {
- /// <summary>
- /// The types of snap on release available.
- /// </summary>
- public enum SnapTypes
- {
- /// <summary>
- /// Will set the interactable object rigidbody to `isKinematic = true`.
- /// </summary>
- UseKinematic,
- /// <summary>
- /// Will attach the interactable object's rigidbody to the provided joint as it's `Connected Body`.
- /// </summary>
- UseJoint,
- /// <summary>
- /// Will set the SnapDropZone as the interactable object's parent and set it's rigidbody to `isKinematic = true`.
- /// </summary>
- UseParenting
- }
-
- [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.")]
- public GameObject highlightObjectPrefab;
- [Tooltip("The Snap Type to apply when a valid interactable object is dropped within the snap zone.")]
- public SnapTypes snapType = SnapTypes.UseKinematic;
- [Tooltip("The amount of time it takes for the object being snapped to move into the new snapped position, rotation and scale.")]
- public float snapDuration = 0f;
- [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.")]
- public bool applyScalingOnSnap = false;
- [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.")]
- public bool cloneNewOnUnsnap = false;
- [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.")]
- public Color highlightColor = Color.clear;
- [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.")]
- public Color validHighlightColor = Color.clear;
- [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.")]
- public bool highlightAlwaysActive = false;
- [Tooltip("A specified VRTK_PolicyList to use to determine which interactable objects will be snapped to the snap drop zone on release.")]
- public VRTK_PolicyList validObjectListPolicy;
- [Tooltip("If this is checked then the drop zone highlight section will be displayed in the scene editor window.")]
- public bool displayDropZoneInEditor = true;
-
- [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.")]
- public VRTK_InteractableObject defaultSnappedInteractableObject;
-
- [Header("Obsolete Settings")]
-
- [System.Obsolete("`VRTK_SnapDropZone.defaultSnappedObject` has been replaced with the `VRTK_SnapDropZone.defaultSnappedInteractableObject`. This parameter will be removed in a future version of VRTK.")]
- [ObsoleteInspector]
- public GameObject defaultSnappedObject;
-
- /// <summary>
- /// Emitted when a valid interactable object enters the snap drop zone trigger collider.
- /// </summary>
- public event SnapDropZoneEventHandler ObjectEnteredSnapDropZone;
- /// <summary>
- /// Emitted when a valid interactable object exists the snap drop zone trigger collider.
- /// </summary>
- public event SnapDropZoneEventHandler ObjectExitedSnapDropZone;
- /// <summary>
- /// Emitted when an interactable object is successfully snapped into a drop zone.
- /// </summary>
- public event SnapDropZoneEventHandler ObjectSnappedToDropZone;
- /// <summary>
- /// Emitted when an interactable object is removed from a snapped drop zone.
- /// </summary>
- public event SnapDropZoneEventHandler ObjectUnsnappedFromDropZone;
-
- protected GameObject previousPrefab;
- protected GameObject highlightContainer;
- protected GameObject highlightObject;
- protected GameObject highlightEditorObject = null;
-
- protected List<VRTK_InteractableObject> currentValidSnapInteractableObjects = new List<VRTK_InteractableObject>();
- protected VRTK_InteractableObject currentSnappedObject = null;
- protected GameObject objectToClone = null;
- protected bool[] clonedObjectColliderStates = new bool[0];
-
- protected bool willSnap = false;
- protected bool isSnapped = false;
- protected bool wasSnapped = false;
- protected bool isHighlighted = false;
-
- protected VRTK_BaseHighlighter objectHighlighter;
- protected Coroutine transitionInPlaceRoutine;
- protected Coroutine attemptTransitionAtEndOfFrameRoutine;
- protected Coroutine checkCanSnapRoutine;
- protected bool originalJointCollisionState = false;
-
- protected Coroutine overridePreviousStateAtEndOfFrameRoutine;
-
- protected const string HIGHLIGHT_CONTAINER_NAME = "HighlightContainer";
- protected const string HIGHLIGHT_OBJECT_NAME = "HighlightObject";
- protected const string HIGHLIGHT_EDITOR_OBJECT_NAME = "EditorHighlightObject";
-
- public virtual void OnObjectEnteredSnapDropZone(SnapDropZoneEventArgs e)
- {
- if (ObjectEnteredSnapDropZone != null)
- {
- ObjectEnteredSnapDropZone(this, e);
- }
- }
-
- public virtual void OnObjectExitedSnapDropZone(SnapDropZoneEventArgs e)
- {
- if (ObjectExitedSnapDropZone != null)
- {
- ObjectExitedSnapDropZone(this, e);
- }
- }
-
- public virtual void OnObjectSnappedToDropZone(SnapDropZoneEventArgs e)
- {
- if (ObjectSnappedToDropZone != null)
- {
- ObjectSnappedToDropZone(this, e);
- }
- }
-
- public virtual void OnObjectUnsnappedFromDropZone(SnapDropZoneEventArgs e)
- {
- UnsnapObject();
- if (ObjectUnsnappedFromDropZone != null)
- {
- ObjectUnsnappedFromDropZone(this, e);
- }
- }
-
- public virtual SnapDropZoneEventArgs SetSnapDropZoneEvent(GameObject interactableObject)
- {
- SnapDropZoneEventArgs e;
- e.snappedObject = interactableObject;
- return e;
- }
-
- /// <summary>
- /// The InitaliseHighlightObject method sets up the highlight object based on the given Highlight Object Prefab.
- /// </summary>
- /// <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>
- public virtual void InitaliseHighlightObject(bool removeOldObject = false)
- {
- //force delete previous created highlight object
- if (removeOldObject)
- {
- DeleteHighlightObject();
- }
- //Always remove editor highlight object at runtime
- ChooseDestroyType(transform.Find(ObjectPath(HIGHLIGHT_EDITOR_OBJECT_NAME)));
- highlightEditorObject = null;
-
- GenerateObjects();
- }
-
- /// <summary>
- /// the ForceSnap method attempts to automatically attach a valid GameObject to the snap drop zone.
- /// </summary>
- /// <param name="objectToSnap">The GameObject to attempt to snap.</param>
- public virtual void ForceSnap(GameObject objectToSnap)
- {
- ForceSnap(objectToSnap.GetComponentInParent<VRTK_InteractableObject>());
- }
-
- /// <summary>
- /// the ForceSnap method attempts to automatically attach a valid Interactable Object to the snap drop zone.
- /// </summary>
- /// <param name="objectToSnap">The Interactable Object to attempt to snap.</param>
- protected virtual void ForceSnap(VRTK_InteractableObject interactableObjectToSnap)
- {
- if (interactableObjectToSnap != null)
- {
- if (attemptTransitionAtEndOfFrameRoutine != null)
- {
- StopCoroutine(attemptTransitionAtEndOfFrameRoutine);
- }
-
- if (checkCanSnapRoutine != null)
- {
- StopCoroutine(checkCanSnapRoutine);
- }
-
- if (interactableObjectToSnap.IsGrabbed())
- {
- interactableObjectToSnap.ForceStopInteracting();
- }
-
- if (gameObject.activeInHierarchy)
- {
- attemptTransitionAtEndOfFrameRoutine = StartCoroutine(AttemptForceSnapAtEndOfFrame(interactableObjectToSnap));
- }
- }
- }
-
- /// <summary>
- /// The ForceUnsnap method attempts to automatically remove the current snapped game object from the snap drop zone.
- /// </summary>
- public virtual void ForceUnsnap()
- {
- if (isSnapped && ValidSnapObject(currentSnappedObject, false))
- {
- currentSnappedObject.ToggleSnapDropZone(this, false);
- }
- }
-
- /// <summary>
- /// The ValidSnappableObjectIsHovering method determines if any valid objects are currently hovering in the snap drop zone area.
- /// </summary>
- /// <returns>Returns true if a valid object is currently in the snap drop zone area.</returns>
- public virtual bool ValidSnappableObjectIsHovering()
- {
- for (int i = 0; i < currentValidSnapInteractableObjects.Count; i++)
- {
- if (currentValidSnapInteractableObjects[i].IsGrabbed())
- {
- return true;
- }
- }
- return false;
- }
-
- /// <summary>
- /// The IsObjectHovering method determines if the given GameObject is currently howvering (but not snapped) in the snap drop zone area.
- /// </summary>
- /// <param name="checkObject">The GameObject to check to see if it's hovering in the snap drop zone area.</param>
- /// <returns>Returns true if the given GameObject is hovering (but not snapped) in the snap drop zone area.</returns>
- public virtual bool IsObjectHovering(GameObject checkObject)
- {
- VRTK_InteractableObject interactableObjectToCheck = checkObject.GetComponentInParent<VRTK_InteractableObject>();
- return (interactableObjectToCheck != null ? currentValidSnapInteractableObjects.Contains(interactableObjectToCheck) : false);
- }
-
- /// <summary>
- /// The IsInteractableObjectHovering method determines if the given Interactable Object script is currently howvering (but not snapped) in the snap drop zone area.
- /// </summary>
- /// <param name="checkObject">The Interactable Object script to check to see if it's hovering in the snap drop zone area.</param>
- /// <returns>Returns true if the given Interactable Object script is hovering (but not snapped) in the snap drop zone area.</returns>
- public virtual bool IsInteractableObjectHovering(VRTK_InteractableObject checkObject)
- {
- return (checkObject != null ? currentValidSnapInteractableObjects.Contains(checkObject) : false);
- }
-
- /// <summary>
- /// The GetHoveringObjects method returns a List of valid GameObjects that are currently hovering (but not snapped) in the snap drop zone area.
- /// </summary>
- /// <returns>The List of valid GameObjects that are hovering (but not snapped) in the snap drop zone area.</returns>
- public virtual List<GameObject> GetHoveringObjects()
- {
- List<GameObject> returnList = new List<GameObject>();
- for (int i = 0; i < currentValidSnapInteractableObjects.Count; i++)
- {
- VRTK_SharedMethods.AddListValue(returnList, currentValidSnapInteractableObjects[i].gameObject);
- }
- return returnList;
- }
-
- /// <summary>
- /// The GetHoveringInteractableObjects method returns a List of valid Interactable Object scripts that are currently hovering (but not snapped) in the snap drop zone area.
- /// </summary>
- /// <returns>The List of valid Interactable Object scripts that are hovering (but not snapped) in the snap drop zone area.</returns>
- public virtual List<VRTK_InteractableObject> GetHoveringInteractableObjects()
- {
- return currentValidSnapInteractableObjects;
- }
-
- /// <summary>
- /// The GetCurrentSnappedObejct method returns the GameObject that is currently snapped in the snap drop zone area.
- /// </summary>
- /// <returns>The GameObject that is currently snapped in the snap drop zone area.</returns>
- public virtual GameObject GetCurrentSnappedObject()
- {
- return (currentSnappedObject != null ? currentSnappedObject.gameObject : null);
- }
-
- /// <summary>
- /// The GetCurrentSnappedInteractableObject method returns the Interactable Object script that is currently snapped in the snap drop zone area.
- /// </summary>
- /// <returns>The Interactable Object script that is currently snapped in the snap drop zone area.</returns>
- public virtual VRTK_InteractableObject GetCurrentSnappedInteractableObject()
- {
- return currentSnappedObject;
- }
-
- /// <summary>
- /// The Clone method returns the GameObject of the cloned snap drop zone
- /// </summary>
- /// <param name="position">Position of the cloned GameObject</param>
- /// <returns>The GameObject of the clone</returns>
- public virtual GameObject Clone(Vector3 position)
- {
- VRTK_SnapDropZone cloneSDZ = Instantiate(gameObject, position, transform.rotation).GetComponent<VRTK_SnapDropZone>();
-
- for (int childID = 0; childID < cloneSDZ.transform.childCount; childID++)
- {
- Transform child = cloneSDZ.transform.GetChild(childID);
- if (child.GetComponent<VRTK_InteractableObject>() != null)
- {
- Destroy(child.gameObject);
- }
- }
-
- if (isSnapped)
- {
- VRTK_InteractableObject currObject = currentSnappedObject;
-
- //Get copy of Objects original state
- Transform previousParent;
- bool previousKinematic;
- bool previousGrabbable;
- currObject.GetPreviousState(out previousParent, out previousKinematic, out previousGrabbable);
-
- GameObject clonedObject = null;
- if (cloneNewOnUnsnap)
- {
- clonedObject = Instantiate(objectToClone);
- clonedObject.SetActive(true);
- }
- else
- {
- clonedObject = Instantiate(currObject.gameObject);
- }
-
- clonedObject.transform.position = cloneSDZ.transform.position;
- cloneSDZ.ForceSnap(clonedObject);
-
- overridePreviousStateAtEndOfFrameRoutine = StartCoroutine(OverridePreviousStateAtEndOfFrame(clonedObject.GetComponent<VRTK_InteractableObject>(), previousParent, previousKinematic, previousGrabbable));
- }
-
- return cloneSDZ.gameObject;
- }
-
- /// <summary>
- /// The Clone method returns the GameObject of the cloned snap drop zone
- /// </summary>
- /// <returns>The GameObject of the clone</returns>
- public virtual GameObject Clone()
- {
- return Clone(Vector3.zero);
- }
-
- protected virtual void Awake()
- {
- if (Application.isPlaying)
- {
- InitaliseHighlightObject();
- }
- }
-
- protected virtual void OnApplicationQuit()
- {
- if (objectHighlighter != null)
- {
- objectHighlighter.Unhighlight();
- }
- }
-
- protected virtual void OnEnable()
- {
- currentValidSnapInteractableObjects.Clear();
- currentSnappedObject = null;
- objectToClone = null;
- clonedObjectColliderStates = new bool[0];
- willSnap = false;
- isSnapped = false;
- wasSnapped = false;
- isHighlighted = false;
-
- #pragma warning disable 618
- if (defaultSnappedObject != null && defaultSnappedInteractableObject == null)
- {
- defaultSnappedInteractableObject = defaultSnappedObject.GetComponentInParent<VRTK_InteractableObject>();
- }
- #pragma warning restore 618
-
- DisableHighlightShadows();
- if (!VRTK_SharedMethods.IsEditTime() && Application.isPlaying && defaultSnappedInteractableObject != null)
- {
- ForceSnap(defaultSnappedInteractableObject);
- }
- }
-
- protected virtual void OnDisable()
- {
- if (transitionInPlaceRoutine != null)
- {
- StopCoroutine(transitionInPlaceRoutine);
- }
-
- if (attemptTransitionAtEndOfFrameRoutine != null)
- {
- StopCoroutine(attemptTransitionAtEndOfFrameRoutine);
- }
-
- if (checkCanSnapRoutine != null)
- {
- StopCoroutine(checkCanSnapRoutine);
- }
-
- if(overridePreviousStateAtEndOfFrameRoutine != null)
- {
- StopCoroutine(overridePreviousStateAtEndOfFrameRoutine);
- }
-
- ForceUnsnap();
- SetHighlightObjectActive(false);
- UnregisterAllUngrabEvents();
- }
-
- protected virtual void Update()
- {
- CheckSnappedItemExists();
- CheckPrefabUpdate();
- CreateHighlightersInEditor();
- CheckCurrentValidSnapObjectStillValid();
- previousPrefab = highlightObjectPrefab;
- SetObjectHighlight();
- }
-
- protected virtual void OnTriggerEnter(Collider collider)
- {
- CheckCanSnap(collider.GetComponentInParent<VRTK_InteractableObject>());
- }
-
- protected virtual void OnTriggerExit(Collider collider)
- {
- CheckCanUnsnap(collider.GetComponentInParent<VRTK_InteractableObject>());
- }
-
- protected virtual void CheckCanSnap(VRTK_InteractableObject interactableObjectCheck)
- {
- if (interactableObjectCheck != null && ValidSnapObject(interactableObjectCheck, true))
- {
- AddCurrentValidSnapObject(interactableObjectCheck);
- if (!isSnapped)
- {
- ToggleHighlight(interactableObjectCheck, true);
- interactableObjectCheck.SetSnapDropZoneHover(this, true);
- if (!willSnap)
- {
- OnObjectEnteredSnapDropZone(SetSnapDropZoneEvent(interactableObjectCheck.gameObject));
- }
- willSnap = true;
- ToggleHighlightColor();
- }
- }
- }
-
- protected virtual void CheckCanUnsnap(VRTK_InteractableObject interactableObjectCheck)
- {
- if (interactableObjectCheck != null && currentValidSnapInteractableObjects.Contains(interactableObjectCheck) && ValidUnsnap(interactableObjectCheck))
- {
- if (isSnapped && currentSnappedObject == interactableObjectCheck)
- {
- ForceUnsnap();
- }
-
- RemoveCurrentValidSnapObject(interactableObjectCheck);
-
- if (!ValidSnappableObjectIsHovering())
- {
- ToggleHighlight(interactableObjectCheck, false);
- willSnap = false;
- }
-
- interactableObjectCheck.SetSnapDropZoneHover(this, false);
-
- if (ValidSnapObject(interactableObjectCheck, true))
- {
- ToggleHighlightColor();
- OnObjectExitedSnapDropZone(SetSnapDropZoneEvent(interactableObjectCheck.gameObject));
- }
- }
- }
-
- protected virtual bool ValidUnsnap(VRTK_InteractableObject interactableObjectCheck)
- {
- return (interactableObjectCheck.IsGrabbed() || ((snapType != SnapTypes.UseJoint || !float.IsInfinity(GetComponent<Joint>().breakForce)) && interactableObjectCheck.validDrop == VRTK_InteractableObject.ValidDropTypes.DropAnywhere));
- }
-
- protected virtual void SnapObjectToZone(VRTK_InteractableObject objectToSnap)
- {
- if (!isSnapped && ValidSnapObject(objectToSnap, false))
- {
- SnapObject(objectToSnap);
- }
- }
-
- protected virtual void UnregisterAllUngrabEvents()
- {
- for (int i = 0; i < currentValidSnapInteractableObjects.Count; i++)
- {
- currentValidSnapInteractableObjects[i].InteractableObjectGrabbed -= InteractableObjectGrabbed;
- currentValidSnapInteractableObjects[i].InteractableObjectUngrabbed -= InteractableObjectUngrabbed;
- }
- }
-
- protected virtual bool ValidSnapObject(VRTK_InteractableObject interactableObjectCheck, bool grabState, bool checkGrabState = true)
- {
- return (interactableObjectCheck != null && (!checkGrabState || interactableObjectCheck.IsGrabbed() == grabState) && !VRTK_PolicyList.Check(interactableObjectCheck.gameObject, validObjectListPolicy));
- }
-
- protected virtual string ObjectPath(string name)
- {
- return HIGHLIGHT_CONTAINER_NAME + "/" + name;
- }
-
- protected virtual void CheckSnappedItemExists()
- {
- if (isSnapped && (currentSnappedObject == null || !currentSnappedObject.gameObject.activeInHierarchy))
- {
- ForceUnsnap();
- OnObjectUnsnappedFromDropZone(SetSnapDropZoneEvent((currentSnappedObject != null ? currentSnappedObject.gameObject : null)));
- }
- }
-
- protected virtual void CheckPrefabUpdate()
- {
- //If the highlightObjectPrefab has changed then delete the highlight object in preparation to create a new one
- if (previousPrefab != null && previousPrefab != highlightObjectPrefab)
- {
- DeleteHighlightObject();
- }
- }
-
- protected virtual void SetObjectHighlight()
- {
- if (highlightAlwaysActive && !isSnapped && !isHighlighted)
- {
- SetHighlightObjectActive(true);
- ToggleHighlightColor();
- }
-
- if (!highlightAlwaysActive && isHighlighted && !ValidSnappableObjectIsHovering())
- {
- SetHighlightObjectActive(false);
- }
- }
-
- protected virtual void ToggleHighlightColor()
- {
- if (Application.isPlaying && highlightAlwaysActive && !isSnapped && objectHighlighter != null)
- {
- objectHighlighter.Highlight((willSnap && validHighlightColor != Color.clear ? validHighlightColor : highlightColor));
- }
- }
-
- protected virtual void CreateHighlightersInEditor()
- {
- if (VRTK_SharedMethods.IsEditTime())
- {
- GenerateHighlightObject();
-
- if (snapType == SnapTypes.UseJoint && GetComponent<Joint>() == null)
- {
- 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`"));
- }
-
- GenerateEditorHighlightObject();
- ForceSetObjects();
- if (highlightEditorObject != null)
- {
- highlightEditorObject.SetActive(displayDropZoneInEditor);
- }
- }
- }
-
- protected virtual void CheckCurrentValidSnapObjectStillValid()
- {
- for (int i = 0; i < currentValidSnapInteractableObjects.Count; i++)
- {
- VRTK_InteractableObject interactableObjectCheck = currentValidSnapInteractableObjects[i];
- //if the interactable object associated with it has been snapped to another zone, then unset the current valid snap object
- if (interactableObjectCheck != null && interactableObjectCheck.GetStoredSnapDropZone() != null && interactableObjectCheck.GetStoredSnapDropZone() != this)
- {
- RemoveCurrentValidSnapObject(interactableObjectCheck);
- if (isHighlighted && highlightObject != null && !highlightAlwaysActive)
- {
- SetHighlightObjectActive(false);
- }
- }
- }
- }
-
- protected virtual void ForceSetObjects()
- {
- if (highlightEditorObject == null)
- {
- Transform forceFindHighlightEditorObject = transform.Find(ObjectPath(HIGHLIGHT_EDITOR_OBJECT_NAME));
- highlightEditorObject = (forceFindHighlightEditorObject ? forceFindHighlightEditorObject.gameObject : null);
- }
-
- if (highlightObject == null)
- {
- Transform forceFindHighlightObject = transform.Find(ObjectPath(HIGHLIGHT_OBJECT_NAME));
- highlightObject = (forceFindHighlightObject ? forceFindHighlightObject.gameObject : null);
- }
-
- if (highlightContainer == null)
- {
- Transform forceFindHighlightContainer = transform.Find(HIGHLIGHT_CONTAINER_NAME);
- highlightContainer = (forceFindHighlightContainer ? forceFindHighlightContainer.gameObject : null);
- }
- }
-
- protected virtual void GenerateContainer()
- {
- if (highlightContainer == null || transform.Find(HIGHLIGHT_CONTAINER_NAME) == null)
- {
- highlightContainer = new GameObject(HIGHLIGHT_CONTAINER_NAME);
- highlightContainer.transform.SetParent(transform);
- highlightContainer.transform.localPosition = Vector3.zero;
- highlightContainer.transform.localRotation = Quaternion.identity;
- highlightContainer.transform.localScale = Vector3.one;
- }
- }
-
- protected virtual void DisableHighlightShadows()
- {
- if (highlightObject != null)
- {
- Renderer[] foundRenderers = highlightObject.GetComponentsInChildren<Renderer>(true);
- for (int i = 0; i < foundRenderers.Length; i++)
- {
- foundRenderers[i].receiveShadows = false;
- foundRenderers[i].shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
- }
- }
- }
-
- protected virtual void SetContainer()
- {
- Transform findContainer = transform.Find(HIGHLIGHT_CONTAINER_NAME);
- if (findContainer != null)
- {
- highlightContainer = findContainer.gameObject;
- }
- }
-
- protected virtual void GenerateObjects()
- {
- GenerateHighlightObject();
- if (highlightObject != null && objectHighlighter == null)
- {
- InitialiseHighlighter();
- }
- }
-
- protected virtual void SnapObject(VRTK_InteractableObject interactableObjectCheck)
- {
- //If the item is in a snappable position and this drop zone isn't snapped and the collider is a valid interactable object
- if (willSnap && !isSnapped && ValidSnapObject(interactableObjectCheck, false))
- {
- //Only snap it to the drop zone if it's not already in a drop zone
- if (!interactableObjectCheck.IsInSnapDropZone())
- {
- if (highlightObject != null)
- {
- //Turn off the drop zone highlighter
- SetHighlightObjectActive(false);
- }
-
- Vector3 newLocalScale = GetNewLocalScale(interactableObjectCheck);
- if (transitionInPlaceRoutine != null)
- {
- StopCoroutine(transitionInPlaceRoutine);
- }
-
- isSnapped = true;
- currentSnappedObject = interactableObjectCheck;
- if (cloneNewOnUnsnap)
- {
- CreatePermanentClone();
- }
-
- if (gameObject.activeInHierarchy)
- {
- transitionInPlaceRoutine = StartCoroutine(UpdateTransformDimensions(interactableObjectCheck, highlightContainer, newLocalScale, snapDuration));
- }
-
- interactableObjectCheck.ToggleSnapDropZone(this, true);
- }
- }
-
- //Force reset isSnapped if the item is grabbed but isSnapped is still true
- isSnapped = (isSnapped && interactableObjectCheck != null && interactableObjectCheck.IsGrabbed() ? false : isSnapped);
- willSnap = !isSnapped;
- wasSnapped = false;
- }
-
- protected virtual void CreatePermanentClone()
- {
- VRTK_BaseHighlighter currentSnappedObjectHighlighter = currentSnappedObject.GetComponent<VRTK_BaseHighlighter>();
- if (currentSnappedObjectHighlighter != null)
- {
- currentSnappedObjectHighlighter.Unhighlight();
- }
- objectToClone = Instantiate(currentSnappedObject.gameObject);
- objectToClone.transform.position = highlightContainer.transform.position;
- objectToClone.transform.rotation = highlightContainer.transform.rotation;
- Collider[] clonedObjectStates = currentSnappedObject.GetComponentsInChildren<Collider>();
- clonedObjectColliderStates = new bool[clonedObjectStates.Length];
- for (int i = 0; i < clonedObjectStates.Length; i++)
- {
- Collider clonedObjectColliderState = clonedObjectStates[i];
- clonedObjectColliderStates[i] = clonedObjectColliderState.isTrigger;
- clonedObjectColliderState.isTrigger = true;
- }
- objectToClone.SetActive(false);
- }
-
- protected virtual void ResetPermanentCloneColliders(GameObject objectToReset)
- {
- if (objectToReset != null && clonedObjectColliderStates.Length > 0)
- {
- Collider[] clonedObjectStates = objectToReset.GetComponentsInChildren<Collider>();
- for (int i = 0; i < clonedObjectStates.Length; i++)
- {
- Collider clonedObjectColliderState = clonedObjectStates[i];
- if (clonedObjectColliderStates.Length > i)
- {
- clonedObjectColliderState.isTrigger = clonedObjectColliderStates[i];
- }
- }
- }
- }
-
- protected virtual void ResnapPermanentClone()
- {
- if (objectToClone != null)
- {
- float savedSnapDuration = snapDuration;
- snapDuration = 0f;
- objectToClone.SetActive(true);
- ResetPermanentCloneColliders(objectToClone);
- ForceSnap(objectToClone);
- snapDuration = savedSnapDuration;
- }
- }
-
- protected virtual void UnsnapObject()
- {
- if (currentSnappedObject != null)
- {
- ResetPermanentCloneColliders(currentSnappedObject.gameObject);
- RemoveCurrentValidSnapObject(currentSnappedObject);
- }
-
- isSnapped = false;
- wasSnapped = true;
- VRTK_InteractableObject checkCanSnapObject = currentSnappedObject;
- currentSnappedObject = null;
- ResetSnapDropZoneJoint();
-
- if (transitionInPlaceRoutine != null)
- {
- StopCoroutine(transitionInPlaceRoutine);
- }
-
- if (cloneNewOnUnsnap)
- {
- ResnapPermanentClone();
- }
-
- if (checkCanSnapRoutine != null)
- {
- StopCoroutine(checkCanSnapRoutine);
- }
-
- if (gameObject.activeInHierarchy)
- {
- checkCanSnapRoutine = StartCoroutine(CheckCanSnapObjectAtEndOfFrame(checkCanSnapObject));
- }
- checkCanSnapObject = null;
- }
-
- protected virtual Vector3 GetNewLocalScale(VRTK_InteractableObject checkObject)
- {
- // If apply scaling is checked then use the drop zone scale to resize the object
- Vector3 newLocalScale = checkObject.transform.localScale;
- if (applyScalingOnSnap)
- {
- checkObject.StoreLocalScale();
- newLocalScale = Vector3.Scale(checkObject.transform.localScale, transform.localScale);
- }
- return newLocalScale;
- }
-
- protected virtual IEnumerator CheckCanSnapObjectAtEndOfFrame(VRTK_InteractableObject interactableObjectCheck)
- {
- yield return new WaitForEndOfFrame();
- CheckCanSnap(interactableObjectCheck);
- }
-
- protected virtual IEnumerator UpdateTransformDimensions(VRTK_InteractableObject ioCheck, GameObject endSettings, Vector3 endScale, float duration)
- {
- float elapsedTime = 0f;
- Transform ioTransform = ioCheck.transform;
- Vector3 startPosition = ioTransform.position;
- Quaternion startRotation = ioTransform.rotation;
- Vector3 startScale = ioTransform.localScale;
- bool storedKinematicState = ioCheck.isKinematic;
- ioCheck.isKinematic = true;
- while (elapsedTime <= duration)
- {
- elapsedTime += Time.deltaTime;
- if (ioTransform != null && endSettings != null)
- {
- ioTransform.position = Vector3.Lerp(startPosition, endSettings.transform.position, (elapsedTime / duration));
- ioTransform.rotation = Quaternion.Lerp(startRotation, endSettings.transform.rotation, (elapsedTime / duration));
- ioTransform.localScale = Vector3.Lerp(startScale, endScale, (elapsedTime / duration));
- }
- yield return null;
- }
-
- if (ioTransform != null && endSettings != null)
- {
- //Force all to the last setting in case anything has moved during the transition
- ioTransform.position = endSettings.transform.position;
- ioTransform.rotation = endSettings.transform.rotation;
- ioTransform.localScale = endScale;
- }
-
- ioCheck.isKinematic = storedKinematicState;
- SetDropSnapType(ioCheck);
- }
-
- protected virtual void SetDropSnapType(VRTK_InteractableObject ioCheck)
- {
- switch (snapType)
- {
- case SnapTypes.UseKinematic:
- ioCheck.SaveCurrentState();
- ioCheck.isKinematic = true;
- break;
- case SnapTypes.UseParenting:
- ioCheck.SaveCurrentState();
- ioCheck.isKinematic = true;
- ioCheck.transform.SetParent(transform);
- break;
- case SnapTypes.UseJoint:
- SetSnapDropZoneJoint(ioCheck.GetComponent<Rigidbody>());
- break;
- }
- OnObjectSnappedToDropZone(SetSnapDropZoneEvent(ioCheck.gameObject));
- }
-
- protected virtual void SetSnapDropZoneJoint(Rigidbody snapTo)
- {
- Joint snapDropZoneJoint = GetComponent<Joint>();
- if (snapDropZoneJoint == null)
- {
- 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`"));
- return;
- }
- if (snapTo == null)
- {
- VRTK_Logger.Error(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_GAMEOBJECT, "VRTK_SnapDropZone", "Rigidbody", "the `VRTK_InteractableObject`"));
- return;
- }
-
- snapDropZoneJoint.connectedBody = snapTo;
- originalJointCollisionState = snapDropZoneJoint.enableCollision;
- //need to set this to true otherwise highlighting doesn't work again on grab
- snapDropZoneJoint.enableCollision = true;
- }
-
- protected virtual void ResetSnapDropZoneJoint()
- {
- Joint snapDropZoneJoint = GetComponent<Joint>();
- if (snapDropZoneJoint != null)
- {
- snapDropZoneJoint.enableCollision = originalJointCollisionState;
- }
- }
-
- protected virtual void AddCurrentValidSnapObject(VRTK_InteractableObject givenObject)
- {
- if (givenObject != null)
- {
- if (VRTK_SharedMethods.AddListValue(currentValidSnapInteractableObjects, givenObject, true))
- {
- givenObject.InteractableObjectGrabbed += InteractableObjectGrabbed;
- givenObject.InteractableObjectUngrabbed += InteractableObjectUngrabbed;
- }
- }
- }
-
- protected virtual void RemoveCurrentValidSnapObject(VRTK_InteractableObject givenObject)
- {
- if (givenObject != null)
- {
- if (currentValidSnapInteractableObjects.Remove(givenObject))
- {
- givenObject.InteractableObjectGrabbed -= InteractableObjectGrabbed;
- givenObject.InteractableObjectUngrabbed -= InteractableObjectUngrabbed;
- }
- }
- }
-
- protected virtual void InteractableObjectGrabbed(object sender, InteractableObjectEventArgs e)
- {
- VRTK_InteractableObject grabbedInteractableObject = sender as VRTK_InteractableObject;
- if (!grabbedInteractableObject.IsInSnapDropZone())
- {
- CheckCanSnap(grabbedInteractableObject);
- }
- }
-
- protected virtual void InteractableObjectUngrabbed(object sender, InteractableObjectEventArgs e)
- {
- VRTK_InteractableObject releasedInteractableObject = sender as VRTK_InteractableObject;
- if (attemptTransitionAtEndOfFrameRoutine != null)
- {
- StopCoroutine(attemptTransitionAtEndOfFrameRoutine);
- }
- attemptTransitionAtEndOfFrameRoutine = StartCoroutine(AttemptForceSnapAtEndOfFrame(releasedInteractableObject));
- }
-
- protected virtual void AttemptForceSnap(VRTK_InteractableObject objectToSnap)
- {
- //force snap settings on
- willSnap = true;
- //Force touch one of the object's colliders on this trigger collider
- SnapObjectToZone(objectToSnap);
- }
-
- protected virtual IEnumerator AttemptForceSnapAtEndOfFrame(VRTK_InteractableObject objectToSnap)
- {
- yield return new WaitForEndOfFrame();
- objectToSnap.SaveCurrentState();
- AttemptForceSnap(objectToSnap);
- }
-
- protected virtual void ToggleHighlight(VRTK_InteractableObject checkObject, bool state)
- {
- if (highlightObject != null && ValidSnapObject(checkObject, true, state))
- {
- //Toggle the highlighter state
- SetHighlightObjectActive(state);
- }
- }
-
- protected virtual void CopyObject(GameObject objectBlueprint, ref GameObject clonedObject, string givenName)
- {
- GenerateContainer();
- Vector3 saveScale = transform.localScale;
- transform.localScale = Vector3.one;
-
- clonedObject = Instantiate(objectBlueprint, highlightContainer.transform) as GameObject;
- clonedObject.name = givenName;
-
- //default position of new highlight object
- clonedObject.transform.localPosition = Vector3.zero;
- clonedObject.transform.localRotation = Quaternion.identity;
-
- transform.localScale = saveScale;
- CleanHighlightObject(clonedObject);
- }
-
- protected virtual void GenerateHighlightObject()
- {
- //If there is a given highlight prefab and no existing highlight object then create a new highlight object
- if (highlightObjectPrefab != null && highlightObject == null && transform.Find(ObjectPath(HIGHLIGHT_OBJECT_NAME)) == null)
- {
- CopyObject(highlightObjectPrefab, ref highlightObject, HIGHLIGHT_OBJECT_NAME);
- }
-
- //if highlight object exists but not in the variable then force grab it
- Transform checkForChild = transform.Find(ObjectPath(HIGHLIGHT_OBJECT_NAME));
- if (checkForChild != null && highlightObject == null)
- {
- highlightObject = checkForChild.gameObject;
- }
-
- //if no highlight object prefab is set but a highlight object is found then destroy the highlight object
- if (highlightObjectPrefab == null && highlightObject != null)
- {
- DeleteHighlightObject();
- }
-
- DisableHighlightShadows();
- SetHighlightObjectActive(false);
- SetContainer();
- }
-
- protected virtual void SetHighlightObjectActive(bool state)
- {
- if (highlightObject != null)
- {
- highlightObject.SetActive(state);
- isHighlighted = state;
- }
- }
-
- protected virtual void DeleteHighlightObject()
- {
- ChooseDestroyType(transform.Find(HIGHLIGHT_CONTAINER_NAME));
- highlightContainer = null;
- highlightObject = null;
- objectHighlighter = null;
- }
-
- protected virtual void GenerateEditorHighlightObject()
- {
- if (highlightObject != null && highlightEditorObject == null && transform.Find(ObjectPath(HIGHLIGHT_EDITOR_OBJECT_NAME)) == null)
- {
- CopyObject(highlightObject, ref highlightEditorObject, HIGHLIGHT_EDITOR_OBJECT_NAME);
- Renderer[] renderers = highlightEditorObject.GetComponentsInChildren<Renderer>();
- for (int i = 0; i < renderers.Length; i++)
- {
- renderers[i].material = Resources.Load("SnapDropZoneEditorObject") as Material;
- }
- highlightEditorObject.SetActive(true);
- }
- }
-
- protected virtual void CleanHighlightObject(GameObject objectToClean)
- {
- //If the highlight object has any child snap zones, then force delete these
- VRTK_SnapDropZone[] deleteSnapZones = objectToClean.GetComponentsInChildren<VRTK_SnapDropZone>(true);
- for (int i = 0; i < deleteSnapZones.Length; i++)
- {
- ChooseDestroyType(deleteSnapZones[i].gameObject);
- }
-
- //determine components that shouldn't be deleted from highlight object
- string[] validComponents = new string[] { "Transform", "MeshFilter", "MeshRenderer", "SkinnedMeshRenderer", "VRTK_GameObjectLinker" };
-
- //First clean out the joints cause RigidBodys depends on them.
- Joint[] joints = objectToClean.GetComponentsInChildren<Joint>(true);
- for (int i = 0; i < joints.Length; i++)
- {
- ChooseDestroyType(joints[i]);
- }
-
- //Go through all of the components on the highlighted object and delete any components that aren't in the valid component list
- Component[] components = objectToClean.GetComponentsInChildren<Component>(true);
- for (int i = 0; i < components.Length; i++)
- {
- Component component = components[i];
- bool valid = false;
-
- //Loop through each valid component and check to see if this component is valid
- for (int j = 0; j < validComponents.Length; j++)
- {
- //if it's a valid component then break the check
- if (component.GetType().ToString().Contains("." + validComponents[j]))
- {
- valid = true;
- break;
- }
- }
-
- //if this is a valid component then just continue to the next component
- if (valid)
- {
- continue;
- }
-
- //If not a valid component then delete it
- ChooseDestroyType(component);
- }
- }
-
- protected virtual void InitialiseHighlighter()
- {
- VRTK_BaseHighlighter existingHighlighter = VRTK_BaseHighlighter.GetActiveHighlighter(gameObject);
- //If no highlighter is found on the GameObject then create the default one
- if (existingHighlighter == null)
- {
- highlightObject.AddComponent<VRTK_MaterialColorSwapHighlighter>();
- }
- else
- {
- VRTK_SharedMethods.CloneComponent(existingHighlighter, highlightObject);
- }
-
- //Initialise highlighter and set highlight colour
- objectHighlighter = highlightObject.GetComponent<VRTK_BaseHighlighter>();
- objectHighlighter.unhighlightOnDisable = false;
- objectHighlighter.Initialise(highlightColor);
- objectHighlighter.Highlight(highlightColor);
-
- //if the object highlighter is using a cloned object then disable the created highlight object's renderers
- if (objectHighlighter.UsesClonedObject())
- {
- Renderer[] renderers = GetComponentsInChildren<Renderer>(true);
- for (int i = 0; i < renderers.Length; i++)
- {
- if (!VRTK_PlayerObject.IsPlayerObject(renderers[i].gameObject, VRTK_PlayerObject.ObjectTypes.Highlighter))
- {
- renderers[i].enabled = false;
- }
- }
- }
- }
-
- protected virtual void ChooseDestroyType(Transform deleteTransform)
- {
- if (deleteTransform != null)
- {
- ChooseDestroyType(deleteTransform.gameObject);
- }
- }
-
- protected virtual void ChooseDestroyType(GameObject deleteObject)
- {
- if (VRTK_SharedMethods.IsEditTime())
- {
- if (deleteObject != null)
- {
- DestroyImmediate(deleteObject);
- }
- }
- else
- {
- if (deleteObject != null)
- {
- Destroy(deleteObject);
- }
- }
- }
-
- protected virtual void ChooseDestroyType(Component deleteComponent)
- {
- if (VRTK_SharedMethods.IsEditTime())
- {
- if (deleteComponent != null)
- {
- DestroyImmediate(deleteComponent);
- }
- }
- else
- {
- if (deleteComponent != null)
- {
- Destroy(deleteComponent);
- }
- }
- }
-
- protected virtual void OnDrawGizmosSelected()
- {
- if (highlightObject != null && !displayDropZoneInEditor)
- {
- Vector3 boxSize = VRTK_SharedMethods.GetBounds(highlightObject.transform).size * 1.05f;
- Gizmos.color = Color.red;
- Gizmos.DrawWireCube(highlightObject.transform.position, boxSize);
- }
- }
-
- protected virtual IEnumerator OverridePreviousStateAtEndOfFrame(VRTK_InteractableObject io, Transform parent, bool kinematic, bool grabbable)
- {
- yield return new WaitForEndOfFrame();
- io.OverridePreviousState(parent, kinematic, grabbable);
- }
- }
- }
|