You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
400 lines
17 KiB
C#
400 lines
17 KiB
C#
using MalbersAnimations.Scriptables;
|
|
using System.Collections;
|
|
using UnityEngine;
|
|
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
namespace MalbersAnimations.Utilities
|
|
{
|
|
[AddComponentMenu("Malbers/Utilities/Aling/Aligner")]
|
|
public class Aligner : MonoBehaviour, IAlign
|
|
{
|
|
/// <summary></summary>
|
|
public TransformReference mainPoint = new TransformReference();
|
|
/// <summary></summary>
|
|
public TransformReference secondPoint = new TransformReference();
|
|
/// <summary></summary>
|
|
public float AlignTime = 0.25f;
|
|
|
|
[Tooltip("Add an offset to the rotation alignment")]
|
|
public float AngleOffset = 0;
|
|
|
|
//[Tooltip("Add an offset to the Position alignment")]
|
|
//public float PosOffset = 0;
|
|
/// <summary></summary>
|
|
public AnimationCurve AlignCurve = new AnimationCurve(MTools.DefaultCurve);
|
|
|
|
/// <summary></summary>
|
|
public bool AlignPos = true;
|
|
/// <summary></summary>
|
|
public bool AlignRot = true;
|
|
/// <summary>When Rotation is Enabled then It will find the closest Rotation</summary>
|
|
public bool DoubleSided = true;
|
|
/// <summary>Align a gameObject Looking at the Aligner</summary>
|
|
public bool AlignLookAt = false;
|
|
/// <summary>The Target will move close to the Aligner equals to the Radius</summary>
|
|
public float LookAtRadius;
|
|
public float LookAtRadiusTime = 0.25f;
|
|
|
|
///// <summary>Minimum Distance the animal will move if the Radius is greater than zero</summary>
|
|
//public float LookAtDistance;
|
|
public Color DebugColor = new Color(1, 0.23f, 0, 1f);
|
|
|
|
|
|
public bool Active { get => enabled; set => enabled = value; }
|
|
|
|
public Transform MainPoint => mainPoint.Value;
|
|
public Transform SecondPoint => secondPoint.Value;
|
|
|
|
public virtual void Align(GameObject Target) => Align(Target.transform);
|
|
|
|
public virtual void Align(Collider Target) => Align(Target.transform.root);
|
|
|
|
public virtual void Align(Component Target) => Align(Target.transform.root);
|
|
public virtual void StopAling() { StopAllCoroutines(); }
|
|
public virtual void Align_Self_To(GameObject Target) => Align_Self_To(Target.transform);
|
|
|
|
public virtual void Align_Self_To(Collider Target) => Align_Self_To(Target.transform.root);
|
|
|
|
public virtual void Align_Self_To(Component Target) => Align_Self_To(Target.transform.root);
|
|
|
|
public virtual void Align_Self_To(Transform reference)
|
|
{
|
|
if (Active && MainPoint && reference != null)
|
|
{
|
|
if (AlignLookAt)
|
|
{
|
|
StartCoroutine(AlignLookAtTransform(mainPoint, reference, AlignTime, AlignCurve)); //Align Look At the Zone
|
|
if (LookAtRadius > 0) StartCoroutine(MTools.AlignTransformRadius(reference, mainPoint.position, LookAtRadiusTime, LookAtRadius, AlignCurve)); //Align Look At the Zone
|
|
}
|
|
else
|
|
{
|
|
if (AlignPos)
|
|
{
|
|
Vector3 AlingPosition = reference.position;
|
|
StartCoroutine(MTools.AlignTransform_Position(MainPoint, AlingPosition, AlignTime, AlignCurve));
|
|
}
|
|
if (AlignRot)
|
|
{
|
|
Quaternion Side1 = reference.rotation;
|
|
Quaternion self = MainPoint.rotation;
|
|
|
|
if (DoubleSided)
|
|
{
|
|
Quaternion Side2 = reference.rotation * Quaternion.Euler(0, 180, 0);
|
|
|
|
var Side1Angle = Quaternion.Angle(self, Side1);
|
|
var Side2Angle = Quaternion.Angle(self, Side2);
|
|
|
|
StartCoroutine(MTools.AlignTransform_Rotation(MainPoint, Side1Angle < Side2Angle ? Side1 : Side2, AlignTime, AlignCurve));
|
|
}
|
|
else
|
|
StartCoroutine(MTools.AlignTransform_Rotation(MainPoint, Side1, AlignTime, AlignCurve));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual void Align(Transform TargetToAlign)
|
|
{
|
|
if (Active && MainPoint && TargetToAlign != null)
|
|
{
|
|
if (AlignLookAt)
|
|
{
|
|
StartCoroutine(AlignLookAtTransform(TargetToAlign, mainPoint, AlignTime, AlignCurve)); //Align Look At the Zone
|
|
if (LookAtRadius > 0)
|
|
StartCoroutine(MTools.AlignTransformRadius(TargetToAlign, mainPoint.position, LookAtRadiusTime, LookAtRadius, AlignCurve)); //Align Look At the Zone
|
|
}
|
|
else
|
|
{
|
|
if (AlignPos)
|
|
{
|
|
Vector3 AlingPosition = MainPoint.position;
|
|
|
|
if (SecondPoint) //In case there's a line ... move to the closest point between the two transforms
|
|
{
|
|
AlingPosition = MTools.ClosestPointOnLine(MainPoint.position, SecondPoint.position, TargetToAlign.transform.position);
|
|
}
|
|
|
|
if (DoubleSided)
|
|
{
|
|
Vector3 AlingPosOpposite = transform.InverseTransformPoint(AlingPosition);
|
|
AlingPosOpposite.z *= -1;
|
|
AlingPosOpposite = transform.TransformPoint(AlingPosOpposite);
|
|
|
|
var Distance1 = Vector3.Distance(TargetToAlign.transform.position, AlingPosition);
|
|
var Distance2 = Vector3.Distance(TargetToAlign.transform.position, AlingPosOpposite);
|
|
|
|
var AlignTransformResult = Distance2 < Distance1 ? AlingPosOpposite : AlingPosition;
|
|
|
|
StartCoroutine(MTools.AlignTransform_Position(TargetToAlign.transform, AlignTransformResult, AlignTime, AlignCurve));
|
|
}
|
|
else
|
|
{
|
|
StartCoroutine(MTools.AlignTransform_Position(TargetToAlign.transform, AlingPosition, AlignTime, AlignCurve));
|
|
}
|
|
}
|
|
if (AlignRot)
|
|
{
|
|
Quaternion Side1 = MainPoint.rotation * Quaternion.Euler(0, AngleOffset, 0);
|
|
Quaternion AnimalRot = TargetToAlign.transform.rotation;
|
|
|
|
if (DoubleSided)
|
|
{
|
|
Quaternion Side2 = MainPoint.rotation * Quaternion.Euler(0, 180, 0) * Quaternion.Euler(0, AngleOffset, 0);
|
|
|
|
var Side1Angle = Quaternion.Angle(AnimalRot, Side1);
|
|
var Side2Angle = Quaternion.Angle(AnimalRot, Side2);
|
|
|
|
StartCoroutine(MTools.AlignTransform_Rotation(TargetToAlign.transform, Side1Angle < Side2Angle ? Side1 : Side2, AlignTime, AlignCurve));
|
|
}
|
|
else
|
|
StartCoroutine(MTools.AlignTransform_Rotation(TargetToAlign.transform, Side1, AlignTime, AlignCurve));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Makes a transform Rotate towards another using LookAt Rotation
|
|
/// </summary>
|
|
/// <param name="t1">Transform that it will be rotated</param>
|
|
/// <param name="t2">Transform reference to Look At</param>
|
|
/// <param name="time">time to do the lookat alignment</param>
|
|
/// <param name="curve">curve for the aligment</param>
|
|
/// <returns></returns>
|
|
IEnumerator AlignLookAtTransform(Transform t1, Transform t2, float time, AnimationCurve curve = null)
|
|
{
|
|
float elapsedTime = 0;
|
|
|
|
var Wait = new WaitForFixedUpdate();
|
|
|
|
Quaternion CurrentRot = t1.rotation;
|
|
Vector3 direction = (t2.position - t1.position).normalized;
|
|
direction.y = t1.forward.y;
|
|
Quaternion FinalRot = Quaternion.LookRotation(direction) * Quaternion.Euler(0, AngleOffset, 0);
|
|
|
|
while ((time > 0) && (elapsedTime <= time))
|
|
{
|
|
float result = curve != null ? curve.Evaluate(elapsedTime / time) : elapsedTime / time; //Evaluation of the Pos curve
|
|
|
|
t1.rotation = Quaternion.SlerpUnclamped(CurrentRot, FinalRot, result);
|
|
|
|
elapsedTime += Time.fixedDeltaTime;
|
|
|
|
yield return Wait;
|
|
}
|
|
t1.rotation = FinalRot;
|
|
}
|
|
|
|
|
|
|
|
IEnumerator AlignTransform_Position(Transform t1, Vector3 NewPosition, float time, AnimationCurve curve = null)
|
|
{
|
|
float elapsedTime = 0;
|
|
|
|
Vector3 CurrentPos = t1.position;
|
|
|
|
while ((time > 0) && (elapsedTime <= time))
|
|
{
|
|
float result = curve != null ? curve.Evaluate(elapsedTime / time) : elapsedTime / time; //Evaluation of the Pos curve
|
|
t1.position = Vector3.LerpUnclamped(CurrentPos, NewPosition, result);
|
|
elapsedTime += Time.deltaTime;
|
|
|
|
yield return null;
|
|
}
|
|
t1.position = NewPosition;
|
|
}
|
|
|
|
|
|
|
|
#if UNITY_EDITOR
|
|
|
|
void Reset()
|
|
{
|
|
mainPoint = transform;
|
|
}
|
|
|
|
|
|
void OnValidate()
|
|
{
|
|
LookAtRadius = LookAtRadius < 0 ? 0 : LookAtRadius;
|
|
AlignTime = AlignTime < 0 ? 0 : AlignTime;
|
|
LookAtRadiusTime = LookAtRadiusTime < 0 ? 0 : LookAtRadiusTime;
|
|
}
|
|
|
|
|
|
void OnDrawGizmos()
|
|
{
|
|
var WireColor = new Color(DebugColor.r, DebugColor.g, DebugColor.b, 1);
|
|
if (MainPoint)
|
|
{
|
|
if (AlignLookAt && LookAtRadius > 0)
|
|
{
|
|
UnityEditor.Handles.color = DebugColor;
|
|
UnityEditor.Handles.DrawWireDisc(MainPoint.position, transform.up, LookAtRadius);
|
|
}
|
|
|
|
if (SecondPoint)
|
|
{
|
|
Gizmos.color = WireColor;
|
|
Gizmos.DrawLine(MainPoint.position, SecondPoint.position);
|
|
Gizmos.DrawCube(MainPoint.position, Vector3.one * 0.05f);
|
|
Gizmos.DrawCube(SecondPoint.position, Vector3.one * 0.05f);
|
|
|
|
if (DoubleSided)
|
|
{
|
|
var AlingPoint1Opp = transform.InverseTransformPoint(MainPoint.position);
|
|
var AlingPoint2Opp = transform.InverseTransformPoint(SecondPoint.position);
|
|
|
|
AlingPoint1Opp.z *= -1;
|
|
AlingPoint2Opp.z *= -1;
|
|
AlingPoint1Opp = transform.TransformPoint(AlingPoint1Opp);
|
|
AlingPoint2Opp = transform.TransformPoint(AlingPoint2Opp);
|
|
|
|
Gizmos.DrawLine(AlingPoint1Opp, AlingPoint2Opp);
|
|
Gizmos.DrawCube(AlingPoint1Opp, Vector3.one * 0.05f);
|
|
Gizmos.DrawCube(AlingPoint2Opp, Vector3.one * 0.05f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnDrawGizmosSelected()
|
|
{
|
|
if (AlignLookAt && LookAtRadius > 0 && MainPoint)
|
|
{
|
|
UnityEditor.Handles.color = new Color(1, 1, 0, 1);
|
|
UnityEditor.Handles.DrawWireDisc(MainPoint.position, transform.up, LookAtRadius);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
#if UNITY_EDITOR
|
|
|
|
[CustomEditor(typeof(Aligner)), CanEditMultipleObjects]
|
|
public class AlignEditor : Editor
|
|
{
|
|
|
|
SerializedProperty
|
|
AlignPos, AlignRot, AlignLookAt, AlingPoint1, AlingPoint2, AlignTime, AlignCurve, DoubleSided, LookAtRadius, DebugColor, LookAtRadiusTime, AngleOffset;
|
|
|
|
// MonoScript script;
|
|
private void OnEnable()
|
|
{
|
|
//script = MonoScript.FromMonoBehaviour((MonoBehaviour)target);
|
|
|
|
AlignPos = serializedObject.FindProperty("AlignPos");
|
|
AngleOffset = serializedObject.FindProperty("AngleOffset");
|
|
AlignRot = serializedObject.FindProperty("AlignRot");
|
|
AlignLookAt = serializedObject.FindProperty("AlignLookAt");
|
|
AlingPoint1 = serializedObject.FindProperty("mainPoint");
|
|
AlingPoint2 = serializedObject.FindProperty("secondPoint");
|
|
AlignTime = serializedObject.FindProperty("AlignTime");
|
|
AlignCurve = serializedObject.FindProperty("AlignCurve");
|
|
DoubleSided = serializedObject.FindProperty("DoubleSided");
|
|
LookAtRadius = serializedObject.FindProperty("LookAtRadius");
|
|
DebugColor = serializedObject.FindProperty("DebugColor");
|
|
//PosOffset = serializedObject.FindProperty("PosOffset");
|
|
LookAtRadiusTime = serializedObject.FindProperty("LookAtRadiusTime");
|
|
}
|
|
|
|
|
|
public override void OnInspectorGUI()
|
|
{
|
|
serializedObject.Update();
|
|
|
|
MalbersEditor.DrawDescription("Aligns the Position and Rotation of an Target object relative this gameobject");
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
EditorGUILayout.BeginVertical(MalbersEditor.StyleGray);
|
|
{
|
|
//MalbersEditor.DrawScript(script);
|
|
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
{
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
var currentGUIColor = GUI.color;
|
|
var selected = (GUI.color + Color.green) / 2;
|
|
|
|
|
|
GUI.color = AlignPos.boolValue ? selected : currentGUIColor;
|
|
AlignPos.boolValue = GUILayout.Toggle(AlignPos.boolValue, new GUIContent("Position", "Align Position"), EditorStyles.miniButton);
|
|
|
|
GUI.color = AlignRot.boolValue ? selected : currentGUIColor;
|
|
AlignRot.boolValue = GUILayout.Toggle(AlignRot.boolValue, new GUIContent("Rotation", "Align Rotation"), EditorStyles.miniButton);
|
|
if (AlignPos.boolValue || AlignRot.boolValue) AlignLookAt.boolValue = false;
|
|
|
|
GUI.color = AlignLookAt.boolValue ? selected : currentGUIColor;
|
|
AlignLookAt.boolValue = GUILayout.Toggle(AlignLookAt.boolValue, new GUIContent("Look At", "Align a gameObject Looking at the Aligner"), EditorStyles.miniButton);
|
|
|
|
GUI.color = currentGUIColor;
|
|
|
|
if (AlignLookAt.boolValue) AlignPos.boolValue = AlignRot.boolValue = false;
|
|
|
|
EditorGUILayout.PropertyField(DebugColor, GUIContent.none, GUILayout.MaxWidth(40));
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
|
|
|
|
if (AlignRot.boolValue || AlignPos.boolValue)
|
|
EditorGUILayout.PropertyField(DoubleSided, new GUIContent("Double Sided", "When Rotation is Enabled then It will find the closest Rotation"));
|
|
|
|
if (AlignLookAt.boolValue)
|
|
{
|
|
EditorGUILayout.PropertyField(LookAtRadius, new GUIContent("Radius", "The Target will move close to the Aligner equals to the Radius"));
|
|
|
|
if (LookAtRadius.floatValue > 0)
|
|
EditorGUILayout.PropertyField(LookAtRadiusTime, new GUIContent("Look At Align Time", "Time to move The Target to the Aligner "));
|
|
}
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
|
|
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
{
|
|
EditorGUILayout.PropertyField(AlingPoint1, new GUIContent("Main Point", "The Target GameObject will move to the Position of the Align Point"));
|
|
if (AlignPos.boolValue)
|
|
{
|
|
EditorGUILayout.PropertyField(AlingPoint2,
|
|
new GUIContent("2nd Point", "If Point End is Active then the Animal will align to the closed position from the 2 align points line"));
|
|
//EditorGUILayout.PropertyField(PosOffset);
|
|
}
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
|
|
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
{
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.PropertyField(AlignTime, new GUIContent("Align Time", "Time needed to make the Aligment"));
|
|
EditorGUILayout.PropertyField(AlignCurve, GUIContent.none, GUILayout.MaxWidth(75));
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
|
|
if (AlignRot.boolValue || AlignLookAt.boolValue)
|
|
EditorGUILayout.PropertyField(AngleOffset);
|
|
|
|
EditorGUILayout.EndVertical();
|
|
|
|
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
Undo.RecordObject(target, "Aligner Inspector");
|
|
EditorUtility.SetDirty(target);
|
|
}
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
}
|
|
}
|
|
#endif
|
|
} |