forked from cgvr/DeltaVR
		
	
		
			
				
	
	
		
			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);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |