using UnityEngine; using FSA = UnityEngine.Serialization.FormerlySerializedAsAttribute; namespace Lean.Common { /// This component allows you to rotate the current GameObject using events. [HelpURL(LeanHelper.PlusHelpUrlPrefix + "LeanManualRotate")] [AddComponentMenu(LeanHelper.ComponentPathPrefix + "Manual Rotate")] public class LeanManualRotate : MonoBehaviour { /// If you want this component to work on a different GameObject, then specify it here. This can be used to improve organization if your GameObject already has many components. public GameObject Target { set { target = value; } get { return target; } } [FSA("Target")] [SerializeField] private GameObject target; /// This allows you to set the coordinate space the rotation will use. public Space Space { set { space = value; } get { return space; } } [FSA("Space")] [SerializeField] private Space space; /// The first rotation axis, used when calling RotateA or RotateAB. public Vector3 AxisA { set { axisA = value; } get { return axisA; } } [FSA("AxisA")] [SerializeField] private Vector3 axisA = Vector3.down; /// The second rotation axis, used when calling RotateB or RotateAB. public Vector3 AxisB { set { axisB = value; } get { return axisB; } } [FSA("AxisB")] [SerializeField] private Vector3 axisB = Vector3.right; /// The rotation angle is multiplied by this. /// 1 = Normal rotation. /// 2 = Double rotation. public float Multiplier { set { multiplier = value; } get { return multiplier; } } [FSA("Multiplier")] [SerializeField] private float multiplier = 1.0f; /// If you want this component to change smoothly over time, then this allows you to control how quick the changes reach their target value. /// -1 = Instantly change. /// 1 = Slowly change. /// 10 = Quickly change. public float Damping { set { damping = value; } get { return damping; } } [FSA("Dampening")] [SerializeField] private float damping = 10.0f; /// If you enable this then the rotation will be multiplied by Time.deltaTime. This allows you to maintain frame rate independent rotation. public bool ScaleByTime { set { scaleByTime = value; } get { return scaleByTime; } } [FSA("ScaleByTime")] [SerializeField] private bool scaleByTime; /// If you call the ResetRotation method, the rotation will be set to this Euler rotation. public Vector3 DefaultRotation { set { defaultRotation = value; } get { return defaultRotation; } } [FSA("DefaultRotation")] [SerializeField] private Vector3 defaultRotation; [SerializeField] private Quaternion remainingDelta = Quaternion.identity; /// This method will reset the rotation to the specified DefaultRotation value. [ContextMenu("Reset Rotation")] public void ResetRotation() { var finalTransform = target != null ? target.transform : transform; var oldRotation = finalTransform.localRotation; if (space == Space.Self) { finalTransform.localRotation = Quaternion.Euler(defaultRotation); } else { finalTransform.rotation = Quaternion.Euler(defaultRotation); } remainingDelta *= Quaternion.Inverse(oldRotation) * finalTransform.localRotation; // Revert finalTransform.localRotation = oldRotation; } /// This method will cause the rotation to immediately snap to its final value. [ContextMenu("Snap To Target")] public void SnapToTarget() { UpdateRotation(1.0f); } /// This method will clear the target rotation value, causing the rotation to stop. [ContextMenu("Stop Rotation")] public void StopRotation() { remainingDelta = Quaternion.identity; } /// This method allows you to rotate around AxisA, with the specified angle in degrees. public void RotateA(float delta) { RotateAB(new Vector2(delta, 0.0f)); } /// This method allows you to rotate around AxisB, with the specified angle in degrees. public void RotateB(float delta) { RotateAB(new Vector2(0.0f, delta)); } /// This method allows you to rotate around AxisA and AxisB, with the specified angles in degrees. public void RotateAB(Vector2 delta) { var finalTransform = target != null ? target.transform : transform; var oldRotation = finalTransform.localRotation; if (scaleByTime == true) { delta *= Time.deltaTime; } finalTransform.Rotate(axisA, delta.x * multiplier, space); finalTransform.Rotate(axisB, delta.y * multiplier, space); remainingDelta *= Quaternion.Inverse(oldRotation) * finalTransform.localRotation; // Revert finalTransform.localRotation = oldRotation; } protected virtual void Update() { var factor = LeanHelper.GetDampenFactor(damping, Time.deltaTime); UpdateRotation(factor); } private void UpdateRotation(float factor) { var finalTransform = target != null ? target.transform : transform; var newDelta = Quaternion.Slerp(remainingDelta, Quaternion.identity, factor); finalTransform.localRotation = finalTransform.localRotation * Quaternion.Inverse(newDelta) * remainingDelta; remainingDelta = newDelta; } } } #if UNITY_EDITOR namespace Lean.Common.Editor { using TARGET = LeanManualRotate; [UnityEditor.CustomEditor(typeof(TARGET))] public class LeanManualRotate_Editor : LeanEditor { protected override void OnInspector() { TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts); Draw("target", "If you want this component to work on a different GameObject, then specify it here. This can be used to improve organization if your GameObject already has many components."); Draw("space", "This allows you to set the coordinate space the rotation will use."); Draw("axisA", "The first rotation axis, used when calling RotateA or RotateAB."); Draw("axisB", "The second rotation axis, used when calling RotateB or RotateAB."); Separator(); Draw("multiplier", "The rotation angle is multiplied by this.\n\n1 = Normal rotation.\n\n2 = Double rotation."); Draw("scaleByTime", "If you enable this then the rotation will be multiplied by Time.deltaTime. This allows you to maintain frame rate independent rotation."); Draw("damping", "If you want this component to change smoothly over time, then this allows you to control how quick the changes reach their target value.\n\n-1 = Instantly change.\n\n1 = Slowly change.\n\n10 = Quickly change."); Draw("defaultRotation", "If you call the ResetRotation method, the rotation will be set to this Euler rotation."); } } } #endif