/// --------------------------------------------- /// Ultimate Character Controller /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.UltimateCharacterController.Character.Abilities { using Opsive.Shared.Events; using Opsive.UltimateCharacterController.Utility; using UnityEngine; /// /// The RestrictPosition ability restricts the character to the specified rotation. /// [DefaultStartType(AbilityStartType.Automatic)] [DefaultStopType(AbilityStopType.Manual)] public class RestrictRotation : Ability { [Tooltip("The number of degrees that the character can rotate between.")] [SerializeField] protected float m_Restriction = 45f; [Tooltip("Any offset that should be applied to the local y rotation.")] [SerializeField] protected float m_Offset; [Tooltip("Should the local y rotation of the look source be applied to the rotation?")] [SerializeField] protected bool m_RelativeLookSourceRotation; [Tooltip("Any offset that should be applied to the look source y rotation.")] [SerializeField] protected float m_LookSourceOffset; public float Restriction { get { return m_Restriction; } set { m_Restriction = value; } } public float Offset { get { return m_Offset; } set { m_Offset = value; } } public bool RelativeLookSourceRotation { get { return m_RelativeLookSourceRotation; } set { m_RelativeLookSourceRotation = value; } } public float LookSourceOffset { get { return m_LookSourceOffset; } set { m_LookSourceOffset = value; } } private ILookSource m_LookSource; public override bool IsConcurrent { get { return true; } } /// /// Initialize the default values. /// public override void Awake() { base.Awake(); // The look source may have already been assigned if the ability was added to the character after the look source was assigned. m_LookSource = m_CharacterLocomotion.LookSource; EventHandler.RegisterEvent(m_GameObject, "OnCharacterAttachLookSource", OnAttachLookSource); } /// /// A new ILookSource object has been attached to the character. /// /// The ILookSource object attached to the character. private void OnAttachLookSource(ILookSource lookSource) { m_LookSource = lookSource; } /// /// Verify the rotation values. Called immediately before the rotation is applied. /// public override void ApplyRotation() { var targetRotation = m_Transform.rotation * Quaternion.Euler(m_CharacterLocomotion.DeltaRotation); var localTargetRotation = MathUtility.InverseTransformQuaternion(Quaternion.LookRotation(Vector3.forward, m_CharacterLocomotion.Up), targetRotation); // Find the closest angle to the degree restriction. var localEulerRotation = localTargetRotation.eulerAngles; var offset = m_Offset; // The rotation can be applied based on the look source angle. if (m_RelativeLookSourceRotation) { var localLookSourceRotation = MathUtility.InverseTransformQuaternion(Quaternion.LookRotation(Vector3.forward, m_CharacterLocomotion.Up), m_LookSource.Transform.rotation); offset += (localLookSourceRotation.eulerAngles.y + m_LookSourceOffset); } // Set the restricted rotation. localEulerRotation.y = MathUtility.ClampAngle(Mathf.Round((localEulerRotation.y - offset) / m_Restriction) * m_Restriction) + offset; // Rotate towards the restricted angle. targetRotation = MathUtility.TransformQuaternion(Quaternion.LookRotation(Vector3.forward, m_CharacterLocomotion.Up), Quaternion.Euler(localEulerRotation)); targetRotation = Quaternion.Slerp(m_Transform.rotation, targetRotation, m_CharacterLocomotion.MotorRotationSpeed * Time.deltaTime); m_CharacterLocomotion.Torque = targetRotation * Quaternion.Inverse(m_Transform.rotation); } /// /// The object has been destroyed. /// public override void OnDestroy() { base.OnDestroy(); EventHandler.UnregisterEvent(m_GameObject, "OnCharacterAttachLookSource", OnAttachLookSource); } } }