2024-12-09 10:02:19 +01:00

996 lines
43 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections;
using UnityEngine;
namespace Passer {
/// <summary>Sockets can hold a Handles.</summary>
/// \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
/// <a href="https://docs.unity3d.com/Manual/Tags.html">Unity tags</a>.
///
/// 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 Handles 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);
}
/// <summary>
/// Does the socket currently have a handle attached?
/// </summary>
public bool isOccupied {
get {
return this.attachedTransform != null;
}
}
/// <summary>
/// A prefab which is used to attach to the socket at startup.
/// </summary>
public GameObject attachedPrefab;
/// <summary>The Transform attached to this socket</summary>
/// 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;
/// <summary>The parent of the attached transform before it was attached</summary>
/// This is used to restore the parent when the transform is released again.
protected Transform attachedTransformParent;
/// <summary>A tag for limiting which handles can be held by the socket</summary>
/// 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<Transform>();
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<Transform>();
Rigidbody handleRigidbody = handle.GetComponentInParent<Rigidbody>();
if (handleRigidbody != null) {
handleTransform = handleRigidbody.transform;
MechanicalJoint handleLimitations = handle.GetComponent<MechanicalJoint>();
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;
}
/// <summary>Tries to attach the given Transform to this socket</summary>
/// 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.
/// <param name="transformToAttach">The Transform to attach to this socket</param>
/// <returns>Boolean indicating whether attachment succeeded</returns>
public virtual bool Attach(Transform transformToAttach, bool rangeCheck = true) {
DebugLog("Attach transform " + transformToAttach);
Handle handle;
Rigidbody rigidbodyToAttach = transformToAttach.GetComponentInParent<Rigidbody>();
if (rigidbodyToAttach != null)
handle = rigidbodyToAttach.GetComponentInChildren<Handle>();
else
handle = transformToAttach.GetComponentInChildren<Handle>();
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<Rigidbody>();
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;
}
/// <summary>Tries to attach the given Transform to this socket</summary>
/// 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.
/// <param name="handle">The Handle to attach to this socket</param>
/// <returns>Boolean indicating whether attachment succeeded</returns>
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<Rigidbody>();
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<Rigidbody>();
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<Rigidbody>();
Joint joint = objRigidbody.GetComponent<Joint>();
// See if these joints are being destroyed
DestroyedJoints destroyedJoints = objRigidbody.GetComponent<DestroyedJoints>();
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<Rigidbody>();
MechanicalJoint handleLimitations = handleRigidbody.GetComponent<MechanicalJoint>();
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<Rigidbody>();
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<ConfigurableJoint>();
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<Collider>();
joint.connectedBody = c.attachedRigidbody;
attachedTransform = objRigidbody.transform;
attachedHandle = handle;
handle.socket = this;
}
/// <summary>Attach handle to socket using a static joint on the handle</summary>
protected virtual void AttachRigidbodyReverseJoint(Rigidbody objRigidbody, Handle handle) {
DebugLog("AttachRigidbodyReverseJoint " + objRigidbody);
MoveHandleToSocket(this.transform, handle);
ConfigurableJoint joint = objRigidbody.gameObject.AddComponent<ConfigurableJoint>();
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<Rigidbody>();
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<FixedJoint>();
Collider c = objTransform.GetComponentInChildren<Collider>();
if (c == null)
c = objTransform.GetComponentInParent<Collider>();
joint.connectedBody = c.attachedRigidbody;
}
virtual protected void AttachStaticJointRotY(Transform objTransform) {
DebugLog("AttachStaticJointRotY is still fixed");
FixedJoint joint = this.transform.gameObject.AddComponent<FixedJoint>();
Collider c = objTransform.GetComponentInChildren<Collider>();
if (c == null)
c = objTransform.GetComponentInParent<Collider>();
joint.connectedBody = c.attachedRigidbody;
}
#endregion Static Object
#endregion Attach
#region Release
/// <summary>Releases a Transform from the socket</summary>
/// 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>();
Rigidbody objRigidbody = attachedTransform.GetComponentInParent<Rigidbody>();
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;
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
protected 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>();
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.linearVelocity = thisRigidbody.linearVelocity;
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<Humanoid.AdvancedHandPhysics>();
if (handPhysics != null) {
Collider[] objColliders = objRigidbody.GetComponentsInChildren<Collider>();
foreach (Collider objCollider in objColliders)
Target.UnsetColliderToTrigger(handPhysics.handTarget.colliders, objCollider);
}
}
protected virtual void ReleaseRigidbodyJoint() {
DebugLog("Release from Joint");
Joint[] joints = this.gameObject.GetComponents<Joint>();
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<DestroyedJoints>();
}
protected void ReleaseRigidbodyReverseJoint() {
DebugLog("Release from reverse Joint");
Joint[] joints = attachedTransform.GetComponents<Joint>();
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<DestroyedJoints>();
}
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<Rigidbody>();
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<Joint>();
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"
};
/// <summary>A GameObject Event for triggering changes in the Transform held by the Socket</summary>
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
}
// <summary>Component to indicate that the joints are being destroyed</summary>
// 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);
}
}
}