using System.Collections; using UnityEngine; namespace Passer { /// Sockets can hold a Handles. /// \image html SocketGizmo.png /// \image rtf SocketGizmo.png /// /// Attaching and Releasing /// ======================= /// Attaching a Handle using Scripting /// --------------------------------- /// You can attach handles to the Socket GameObject by calling an Attach() function on the Socket. /// This will try to attach the Transform, Rigidbody or Handle parameter to the Socket. /// On the other hand you can release Handles again by calling an Release() function on the Socket. /// This will release the currently attached Handle from the Socket. /// /// Attaching a Prefab with a Handle in the Editor /// ----------------------------------------------- /// It is possible to attach a Handle prefab to a socket in the Unity editor. /// In this way the socket will start with a Handle attached. /// This is done by assigning a Prefab to the Attached Prefab field of the Socket. /// This will automatically create an instance of the prefab which is attached to the socket. /// When the Attached Prefab field is cleared again, /// the prefab instance attached to the socket is released and destroyed again. /// /// Releasing a Handle to another Socket /// ------------------------------------ /// When an handle is attached to a socket and the Handle is attached to another socket /// it will be automatically released from the original socket. /// /// Socket Tag /// ========== /// With the Socket Tag field to is possible to limit the Handles /// which can attach to this socket to those who have the right Tag. /// This construction is using the standard /// Unity tags. /// /// Events /// ====== /// * \ref Socket::attachEvent "Attach Events" /// /// Attachment Implementation /// ========================= /// Depending on the type of socket and its GameObject handles are attached to sockets in various ways. /// /// A Static Socket... /// ------------------- /// ...with a Non-Kinematic Rigidbody Handle /// ----------------------------------------- /// A non-kinematic Rigidbody will get a Joint which limits its movement to /// the location and orientation of the Socket. This joint is implemented using a Configurable Joint. /// When the Handle is released, the joint is destroyed again. /// /// ...with a Kinematic Rigidbody Handle /// -------------------------------------- /// A kinematic Rigidbody with an handle will the parented to the static Socket Transform. /// The result will be that the Transform of the Rigidbody is a child of the Socket Transform. /// The Rigidbody will be temporarily removed.The settings will be conserved /// in a RigidbodyDisabled Component which is used to restore the Rigidbody when the Handle /// is released from the Socket. /// /// ...with a Static Handle /// ------------------------ /// Static Handles cannot be attached to static Sockets. /// This will lead to a warning message in the Console: “Cannot attach static handle to static socket”. /// /// A Kinematic Rigidbody Socket... /// ------------------------------- /// ...with a Non-Kinematic Rigidbody Handle /// ----------------------------------------- /// If the Non-Kinematic Rigidbody Handle has one or more Joints or when it has constraints set, /// it will be attached to the Socket using a Joint. /// This joint is implemented using a Configurable Joint. /// When the Handle is released, the joint is destroyed again. /// In all other case, the non-kinematic Rigidbody will the parented to /// the kinematic Rigidbody Socket Transform. /// The result will be that the Transform of the Rigidbody is a child of the Socket Transform. /// The Rigidbody will be temporarily removed. /// The settings will be conserved in a RigidbodyDisabled Component /// which is used to restore the Rigidbody when the Handle is released from the Socket. /// /// ...with a Kinematic Rigidbody Handle /// ------------------------------------- /// A kinematic Rigidbody with an handle will the parented to the kinematic Rigidbody Socket Transform. /// The result will be that the Transform of the Rigidbody is a child of the Socket Transform. /// /// ...with a Static Handle /// ------------------------ /// In this case, the kinematic Rigidbody Socket is parented to the static Handle Transform. /// The result will be that the Transform of the Scoket is a child of the Handle Transform. /// The Rigidbody of the Socket will be temporarily removed. /// The settings will be conserved in a RigidbodyDisabled Component /// which is used to restore the Rigidbody when the Handle is released from the Socket. /// /// A Non-Kinematic Rigidbody Socket... /// ----------------------------------- /// ...with a Non-Kinematic Rigidbody Handle /// ---------------------------------------- /// If the Non-Kinematic Rigidbody Handle has one or more Joints or when it has constraints set, /// it will be attached to the Socket using a Joint. /// This joint is implemented using a Configurable Joint. /// When the Handle is released, the joint is destroyed again. /// In all other cases, the non-kinematic Rigidbody will the parented to /// the kinematic Rigidbody Socket Transform. /// The result will be that the Transform of the Rigidbody is a child of the Socket Transform. /// The Rigidbody will be temporarily removed. /// The settings will be conserved in a RigidbodyDisabled Component /// which is used to restore the Rigidbody when the Handle is released from the Socket. /// /// ...with a Kinematic Rigidbody Handle /// ------------------------------------ /// A kinematic Rigidbody with an handle will the parented to the non-kinematic Socket Transform. /// The result will be that the Transform of the Rigidbody is a child of the Socket Transform. /// The Rigidbody will be temporarily removed. /// The settings will be conserved in a RigidbodyDisabled Component /// which is used to restore the Rigidbody when the Handle is released from the Socket. /// /// ...with a Static Handle /// ----------------------- /// In this case, the socket Rigidbody will be attached to the Handle using a Joint. /// This joint is implemented using a Configurable Joint /// such that it will follow the Handle’s Transform as closely as possible. /// When the Handle is released, the joint is destroyed again. [HelpURL("https://passervr.com/apis/HumanoidControl/Unity/class_passer_1_1_socket.html")] public class Socket : MonoBehaviour { protected static void DebugLog(string s) { //Debug.Log(s); } /// /// Does the socket currently have a handle attached? /// public bool isOccupied { get { return this.attachedTransform != null; } } /// /// A prefab which is used to attach to the socket at startup. /// public GameObject attachedPrefab; /// The Transform attached to this socket /// If the socket holds a Handle, this will contain the Transform of the Handle. /// It will be null otherwise public Transform attachedTransform; protected Transform releasingTransform; public Handle attachedHandle; /// The parent of the attached transform before it was attached /// This is used to restore the parent when the transform is released again. protected Transform attachedTransformParent; /// A tag for limiting which handles can be held by the socket /// If set (not null or empty) only Handles with the given tag will fit in the socket. public string socketTag; public enum AttachMethod { Unknown, Parenting, ReverseParenting, Joint, StaticJoint, ColliderDuplication } public AttachMethod attachMethod = AttachMethod.Unknown; protected RigidbodyDisabled rigidbodyDisabled = null; public bool destroyOnLoad = false; #region Alignment protected virtual void MoveHandleToSocket(Transform socketTransform, Rigidbody handleRigidbody, Handle handle) { DebugLog("MoveHandleToSocket"); Transform handleTransform = handle.GetComponent(); if (handleRigidbody != null) handleTransform = handleRigidbody.transform; if (handle.grabType == Handle.GrabType.RailGrab) { MoveRailToSocket(socketTransform, handleTransform, handle); } else { handleTransform.rotation = handle.RotationTo(socketTransform.rotation) * handleTransform.rotation; handleTransform.position += handle.TranslationTo(socketTransform.position); } } protected virtual void MoveRailToSocket(Transform socketTransform, Transform railTransform, Handle rail) { Vector3 handleYaxis = rail.transform.up; Vector3 socketYaxis = socketTransform.up; float angle = Vector3.Angle(handleYaxis, socketYaxis); if (angle > 90) socketYaxis = -socketTransform.up; Quaternion socketToHandleRotation = Quaternion.FromToRotation(socketYaxis, handleYaxis); Quaternion targetRotation = Quaternion.Inverse(socketToHandleRotation) * railTransform.rotation; railTransform.rotation = rail.RotationTo(targetRotation) * railTransform.rotation; // Socket along rail Vector3 localSocketPosition = socketTransform.position - rail.transform.position; Vector3 positionOnRail = Vector3.Project(localSocketPosition, rail.transform.up); Vector3 railToSocket = localSocketPosition - positionOnRail; // Socket within rail length float maxDistance = rail.transform.lossyScale.y / 2; float distance = positionOnRail.magnitude - maxDistance; if (distance > 0) { float scale = distance / positionOnRail.magnitude; railToSocket += Vector3.Scale(positionOnRail, Vector3.one * scale); } railTransform.position += railToSocket; } protected virtual void MoveHandleToSocket(Transform socketTransform, Handle handle) { DebugLog("MoveHandleToSocket"); Transform handleTransform = handle.GetComponent(); Rigidbody handleRigidbody = handle.GetComponentInParent(); if (handleRigidbody != null) { handleTransform = handleRigidbody.transform; MechanicalJoint handleLimitations = handle.GetComponent(); if (handleRigidbody.isKinematic && handleLimitations != null && handleLimitations.enabled) { //handleTransform.position += handle.TranslationTo(socketTransform.position); // No rotations for kinematic Limitations return; } } if (handle.grabType == Handle.GrabType.RailGrab) { MoveRailToSocket(socketTransform, handleTransform, handle); } else { handleTransform.rotation = handle.RotationTo(socketTransform.rotation) * handleTransform.rotation; handleTransform.position += handle.TranslationTo(socketTransform.position); } } protected virtual void MoveSocketToHandle(Transform socketTransform, Handle handle) { DebugLog("MoveSocketToHandle"); socketTransform.rotation *= Quaternion.Inverse(handle.RotationTo(socketTransform.rotation)); socketTransform.position -= handle.TranslationTo(socketTransform.position); } protected virtual void MoveSocketToHandle(Transform socketTransform, Rigidbody socketRigidbody, Handle handle) { DebugLog("MoveSocketToHandle"); //Quaternion rotation = Quaternion.Inverse(handle.RotationTo(socketTransform.rotation)); //socketRigidbody.transform.rotation *= rotation; //Vector3 translation = handle.TranslationTo(socketTransform.position); //socketRigidbody.transform.position -= translation; // code based on HandSocket, potentially better, but rotation is somehow wrong Quaternion socket2rigidbodyRotation = Quaternion.Inverse(socketTransform.localRotation); socketRigidbody.transform.rotation = handle.transform.rotation * socket2rigidbodyRotation; Vector3 socket2RigidbodyPosition = socketRigidbody.transform.position - socketTransform.position; socketRigidbody.transform.position = handle.transform.position + socket2RigidbodyPosition; } #endregion Alignment #region Attach public void Attach(GameObject objectToAttach) { Attach(objectToAttach, false); } public bool Attach(GameObject objectToAttach, bool rangeCheck) { bool success = Attach(objectToAttach.transform, rangeCheck); return success; } /// Tries to attach the given Transform to this socket /// If the Transform has a Handle with the right Socket Tag it will be attached to the socket. /// Static and Kinematic Rigidbodies will be attached by parenting. /// Non-Kinematic Rigidbodies will be attached using a joint. /// The Transform to attach to this socket /// Boolean indicating whether attachment succeeded public virtual bool Attach(Transform transformToAttach, bool rangeCheck = true) { DebugLog("Attach transform " + transformToAttach); Handle handle; Rigidbody rigidbodyToAttach = transformToAttach.GetComponentInParent(); if (rigidbodyToAttach != null) handle = rigidbodyToAttach.GetComponentInChildren(); else handle = transformToAttach.GetComponentInChildren(); if (handle == null) { // Transform does not have a handle DebugLog(gameObject.name + ": Attach failed. Object " + transformToAttach.name + " does not have a handle"); return false; } if (handle.socket != null) { // Handle is already in a socket return false; } if (socketTag != null && socketTag != "" && !transformToAttach.gameObject.CompareTag(socketTag)) { // Object did not have the right tag Debug.Log(gameObject.name + ": Attach failed. Object " + transformToAttach + " does not have the right tag"); ; return false; } Rigidbody handleRigidbody = handle.GetComponentInParent(); if (handleRigidbody != rigidbodyToAttach) { // Object does not have a handle, // found handle is of child rigidbody //Debug.Log(gameObject.name + ": Attach failed. Object does not have a handle. Handle is on child Rigidbody."); return false; } bool success = Attach(handle, rangeCheck); return success; } /// Tries to attach the given Transform to this socket /// If the Handle has the right Socket Tag it will be attached to the socket. /// Static and Kinematic Rigidbodies will be attached by parenting. /// Non-Kinematic Rigidbodies will be attached using a joint. /// The Handle to attach to this socket /// Boolean indicating whether attachment succeeded public virtual bool Attach(Handle handle, bool rangeCheck = true) { DebugLog(this.gameObject.name + " Attach handle " + handle); if (handle == null) { // Transform does not have a handle DebugLog(gameObject.name + ": Attach failed. Transform does not have a handle"); return false; } if (handle.socket != null) { // Handle is already in a socket return false; } if (socketTag != null && socketTag != "" && !handle.gameObject.CompareTag(socketTag)) { // Object did not have the right tag Debug.Log(gameObject.name + ": Attach failed. Handle " + handle + " does not have the right tag"); ; return false; } Rigidbody rigidbodyToAttach = handle.GetComponentInParent(); if (rigidbodyToAttach == null) return AttachStaticObject(handle.transform, handle); else { RigidbodyDisabled disabledRigidbody = RigidbodyDisabled.Get(rigidbodyToAttach.transform); if (disabledRigidbody != null && rigidbodyToAttach.transform.parent != null) rigidbodyToAttach = rigidbodyToAttach.transform.parent.GetComponentInParent(); if (rigidbodyToAttach == null) return AttachStaticObject(handle.transform, handle); else return AttachRigidbody(rigidbodyToAttach, handle, rangeCheck); } } #region Rigidbody protected virtual bool AttachRigidbody(Rigidbody objRigidbody, Handle handle, bool rangeCheck = true) { DebugLog("AttachRigidbody " + objRigidbody); float grabDistance = Vector3.Distance(this.transform.position, handle.transform.position); if (rangeCheck && handle.range > 0 && grabDistance > handle.range) { //Debug.Log("Socket is outside range of handle"); return false; } Transform objTransform = objRigidbody.transform; Rigidbody thisRigidbody = this.GetComponentInParent(); Joint joint = objRigidbody.GetComponent(); // See if these joints are being destroyed DestroyedJoints destroyedJoints = objRigidbody.GetComponent(); if (objRigidbody.isKinematic) { if (thisRigidbody == null) AttachRigidbodyParenting(objRigidbody, handle); else if (thisRigidbody.isKinematic) AttachTransformParenting(objRigidbody.transform, handle); else AttachSocketParenting(objRigidbody, handle, thisRigidbody); } else if (thisRigidbody == null) { AttachRigidbodyReverseJoint(objRigidbody, handle); } else if ( (joint != null && destroyedJoints == null) || objRigidbody.constraints != RigidbodyConstraints.None ) { //|| otherHandPhysics != null) { AttachRigidbodyJoint(objRigidbody, handle); } else { AttachRigidbodyParenting(objRigidbody, handle); } releasingTransform = null; attachedTransform = objTransform; handle.socket = this; return true; } protected void AttachTransformParenting(Transform objTransform, Handle handle) { Debug.Log("AttachTransformParenting: " + objTransform); attachedTransformParent = objTransform.parent; Rigidbody handleRigidbody = handle.GetComponentInParent(); MechanicalJoint handleLimitations = handleRigidbody.GetComponent(); if (handleRigidbody.isKinematic && handleLimitations != null && handleLimitations.enabled) { MoveSocketToHandle(this.transform, handle); } else MoveHandleToSocket(this.transform, handle); objTransform.SetParent(this.transform); attachedTransform = objTransform; attachedHandle = handle; handle.socket = this; } protected virtual void AttachRigidbodyParenting(Rigidbody objRigidbody, Handle handle) { DebugLog("AttachRigidbodyParenting"); MoveHandleToSocket(this.transform, objRigidbody, handle); attachedTransform = objRigidbody.transform; Rigidbody thisRigidbody = this.GetComponentInParent(); if (thisRigidbody != null) MassRedistribution(thisRigidbody, objRigidbody); RigidbodyDisabled.ParentRigidbody(this.transform, objRigidbody); Humanoid.HumanoidNetworking.DisableNetworkSync(attachedTransform.gameObject); attachedHandle = handle; handle.socket = this; } protected virtual void AttachRigidbodyJoint(Rigidbody objRigidbody, Handle handle) { DebugLog("AttachRigidbodyJoint " + objRigidbody); //MassRedistribution(thisRididbody, objRigidbody); MoveHandleToSocket(this.transform, handle); ConfigurableJoint joint = this.gameObject.AddComponent(); joint.xMotion = ConfigurableJointMotion.Locked; joint.yMotion = ConfigurableJointMotion.Locked; joint.zMotion = ConfigurableJointMotion.Locked; joint.angularXMotion = ConfigurableJointMotion.Locked; joint.angularYMotion = ConfigurableJointMotion.Locked; joint.angularZMotion = ConfigurableJointMotion.Locked; joint.projectionMode = JointProjectionMode.PositionAndRotation; joint.projectionDistance = 0.01F; joint.projectionAngle = 1; Collider c = objRigidbody.transform.GetComponentInChildren(); joint.connectedBody = c.attachedRigidbody; attachedTransform = objRigidbody.transform; attachedHandle = handle; handle.socket = this; } /// Attach handle to socket using a static joint on the handle protected virtual void AttachRigidbodyReverseJoint(Rigidbody objRigidbody, Handle handle) { DebugLog("AttachRigidbodyReverseJoint " + objRigidbody); MoveHandleToSocket(this.transform, handle); ConfigurableJoint joint = objRigidbody.gameObject.AddComponent(); joint.xMotion = ConfigurableJointMotion.Locked; joint.yMotion = ConfigurableJointMotion.Locked; joint.zMotion = ConfigurableJointMotion.Locked; joint.angularXMotion = ConfigurableJointMotion.Locked; joint.angularYMotion = ConfigurableJointMotion.Locked; joint.angularZMotion = ConfigurableJointMotion.Locked; joint.projectionMode = JointProjectionMode.PositionAndRotation; joint.projectionDistance = 0.01F; joint.projectionAngle = 1; joint.breakForce = float.PositiveInfinity; joint.breakTorque = float.PositiveInfinity; attachedTransform = objRigidbody.transform; attachedHandle = handle; handle.socket = this; } protected virtual void AttachSocketParenting(Rigidbody objRigidbody, Handle handle, Rigidbody socketRigidbody) { DebugLog("AttachSocketParenting"); RigidbodyDisabled.ParentRigidbody(objRigidbody, socketRigidbody); MoveSocketToHandle(this.transform, handle); attachedTransform = objRigidbody.transform; attachedHandle = handle; handle.socket = this; attachMethod = AttachMethod.ReverseParenting; } protected float originalMass = 1; protected bool originalUseGravity = false; protected virtual void MassRedistribution(Rigidbody socketRigidbody, Rigidbody objRigidbody) { originalMass = socketRigidbody.mass; socketRigidbody.mass = HybridPhysics.CalculateTotalMass(objRigidbody); originalUseGravity = socketRigidbody.useGravity; // This gives an issue when attaching things to a socket which is already in the hand // It will enable gravity on the hand in that case (not on the object in the hand) // Which gives less stable hand tracking. // Update: added additional check such that gravity is only enabled when // holding heavy objects if (objRigidbody.useGravity && objRigidbody.mass > HybridPhysics.kinematicMass) socketRigidbody.useGravity = true; } #endregion Rigidbody #region Static Object virtual protected bool AttachStaticObject(Transform objTransform, Handle handle) { DebugLog("AttachStaticObject " + objTransform); Rigidbody thisRigidbody = this.GetComponentInParent(); if (thisRigidbody == null) { Debug.LogWarning("Cannot attach static handle to static socket"); return false; } // To do: rangecheck if (thisRigidbody.isKinematic) { AttachSocketParenting(objTransform, handle, thisRigidbody); } else { MoveSocketToHandle(this.transform, handle); if (handle.grabType == Handle.GrabType.RailGrab) AttachStaticJointRotY(objTransform); else AttachStaticJoint(objTransform); } releasingTransform = null; attachedTransform = objTransform; attachedHandle = handle; handle.socket = this; return true; } protected virtual void AttachSocketParenting(Transform objTransform, Handle handle, Rigidbody thisRigidbody) { DebugLog("AttachSocketParenting"); RigidbodyDisabled.ParentRigidbody(objTransform, thisRigidbody); MoveSocketToHandle(this.transform, handle); } public virtual void AttachStaticJoint(Transform objTransform) { DebugLog("AttachStaticJoint"); FixedJoint joint = this.transform.gameObject.AddComponent(); Collider c = objTransform.GetComponentInChildren(); if (c == null) c = objTransform.GetComponentInParent(); joint.connectedBody = c.attachedRigidbody; } virtual protected void AttachStaticJointRotY(Transform objTransform) { DebugLog("AttachStaticJointRotY is still fixed"); FixedJoint joint = this.transform.gameObject.AddComponent(); Collider c = objTransform.GetComponentInChildren(); if (c == null) c = objTransform.GetComponentInParent(); joint.connectedBody = c.attachedRigidbody; } #endregion Static Object #endregion Attach #region Release /// Releases a Transform from the socket /// Note that if the Transform is not taken out of the range of the socket /// or held by another Socket, it will automatically snap back to the Socket. public virtual void Release(bool releaseSticky = false) { DebugLog("Release " + attachedTransform); if (attachedTransform == null) { attachedHandle = null; return; } if (attachedHandle != null && attachedHandle.sticky && !releaseSticky) return; Rigidbody thisRigidbody = this.GetComponentInParent(); Rigidbody objRigidbody = attachedTransform.GetComponentInParent(); RigidbodyDisabled objRigidbodyDisabled = RigidbodyDisabled.Get(attachedTransform); // RigidbodyDisabled.GetInParent does not work well here. // When a socket is held in another socket, it can find the disabledRigidbody of the other socket //RigidbodyDisabled thisRigidbodyDisabled = RigidbodyDisabled.Get(this.transform); switch (attachMethod) { case AttachMethod.ReverseParenting: if (this.rigidbodyDisabled != null) ReleaseSocketParenting(objRigidbody, this.rigidbodyDisabled.transform); else if (thisRigidbody != null) ReleaseSocketParenting(objRigidbody, this.transform); else { Debug.LogError("Release ReverseParenting without a (distroyed) rigidbody present"); return; } break; default: if (this.rigidbodyDisabled != null) ReleaseSocketParenting(objRigidbody, this.rigidbodyDisabled.transform); else if (objRigidbodyDisabled != null) ReleaseRigidbodyParenting(); else if (objRigidbody == null) ReleaseStaticObject(); else if (thisRigidbody != null && thisRigidbody.isKinematic) ReleaseTransformParenting(); else if (!objRigidbody.isKinematic && thisRigidbody == null) ReleaseRigidbodyReverseJoint(); else ReleaseRigidbodyJoint(); break; } Handle handle = attachedHandle; // attachedTransform.GetComponentInChildren(); if (handle != null) { handle.socket = null; } this.releasingTransform = this.attachedTransform; this.attachedTransform = null; this.attachedHandle = null; attachMethod = AttachMethod.Unknown; StartCoroutine(ClearReleasingTransform()); } // Released objects should be gone within 1 second or they will be reattached private IEnumerator ClearReleasingTransform() { yield return new WaitForSeconds(1); this.releasingTransform = null; } #region Rigidbody protected virtual void ReleaseRigidbodyParenting() { DebugLog("Release Rigidbody from Parenting"); Rigidbody thisRigidbody = this.GetComponentInParent(); Rigidbody objRigidbody; if (thisRigidbody == null) objRigidbody = RigidbodyDisabled.UnparentRigidbody(this.transform, attachedTransform); else objRigidbody = RigidbodyDisabled.UnparentRigidbody(thisRigidbody, attachedTransform); MassRestoration(thisRigidbody, objRigidbody); Humanoid.HumanoidNetworking.ReenableNetworkSync(objRigidbody.gameObject); if (thisRigidbody != null) { objRigidbody.velocity = thisRigidbody.velocity; objRigidbody.angularVelocity = thisRigidbody.angularVelocity; } // check if the object is released from an hybrid physics rigidbody (just hands for now) Humanoid.AdvancedHandPhysics handPhysics = this.transform.GetComponentInParent(); if (handPhysics != null) { Collider[] objColliders = objRigidbody.GetComponentsInChildren(); foreach (Collider objCollider in objColliders) Target.UnsetColliderToTrigger(handPhysics.handTarget.colliders, objCollider); } } protected virtual void ReleaseRigidbodyJoint() { DebugLog("Release from Joint"); Joint[] joints = this.gameObject.GetComponents(); foreach (Joint joint in joints) { #if UNITY_EDITOR if (!Application.isPlaying) DestroyImmediate(joint, true); else #endif Destroy(joint); } //MassRestoration(..., ...); // Trick: when released and immediately attached to anther socket (e.g. grabbing) // the joints are not yet destroyed, because Destroy is executed with a delay. // Adding the DestroyedJoints component indicates that the joints which may // still be there are to be destroyed. attachedTransform.gameObject.AddComponent(); } protected void ReleaseRigidbodyReverseJoint() { DebugLog("Release from reverse Joint"); Joint[] joints = attachedTransform.GetComponents(); foreach (Joint joint in joints) { #if UNITY_EDITOR if (!Application.isPlaying) DestroyImmediate(joint, true); else #endif Destroy(joint); } //MassRestoration(..., ...); // Trick: when released and immediately attached to anther socket (e.g. grabbing) // the joints are not yet destroyed, because Destroy is executed with a delay. // Adding the DestroyedJoints component indicates that the joints which may // still be there are to be destroyed. attachedTransform.gameObject.AddComponent(); } protected virtual void ReleaseSocketParenting(Rigidbody objRigidbody, Transform socketTransform) { DebugLog("ReleaseSocketParenting"); RigidbodyDisabled.UnparentRigidbody(objRigidbody, socketTransform); } protected virtual void MassRestoration(Rigidbody socketRigidbody, Rigidbody objRigidbody) { if (socketRigidbody != null) { socketRigidbody.mass = originalMass; socketRigidbody.useGravity = originalUseGravity; } } #endregion Rigidbody #region Static Object protected virtual void ReleaseStaticObject() { DebugLog("ReleaseStaticObject"); Rigidbody thisRigidbody = this.GetComponent(); RigidbodyDisabled thisDisabledRigidbody = RigidbodyDisabled.Get(this.transform); if (thisRigidbody != null) ReleaseStaticJoint(); else if (thisDisabledRigidbody != null) ReleaseSocketParenting(attachedTransform); else ReleaseTransformParenting(); } public virtual void ReleaseStaticJoint() { DebugLog("ReleaseStaticJoint"); Joint[] joints = this.GetComponents(); foreach (Joint joint in joints) { #if UNITY_EDITOR if (!Application.isPlaying) DestroyImmediate(joint, true); else #endif Destroy(joint); } } protected void ReleaseTransformParenting() { DebugLog("Release Transform from Parenting"); attachedTransform.SetParent(attachedTransformParent); } protected void ReleaseSocketParenting(Transform objTransform) { DebugLog("ReleaseSocketParenting"); RigidbodyDisabled.UnparentRigidbody(objTransform, this.transform); } #endregion Static Object #endregion Release #region Start protected virtual void Awake() { UnityEngine.SceneManagement.SceneManager.sceneUnloaded += OnSceneUnload; } #endregion Start #region Update protected virtual void Update() { UpdateHolding(); } #endregion #region Events protected static string[] attachEventTypeLabels = { "Never", "On Attach", "On Release", "While Attached", "While Released", "When Changed", "Always" }; /// A GameObject Event for triggering changes in the Transform held by the Socket public GameObjectEventHandlers attachEvent = new GameObjectEventHandlers() { label = "Hold Event", tooltip = "Call functions using what the socket is holding\n" + "Parameter: the GameObject held by the socket", eventTypeLabels = attachEventTypeLabels }; protected void UpdateHolding() { if (attachedTransform != null) attachEvent.value = attachedTransform.gameObject; else attachEvent.value = null; } protected virtual void OnSceneUnload(UnityEngine.SceneManagement.Scene _) { if (destroyOnLoad) { if (attachedTransform != null) { Destroy(attachedTransform.gameObject); } } } #endregion #region Gizmos protected Mesh gizmoMesh; public void OnDrawGizmosSelected() { if (gizmoMesh == null) gizmoMesh = GenerateGizmoMesh1(); Gizmos.color = Color.yellow; Gizmos.DrawWireMesh(gizmoMesh, transform.position, transform.rotation); } public static Mesh GenerateGizmoMesh1() { Vector3 p0 = new Vector3(-0.01F, 0.03F, -0.01F); Vector3 p1 = new Vector3(-0.01F, -0.03F, -0.01F); Vector3 p2 = new Vector3(-0.01F, -0.03F, 0.01F); Vector3 p3 = new Vector3(-0.01F, 0.01F, 0.01F); Vector3 p4 = new Vector3(-0.01F, 0.01F, 0.03F); Vector3 p5 = new Vector3(-0.01F, 0.03F, 0.03F); Vector3 p6 = new Vector3(0.01F, 0.03F, -0.01F); Vector3 p7 = new Vector3(0.01F, -0.03F, -0.01F); Vector3 p8 = new Vector3(0.01F, -0.03F, 0.01F); Vector3 p9 = new Vector3(0.01F, 0.01F, 0.01F); Vector3 p10 = new Vector3(0.01F, 0.01F, 0.03F); Vector3 p11 = new Vector3(0.01F, 0.03F, 0.03F); Vector3[] vertices = { p0, p1, p6, p7, // 0, 1, 2, 3 p1, p2, p7, p8, // 4, 5, 6, 7 p2, p3, p8, p9, // 8, 9, 10, 11 p3, p4, p9, p10, // 12, 13, 14, 15 p4, p5, p10, p11, // 16, 17, 18, 19 p5, p0, p11, p6, // 20, 21, 22, 23 //p0, p1, p2, p3, p4, p5, // 24, 25, 26, 27, 28, 29 //p6, p7, p8, p9, p10, p11, // 30, 31, 32, 33, 34, 35 }; int[] triangles = { 0, 2, 2, 3, 1, 1, 0, 1, 1, 3, 2, 2, 4, 6, 6, 7, 5, 5, 4, 5, 5, 7, 6, 6, 8, 10, 10, 11, 9, 9, 8, 9, 9, 11, 10, 10, 12, 14, 14, 15, 13, 13, 12, 13, 13, 15, 14, 14, 16, 18, 18, 19, 17, 17, 16, 17, 17, 19, 18, 18, 20, 22, 22, 23, 21, 21, 20, 21, 21, 23, 22, 22, }; Vector3[] normals = { Vector3.back, Vector3.back, Vector3.back, Vector3.back, //back Vector3.down, Vector3.down, Vector3.down, Vector3.down, //down Vector3.forward, Vector3.forward, Vector3.forward, Vector3.forward, //forward Vector3.down, Vector3.down, Vector3.down, Vector3.down, //down Vector3.forward, Vector3.forward, Vector3.forward, Vector3.forward, //forward Vector3.up, Vector3.up, Vector3.up, Vector3.up, //up //Vector3.left, Vector3.left, Vector3.left, Vector3.left, Vector3.left, Vector3.left, //Vector3.right, Vector3.right, Vector3.right, Vector3.right, Vector3.right, Vector3.right, }; Mesh gizmoMesh = new Mesh() { vertices = vertices, triangles = triangles, normals = normals }; return gizmoMesh; } public static Mesh GenerateGizmoMesh() { Vector3 p0 = new Vector3(-0.01F, 0.03F, -0.01F); Vector3 p1 = new Vector3(-0.01F, -0.03F, -0.01F); Vector3 p2 = new Vector3(-0.01F, -0.03F, 0.01F); Vector3 p3 = new Vector3(-0.01F, 0.01F, 0.01F); Vector3 p4 = new Vector3(-0.01F, 0.01F, 0.03F); Vector3 p5 = new Vector3(-0.01F, 0.03F, 0.03F); Vector3 p6 = new Vector3(0.01F, 0.03F, -0.01F); Vector3 p7 = new Vector3(0.01F, -0.03F, -0.01F); Vector3 p8 = new Vector3(0.01F, -0.03F, 0.01F); Vector3 p9 = new Vector3(0.01F, 0.01F, 0.01F); Vector3 p10 = new Vector3(0.01F, 0.01F, 0.03F); Vector3 p11 = new Vector3(0.01F, 0.03F, 0.03F); Vector3[] vertices = { p0, p1, p6, p7, // 0, 1, 2, 3 p1, p2, p7, p8, // 4, 5, 6, 7 p2, p3, p8, p9, // 8, 9, 10, 11 p3, p4, p9, p10, // 12, 13, 14, 15 p4, p5, p10, p11, // 16, 17, 18, 19 p5, p0, p11, p6, // 20, 21, 22, 23 p0, p1, p2, p3, p4, p5, // 24, 25, 26, 27, 28, 29 p6, p7, p8, p9, p10, p11, // 30, 31, 32, 33, 34, 35 }; int[] triangles = { 0, 2, 1, 3, 1, 2, 4, 6, 5, 7, 5, 6, 8, 10, 9, 11, 9, 10, 12, 14, 13, 15, 13, 14, 16, 18, 17, 19, 17, 18, 20, 22, 21, 23, 21, 22, 27,24,25, 27,25,26, 29,27,28, 27,29,24, 30,33,31, 31,33,32, 33,35,34, 35,33,30, }; Vector3[] normals = { Vector3.back, Vector3.back, Vector3.back, Vector3.back, //back Vector3.down, Vector3.down, Vector3.down, Vector3.down, //down Vector3.forward, Vector3.forward, Vector3.forward, Vector3.forward, //forward Vector3.down, Vector3.down, Vector3.down, Vector3.down, //down Vector3.forward, Vector3.forward, Vector3.forward, Vector3.forward, //forward Vector3.up, Vector3.up, Vector3.up, Vector3.up, //up Vector3.left, Vector3.left, Vector3.left, Vector3.left, Vector3.left, Vector3.left, Vector3.right, Vector3.right, Vector3.right, Vector3.right, Vector3.right, Vector3.right, }; Mesh gizmoMesh = new Mesh() { vertices = vertices, triangles = triangles, normals = normals }; return gizmoMesh; } #endregion } // Component to indicate that the joints are being destroyed // When a rigidbody is released and immediately attached to anther socket (e.g. grabbing) // the joints may not yet be destroyed when they are grabbed, because Destroy is executed with a delay. // Adding the DestroyedJoints component indicates that the joints which may // still be there are to be destroyed. public class DestroyedJoints : MonoBehaviour { private void LateUpdate() { Destroy(this); } } }