// Chest|Controls3D|100030 namespace VRTK { using UnityEngine; /// /// Transforms a game object into a chest with a lid. The direction can be auto-detected with very high reliability or set manually. /// /// /// The script will instantiate the required Rigidbody, Interactable and HingeJoint components automatically in case they do not exist yet. It will expect three distinct game objects: a body, a lid and a handle. These should be independent and not children of each other. /// /// /// `VRTK/Examples/025_Controls_Overview` shows a chest that can be open and closed, it also displays the current opening angle of the chest. /// [AddComponentMenu("VRTK/Scripts/Controls/3D/VRTK_Chest")] [System.Obsolete("`VRTK.VRTK_Chest` has been deprecated and can be recreated with `VRTK.Controllables.PhysicsBased.VRTK_PhysicsRotator`. This script will be removed in a future version of VRTK.")] public class VRTK_Chest : VRTK_Control { [Tooltip("The axis on which the chest should open. All other axis will be frozen.")] public Direction direction = Direction.autodetect; [Tooltip("The game object for the lid.")] public GameObject lid; [Tooltip("The game object for the body.")] public GameObject body; [Tooltip("The game object for the handle.")] public GameObject handle; [Tooltip("The parent game object for the chest content elements.")] public GameObject content; [Tooltip("Makes the content invisible while the chest is closed.")] public bool hideContent = true; [Tooltip("The maximum opening angle of the chest.")] public float maxAngle = 160f; protected float minAngle = 0f; protected float stepSize = 1f; protected Rigidbody bodyRigidbody; protected Rigidbody handleRigidbody; protected FixedJoint handleJoint; protected Rigidbody lidRigidbody; protected HingeJoint lidJoint; protected bool lidJointCreated; protected Direction finalDirection; protected float subDirection = 1; // positive or negative can be determined automatically since handle dictates that protected override void OnDrawGizmos() { base.OnDrawGizmos(); if (!enabled || !setupSuccessful) { return; } // show opening direction Bounds bounds; if (handle) { bounds = VRTK_SharedMethods.GetBounds(handle.transform, handle.transform); } else { bounds = VRTK_SharedMethods.GetBounds(lid.transform, lid.transform); } float length = bounds.extents.y * 5f; Vector3 point = bounds.center + new Vector3(0, length, 0); switch (finalDirection) { case Direction.x: point += transform.right.normalized * (length / 2f) * subDirection; break; case Direction.y: point += transform.up.normalized * (length / 2f) * subDirection; break; case Direction.z: point += transform.forward.normalized * (length / 2f) * subDirection; break; } Gizmos.DrawLine(bounds.center + new Vector3(0, bounds.extents.y, 0), point); Gizmos.DrawSphere(point, length / 8f); } protected override void InitRequiredComponents() { InitBody(); InitLid(); InitHandle(); SetContent(content, hideContent); } protected override bool DetectSetup() { if (lid == null || body == null) { return false; } finalDirection = (direction == Direction.autodetect) ? DetectDirection() : direction; if (finalDirection == Direction.autodetect) { return false; } Bounds lidBounds = VRTK_SharedMethods.GetBounds(lid.transform, transform); // determin sub-direction depending on handle if (handle) { Bounds handleBounds = VRTK_SharedMethods.GetBounds(handle.transform, transform); switch (finalDirection) { case Direction.x: subDirection = (handleBounds.center.x > lidBounds.center.x) ? -1 : 1; break; case Direction.y: subDirection = (handleBounds.center.y > lidBounds.center.y) ? -1 : 1; break; case Direction.z: subDirection = (handleBounds.center.z > lidBounds.center.z) ? -1 : 1; break; } // handle should be outside lid hierarchy, otherwise anchor-by-bounds calculation is off if (handle.transform.IsChildOf(lid.transform)) { return false; } } else { subDirection = -1; } if (lidJointCreated) { lidJoint.useLimits = true; lidJoint.enableCollision = true; JointLimits limits = lidJoint.limits; switch (finalDirection) { case Direction.x: lidJoint.anchor = new Vector3(subDirection * lidBounds.extents.x, 0, 0); lidJoint.axis = new Vector3(0, 0, 1); if (subDirection > 0) { limits.min = -maxAngle; limits.max = minAngle; } else { limits.min = minAngle; limits.max = maxAngle; } break; case Direction.y: lidJoint.anchor = new Vector3(0, subDirection * lidBounds.extents.y, 0); lidJoint.axis = new Vector3(0, 1, 0); if (subDirection > 0) { limits.min = -maxAngle; limits.max = minAngle; } else { limits.min = minAngle; limits.max = maxAngle; } break; case Direction.z: lidJoint.anchor = new Vector3(0, 0, subDirection * lidBounds.extents.z); lidJoint.axis = new Vector3(1, 0, 0); if (subDirection < 0) { limits.min = -maxAngle; limits.max = minAngle; } else { limits.min = minAngle; limits.max = maxAngle; } break; } lidJoint.limits = limits; } return true; } protected override ControlValueRange RegisterValueRange() { return new ControlValueRange() { controlMin = lidJoint.limits.min, controlMax = lidJoint.limits.max }; } protected override void HandleUpdate() { value = CalculateValue(); } protected virtual Direction DetectDirection() { Direction returnDirection = Direction.autodetect; if (!handle) { return returnDirection; } Bounds handleBounds = VRTK_SharedMethods.GetBounds(handle.transform, transform); Bounds lidBounds = VRTK_SharedMethods.GetBounds(lid.transform, transform); float lengthX = Mathf.Abs(handleBounds.center.x - (lidBounds.center.x + lidBounds.extents.x)); float lengthZ = Mathf.Abs(handleBounds.center.z - (lidBounds.center.z + lidBounds.extents.z)); float lengthNegX = Mathf.Abs(handleBounds.center.x - (lidBounds.center.x - lidBounds.extents.x)); float lengthNegZ = Mathf.Abs(handleBounds.center.z - (lidBounds.center.z - lidBounds.extents.z)); if (VRTK_SharedMethods.IsLowest(lengthX, new float[] { lengthZ, lengthNegX, lengthNegZ })) { returnDirection = Direction.x; } else if (VRTK_SharedMethods.IsLowest(lengthNegX, new float[] { lengthX, lengthZ, lengthNegZ })) { returnDirection = Direction.x; } else if (VRTK_SharedMethods.IsLowest(lengthZ, new float[] { lengthX, lengthNegX, lengthNegZ })) { returnDirection = Direction.z; } else if (VRTK_SharedMethods.IsLowest(lengthNegZ, new float[] { lengthX, lengthZ, lengthNegX })) { returnDirection = Direction.z; } return returnDirection; } protected virtual void InitBody() { bodyRigidbody = body.GetComponent(); if (bodyRigidbody == null) { bodyRigidbody = body.AddComponent(); bodyRigidbody.isKinematic = true; // otherwise body moves/falls over when lid is moved or fully open } } protected virtual void InitLid() { lidRigidbody = lid.GetComponent(); if (lidRigidbody == null) { lidRigidbody = lid.AddComponent(); } lidJoint = lid.GetComponent(); if (lidJoint == null) { lidJoint = lid.AddComponent(); lidJointCreated = true; } lidJoint.connectedBody = bodyRigidbody; if (!handle) { CreateInteractableObject(lid); } } protected virtual void InitHandle() { if (!handle) { return; } handleRigidbody = handle.GetComponent(); if (handleRigidbody == null) { handleRigidbody = handle.AddComponent(); } handleRigidbody.isKinematic = false; handleRigidbody.useGravity = false; handleJoint = handle.GetComponent(); if (handleJoint == null) { handleJoint = handle.AddComponent(); handleJoint.connectedBody = lidRigidbody; } CreateInteractableObject(handle); } protected virtual void CreateInteractableObject(GameObject targetGameObject) { VRTK_InteractableObject targetInteractableObject = targetGameObject.GetComponent(); if (targetInteractableObject == null) { targetInteractableObject = targetGameObject.AddComponent(); } targetInteractableObject.isGrabbable = true; targetInteractableObject.grabAttachMechanicScript = gameObject.AddComponent(); targetInteractableObject.secondaryGrabActionScript = gameObject.AddComponent(); targetInteractableObject.grabAttachMechanicScript.precisionGrab = true; targetInteractableObject.stayGrabbedOnTeleport = false; } protected virtual float CalculateValue() { return (Mathf.Round((minAngle + Mathf.Clamp01(Mathf.Abs(lidJoint.angle / (lidJoint.limits.max - lidJoint.limits.min))) * (maxAngle - minAngle)) / stepSize) * stepSize); } } }