229 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			229 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using UnityEngine.Events;
 | |
| using UnityEngine.XR.Interaction.Toolkit;
 | |
| 
 | |
| namespace UnityEngine.XR.Content.Interaction
 | |
| {
 | |
|     /// <summary>
 | |
|     /// An interactable lever that snaps into an on or off position by a direct interactor
 | |
|     /// </summary>
 | |
|     public class XRLever : XRBaseInteractable
 | |
|     {
 | |
|         const float k_LeverDeadZone = 0.1f; // Prevents rapid switching between on and off states when right in the middle
 | |
| 
 | |
|         [SerializeField]
 | |
|         [Tooltip("The object that is visually grabbed and manipulated")]
 | |
|         Transform m_Handle = null;
 | |
| 
 | |
|         [SerializeField]
 | |
|         [Tooltip("The value of the lever")]
 | |
|         bool m_Value = false;
 | |
| 
 | |
|         [SerializeField]
 | |
|         [Tooltip("If enabled, the lever will snap to the value position when released")]
 | |
|         bool m_LockToValue;
 | |
| 
 | |
|         [SerializeField]
 | |
|         [Tooltip("Angle of the lever in the 'on' position")]
 | |
|         [Range(-90.0f, 90.0f)]
 | |
|         float m_MaxAngle = 90.0f;
 | |
| 
 | |
|         [SerializeField]
 | |
|         [Tooltip("Angle of the lever in the 'off' position")]
 | |
|         [Range(-90.0f, 90.0f)]
 | |
|         float m_MinAngle = -90.0f;
 | |
| 
 | |
|         [SerializeField]
 | |
|         [Tooltip("Events to trigger when the lever activates")]
 | |
|         UnityEvent m_OnLeverActivate = new UnityEvent();
 | |
| 
 | |
|         [SerializeField]
 | |
|         [Tooltip("Events to trigger when the lever deactivates")]
 | |
|         UnityEvent m_OnLeverDeactivate = new UnityEvent();
 | |
| 
 | |
|         IXRSelectInteractor m_Interactor;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The object that is visually grabbed and manipulated
 | |
|         /// </summary>
 | |
|         public Transform handle
 | |
|         {
 | |
|             get => m_Handle;
 | |
|             set => m_Handle = value;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The value of the lever
 | |
|         /// </summary>
 | |
|         public bool value
 | |
|         {
 | |
|             get => m_Value;
 | |
|             set => SetValue(value, true);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// If enabled, the lever will snap to the value position when released
 | |
|         /// </summary>
 | |
|         public bool lockToValue { get; set; }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Angle of the lever in the 'on' position
 | |
|         /// </summary>
 | |
|         public float maxAngle
 | |
|         {
 | |
|             get => m_MaxAngle;
 | |
|             set => m_MaxAngle = value;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Angle of the lever in the 'off' position
 | |
|         /// </summary>
 | |
|         public float minAngle
 | |
|         {
 | |
|             get => m_MinAngle;
 | |
|             set => m_MinAngle = value;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Events to trigger when the lever activates
 | |
|         /// </summary>
 | |
|         public UnityEvent onLeverActivate => m_OnLeverActivate;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Events to trigger when the lever deactivates
 | |
|         /// </summary>
 | |
|         public UnityEvent onLeverDeactivate => m_OnLeverDeactivate;
 | |
| 
 | |
|         void Start()
 | |
|         {
 | |
|             SetValue(m_Value, true);
 | |
|         }
 | |
| 
 | |
|         protected override void OnEnable()
 | |
|         {
 | |
|             base.OnEnable();
 | |
|             selectEntered.AddListener(StartGrab);
 | |
|             selectExited.AddListener(EndGrab);
 | |
|         }
 | |
| 
 | |
|         protected override void OnDisable()
 | |
|         {
 | |
|             selectEntered.RemoveListener(StartGrab);
 | |
|             selectExited.RemoveListener(EndGrab);
 | |
|             base.OnDisable();
 | |
|         }
 | |
| 
 | |
|         void StartGrab(SelectEnterEventArgs args)
 | |
|         {
 | |
|             m_Interactor = args.interactorObject;
 | |
|         }
 | |
| 
 | |
|         void EndGrab(SelectExitEventArgs args)
 | |
|         {
 | |
|             SetValue(m_Value, true);
 | |
|             m_Interactor = null;
 | |
|         }
 | |
| 
 | |
|         public override void ProcessInteractable(XRInteractionUpdateOrder.UpdatePhase updatePhase)
 | |
|         {
 | |
|             base.ProcessInteractable(updatePhase);
 | |
| 
 | |
|             if (updatePhase == XRInteractionUpdateOrder.UpdatePhase.Dynamic)
 | |
|             {
 | |
|                 if (isSelected)
 | |
|                 {
 | |
|                     UpdateValue();
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         Vector3 GetLookDirection()
 | |
|         {
 | |
|             Vector3 direction = m_Interactor.GetAttachTransform(this).position - m_Handle.position;
 | |
|             direction = transform.InverseTransformDirection(direction);
 | |
|             direction.x = 0;
 | |
| 
 | |
|             return direction.normalized;
 | |
|         }
 | |
| 
 | |
|         void UpdateValue()
 | |
|         {
 | |
|             var lookDirection = GetLookDirection();
 | |
|             var lookAngle = Mathf.Atan2(lookDirection.z, lookDirection.y) * Mathf.Rad2Deg;
 | |
| 
 | |
|             if (m_MinAngle < m_MaxAngle)
 | |
|                 lookAngle = Mathf.Clamp(lookAngle, m_MinAngle, m_MaxAngle);
 | |
|             else
 | |
|                 lookAngle = Mathf.Clamp(lookAngle, m_MaxAngle, m_MinAngle);
 | |
| 
 | |
|             var maxAngleDistance = Mathf.Abs(m_MaxAngle - lookAngle);
 | |
|             var minAngleDistance = Mathf.Abs(m_MinAngle - lookAngle);
 | |
| 
 | |
|             if (m_Value)
 | |
|                 maxAngleDistance *= (1.0f - k_LeverDeadZone);
 | |
|             else
 | |
|                 minAngleDistance *= (1.0f - k_LeverDeadZone);
 | |
| 
 | |
|             var newValue = (maxAngleDistance < minAngleDistance);
 | |
| 
 | |
|             SetHandleAngle(lookAngle);
 | |
| 
 | |
|             SetValue(newValue);
 | |
|         }
 | |
| 
 | |
|         void SetValue(bool isOn, bool forceRotation = false)
 | |
|         {
 | |
|             if (m_Value == isOn)
 | |
|             {
 | |
|                 if (forceRotation)
 | |
|                     SetHandleAngle(m_Value ? m_MaxAngle : m_MinAngle);
 | |
| 
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             m_Value = isOn;
 | |
| 
 | |
|             if (m_Value)
 | |
|             {
 | |
|                 m_OnLeverActivate.Invoke();
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 m_OnLeverDeactivate.Invoke();
 | |
|             }
 | |
| 
 | |
|             if (!isSelected && (m_LockToValue || forceRotation))
 | |
|                 SetHandleAngle(m_Value ? m_MaxAngle : m_MinAngle);
 | |
|         }
 | |
| 
 | |
|         void SetHandleAngle(float angle)
 | |
|         {
 | |
|             if (m_Handle != null)
 | |
|                 m_Handle.localRotation = Quaternion.Euler(angle, 0.0f, 0.0f);
 | |
|         }
 | |
| 
 | |
|         void OnDrawGizmosSelected()
 | |
|         {
 | |
|             var angleStartPoint = transform.position;
 | |
| 
 | |
|             if (m_Handle != null)
 | |
|                 angleStartPoint = m_Handle.position;
 | |
| 
 | |
|             const float k_AngleLength = 0.25f;
 | |
| 
 | |
|             var angleMaxPoint = angleStartPoint + transform.TransformDirection(Quaternion.Euler(m_MaxAngle, 0.0f, 0.0f) * Vector3.up) * k_AngleLength;
 | |
|             var angleMinPoint = angleStartPoint + transform.TransformDirection(Quaternion.Euler(m_MinAngle, 0.0f, 0.0f) * Vector3.up) * k_AngleLength;
 | |
| 
 | |
|             Gizmos.color = Color.green;
 | |
|             Gizmos.DrawLine(angleStartPoint, angleMaxPoint);
 | |
| 
 | |
|             Gizmos.color = Color.red;
 | |
|             Gizmos.DrawLine(angleStartPoint, angleMinPoint);
 | |
|         }
 | |
| 
 | |
|         void OnValidate()
 | |
|         {
 | |
|             SetHandleAngle(m_Value ? m_MaxAngle : m_MinAngle);
 | |
|         }
 | |
|     }
 | |
| }
 |