|
|
- // Wheel|Controls3D|100062
- namespace VRTK
- {
- using UnityEngine;
- using GrabAttachMechanics;
-
- /// <summary>
- /// Attaching the script to a game object will allow the user to interact with it as if it were a spinnable wheel.
- /// </summary>
- /// <remarks>
- /// The script will instantiate the required Rigidbody and Interactable components automatically in case they do not exist yet.
- /// </remarks>
- /// <example>
- /// `VRTK/Examples/025_Controls_Overview` has a collection of wheels that can be rotated by grabbing with the controller and then rotating the controller in the desired direction.
- /// </example>
- [AddComponentMenu("VRTK/Scripts/Controls/3D/VRTK_Wheel")]
- [System.Obsolete("`VRTK.VRTK_Wheel` has been deprecated and can be recreated with `VRTK.Controllables.ArtificialBased.VRTK_ArtificialRotator`. This script will be removed in a future version of VRTK.")]
- public class VRTK_Wheel : VRTK_Control
- {
- /// <summary>
- /// The grab attach mechanic to use.
- /// </summary>
- public enum GrabTypes
- {
- /// <summary>
- /// Utilise the track object grab mechanic.
- /// </summary>
- TrackObject,
- /// <summary>
- /// Utilise the rotator track grab mechanic.
- /// </summary>
- RotatorTrack
- }
-
- [Tooltip("An optional game object to which the wheel will be connected. If the game object moves the wheel will follow along.")]
- public GameObject connectedTo;
- [Tooltip("The grab attach mechanic to use. Track Object allows for rotations of the controller, Rotator Track allows for grabbing the wheel and spinning it.")]
- public GrabTypes grabType = GrabTypes.TrackObject;
- [Tooltip("The maximum distance the grabbing controller is away from the wheel before it is automatically released.")]
- public float detatchDistance = 0.5f;
- [Tooltip("The minimum value the wheel can be set to.")]
- public float minimumValue = 0f;
- [Tooltip("The maximum value the wheel can be set to.")]
- public float maximumValue = 10f;
- [Tooltip("The increments in which values can change.")]
- public float stepSize = 1f;
- [Tooltip("If this is checked then when the wheel is released, it will snap to the step rotation.")]
- public bool snapToStep = false;
- [Tooltip("The amount of friction the wheel will have when it is grabbed.")]
- public float grabbedFriction = 25f;
- [Tooltip("The amount of friction the wheel will have when it is released.")]
- public float releasedFriction = 10f;
- [Range(0f, 359f)]
- [Tooltip("The maximum angle the wheel has to be turned to reach it's maximum value.")]
- public float maxAngle = 359f;
- [Tooltip("If this is checked then the wheel cannot be turned beyond the minimum and maximum value.")]
- public bool lockAtLimits = false;
-
- protected float angularVelocityLimit = 150f;
- protected float springStrengthValue = 150f;
- protected float springDamperValue = 5f;
- protected Quaternion initialLocalRotation;
- protected Rigidbody wheelRigidbody;
- protected HingeJoint wheelHinge;
- protected bool wheelHingeCreated = false;
- protected bool initialValueCalculated = false;
- protected float springAngle;
-
- protected override void InitRequiredComponents()
- {
- initialLocalRotation = transform.localRotation;
- InitWheel();
- }
-
- protected override bool DetectSetup()
- {
- if (wheelHingeCreated)
- {
- wheelHinge.anchor = Vector3.up;
- wheelHinge.axis = Vector3.up;
-
- if (connectedTo)
- {
- wheelHinge.connectedBody = connectedTo.GetComponent<Rigidbody>();
- }
- }
-
- return true;
- }
-
- protected override ControlValueRange RegisterValueRange()
- {
- return new ControlValueRange()
- {
- controlMin = minimumValue,
- controlMax = maximumValue
- };
- }
-
- protected override void HandleUpdate()
- {
- CalculateValue();
- if (lockAtLimits && !initialValueCalculated)
- {
- transform.localRotation = initialLocalRotation;
- initialValueCalculated = true;
- }
- }
-
- protected virtual void InitWheel()
- {
- SetupRigidbody();
- SetupHinge();
- SetupInteractableObject();
- }
-
- protected virtual void SetupRigidbody()
- {
- wheelRigidbody = GetComponent<Rigidbody>();
- if (wheelRigidbody == null)
- {
- wheelRigidbody = gameObject.AddComponent<Rigidbody>();
- wheelRigidbody.angularDrag = releasedFriction;
- }
- wheelRigidbody.isKinematic = false;
- wheelRigidbody.useGravity = false;
-
- if (connectedTo)
- {
- Rigidbody connectedToRigidbody = connectedTo.GetComponent<Rigidbody>();
- if (connectedToRigidbody == null)
- {
- connectedToRigidbody = connectedTo.AddComponent<Rigidbody>();
- connectedToRigidbody.useGravity = false;
- connectedToRigidbody.isKinematic = true;
- }
- }
- }
-
- protected virtual void SetupHinge()
- {
- wheelHinge = GetComponent<HingeJoint>();
- if (wheelHinge == null)
- {
- wheelHinge = gameObject.AddComponent<HingeJoint>();
- wheelHingeCreated = true;
- }
-
- SetupHingeRestrictions();
- }
-
- protected virtual void SetupHingeRestrictions()
- {
- float minJointLimit = 0f;
- float maxJointLimit = maxAngle;
- float limitOffset = maxAngle - 180f;
-
- if (limitOffset > 0f)
- {
- minJointLimit -= limitOffset;
- maxJointLimit = 180f;
- }
-
- if (lockAtLimits)
- {
- wheelHinge.useLimits = true;
- JointLimits wheelLimits = new JointLimits();
- wheelLimits.min = minJointLimit;
- wheelLimits.max = maxJointLimit;
- wheelHinge.limits = wheelLimits;
- Vector3 adjustedLimitsAngle = transform.localEulerAngles;
-
- switch (Mathf.RoundToInt(initialLocalRotation.eulerAngles.z))
- {
- case 0:
- adjustedLimitsAngle = new Vector3(transform.localEulerAngles.x, transform.localEulerAngles.y - minJointLimit, transform.localEulerAngles.z);
- break;
- case 90:
- adjustedLimitsAngle = new Vector3(transform.localEulerAngles.x + minJointLimit, transform.localEulerAngles.y, transform.localEulerAngles.z);
- break;
- case 180:
- adjustedLimitsAngle = new Vector3(transform.localEulerAngles.x, transform.localEulerAngles.y + minJointLimit, transform.localEulerAngles.z);
- break;
- }
- transform.localEulerAngles = adjustedLimitsAngle;
- initialValueCalculated = false;
- }
- }
-
- protected virtual void ConfigureHingeSpring()
- {
- JointSpring snapSpring = new JointSpring();
- snapSpring.spring = springStrengthValue;
- snapSpring.damper = springDamperValue;
- snapSpring.targetPosition = springAngle + wheelHinge.limits.min;
- wheelHinge.spring = snapSpring;
- }
-
- protected virtual void SetupInteractableObject()
- {
- VRTK_InteractableObject wheelInteractableObject = GetComponent<VRTK_InteractableObject>();
- if (wheelInteractableObject == null)
- {
- wheelInteractableObject = gameObject.AddComponent<VRTK_InteractableObject>();
- }
- wheelInteractableObject.isGrabbable = true;
-
- VRTK_TrackObjectGrabAttach attachMechanic;
-
- if (grabType == GrabTypes.TrackObject)
- {
- attachMechanic = gameObject.AddComponent<VRTK_TrackObjectGrabAttach>();
- if (lockAtLimits)
- {
- attachMechanic.velocityLimit = 0f;
- attachMechanic.angularVelocityLimit = angularVelocityLimit;
- }
- }
- else
- {
- attachMechanic = gameObject.AddComponent<VRTK_RotatorTrackGrabAttach>();
- }
-
- attachMechanic.precisionGrab = true;
- attachMechanic.detachDistance = detatchDistance;
-
- wheelInteractableObject.grabAttachMechanicScript = attachMechanic;
-
- wheelInteractableObject.secondaryGrabActionScript = gameObject.AddComponent<SecondaryControllerGrabActions.VRTK_SwapControllerGrabAction>();
- wheelInteractableObject.stayGrabbedOnTeleport = false;
-
- wheelInteractableObject.InteractableObjectGrabbed += WheelInteractableObjectGrabbed;
- wheelInteractableObject.InteractableObjectUngrabbed += WheelInteractableObjectUngrabbed;
- }
-
- protected virtual void WheelInteractableObjectGrabbed(object sender, InteractableObjectEventArgs e)
- {
- wheelRigidbody.angularDrag = grabbedFriction;
- wheelHinge.useSpring = false;
- }
-
- protected virtual void WheelInteractableObjectUngrabbed(object sender, InteractableObjectEventArgs e)
- {
- wheelRigidbody.angularDrag = releasedFriction;
- if (snapToStep)
- {
- wheelHinge.useSpring = true;
- ConfigureHingeSpring();
- }
- }
-
- protected virtual void CalculateValue()
- {
- ControlValueRange controlValueRange = RegisterValueRange();
- float angle;
- Vector3 axis;
-
- Quaternion rotationDelta = transform.localRotation * Quaternion.Inverse(initialLocalRotation);
- rotationDelta.ToAngleAxis(out angle, out axis);
-
- float calculatedValue = Mathf.Round((controlValueRange.controlMin + Mathf.Clamp01(angle / maxAngle) * (controlValueRange.controlMax - controlValueRange.controlMin)) / stepSize) * stepSize;
-
- float flatValue = calculatedValue - controlValueRange.controlMin;
- float controlRange = controlValueRange.controlMax - controlValueRange.controlMin;
- springAngle = (flatValue / controlRange) * maxAngle;
-
- value = calculatedValue;
- }
- }
- }
|