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.

423 lines
18 KiB
C#

3 years ago
using UnityEngine;
using MalbersAnimations.Scriptables;
using MalbersAnimations.Utilities;
using System.Linq;
using UnityEngine.Animations;
#if UNITY_EDITOR
using UnityEditorInternal;
using UnityEditor;
#endif
namespace MalbersAnimations
{
public class MessagesBehavior : StateMachineBehaviour
{
public bool UseSendMessage;
public bool SendToChildren = false;
public bool debug;
public bool NormalizeTime = true;
public MesssageItem[] onEnterMessage; //Store messages to send it when Enter the animation State
public MesssageItem[] onExitMessage; //Store messages to send it when Exit the animation State
public MesssageItem[] onTimeMessage; //Store messages to send on a specific time in the animation State
IAnimatorListener[] listeners; //To all the MonoBehavious that Have this
private bool firstime = false;
public bool OnEnter = true;
public bool OnExit= true;
public bool OnTime = true;
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
if (!firstime)
{
if (SendToChildren)
listeners = animator.GetComponentsInChildren<IAnimatorListener>();
else
listeners = animator.GetComponents<IAnimatorListener>();
firstime = true;
}
if (OnTime)
foreach (MesssageItem ontimeM in onTimeMessage) ontimeM.sent = false; //Set all the messages Ontime Sent = false when start
if (OnEnter)
{
foreach (MesssageItem onEnterM in onEnterMessage)
{
if (onEnterM.Active && !string.IsNullOrEmpty(onEnterM.message))
{
if (UseSendMessage)
onEnterM.DeliverMessage(animator, SendToChildren, debug);
else
foreach (var animListener in listeners)
onEnterM.DeliverAnimListener(animListener,debug);
}
}
}
}
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller)
{
if (OnExit)
{
foreach (MesssageItem onExitM in onExitMessage)
{
if (onExitM.Active && !string.IsNullOrEmpty(onExitM.message))
{
if (onEnterMessage != null && onEnterMessage.Length > 0 && onEnterMessage.ToList().Exists(x => x.message == onExitM.message))
{
if (animator.GetCurrentAnimatorStateInfo(layerIndex).fullPathHash == stateInfo.fullPathHash)
{
return; //Means is Looping to itself So Skip the Exit Mode because an Enter Mode is Playing
}
}
if (UseSendMessage)
onExitM.DeliverMessage(animator, SendToChildren, debug);
else
foreach (var animListener in listeners)
onExitM.DeliverAnimListener(animListener, debug);
}
}
}
}
override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
if (OnTime)
{
if (stateInfo.fullPathHash == animator.GetNextAnimatorStateInfo(layerIndex).fullPathHash) return; //means is transitioning to itself
foreach (MesssageItem onTimeM in onTimeMessage)
{
if (onTimeM.Active && !string.IsNullOrEmpty(onTimeM.message))
{
// float stateTime = stateInfo.loop ? stateInfo.normalizedTime % 1 : stateInfo.normalizedTime;
float stateTime = NormalizeTime ? stateInfo.normalizedTime % 1 : stateInfo.normalizedTime;
if (!onTimeM.sent && (stateTime >= onTimeM.time))
{
onTimeM.sent = true;
if (UseSendMessage)
onTimeM.DeliverMessage(animator, SendToChildren, debug);
else
foreach (var item in listeners)
onTimeM.DeliverAnimListener(item, debug);
}
}
}
}
}
}
//INSPECTOR
#if UNITY_EDITOR
[CustomEditor(typeof(MessagesBehavior))]
public class MessageBehaviorsEd : Editor
{
private ReorderableList listOnEnter, listOnExit, listOnTime;
private MessagesBehavior MMessage;
private SerializedProperty onExitMessage, onEnterMessage, onTimeMessage, UseSendMessage, SendToChildren, NormalizeTime, debug, OnExit, OnTime, OnEnter;
Color selected = new Color(0, 0.6f, 1f, 1f);
private void OnEnable()
{
MMessage = ((MessagesBehavior)target);
onExitMessage = serializedObject.FindProperty("onExitMessage");
onEnterMessage = serializedObject.FindProperty("onEnterMessage");
onTimeMessage = serializedObject.FindProperty("onTimeMessage");
UseSendMessage = serializedObject.FindProperty("UseSendMessage");
SendToChildren = serializedObject.FindProperty("SendToChildren");
NormalizeTime = serializedObject.FindProperty("NormalizeTime");
debug = serializedObject.FindProperty("debug");
OnEnter = serializedObject.FindProperty("OnEnter");
OnExit = serializedObject.FindProperty("OnExit");
OnTime = serializedObject.FindProperty("OnTime");
//script = MonoScript.FromScriptableObject(MMessage);
listOnEnter = new ReorderableList(serializedObject, onEnterMessage, true, true, true, true);
listOnExit = new ReorderableList(serializedObject, onExitMessage, true, true, true, true);
listOnTime = new ReorderableList(serializedObject, onTimeMessage, true, true, true, true);
listOnEnter.drawHeaderCallback = HeaderCallbackDelegate1;
listOnExit.drawHeaderCallback = HeaderCallbackDelegate2;
listOnTime.drawHeaderCallback = HeaderCallbackDelegate3;
listOnEnter.drawElementCallback = (rect, index, isActive, isFocused) =>
{
EditorGUI.PropertyField(rect, onEnterMessage.GetArrayElementAtIndex(index), GUIContent.none);
};
listOnExit.drawElementCallback = (rect, index, isActive, isFocused) =>
{
EditorGUI.PropertyField(rect, onExitMessage.GetArrayElementAtIndex(index), GUIContent.none);
};
listOnTime.drawElementCallback = drawElementCallback3;
}
public override void OnInspectorGUI()
{
serializedObject.Update();
MalbersEditor.DrawDescription("Send Messages to all the MonoBehaviours that uses the Interface |IAnimatorListener|");
EditorGUI.BeginChangeCheck();
EditorGUILayout.BeginVertical(MTools.StyleGray);
{
EditorGUILayout.BeginHorizontal();
var currentGUIColor = GUI.color;
GUI.color = OnEnter.boolValue ? selected : currentGUIColor;
OnEnter.boolValue = GUILayout.Toggle(OnEnter.boolValue,
new GUIContent("Enter", "Send the message On Enter State "), EditorStyles.miniButton);
GUI.color = OnExit.boolValue ? selected : currentGUIColor;
OnExit.boolValue = GUILayout.Toggle(OnExit.boolValue,
new GUIContent("Exit", "Send the message On Exit State"), EditorStyles.miniButton);
GUI.color = OnTime.boolValue ? selected : currentGUIColor;
OnTime.boolValue = GUILayout.Toggle(OnTime.boolValue,
new GUIContent("Time", "Send the message On Update State using a time"), EditorStyles.miniButton);
GUI.color = SendToChildren.boolValue ? ( Color.green) : currentGUIColor;
SendToChildren.boolValue = GUILayout.Toggle(SendToChildren.boolValue,
new GUIContent("Children", "The Messages will be sent also to the Animator gameobject children"), EditorStyles.miniButton);
GUI.color = UseSendMessage.boolValue ? (Color.green) : currentGUIColor;
UseSendMessage.boolValue = GUILayout.Toggle(UseSendMessage.boolValue,
new GUIContent("SendMessage()", "Uses the SendMessage() method, instead of checking for IAnimator Listener Interfaces"), EditorStyles.miniButton);
//EditorGUILayout.PropertyField(SendToChildren, new GUIContent("Message Children", "All the children gameObjects in the hierarchy will receive the message"));
GUI.color = currentGUIColor;
MalbersEditor.DrawDebugIcon(debug);
EditorGUILayout.EndHorizontal();
if (OnEnter.boolValue)
listOnEnter.DoLayoutList();
if (OnExit.boolValue)
listOnExit.DoLayoutList();
if (OnTime.boolValue)
listOnTime.DoLayoutList();
EditorGUIUtility.labelWidth = 0;
}
EditorGUILayout.EndVertical();
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Message Behaviour Inspector");
//EditorUtility.SetDirty(target);
}
serializedObject.ApplyModifiedProperties();
}
/// <summary> Reordable List Header </summary>
void HeaderCallbackDelegate1(Rect rect)
{
Rect R_1 = new Rect(rect.x, rect.y, (rect.width / 3) + 30, EditorGUIUtility.singleLineHeight);
EditorGUI.LabelField(R_1, "Msg (On Enter)");
Rect R_3 = new Rect(rect.x + 10 + ((rect.width) / 3) + 5 + 30, rect.y, ((rect.width) / 3) - 5 - 15, EditorGUIUtility.singleLineHeight);
EditorGUI.LabelField(R_3, "Type");
Rect R_5 = new Rect(rect.x + 10 + ((rect.width) / 3) * 2 + 5 + 15, rect.y, ((rect.width) / 3) - 5 - 15, EditorGUIUtility.singleLineHeight);
EditorGUI.LabelField(R_5, "Value");
}
void HeaderCallbackDelegate2(Rect rect)
{
Rect R_1 = new Rect(rect.x, rect.y, (rect.width / 3) + 30, EditorGUIUtility.singleLineHeight);
EditorGUI.LabelField(R_1, "Msg (On Exit)");
Rect R_3 = new Rect(rect.x + 10 + ((rect.width) / 3) + 5 + 30, rect.y, ((rect.width) / 3) - 5 - 15, EditorGUIUtility.singleLineHeight);
EditorGUI.LabelField(R_3, "Type");
Rect R_5 = new Rect(rect.x + 10 + ((rect.width) / 3) * 2 + 5 + 15, rect.y, ((rect.width) / 3) - 5 - 15, EditorGUIUtility.singleLineHeight);
EditorGUI.LabelField(R_5, "Value");
}
void HeaderCallbackDelegate3(Rect rect)
{
Rect R_1 = new Rect(rect.x, rect.y, (rect.width / 4) + 30, EditorGUIUtility.singleLineHeight);
EditorGUI.LabelField(R_1, "Msg (OnTime)");
Rect R_3 = new Rect(rect.x + 10 + ((rect.width) / 4) + 5 + 30, rect.y, 32, EditorGUIUtility.singleLineHeight);
EditorGUI.LabelField(R_3, "Type");
Rect R_4 = new Rect(rect.x + 10 + ((rect.width) / 4) * 2 + 5 + 20, rect.y, ((rect.width) / 4) - 5, EditorGUIUtility.singleLineHeight);
EditorGUI.LabelField(R_4, "Time");
Rect R_4_1 = new Rect(R_4);
R_4_1.x += 32;
R_4_1.width = 23;
NormalizeTime.boolValue = GUI.Toggle(R_4_1,NormalizeTime.boolValue, new GUIContent("N", "Normalize the State Animation Time"),EditorStyles.miniButton);
Rect R_5 = new Rect(rect.x + ((rect.width) / 4) * 3 + 5 + 10, rect.y, ((rect.width) / 4) - 5, EditorGUIUtility.singleLineHeight);
EditorGUI.LabelField(R_5, "Value");
}
//ON ENTER
void drawElementCallback1(Rect rect, int index, bool isActive, bool isFocused)
{
var element = MMessage.onEnterMessage[index];
rect.y += 2;
Rect R_0 = new Rect(rect.x, rect.y, 15, EditorGUIUtility.singleLineHeight);
element.Active = EditorGUI.Toggle(R_0, element.Active);
Rect R_1 = new Rect(rect.x + 15, rect.y, (rect.width / 3) + 15, EditorGUIUtility.singleLineHeight);
element.message = EditorGUI.TextField(R_1, element.message);
Rect R_3 = new Rect(rect.x + ((rect.width) / 3) + 5 + 30, rect.y, ((rect.width) / 3) - 5 - 15, EditorGUIUtility.singleLineHeight);
element.typeM = (TypeMessage)EditorGUI.EnumPopup(R_3, element.typeM);
Rect R_5 = new Rect(rect.x + ((rect.width) / 3) * 2 + 5 + 15, rect.y, ((rect.width) / 3) - 5 - 15, EditorGUIUtility.singleLineHeight);
switch (element.typeM)
{
case TypeMessage.Bool:
element.boolValue = EditorGUI.ToggleLeft(R_5, element.boolValue ? " True" : " False", element.boolValue);
break;
case TypeMessage.Int:
element.intValue = EditorGUI.IntField(R_5, element.intValue);
break;
case TypeMessage.Float:
element.floatValue = EditorGUI.FloatField(R_5, element.floatValue);
break;
case TypeMessage.String:
element.stringValue = EditorGUI.TextField(R_5, element.stringValue);
break;
case TypeMessage.IntVar:
element.intVarValue = (IntVar)EditorGUI.ObjectField(R_5, element.intVarValue, typeof(IntVar), false);
break;
default:
break;
}
}
//ON EXIT
void drawElementCallback2(Rect rect, int index, bool isActive, bool isFocused)
{
var element = MMessage.onExitMessage[index];
var sp_element = onExitMessage.GetArrayElementAtIndex(index);
rect.y += 2;
Rect R_0 = new Rect(rect.x, rect.y, 15, EditorGUIUtility.singleLineHeight);
element.Active = EditorGUI.Toggle(R_0, element.Active);
Rect R_1 = new Rect(rect.x + 15, rect.y, (rect.width / 3) + 15, EditorGUIUtility.singleLineHeight);
element.message = EditorGUI.TextField(R_1, element.message);
Rect R_3 = new Rect(rect.x + ((rect.width) / 3) + 5 + 30, rect.y, ((rect.width) / 3) - 5 - 15, EditorGUIUtility.singleLineHeight);
element.typeM = (TypeMessage)EditorGUI.EnumPopup(R_3, element.typeM);
Rect R_5 = new Rect(rect.x + ((rect.width) / 3) * 2 + 5 + 15, rect.y, ((rect.width) / 3) - 5 - 15, EditorGUIUtility.singleLineHeight);
switch (element.typeM)
{
case TypeMessage.Bool:
element.boolValue = EditorGUI.ToggleLeft(R_5, element.boolValue ? " True" : " False", element.boolValue);
break;
case TypeMessage.Int:
EditorGUI.PropertyField(R_5, sp_element.FindPropertyRelative("intValue"), GUIContent.none);
break;
case TypeMessage.Float:
EditorGUI.PropertyField(R_5, sp_element.FindPropertyRelative("floatValue"), GUIContent.none);
break;
case TypeMessage.String:
EditorGUI.PropertyField(R_5, sp_element.FindPropertyRelative("stringValue"), GUIContent.none);
break;
case TypeMessage.IntVar:
EditorGUI.PropertyField(R_5, sp_element.FindPropertyRelative("intVarValue"), GUIContent.none);
break;
case TypeMessage.Transform:
EditorGUI.PropertyField(R_5, sp_element.FindPropertyRelative("transformValue"), GUIContent.none);
break;
default:
break;
}
}
//ON Time
void drawElementCallback3(Rect rect, int index, bool isActive, bool isFocused)
{
var element = MMessage.onTimeMessage[index];
rect.y += 2;
Rect R_0 = new Rect(rect.x, rect.y, 15, EditorGUIUtility.singleLineHeight);
element.Active = EditorGUI.Toggle(R_0, element.Active);
Rect R_1 = new Rect(rect.x + 15, rect.y, (rect.width / 4) + 15, EditorGUIUtility.singleLineHeight);
element.message = EditorGUI.TextField(R_1, element.message);
Rect R_3 = new Rect(rect.x + ((rect.width) / 4) + 5 + 30, rect.y, ((rect.width) / 4) - 5 - 5, EditorGUIUtility.singleLineHeight);
element.typeM = (TypeMessage)EditorGUI.EnumPopup(R_3, element.typeM);
Rect R_4 = new Rect(rect.x + ((rect.width) / 4) * 2 + 5 + 25, rect.y, ((rect.width) / 4) - 5 - 15, EditorGUIUtility.singleLineHeight);
element.time = EditorGUI.FloatField(R_4, element.time);
//if (element.time > 1) element.time = 1;
if (element.time < 0) element.time = 0;
Rect R_5 = new Rect(rect.x + ((rect.width) / 4) * 3 + 15, rect.y, ((rect.width) / 4) - 15, EditorGUIUtility.singleLineHeight);
switch (element.typeM)
{
case TypeMessage.Bool:
element.boolValue = EditorGUI.ToggleLeft(R_5, element.boolValue ? " True" : " False", element.boolValue);
break;
case TypeMessage.Int:
element.intValue = EditorGUI.IntField(R_5, element.intValue);
break;
case TypeMessage.Float:
element.floatValue = EditorGUI.FloatField(R_5, element.floatValue);
break;
case TypeMessage.String:
element.stringValue = EditorGUI.TextField(R_5, element.stringValue);
break;
case TypeMessage.IntVar:
element.intVarValue = (IntVar)EditorGUI.ObjectField(R_5, element.intVarValue, typeof(IntVar), false);
break;
default:
break;
}
}
}
#endif
}