init 1.1.2
This commit is contained in:
@@ -0,0 +1,951 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace EmeraldAI.Utility
|
||||
{
|
||||
public class AnimationViewerManager : EditorWindow
|
||||
{
|
||||
public static AnimationViewerManager Instance;
|
||||
|
||||
Color TimelineOutlineColor = new Color(0.7f, 0.7f, 0.7f, 1f);
|
||||
|
||||
public bool UseRootMotion;
|
||||
public int CurrentPreviewAnimationIndex = 0;
|
||||
public int PresetAnimationEventIndex = 0;
|
||||
|
||||
float TimeScale = 1.0f;
|
||||
Vector2 WindowOffset;
|
||||
protected float time = 0.0f;
|
||||
Texture AnimationProfileEditorIcon;
|
||||
Texture PlayButtonIcon;
|
||||
Texture PauseButtonIcon;
|
||||
Texture AnimationEventIcon;
|
||||
public static Vector3 DefaultPosition;
|
||||
public static Vector3 DefaultEuler;
|
||||
bool AnimationIsPlaying;
|
||||
|
||||
bool RootMotionChanged;
|
||||
GameObject CurrentAnimationViewerAI = null;
|
||||
|
||||
AnimationClip PreviewClip = null;
|
||||
List<AnimationClip> PreviewClips = new List<AnimationClip>();
|
||||
List<string> AnimationNames = new List<string>();
|
||||
List<string> AnimationEventNames = new List<string>();
|
||||
List<EmeraldAnimationEventsClass> AnimationEventPresets = new List<EmeraldAnimationEventsClass>();
|
||||
Rect AnimationClipTimelineArea;
|
||||
Rect AnimationClipTimelinePoint;
|
||||
int AnimationEventIndex;
|
||||
int PreviousPreviewAnimationIndex;
|
||||
public AnimationEvent CurrentAnimationEvent;
|
||||
Rect CurrentEventArea;
|
||||
GameObject AnimationPreviewParent;
|
||||
Transform PreviousParent;
|
||||
Vector3 StartingPosition;
|
||||
Vector3 StartingEuler;
|
||||
bool InitializeTimelineMovement;
|
||||
bool InitializeAnimationEventMovement;
|
||||
bool EnableDebugging = false; //Internal Use Only
|
||||
|
||||
[SerializeField]
|
||||
public List<AnimationClip> DuplicateAnimationEvents = new List<AnimationClip>();
|
||||
[SerializeField]
|
||||
public List<AnimationEventElement> CurrentAnimationEvents = new List<AnimationEventElement>();
|
||||
[System.Serializable]
|
||||
public class AnimationEventElement
|
||||
{
|
||||
public AnimationClip Clip;
|
||||
public List<AnimationEvent> AnimationEvents = new List<AnimationEvent>();
|
||||
public bool Modified;
|
||||
|
||||
public AnimationEventElement (AnimationClip m_Clip, List<AnimationEvent> m_AnimationEvents)
|
||||
{
|
||||
Clip = m_Clip;
|
||||
AnimationEvents = new List<AnimationEvent>(m_AnimationEvents);
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (EditorApplication.isPlaying)
|
||||
return;
|
||||
|
||||
if (AnimationProfileEditorIcon == null) AnimationProfileEditorIcon = Resources.Load("Editor Icons/EmeraldDetection") as Texture;
|
||||
if (PlayButtonIcon == null) PlayButtonIcon = Resources.Load("Editor Icons/EmeraldPlayButton") as Texture;
|
||||
if (PauseButtonIcon == null) PauseButtonIcon = Resources.Load("Editor Icons/EmeraldPauseButton") as Texture;
|
||||
if (AnimationEventIcon == null) AnimationEventIcon = Resources.Load("Editor Icons/EmeraldAnimationEvent") as Texture;
|
||||
|
||||
this.minSize = new Vector2(Screen.currentResolution.width / 6f, Screen.currentResolution.height / 1.7f);
|
||||
this.maxSize = new Vector2(Screen.currentResolution.width / 4f, 1500);
|
||||
|
||||
Instance = this;
|
||||
SubscribeCallbacks(); //Subscribe callbacks
|
||||
InitiailizeList();
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (EnableDebugging) Debug.Log("OnDisable");
|
||||
UnsubscribeCallbacks(); //Unsubscribe callbacks
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe callbacks, which are used for handling the animation preview state when saving, scene changes, and recompiling.
|
||||
/// </summary>
|
||||
void SubscribeCallbacks ()
|
||||
{
|
||||
AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload;
|
||||
AssemblyReloadEvents.afterAssemblyReload += OnAfterAssemblyReload;
|
||||
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||
EditorSceneManager.sceneSaved += OnSceneSaved;
|
||||
EditorSceneManager.sceneSaving += OnSceneSaving;
|
||||
SceneView.duringSceneGui += OnSceneGUI;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe callbacks, which are used for handling the animation preview state when saving, scene changes, and recompiling.
|
||||
/// </summary>
|
||||
void UnsubscribeCallbacks()
|
||||
{
|
||||
AssemblyReloadEvents.beforeAssemblyReload -= OnBeforeAssemblyReload;
|
||||
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
||||
AssemblyReloadEvents.afterAssemblyReload -= OnAfterAssemblyReload;
|
||||
EditorSceneManager.sceneSaved -= OnSceneSaved;
|
||||
EditorSceneManager.sceneSaving -= OnSceneSaving;
|
||||
SceneView.duringSceneGui -= OnSceneGUI;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (Done through using a OnSceneGUI callback) Used for drawing a GUI at the base of the AI when an Animation Event is triggered.
|
||||
/// This allows the user to focus on the animation and what frame the event is triggerd, rather than on the editor window itself.
|
||||
/// The GUI is change color while the event is within the time frame of triggering.
|
||||
/// </summary>
|
||||
private void OnSceneGUI(SceneView obj)
|
||||
{
|
||||
if (PreviewClip != null)
|
||||
{
|
||||
Vector3 pos = CurrentAnimationViewerAI.transform.position;
|
||||
Color OutLineColor = new Color(0, 0, 0, 1);
|
||||
Color FaceColor = new Color(0.5f, 0.5f, 0.5f, 0.1f);
|
||||
|
||||
Vector3[] verts = new Vector3[]
|
||||
{
|
||||
new Vector3(pos.x - 0.5f, pos.y, pos.z - 0.5f),
|
||||
new Vector3(pos.x - 0.5f, pos.y, pos.z + 0.5f),
|
||||
new Vector3(pos.x + 0.5f, pos.y, pos.z + 0.5f),
|
||||
new Vector3(pos.x + 0.5f, pos.y, pos.z - 0.5f)
|
||||
};
|
||||
|
||||
//Calculate the time so that each Animation Event changes the GUI Handle color when the timeline point is within range.
|
||||
float MouseLerp = (AnimationClipTimelinePoint.x / AnimationClipTimelineArea.width);
|
||||
float MouseOffset = Mathf.LerpAngle(AnimationClipTimelineArea.min.x - 2.5f, AnimationClipTimelineArea.min.x + 1f, MouseLerp);
|
||||
float ModifiedTime = ((AnimationClipTimelinePoint.x - MouseOffset) / (AnimationClipTimelineArea.width)) * (PreviewClip.length);
|
||||
|
||||
for (int i = 0; i < CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents.Count; i++)
|
||||
{
|
||||
if (ModifiedTime >= (CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[i].time - 0.005f) && ModifiedTime <= (CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[i].time + 0.005f))
|
||||
{
|
||||
OutLineColor = new Color(0, 0, 0f, 1);
|
||||
FaceColor = new Color(0.5f, 1f, 0.5f, 0.15f);
|
||||
}
|
||||
|
||||
}
|
||||
Handles.DrawSolidRectangleWithOutline(verts, FaceColor, OutLineColor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A callback for detecting before a scene has been saved. This is used to disable the animation preview state so it isn't included with the save.
|
||||
/// </summary>
|
||||
private void OnSceneSaving(UnityEngine.SceneManagement.Scene scene, string path)
|
||||
{
|
||||
if (AnimationMode.InAnimationMode())
|
||||
AnimationMode.StopAnimationMode();
|
||||
SetAnimatorStates(true);
|
||||
DeparentPreviewObject();
|
||||
if (EnableDebugging) Debug.Log("OnSceneSaving");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A callback for detecting when a scene has been saved. This is used to reapply the animation preview state so it wasn't included with the save.
|
||||
/// </summary>
|
||||
void OnSceneSaved(UnityEngine.SceneManagement.Scene scene)
|
||||
{
|
||||
SetAnimatorStates(false);
|
||||
|
||||
if (!AnimationMode.InAnimationMode())
|
||||
AnimationMode.StartAnimationMode();
|
||||
|
||||
if (GameObject.Find("Animation Viewer Parent") == null)
|
||||
{
|
||||
AnimationPreviewParent = new GameObject("Animation Viewer Parent");
|
||||
Selection.activeObject = AnimationPreviewParent;
|
||||
AnimationPreviewParent.transform.position = CurrentAnimationViewerAI.transform.position;
|
||||
AnimationPreviewParent.transform.eulerAngles = CurrentAnimationViewerAI.transform.eulerAngles;
|
||||
CurrentAnimationViewerAI.transform.SetParent(AnimationPreviewParent.transform);
|
||||
}
|
||||
|
||||
if (EnableDebugging) Debug.Log("OnSceneSaved");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display the ApplyChanges menu. If the user selects apply, write all changes to all modified animation clips. If the user reverts, discard all changes and close the editor window.
|
||||
/// </summary>
|
||||
void DisplayApplyChangesMenu (GameObject G = null)
|
||||
{
|
||||
if (G != CurrentAnimationViewerAI && CurrentAnimationEvents.Any(x => x.Modified == true))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Unapplied Changes Detected", "Changes have not been applied on: \n" + CurrentAnimationViewerAI.name + "\n\nWould you like to apply your changes?", "Apply", "Revert"))
|
||||
{
|
||||
ApplyChanges(false);
|
||||
if (EnableDebugging) Debug.Log("OnChangesAppliedMenuMessage");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (EnableDebugging) Debug.Log("OnChangesRevertedMenuMessage");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfirmDiscardingMessage ()
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Discard Changes?", "Are you sure you would like to discard your changes, this process cannot be undone?", "Yes", "No"))
|
||||
{
|
||||
Initialize(CurrentAnimationViewerAI);
|
||||
CurrentAnimationEvent = null;
|
||||
if (EnableDebugging) Debug.Log("OnConfirmDiscardingMessageYes");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (EnableDebugging) Debug.Log("OnConfirmDiscardingMessageNo");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disable animation sampling before compiling to stop AI from getting stuck in the animation sampling state.
|
||||
/// </summary>
|
||||
public void OnBeforeAssemblyReload()
|
||||
{
|
||||
if (AnimationMode.InAnimationMode())
|
||||
AnimationMode.StopAnimationMode();
|
||||
|
||||
SetAnimatorStates(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-enable animation sampling after compiling to put an AI back in the animation sampling state.
|
||||
/// </summary>
|
||||
public void OnAfterAssemblyReload()
|
||||
{
|
||||
SetAnimatorStates(false);
|
||||
|
||||
if (!AnimationMode.InAnimationMode())
|
||||
AnimationMode.StartAnimationMode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close the editor window and animation sampling before entering play mode to stop AI from getting stuck in the animation sampling state.
|
||||
/// </summary>
|
||||
void OnPlayModeStateChanged(PlayModeStateChange state)
|
||||
{
|
||||
this.Close();
|
||||
if (EnableDebugging) Debug.Log("OnPlayModeChanged");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the Editor Window lists with their needed data.
|
||||
/// </summary>
|
||||
void InitiailizeList ()
|
||||
{
|
||||
//Get all of the commonly used Emerald AI Event so they can be automatically added through an enum.
|
||||
AnimationEventPresets = AnimationEventInitializer.GetEmeraldAnimationEvents();
|
||||
|
||||
//Add each animation event display name and add it to a list so it can be displayed through an enum.
|
||||
for (int i = 0; i < AnimationEventPresets.Count; i++)
|
||||
{
|
||||
AnimationEventNames.Add(AnimationEventPresets[i].eventDisplayName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the Editor Window with the data from the Emerald AI Animation Editor and its Animation Profile.
|
||||
/// </summary>
|
||||
public void Initialize (GameObject G)
|
||||
{
|
||||
DisplayApplyChangesMenu(G); //Ask to save changes before initializing another AI.
|
||||
|
||||
SetAnimatorStates(false); //The Animator has to be set to Always Animate to avoid bug. Can be set back to default after closing.
|
||||
|
||||
if (AnimationMode.InAnimationMode())
|
||||
AnimationMode.StopAnimationMode();
|
||||
|
||||
DeparentPreviewObject(); //Deparent the previous AI, given that one exists, before initializing a new one.
|
||||
CurrentAnimationViewerAI = G; //Cache the current object the user is editing.
|
||||
|
||||
//Store the starting position and euler angles on initialization.
|
||||
StartingPosition = CurrentAnimationViewerAI.transform.position;
|
||||
StartingEuler = CurrentAnimationViewerAI.transform.eulerAngles;
|
||||
|
||||
ParentPreviewObject(); //Create a temporary parent object to house the AI in while the user is previewing its animations. This allows Root Motion animations to play at the starting position and not at the (0,0,0) position.
|
||||
|
||||
InitializeAnimationData(); //Initialize the animation enum with all animations form the current AI's Animation Profile.
|
||||
|
||||
if (!AnimationMode.InAnimationMode())
|
||||
AnimationMode.StartAnimationMode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the animation enum with all the animations from its source.
|
||||
/// </summary>
|
||||
void InitializeAnimationData ()
|
||||
{
|
||||
PreviewClips.Clear();
|
||||
AnimationNames.Clear();
|
||||
CurrentAnimationEvents.Clear();
|
||||
var m_AnimationProfile = CurrentAnimationViewerAI.GetComponent<EmeraldAnimation>().m_AnimationProfile;
|
||||
|
||||
AssignAnimationNames(m_AnimationProfile.NonCombatAnimations, ""); //Non-Combat
|
||||
AssignAnimationNames(m_AnimationProfile.Type1Animations, "Type 1 -"); //Type 1
|
||||
AssignAnimationNames(m_AnimationProfile.Type2Animations, "Type 2 -"); //Type 2
|
||||
|
||||
//Cancel the Animation Viewer Manager if there are no detected animations.
|
||||
if (CurrentAnimationEvents.Count == 0)
|
||||
{
|
||||
Close();
|
||||
if (EditorUtility.DisplayDialog("No Animations", "The attached Animation Profile doesn't have any animations. Press the 'Edit Animation Profile' button to add some animations.", "Okay"))
|
||||
{
|
||||
Selection.activeGameObject = CurrentAnimationViewerAI;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assign the names of each animation using reflection.
|
||||
/// </summary>
|
||||
void AssignAnimationNames (AnimationParentClass AnimationCategory, string AnimationCategoryName)
|
||||
{
|
||||
foreach (var field in AnimationCategory.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
||||
{
|
||||
if (AnimationCategory.GetType().GetField(field.Name.ToString()).FieldType.ToString() == "System.Collections.Generic.List`1[EmeraldAI.AnimationClass]")
|
||||
{
|
||||
List<AnimationClass> m_AnimationClass = (List<AnimationClass>)AnimationCategory.GetType().GetField(field.Name.ToString()).GetValue(AnimationCategory);
|
||||
|
||||
for (int i = 0; i < m_AnimationClass.Count; i++)
|
||||
{
|
||||
AnimationClip m_AnimationClip = m_AnimationClass[i].AnimationClip;
|
||||
|
||||
if (m_AnimationClip != null) // && !PreviewClips.Contains(m_AnimationClip)
|
||||
{
|
||||
CurrentAnimationEvents.Add(new AnimationEventElement(m_AnimationClip, m_AnimationClip.events.ToList()));
|
||||
PreviewClips.Add(m_AnimationClip);
|
||||
|
||||
string TempName = field.Name.Replace("List", "");
|
||||
TempName = TempName.Replace("Animation", "");
|
||||
TempName = System.Text.RegularExpressions.Regex.Replace(TempName, "[A-Z]", " $0");
|
||||
TempName = "(" + AnimationCategoryName + TempName + " " + (i + 1) + ")";
|
||||
TempName = TempName.Replace("( ", "(");
|
||||
AnimationNames.Add(m_AnimationClip.name + " - " + TempName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (AnimationCategory.GetType().GetField(field.Name.ToString()).FieldType.ToString() == "EmeraldAI.AnimationClass")
|
||||
{
|
||||
AnimationClass m_AnimationClass = (AnimationClass)AnimationCategory.GetType().GetField(field.Name.ToString()).GetValue(AnimationCategory);
|
||||
AnimationClip m_AnimationClip = m_AnimationClass.AnimationClip;
|
||||
|
||||
if (m_AnimationClip != null)
|
||||
{
|
||||
CurrentAnimationEvents.Add(new AnimationEventElement(m_AnimationClip, m_AnimationClip.events.ToList()));
|
||||
PreviewClips.Add(m_AnimationClip);
|
||||
|
||||
string TempName = field.Name.Replace("List", " ");
|
||||
TempName = TempName.Replace("Animation", "");
|
||||
TempName = System.Text.RegularExpressions.Regex.Replace(TempName, "[A-Z]", " $0");
|
||||
TempName = "(" + AnimationCategoryName + TempName + ")";
|
||||
TempName = TempName.Replace("( ", "(");
|
||||
AnimationNames.Add(m_AnimationClip.name + " - " + TempName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the changes to all modified animation clips.
|
||||
/// </summary>
|
||||
void ApplyChanges (bool AnimationModeEnabled)
|
||||
{
|
||||
List<string> PathList = new List<string>();
|
||||
|
||||
//Store all clip file paths that have been modified
|
||||
for (int i = 0; i < CurrentAnimationEvents.Count; i++)
|
||||
{
|
||||
if (CurrentAnimationEvents[i].Modified)
|
||||
{
|
||||
if (!PathList.Contains(AssetDatabase.GetAssetPath(CurrentAnimationEvents[i].Clip)))
|
||||
{
|
||||
PathList.Add(AssetDatabase.GetAssetPath(CurrentAnimationEvents[i].Clip));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < CurrentAnimationEvents.Count; i++)
|
||||
{
|
||||
if (CurrentAnimationEvents[i].Modified && !DuplicateAnimationEvents.Contains(CurrentAnimationEvents[i].Clip))
|
||||
{
|
||||
var path = AssetDatabase.GetAssetPath(CurrentAnimationEvents[i].Clip);
|
||||
string PathType = AssetImporter.GetAtPath(path).ToString(); //Get the path type, this will help determine whether the clip is from an FBX or an individual animation clip.
|
||||
PathType = PathType.Replace(" ", "");
|
||||
PathType = PathType.Replace("(", "");
|
||||
PathType = PathType.Replace(")", "");
|
||||
|
||||
if (PathType == "UnityEngine.FBXImporter")
|
||||
{
|
||||
var modelImporter = (ModelImporter)AssetImporter.GetAtPath(path) as ModelImporter;
|
||||
SerializedObject so = new SerializedObject(modelImporter);
|
||||
SerializedProperty clips = so.FindProperty("m_ClipAnimations");
|
||||
|
||||
//Set the events equal to the events from the CurrentAnimationEvents for each clip, given that it has been modified.
|
||||
for (int m = 0; m < modelImporter.clipAnimations.Length; m++)
|
||||
{
|
||||
if (clips.GetArrayElementAtIndex(m).displayName == CurrentAnimationEvents[i].Clip.name)
|
||||
{
|
||||
Debug.Log(clips.GetArrayElementAtIndex(m).displayName + "'s Animation Events have been updated");
|
||||
SerializedProperty prop = clips.GetArrayElementAtIndex(m).FindPropertyRelative("events");
|
||||
if (CurrentAnimationEvents[i].AnimationEvents.Count == 0)
|
||||
{
|
||||
prop.ClearArray();
|
||||
if (!DuplicateAnimationEvents.Contains(CurrentAnimationEvents[i].Clip)) DuplicateAnimationEvents.Add(CurrentAnimationEvents[i].Clip);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetEvents(clips.GetArrayElementAtIndex(m), CurrentAnimationEvents[i].AnimationEvents.ToArray(), CurrentAnimationEvents[i].Clip);
|
||||
if (!DuplicateAnimationEvents.Contains(CurrentAnimationEvents[i].Clip)) DuplicateAnimationEvents.Add(CurrentAnimationEvents[i].Clip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CurrentAnimationEvents[i].Modified = false;
|
||||
so.ApplyModifiedProperties();
|
||||
}
|
||||
else
|
||||
{
|
||||
//Get the single animation clip's path
|
||||
AnimationClip animClip = (AnimationClip)AssetDatabase.LoadAssetAtPath(path, typeof(AnimationClip));
|
||||
SerializedObject so = new SerializedObject(animClip);
|
||||
|
||||
if (CurrentAnimationEvents[i].AnimationEvents.Count == 0)
|
||||
{
|
||||
if (!DuplicateAnimationEvents.Contains(CurrentAnimationEvents[i].Clip)) DuplicateAnimationEvents.Add(CurrentAnimationEvents[i].Clip);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log(animClip.name + "'s Animation Events have been updated");
|
||||
SetEventsOnClip(CurrentAnimationEvents[i].AnimationEvents.ToArray(), CurrentAnimationEvents[i].Clip);
|
||||
if (!DuplicateAnimationEvents.Contains(CurrentAnimationEvents[i].Clip)) DuplicateAnimationEvents.Add(CurrentAnimationEvents[i].Clip);
|
||||
}
|
||||
|
||||
CurrentAnimationEvents[i].Modified = false;
|
||||
so.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
else if (DuplicateAnimationEvents.Contains(CurrentAnimationEvents[i].Clip))
|
||||
{
|
||||
CurrentAnimationEvents[i].Modified = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < PathList.Count; i++)
|
||||
{
|
||||
string PathType = AssetImporter.GetAtPath(PathList[i]).ToString();
|
||||
PathType = PathType.Replace(" ", "");
|
||||
PathType = PathType.Replace("(", "");
|
||||
PathType = PathType.Replace(")", "");
|
||||
|
||||
if (PathType == "UnityEngine.FBXImporter")
|
||||
{
|
||||
var modelImporter = (ModelImporter)AssetImporter.GetAtPath(PathList[i]) as ModelImporter;
|
||||
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(modelImporter));
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
}
|
||||
|
||||
if (!AnimationMode.InAnimationMode() && AnimationModeEnabled)
|
||||
AnimationMode.StartAnimationMode();
|
||||
|
||||
//Remove the focus of the currently selected Animation Event
|
||||
CurrentAnimationEvent = null;
|
||||
CurrentEventArea = new Rect();
|
||||
GUI.FocusControl(null);
|
||||
Repaint();
|
||||
DuplicateAnimationEvents.Clear();
|
||||
}
|
||||
|
||||
// Main editor window
|
||||
public void OnGUI()
|
||||
{
|
||||
GUIStyle Style = EditorStyles.wordWrappedLabel;
|
||||
Style.fontStyle = FontStyle.Bold;
|
||||
Style.fontSize = 16;
|
||||
Style.padding.top = -11;
|
||||
Style.alignment = TextAnchor.UpperCenter;
|
||||
|
||||
GUI.backgroundColor = new Color(0.62f, 0.62f, 0.62f, 1f);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.BeginVertical("Window");
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.LabelField(new GUIContent(AnimationProfileEditorIcon), Style, GUILayout.ExpandWidth(true), GUILayout.Height(32));
|
||||
EditorGUILayout.LabelField("Animation Viewer", Style, GUILayout.ExpandWidth(true));
|
||||
GUILayout.Space(4);
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window");
|
||||
GUILayout.Space(-18);
|
||||
|
||||
CustomEditorProperties.TextTitleWithDescription("Animation Clip Selection", "Select an animation clip (pulled from the AI's current Animation Profile) to preview the animation on the recntly selected AI right within the scene.", false);
|
||||
CustomEditorProperties.NoticeTextDescription("Note: Inverse Kinematics and animation blending are not used during this process. Animations will have better quality during runtime usage.", true);
|
||||
|
||||
//Populate an enum with the selected AI's current animations.
|
||||
CurrentPreviewAnimationIndex = EditorGUILayout.Popup("Current Animation", CurrentPreviewAnimationIndex, AnimationNames.ToArray());
|
||||
PreviewClip = CurrentAnimationEvents[CurrentPreviewAnimationIndex].Clip;
|
||||
|
||||
if (PreviousPreviewAnimationIndex != CurrentPreviewAnimationIndex)
|
||||
{
|
||||
CurrentAnimationEvent = null;
|
||||
PreviousPreviewAnimationIndex = CurrentPreviewAnimationIndex;
|
||||
}
|
||||
|
||||
GUILayout.Space(15);
|
||||
|
||||
CustomEditorProperties.CustomHelpLabelField("Makes the currently selected object the Current Animation Clip within the Project tab.", false);
|
||||
if (GUILayout.Button("View Current Clip in Project"))
|
||||
{
|
||||
Selection.activeObject = PreviewClip;
|
||||
}
|
||||
|
||||
GUILayout.Space(10);
|
||||
UseRootMotion = EditorGUILayout.Toggle("Use Root Motion", UseRootMotion);
|
||||
GUILayout.Space(10);
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
GUILayout.Space(5);
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window");
|
||||
GUILayout.Space(-18);
|
||||
|
||||
if (PreviewClip != null)
|
||||
{
|
||||
CustomEditorProperties.TextTitleWithDescription("Animation Timeline", "You can select anywhere within the timeline area below to cycle through an animation clip. Pressing the play button will allow the animation to play in real-time, continiously, " +
|
||||
"until the pause button is pressed. You can add preset, and custom, Animation Events by using the Event Type popup. You can hover over the Event Type dropdown for a tooltip description of the currently selected event.", false);
|
||||
GUILayout.Space(2.5f);
|
||||
|
||||
GUIStyle BoldStyle = GUI.skin.button;
|
||||
BoldStyle.fontStyle = FontStyle.Bold;
|
||||
|
||||
if (AnimationIsPlaying)
|
||||
{
|
||||
GUI.backgroundColor = new Color(1.5f, 0.1f, 0f, 0.75f);
|
||||
}
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(5);
|
||||
if (GUILayout.Button(new GUIContent(PlayButtonIcon, "Play and pause the Current Animation."), BoldStyle))
|
||||
{
|
||||
AnimationIsPlaying = !AnimationIsPlaying;
|
||||
if (AnimationIsPlaying)
|
||||
PlayButtonIcon = Resources.Load("Editor Icons/EmeraldPauseButton") as Texture;
|
||||
else
|
||||
PlayButtonIcon = Resources.Load("Editor Icons/EmeraldPlayButton") as Texture;
|
||||
}
|
||||
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
//Add Event Button
|
||||
if (GUILayout.Button(new GUIContent(AnimationEventIcon, "Adds the current Event Type at the current frame. This will also apply some needed parameters."), BoldStyle))
|
||||
{
|
||||
GUI.FocusControl(null); //Remove the focus of the currently selected Animation Event
|
||||
var m_event = new AnimationEvent();
|
||||
m_event.functionName = AnimationEventPresets[PresetAnimationEventIndex].animationEvent.functionName;
|
||||
m_event.stringParameter = AnimationEventPresets[PresetAnimationEventIndex].animationEvent.stringParameter;
|
||||
m_event.floatParameter = AnimationEventPresets[PresetAnimationEventIndex].animationEvent.floatParameter;
|
||||
m_event.intParameter = AnimationEventPresets[PresetAnimationEventIndex].animationEvent.intParameter;
|
||||
m_event.time = time + Mathf.Lerp(0.009f, -0.0111f, time / PreviewClip.length);
|
||||
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents.Add(m_event); //Add the event to the AnimationEvents list
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].Modified = true;
|
||||
|
||||
UpdateIdenticalAnimationClips();
|
||||
|
||||
//Set the CurrentAnimationEvent and other info to the newly created event.
|
||||
CurrentAnimationEvent = m_event;
|
||||
AnimationEventIndex = CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents.IndexOf(m_event);
|
||||
Repaint();
|
||||
}
|
||||
|
||||
Rect r2 = EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.BeginVertical();
|
||||
GUILayout.Space(20);
|
||||
PresetAnimationEventIndex = EditorGUILayout.Popup("", PresetAnimationEventIndex, AnimationEventNames.ToArray(), GUILayout.MinWidth(100));
|
||||
Rect LastRect = GUILayoutUtility.GetLastRect();
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.Space(10);
|
||||
EditorGUILayout.BeginVertical();
|
||||
GUILayout.Space(20);
|
||||
|
||||
TimeScale = EditorGUILayout.Slider("", TimeScale, 0.01f, 1f, GUILayout.MaxHeight(0), GUILayout.MinWidth(100));
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUI.LabelField(new Rect((r2.min.x + 45), r2.position.y - 2, (r2.width), 20), new GUIContent("Event Type"));
|
||||
EditorGUI.LabelField(new Rect((r2.min.x + 6), r2.position.y + 18, LastRect.width, 20), new GUIContent("", AnimationEventPresets[PresetAnimationEventIndex].eventDescription));
|
||||
EditorGUI.LabelField(new Rect((r2.max.x - 265) + r2.min.x, r2.position.y - 2, 100, 20), "Playback Speed");
|
||||
EditorGUI.LabelField(new Rect((r2.min.x + r2.width - 40), r2.position.y + 19, (r2.width), 20), System.Math.Round(TimeScale, 2) + "x");
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
GUILayout.Space(10);
|
||||
|
||||
float stopTime = PreviewClip.length;
|
||||
Rect r = EditorGUILayout.BeginVertical();
|
||||
WindowOffset.x = 5;
|
||||
var EditedTime = time * PreviewClip.frameRate;
|
||||
var niceTime = Mathf.Floor(EditedTime / PreviewClip.frameRate).ToString("00") + ":" + Mathf.FloorToInt(EditedTime % PreviewClip.frameRate).ToString("00");
|
||||
var ClipPercentage = ((time / stopTime) * 100f).ToString("F2");
|
||||
|
||||
EditorGUI.DrawRect(new Rect(r.x + WindowOffset.x - 3, r.position.y - 3f, r.width - (WindowOffset.x * 2) + 6, 105.5f), TimelineOutlineColor); //Draws the outline around the whole timeline area
|
||||
EditorGUI.DrawRect(new Rect(r.x + WindowOffset.x, r.position.y + 0.25f, r.width - (WindowOffset.x * 2), 100f), new Color(0.3f, 0.3f, 0.3f, 1f)); //Draws the background over the whole timeline area
|
||||
|
||||
AnimationClipTimelineArea = new Rect(r.x + WindowOffset.x - 3, r.position.y - 0.75f, r.width - (WindowOffset.x * 2) + 3, 50f);
|
||||
float AdjustedTime = (time / stopTime);
|
||||
EditorGUI.DrawRect(new Rect((r.x + WindowOffset.x), r.position.y + 0.25f, (r.width - (WindowOffset.x * 2) - 2) * AdjustedTime, 50), new Color(0.1f, 0.25f, 0.5f, 1f)); //Draws the timeline progress
|
||||
|
||||
//---Draw the timeline info---
|
||||
GUIStyle LabelStyle = new GUIStyle();
|
||||
LabelStyle.alignment = TextAnchor.MiddleCenter;
|
||||
LabelStyle.fontStyle = FontStyle.Bold;
|
||||
LabelStyle.normal.textColor = Color.white;
|
||||
EditorGUI.DrawRect(new Rect(r.x + WindowOffset.x , r.position.y + 50f, r.width - (WindowOffset.x * 2), 50f), new Color(0.1f, 0.1f, 0.1f, 1f)); //Draws the background of the timeline
|
||||
EditorGUI.LabelField(new Rect(r.x + WindowOffset.x, r.position.y + 50, r.width - (WindowOffset.x * 2), 50), niceTime + " (" + (ClipPercentage + "%") + ") Frame " + Mathf.Round(time * PreviewClip.frameRate).ToString(), LabelStyle); //Draw Animation Info on the Timeline
|
||||
//---Draw the timeline info---
|
||||
|
||||
//-------------ANIMATION EVENT TIMELINE----------------
|
||||
GUIStyle AnimationEventStyle = GUI.skin.box;
|
||||
|
||||
//Draws a line for each 10% within the Animation Event timeline.
|
||||
for (int i = 1; i <= 60; i++)
|
||||
{
|
||||
EditorGUI.DrawRect(new Rect(((r.x + WindowOffset.x) + (float)(i / 60f) * (r.width - (WindowOffset.x * 2) - (r.min.x * 2.5f))), r.position.y + 37.5f, 1f, 12f), new Color(0.6f, 0.6f, 0.6f, 1f)); //Draws the Animation Event timeline Dashes
|
||||
}
|
||||
|
||||
for (int i = 0; i < CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents.Count; i++)
|
||||
{
|
||||
if (CurrentAnimationEvent != null && CurrentAnimationEvent.time == CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[i].time)
|
||||
GUI.backgroundColor = new Color(1, 1, 1, 2f);
|
||||
else
|
||||
GUI.backgroundColor = new Color(3, 3, 3, 3);
|
||||
|
||||
//Draw each Animation Event to the Animation Event timeline based on its time within the PreviewAnimation clip.
|
||||
Rect AnimationEventRect = new Rect((WindowOffset.x - AnimationClipTimelineArea.min.x / WindowOffset.x) + (CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[i].time / stopTime) * (r.width - (WindowOffset.x - (AnimationClipTimelineArea.min.x / WindowOffset.x) * 2) - (r.min.x)), r.position.y + 19.5f, 7.5f, 30f); //Was 41
|
||||
|
||||
if (CurrentAnimationEvent != null && CurrentAnimationEvent.time == CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[i].time)
|
||||
EditorGUI.DrawRect(AnimationEventRect, new Color(1f, 0.25f, 0.25f, 1f));
|
||||
else
|
||||
EditorGUI.DrawRect(AnimationEventRect, Color.white);
|
||||
|
||||
if (AnimationEventRect.Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown)
|
||||
{
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].Modified = true;
|
||||
CurrentAnimationEvent = CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[i];
|
||||
UpdateIdenticalAnimationClips();
|
||||
AnimationEventIndex = i;
|
||||
CurrentEventArea = AnimationEventRect;
|
||||
InitializeAnimationEventMovement = true;
|
||||
Repaint();
|
||||
GUI.FocusControl(null); //Remove the focus of the currently selected Animation Event
|
||||
}
|
||||
}
|
||||
|
||||
//Draws the timeline point
|
||||
AnimationClipTimelinePoint = new Rect(((r.x + WindowOffset.x) + (time / stopTime) * (r.width - (WindowOffset.x * 2) - (r.min.x))), r.position.y, 3.5f, 50f);
|
||||
EditorGUI.DrawRect(AnimationClipTimelinePoint, new Color(0.8f, 0.8f, 0.8f, 1f));
|
||||
//Draws the timeline point
|
||||
|
||||
ChangeEventTime();
|
||||
|
||||
EditorGUI.DrawRect(new Rect(r.x + WindowOffset.x - 3, r.position.y + 50, r.width - (WindowOffset.x * 2) + 6, 2.5f), TimelineOutlineColor); //Draws the outline around the whole timeline area
|
||||
|
||||
GUI.backgroundColor = Color.white;
|
||||
//-------------ANIMATION EVENT TIMELINE----------------
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.Space(120);
|
||||
|
||||
//-------------ANIMATION EVENT DATA--------------------
|
||||
if (CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents.Count > 0 && CurrentAnimationEvent != null)
|
||||
{
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].functionName = EditorGUILayout.TextField("Function Name", CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].functionName);
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].floatParameter = EditorGUILayout.FloatField("Float", CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].floatParameter);
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].intParameter = EditorGUILayout.IntField("Int", CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].intParameter);
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].stringParameter = EditorGUILayout.TextField("String", CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].stringParameter);
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].objectReferenceParameter = EditorGUILayout.ObjectField("Object", CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].objectReferenceParameter, typeof(object), false);
|
||||
|
||||
GUILayout.Space(25);
|
||||
}
|
||||
|
||||
EditorGUI.BeginDisabledGroup(CurrentAnimationEvents.Count > 0 && !CurrentAnimationEvents.Any(x => x.Modified == true));
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(5);
|
||||
if (GUILayout.Button("Apply Changes", GUILayout.MaxHeight(30)))
|
||||
{
|
||||
ApplyChanges(true);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Discard Changes", GUILayout.MaxHeight(30)))
|
||||
{
|
||||
ConfirmDiscardingMessage();
|
||||
}
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUI.EndDisabledGroup();
|
||||
//-------------ANIMATION EVENT DATA--------------------
|
||||
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update all of the passed AnimationClip's events to be equal to those of the CurrentAnimationEvents (based on its index).
|
||||
/// </summary>
|
||||
public void SetEvents(SerializedProperty sp, AnimationEvent[] newEvents, AnimationClip clip)
|
||||
{
|
||||
SerializedProperty serializedProperty = sp.FindPropertyRelative("events");
|
||||
if (serializedProperty != null && serializedProperty.isArray && newEvents != null && newEvents.Length > 0)
|
||||
{
|
||||
serializedProperty.ClearArray();
|
||||
for (int i = 0; i < newEvents.Length; i++)
|
||||
{
|
||||
AnimationEvent animationEvent = newEvents[i];
|
||||
serializedProperty.InsertArrayElementAtIndex(serializedProperty.arraySize);
|
||||
|
||||
SerializedProperty eventProperty = serializedProperty.GetArrayElementAtIndex(i);
|
||||
eventProperty.FindPropertyRelative("floatParameter").floatValue = animationEvent.floatParameter;
|
||||
eventProperty.FindPropertyRelative("functionName").stringValue = animationEvent.functionName;
|
||||
eventProperty.FindPropertyRelative("intParameter").intValue = animationEvent.intParameter;
|
||||
eventProperty.FindPropertyRelative("objectReferenceParameter").objectReferenceValue = animationEvent.objectReferenceParameter;
|
||||
eventProperty.FindPropertyRelative("data").stringValue = animationEvent.stringParameter;
|
||||
eventProperty.FindPropertyRelative("time").floatValue = animationEvent.time / clip.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update a single passed AnimationClip's events to be equal to those of the CurrentAnimationEvents.
|
||||
/// </summary>
|
||||
public void SetEventsOnClip(AnimationEvent[] newEvents, AnimationClip clip)
|
||||
{
|
||||
if (newEvents != null && newEvents.Length > 0)
|
||||
{
|
||||
AnimationUtility.SetAnimationEvents(clip, newEvents);
|
||||
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(clip));
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Track the mouse input and position to modify animation events on the current animation clip.
|
||||
/// </summary>
|
||||
void ChangeEventTime ()
|
||||
{
|
||||
int controlId = GUIUtility.GetControlID(FocusType.Passive); //Allows Event.current to work outside of the Editor Window.
|
||||
|
||||
var current = Event.current;
|
||||
if (current != null)
|
||||
{
|
||||
switch (current.type)
|
||||
{
|
||||
case EventType.MouseDrag:
|
||||
if (CurrentEventArea != new Rect())
|
||||
{
|
||||
if (InitializeAnimationEventMovement)
|
||||
{
|
||||
GUIUtility.hotControl = controlId;
|
||||
float MouseLerp = (Event.current.mousePosition.x / AnimationClipTimelineArea.width);
|
||||
float MouseOffset = Mathf.LerpAngle(AnimationClipTimelineArea.min.x - 1.75f, AnimationClipTimelineArea.min.x + 3f, MouseLerp);
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].time = Mathf.Clamp(((Event.current.mousePosition.x - MouseOffset) / (AnimationClipTimelineArea.width)) * (PreviewClip.length), 0.015f, PreviewClip.length - 0.015f);
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
else if (CurrentEventArea == new Rect() && InitializeTimelineMovement)
|
||||
{
|
||||
GUIUtility.hotControl = controlId;
|
||||
float MouseLerp = (Event.current.mousePosition.x / AnimationClipTimelineArea.width);
|
||||
float MouseOffset = Mathf.LerpAngle(AnimationClipTimelineArea.min.x + 2.5f, AnimationClipTimelineArea.min.x - 2.5f, MouseLerp);
|
||||
time = Mathf.Clamp(((Event.current.mousePosition.x - MouseOffset) / (AnimationClipTimelineArea.width)) * (PreviewClip.length), 0, PreviewClip.length);
|
||||
Repaint();
|
||||
}
|
||||
break;
|
||||
case EventType.MouseUp:
|
||||
if (EnableDebugging) Debug.Log("MouseUp");
|
||||
InitializeTimelineMovement = false;
|
||||
InitializeAnimationEventMovement = false;
|
||||
|
||||
if (CurrentEventArea != new Rect())
|
||||
{
|
||||
UpdateIdenticalAnimationClips();
|
||||
CurrentEventArea = new Rect();
|
||||
Repaint();
|
||||
}
|
||||
break;
|
||||
case EventType.MouseDown:
|
||||
if (EnableDebugging) Debug.Log("MouseDown");
|
||||
if (new Rect(AnimationClipTimelineArea.x + 3.5f, AnimationClipTimelineArea.position.y, AnimationClipTimelineArea.width - 6.5f, AnimationClipTimelineArea.height).Contains(current.mousePosition) && CurrentEventArea == new Rect())
|
||||
{
|
||||
GUIUtility.hotControl = controlId;
|
||||
float MouseLerp = (Event.current.mousePosition.x / AnimationClipTimelineArea.width);
|
||||
float MouseOffset = Mathf.LerpAngle(AnimationClipTimelineArea.min.x + 2.5f, AnimationClipTimelineArea.min.x - 2.5f, MouseLerp);
|
||||
time = ((Event.current.mousePosition.x - MouseOffset) / (AnimationClipTimelineArea.width)) * (PreviewClip.length);
|
||||
InitializeTimelineMovement = true; //The mouse has been detected and pressed within the timeline area. The timeline will continusouly detect movement until the mouse button has been lifted. This is to allow smooth movement outside of the window area.
|
||||
Repaint();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Event.current.isKey)
|
||||
{
|
||||
KeyCode key = Event.current.keyCode;
|
||||
|
||||
if (key == KeyCode.Delete && CurrentAnimationEvent != null)
|
||||
{
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents.RemoveAt(AnimationEventIndex);
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].Modified = true;
|
||||
|
||||
UpdateIdenticalAnimationClips();
|
||||
|
||||
Repaint();
|
||||
CurrentAnimationEvent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update everything while animation sampling is enabled, given that the EditorApplication is not playing.
|
||||
/// </summary>
|
||||
void Update()
|
||||
{
|
||||
if (!EditorApplication.isPlaying && AnimationMode.InAnimationMode() && PreviewClip != null)
|
||||
{
|
||||
//Reset the AI back to the parent's root position when disabling Root Motion.
|
||||
if (UseRootMotion != RootMotionChanged)
|
||||
{
|
||||
CurrentAnimationViewerAI.transform.localPosition = Vector3.zero;
|
||||
CurrentAnimationViewerAI.transform.localEulerAngles = Vector3.zero;
|
||||
RootMotionChanged = UseRootMotion;
|
||||
}
|
||||
|
||||
//Update the timeline when an animation is playing.
|
||||
if (AnimationIsPlaying)
|
||||
{
|
||||
time += Time.deltaTime * TimeScale;
|
||||
if (time >= PreviewClip.length)
|
||||
time = 0;
|
||||
}
|
||||
|
||||
AnimationMode.BeginSampling();
|
||||
DefaultPosition = CurrentAnimationViewerAI.transform.position;
|
||||
DefaultEuler = CurrentAnimationViewerAI.transform.eulerAngles;
|
||||
AnimationMode.SampleAnimationClip(CurrentAnimationViewerAI, PreviewClip, time);
|
||||
|
||||
//Keep the AI in-placw when Root Motion is disabled.
|
||||
if (!UseRootMotion)
|
||||
{
|
||||
CurrentAnimationViewerAI.transform.position = DefaultPosition;
|
||||
CurrentAnimationViewerAI.transform.eulerAngles = DefaultEuler;
|
||||
}
|
||||
|
||||
AnimationMode.EndSampling();
|
||||
|
||||
//Always repaint when sampling an animation to have smooth UI
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset all states back to their original values and positions when closing the editor window.
|
||||
/// </summary>
|
||||
void OnDestroy()
|
||||
{
|
||||
DisplayApplyChangesMenu();
|
||||
|
||||
if (AnimationMode.InAnimationMode())
|
||||
AnimationMode.StopAnimationMode();
|
||||
|
||||
SetAnimatorStates(true);
|
||||
DeparentPreviewObject();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update all identical animation clip and their modifications so they are present on all duplicate clips.
|
||||
/// </summary>
|
||||
void UpdateIdenticalAnimationClips ()
|
||||
{
|
||||
for (int i = 0; i < CurrentAnimationEvents.Count; i++)
|
||||
{
|
||||
if (CurrentAnimationEvents[i].Clip == CurrentAnimationEvents[CurrentPreviewAnimationIndex].Clip)
|
||||
{
|
||||
CurrentAnimationEvents[i].AnimationEvents = CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents;
|
||||
CurrentAnimationEvents[i].Modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deparent the AI from the Parent Holder object. This is used to sample Root Motion animations while retaining the AI's starting position.
|
||||
/// </summary>
|
||||
public void DeparentPreviewObject()
|
||||
{
|
||||
if (CurrentAnimationViewerAI != null)
|
||||
{
|
||||
CurrentAnimationViewerAI.transform.SetParent(PreviousParent);
|
||||
CurrentAnimationViewerAI.transform.position = StartingPosition;
|
||||
CurrentAnimationViewerAI.transform.eulerAngles = StartingEuler;
|
||||
if (AnimationPreviewParent != null && AnimationPreviewParent.transform.childCount == 0)
|
||||
DestroyImmediate(AnimationPreviewParent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a temporary parent object to house the AI in while the user is previewing its animations. This allows Root Motion animations to play at the starting position and not at the (0,0,0) position.
|
||||
/// </summary>
|
||||
public void ParentPreviewObject ()
|
||||
{
|
||||
AnimationPreviewParent = new GameObject("Animation Viewer Parent");
|
||||
Selection.activeObject = AnimationPreviewParent;
|
||||
AnimationPreviewParent.transform.position = CurrentAnimationViewerAI.transform.position;
|
||||
AnimationPreviewParent.transform.eulerAngles = CurrentAnimationViewerAI.transform.eulerAngles;
|
||||
PreviousParent = CurrentAnimationViewerAI.transform.parent;
|
||||
CurrentAnimationViewerAI.transform.SetParent(AnimationPreviewParent.transform);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Due to a Unity bug, all Animator Controllers have to be disabled within the scene when testing animations using AnimationMode.
|
||||
/// Callbacks for saving, recompiling, and entering play mode are used to avoid unwanted modifications of Animator Controllers' enabled states.
|
||||
/// </summary>
|
||||
public void SetAnimatorStates (bool state)
|
||||
{
|
||||
var AnimatorsInScene = GameObject.FindObjectsOfType<Animator>();
|
||||
for (int i = 0; i < AnimatorsInScene.Length; i++)
|
||||
{
|
||||
AnimatorsInScene[i].enabled = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1fe66a7f17c8ff243b2261df11af96d6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6080e363cf6770841b6e8b8abb36ce61
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,244 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
|
||||
namespace EmeraldAI.Utility
|
||||
{
|
||||
public class EmeraldCombatTextManager : EditorWindow
|
||||
{
|
||||
EmeraldCombatTextData m_EmeraldAICombatTextData;
|
||||
GUIStyle TitleStyle;
|
||||
Texture CombatTextIcon;
|
||||
int CurrentTab = 0;
|
||||
|
||||
SerializedObject serializedObject;
|
||||
SerializedProperty TextFont;
|
||||
SerializedProperty CombatTextState;
|
||||
SerializedProperty PlayerTextColor;
|
||||
SerializedProperty PlayerCritTextColor;
|
||||
SerializedProperty PlayerTakeDamageTextColor;
|
||||
SerializedProperty AITextColor;
|
||||
SerializedProperty AICritTextColor;
|
||||
SerializedProperty HealingTextColor;
|
||||
SerializedProperty FontSize;
|
||||
SerializedProperty MaxFontSize;
|
||||
SerializedProperty AnimationType;
|
||||
SerializedProperty CombatTextTargets;
|
||||
SerializedProperty OutlineEffect;
|
||||
SerializedProperty UseAnimateFontSize;
|
||||
SerializedProperty TextHeight;
|
||||
|
||||
[MenuItem("Window/Emerald AI/Combat Text Manager #%c", false, 200)]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
EditorWindow APS = EditorWindow.GetWindow(typeof(EmeraldCombatTextManager), false, "Combat Text Manager");
|
||||
APS.minSize = new Vector2(600f, 650);
|
||||
}
|
||||
|
||||
void OnInspectorUpdate()
|
||||
{
|
||||
Repaint();
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
m_EmeraldAICombatTextData = (EmeraldCombatTextData)Resources.Load("Combat Text Data") as EmeraldCombatTextData;
|
||||
if (CombatTextIcon == null) CombatTextIcon = Resources.Load("Editor Icons/EmeraldCTM") as Texture;
|
||||
|
||||
serializedObject = new SerializedObject(m_EmeraldAICombatTextData);
|
||||
TextFont = serializedObject.FindProperty("TextFont");
|
||||
CombatTextState = serializedObject.FindProperty("CombatTextState");
|
||||
PlayerTextColor = serializedObject.FindProperty("PlayerTextColor");
|
||||
PlayerCritTextColor = serializedObject.FindProperty("PlayerCritTextColor");
|
||||
PlayerTakeDamageTextColor = serializedObject.FindProperty("PlayerTakeDamageTextColor");
|
||||
AITextColor = serializedObject.FindProperty("AITextColor");
|
||||
AICritTextColor = serializedObject.FindProperty("AICritTextColor");
|
||||
HealingTextColor = serializedObject.FindProperty("HealingTextColor");
|
||||
FontSize = serializedObject.FindProperty("FontSize");
|
||||
MaxFontSize = serializedObject.FindProperty("MaxFontSize");
|
||||
AnimationType = serializedObject.FindProperty("AnimationType");
|
||||
CombatTextTargets = serializedObject.FindProperty("CombatTextTargets");
|
||||
OutlineEffect = serializedObject.FindProperty("OutlineEffect");
|
||||
UseAnimateFontSize = serializedObject.FindProperty("UseAnimateFontSize");
|
||||
TextHeight = serializedObject.FindProperty("DefaultHeight");
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
GUILayout.Space(10);
|
||||
|
||||
GUI.backgroundColor = new Color(0.62f, 0.62f, 0.62f, 1f);
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(15); //Top Left Side Indent
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(45));
|
||||
GUI.backgroundColor = Color.white;
|
||||
TitleStyle = CustomEditorProperties.UpdateTitleStyle();
|
||||
EditorGUIUtility.SetIconSize(new Vector2(32, 32));
|
||||
EditorGUILayout.LabelField(new GUIContent(" " + "Combat Text Manager", CombatTextIcon), TitleStyle);
|
||||
EditorGUIUtility.SetIconSize(new Vector2(16, 16));
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.Space(15); //Top Right Side Indent
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(15); //Bottom Left Side Indent
|
||||
EditorGUILayout.BeginVertical();
|
||||
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(45));
|
||||
GUILayout.Space(-18);
|
||||
CustomEditorProperties.TextTitleWithDescription("Combat Text Manager", "With the Combat Text Manager, you can globally handle all combat text settings such as text size, text font, text color, text animation, and more.", true);
|
||||
|
||||
|
||||
GUIContent[] CombatTextManagerButtons = new GUIContent[2] { new GUIContent("Combat Text Settings"), new GUIContent("Combat Text Colors") };
|
||||
CurrentTab = GUILayout.Toolbar(CurrentTab, CombatTextManagerButtons, EditorStyles.miniButton, GUILayout.Height(25));
|
||||
|
||||
CombatTextSettings();
|
||||
ColorSettings();
|
||||
|
||||
GUILayout.Space(10);
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.Space(10);
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.Space(15); //Bottom Right Side Indent
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
if (!Application.isPlaying && GUI.changed)
|
||||
{
|
||||
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
|
||||
}
|
||||
}
|
||||
|
||||
void CombatTextSettings()
|
||||
{
|
||||
if (CurrentTab == 0)
|
||||
{
|
||||
GUI.backgroundColor = new Color(0.2f, 0.2f, 0.2f, 0.25f);
|
||||
EditorGUILayout.BeginVertical("Box");
|
||||
EditorGUILayout.LabelField("Combat Text Settings", EditorStyles.boldLabel);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.EndVertical();
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(CombatTextState, new GUIContent("Combat Text State"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("Controls the whether the Combat Text System is enabled or disabled.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(TextHeight, new GUIContent("Combat Text Height"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("Controls the overall height the Combat Text will spawn above targets.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(CombatTextTargets, new GUIContent("Combat Text Targets"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.LabelField("Controls the target types that will be able to have the Combat Text displayed.", EditorStyles.helpBox);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(OutlineEffect, new GUIContent("Use Outline Effect"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.LabelField("Controls the whether the Combat Text System will use a text outline effect.", EditorStyles.helpBox);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(AnimationType, new GUIContent("Animation Type"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.LabelField("Controls the Combat Text's Animation Type.", EditorStyles.helpBox);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
CustomEditorProperties.CustomObjectField(new Rect(), new GUIContent(), TextFont, "Text Font", typeof(Font), true);
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("The Combat Text's font.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
CustomEditorProperties.CustomIntSlider(new Rect(), new GUIContent(), FontSize, "Font Size", 10, 50);
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("Controls the size of the Combat Text's font.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(UseAnimateFontSize, new GUIContent("Use Animate Font"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.LabelField("Controls whether or not the font size is animated.", EditorStyles.helpBox);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (m_EmeraldAICombatTextData.UseAnimateFontSize == EmeraldCombatTextData.UseAnimateFontSizeEnum.Enabled)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(10);
|
||||
EditorGUILayout.BeginVertical();
|
||||
|
||||
CustomEditorProperties.CustomIntSlider(new Rect(), new GUIContent(), MaxFontSize, "Animated Font Size", 1, 30);
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("Controls the additional size of the Combat Text's font when Animate Size is enabled. After the text has been animated, it will return to the Font Size.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ColorSettings ()
|
||||
{
|
||||
if (CurrentTab == 1)
|
||||
{
|
||||
GUI.backgroundColor = new Color(0.2f, 0.2f, 0.2f, 0.25f);
|
||||
EditorGUILayout.BeginVertical("Box");
|
||||
EditorGUILayout.LabelField("Combat Text Colors", EditorStyles.boldLabel);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.EndVertical();
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(PlayerTextColor, new GUIContent("Player Color"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("The Combat Text's color when used by the player.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(PlayerTakeDamageTextColor, new GUIContent("Take Damage Color"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("The Combat Text's color when a player takes damage.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(PlayerCritTextColor, new GUIContent("Player Crit Color"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("The Combat Text's critical hit color when used by the player.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(AITextColor, new GUIContent("AI Color"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("The Combat Text's AI color when used between other AI.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(AICritTextColor, new GUIContent("AI Crit Color"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("The Combat Text's critical hit color when used between other AI.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(HealingTextColor, new GUIContent("Healing Color"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("The Combat Text's color when used for healing.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de35206336289c2498f7bae2e0c416f3
|
||||
timeCreated: 1517957035
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,151 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEditorInternal;
|
||||
|
||||
namespace EmeraldAI.Utility
|
||||
{
|
||||
public class EmeraldFactionManager : EditorWindow
|
||||
{
|
||||
GUIStyle TitleStyle;
|
||||
Texture FactionIcon;
|
||||
Vector2 scrollPos;
|
||||
SerializedObject serializedObject;
|
||||
ReorderableList FactionList;
|
||||
bool RefreshInspector;
|
||||
|
||||
[MenuItem("Window/Emerald AI/Faction Manager #%r", false, 200)]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
EditorWindow APS = EditorWindow.GetWindow(typeof(EmeraldFactionManager), false, "Faction Manager");
|
||||
APS.minSize = new Vector2(300f, 300f);
|
||||
}
|
||||
|
||||
void OnInspectorUpdate()
|
||||
{
|
||||
Repaint();
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (FactionIcon == null) FactionIcon = Resources.Load("FactionExtension") as Texture;
|
||||
LoadFactionData();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
EditorApplication.update += OnEditorUpdate;
|
||||
#endif
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
EditorApplication.update -= OnEditorUpdate;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is needed (using callback from EditorApplication.update) to reliably update the faction enums within an AI's editor, given factions are being changed while an AI is selected.
|
||||
/// </summary>
|
||||
protected virtual void OnEditorUpdate()
|
||||
{
|
||||
if (RefreshInspector)
|
||||
{
|
||||
UpdateStaticFactionData();
|
||||
RefreshInspector = false;
|
||||
}
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
GUILayout.Space(10);
|
||||
|
||||
GUI.backgroundColor = new Color(0.62f, 0.62f, 0.62f, 1f);
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(15); //Top Left Side Indent
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(45));
|
||||
GUI.backgroundColor = Color.white;
|
||||
TitleStyle = CustomEditorProperties.UpdateTitleStyle();
|
||||
EditorGUIUtility.SetIconSize(new Vector2(32, 32));
|
||||
EditorGUILayout.LabelField(new GUIContent(" " + "Faction Manager", FactionIcon), TitleStyle);
|
||||
EditorGUIUtility.SetIconSize(new Vector2(16, 16));
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.Space(15); //Top Right Side Indent
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(15); //Bottom Left Side Indent
|
||||
EditorGUILayout.BeginVertical();
|
||||
|
||||
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
|
||||
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(45));
|
||||
GUILayout.Space(-18);
|
||||
CustomEditorProperties.TextTitleWithDescription("Faction Manager", "With the Faction Manager, you can create factions that your AI will use to identify targets. " +
|
||||
"Factions created here will be globally available for all Emerald AI agents to use. You can assign factions through an AI's Detection component within the Faction Settings foldout.", true);
|
||||
|
||||
FactionList.DoLayoutList();
|
||||
|
||||
GUILayout.Space(10);
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.Space(10);
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.EndScrollView();
|
||||
GUILayout.Space(15); //Bottom Right Side Indent
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
void UpdateStaticFactionData ()
|
||||
{
|
||||
string path = AssetDatabase.GetAssetPath(Resources.Load("Faction Data"));
|
||||
EmeraldFactionData FactionData = (EmeraldFactionData)AssetDatabase.LoadAssetAtPath(path, typeof(EmeraldFactionData));
|
||||
EmeraldDetection.StringFactionList = new List<string>(FactionData.FactionNameList);
|
||||
FactionExtension.StringFactionList = new List<string>(FactionData.FactionNameList);
|
||||
//Repaint();
|
||||
|
||||
EditorUtility.SetDirty(FactionData);
|
||||
EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene());
|
||||
InternalEditorUtility.RepaintAllViews();
|
||||
SceneView.RepaintAll();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
void LoadFactionData()
|
||||
{
|
||||
string path = AssetDatabase.GetAssetPath(Resources.Load("Faction Data"));
|
||||
EmeraldFactionData FactionData = (EmeraldFactionData)AssetDatabase.LoadAssetAtPath(path, typeof(EmeraldFactionData));
|
||||
serializedObject = new SerializedObject(FactionData);
|
||||
|
||||
FactionList = new ReorderableList(serializedObject, serializedObject.FindProperty("FactionNameList"), false, true, true, true);
|
||||
FactionList.drawHeaderCallback = rect =>
|
||||
{
|
||||
EditorGUI.LabelField(rect, "Faction List", EditorStyles.boldLabel);
|
||||
};
|
||||
FactionList.drawElementCallback =
|
||||
(Rect rect, int index, bool isActive, bool isFocused) =>
|
||||
{
|
||||
var element = FactionList.serializedProperty.GetArrayElementAtIndex(index);
|
||||
FactionList.elementHeight = EditorGUIUtility.singleLineHeight * 1.25f;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUI.LabelField(new Rect(rect.x, rect.y + 3f, rect.width, EditorGUIUtility.singleLineHeight), "Faction " + (index + 1));
|
||||
EditorGUI.PropertyField(new Rect(rect.x + 75, rect.y + 3f, rect.width - 75, EditorGUIUtility.singleLineHeight), element, GUIContent.none);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
RefreshInspector = true;
|
||||
UpdateStaticFactionData();
|
||||
}
|
||||
};
|
||||
|
||||
FactionList.onChangedCallback = (FactionList) =>
|
||||
{
|
||||
UpdateStaticFactionData();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5254bc5a28e5a584c82d06e89faa02d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,465 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Linq;
|
||||
|
||||
namespace EmeraldAI.Utility
|
||||
{
|
||||
public class EmeraldSetupManager : EditorWindow
|
||||
{
|
||||
public GameObject ObjectToSetup;
|
||||
public LayerMask AILayer = 4;
|
||||
public string AITag = "Untagged";
|
||||
public List<GameObject> ObjectsToSetup = new List<GameObject>();
|
||||
|
||||
//Required
|
||||
public bool m_EmeraldSystem = true;
|
||||
public bool m_EmeraldAnimation = true;
|
||||
public bool m_EmeraldCombat = true;
|
||||
public bool m_EmeraldSounds = true;
|
||||
public bool m_EmeraldMovement = true;
|
||||
public bool m_EmeraldHealth = true;
|
||||
public bool m_EmeraldBehaviors = true;
|
||||
public bool m_EmeraldDetection = true;
|
||||
|
||||
//Optional
|
||||
public bool m_EmeraldEvents = false;
|
||||
public bool m_EmeraldItems = false;
|
||||
public bool m_EmeraldInverseKinematics = false;
|
||||
public bool m_EmeraldOptimization = false;
|
||||
public bool m_EmeraldSoundDetector = false;
|
||||
public bool m_EmeraldUI = false;
|
||||
public bool m_EmeraldFootsteps = false;
|
||||
public bool m_EmeraldDebugger = false;
|
||||
public bool m_TargetPositionModifier = false;
|
||||
|
||||
public AnimationProfile m_AnimationProfile;
|
||||
public EmeraldSoundProfile m_SoundProfile;
|
||||
|
||||
public AnimatorTypeState AnimatorType = AnimatorTypeState.RootMotion;
|
||||
public enum AnimatorTypeState { RootMotion, NavMeshDriven }
|
||||
|
||||
GUIStyle TitleStyle;
|
||||
Vector2 scrollPos;
|
||||
Texture SettingsIcon;
|
||||
bool DisplayConfirmation = false;
|
||||
bool IsInScene;
|
||||
static bool DontShowDisplayConfirmation = false;
|
||||
|
||||
void OnInspectorUpdate()
|
||||
{
|
||||
Repaint();
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Window/Emerald AI/Setup Manager #%e", false, 200)]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
EditorWindow APS = EditorWindow.GetWindow(typeof(EmeraldSetupManager), false, "Setup Manager");
|
||||
APS.minSize = new Vector2(300, 250f); //500
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (SettingsIcon == null) SettingsIcon = Resources.Load("Editor Icons/EmeraldSetupManager") as Texture;
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
if (ObjectToSetup != null) IsInScene = ObjectToSetup.scene.IsValid();
|
||||
else IsInScene = true;
|
||||
|
||||
GUILayout.Space(10);
|
||||
GUI.backgroundColor = new Color(0.62f, 0.62f, 0.62f, 1f);
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(15); //Top Left Side Indent
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(45));
|
||||
GUI.backgroundColor = Color.white;
|
||||
TitleStyle = CustomEditorProperties.UpdateTitleStyle();
|
||||
EditorGUIUtility.SetIconSize(new Vector2(32, 32));
|
||||
EditorGUILayout.LabelField(new GUIContent(" " + "Setup Manager", SettingsIcon), TitleStyle);
|
||||
EditorGUIUtility.SetIconSize(new Vector2(16, 16));
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.Space(15); //Top Right Side Indent
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(15); //Bottom Left Side Indent
|
||||
EditorGUILayout.BeginVertical();
|
||||
|
||||
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
|
||||
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(45));
|
||||
GUILayout.Space(-18);
|
||||
CustomEditorProperties.TextTitleWithDescription("Setup Settings", "Set the settings this AI wil be setup with. If you do not assign an Animation Profile or Sound Profile here, you will need to do so through their editors after the setup process has been complete.", true);
|
||||
|
||||
if (ObjectToSetup == null)
|
||||
{
|
||||
GUI.backgroundColor = new Color(10f, 0.0f, 0.0f, 0.25f);
|
||||
EditorGUILayout.LabelField("This field cannot be left blank.", EditorStyles.helpBox);
|
||||
GUI.backgroundColor = Color.white;
|
||||
}
|
||||
|
||||
ObjectToSetup = (GameObject)EditorGUILayout.ObjectField("Object to Setup", ObjectToSetup, typeof(GameObject), true);
|
||||
if (!IsInScene) CustomEditorProperties.DisplayImportantMessage("The Setup Manager will not work with objects from the Project Tab. Please ensure your object is within the Scene before assigning it to the Setup Manager.");
|
||||
DescriptionElement("The object that the Setup Manager will create an AI with.");
|
||||
GUILayout.Space(10);
|
||||
|
||||
|
||||
AITag = EditorGUILayout.TagField("Tag for AI", AITag);
|
||||
DescriptionElement("The Unity Tag that will be applied to your AI. Note: Untagged cannot be used.");
|
||||
GUILayout.Space(10);
|
||||
|
||||
AILayer = EditorGUILayout.LayerField("Layer for AI", AILayer);
|
||||
DescriptionElement("The Unity Layer that will be applied to your AI. Note: Default cannot be used.");
|
||||
GUILayout.Space(10);
|
||||
|
||||
|
||||
m_AnimationProfile = (AnimationProfile)EditorGUILayout.ObjectField("Animation Profile", m_AnimationProfile, typeof(AnimationProfile), false);
|
||||
DescriptionElement("(Optional) You can assign an Animation Profile to this AI that will be applied during the setup process.");
|
||||
GUILayout.Space(10);
|
||||
|
||||
m_SoundProfile = (EmeraldSoundProfile)EditorGUILayout.ObjectField("Sound Profile", m_SoundProfile, typeof(EmeraldSoundProfile), false);
|
||||
DescriptionElement("(Optional) You can assign an Sound Profile to this AI that will be applied during the setup process.");
|
||||
GUILayout.Space(10);
|
||||
|
||||
|
||||
AnimatorType = (AnimatorTypeState)EditorGUILayout.EnumPopup("Animator Type", AnimatorType);
|
||||
DescriptionElement("Controls whether this AI will use Root Motion or NavMesh for its movement and speed.");
|
||||
GUILayout.Space(10);
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
//Displays the toggle settings that control which components are added to an object.
|
||||
DisplayToggleOptions();
|
||||
|
||||
if (ObjectToSetup == null)
|
||||
{
|
||||
GUI.backgroundColor = new Color(10f, 0.0f, 0.0f, 0.25f);
|
||||
EditorGUILayout.LabelField("You must have an object applied to the AI Object slot before you can complete the setup process.", EditorStyles.helpBox);
|
||||
GUI.backgroundColor = Color.white;
|
||||
}
|
||||
|
||||
if (!IsInScene)
|
||||
{
|
||||
GUI.backgroundColor = new Color(10f, 0.0f, 0.0f, 0.25f);
|
||||
EditorGUILayout.LabelField("The Setup Manager will not work with objects from the Project Tab. Please ensure your object is within the Scene before assigning it as the Object to Setup.", EditorStyles.helpBox);
|
||||
GUI.backgroundColor = Color.white;
|
||||
}
|
||||
|
||||
EditorGUI.BeginDisabledGroup(ObjectToSetup == null || !IsInScene);
|
||||
if (GUILayout.Button("Setup AI", GUILayout.Height(25)))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Emerald Setup Manager", "Are you sure you'd like to setup an AI on this object?", "Setup", "Cancel"))
|
||||
{
|
||||
UnpackPrefab(ObjectToSetup);
|
||||
InitializeSetup();
|
||||
}
|
||||
}
|
||||
GUILayout.Space(25);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.EndScrollView();
|
||||
GUILayout.Space(15); //Bottom Right Side Indent
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
if (DisplayConfirmation && !DontShowDisplayConfirmation)
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Setup Manager", "Your AI has been successfully created. Several components still need to be configured, which is indicated by a warning message at the top some components. " +
|
||||
"These warning messages will tell you how to configure the components that need it.", "Okay", "Okay, Don't Show Again"))
|
||||
{
|
||||
DisplayConfirmation = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayConfirmation = false;
|
||||
DontShowDisplayConfirmation = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpack the passed GameObject if it is a prefab.
|
||||
/// </summary>
|
||||
void UnpackPrefab(GameObject ObjectToUnpack)
|
||||
{
|
||||
PrefabAssetType m_AssetType = PrefabUtility.GetPrefabAssetType(ObjectToUnpack);
|
||||
|
||||
//Only unpack prefab if the ObjectToUnpack is a prefab.
|
||||
if (m_AssetType != PrefabAssetType.NotAPrefab)
|
||||
{
|
||||
PrefabUtility.UnpackPrefabInstance(ObjectToUnpack, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Displays the toggle settings that control which components are added to an object.
|
||||
/// </summary>
|
||||
void DisplayToggleOptions()
|
||||
{
|
||||
if (position.width > 500)
|
||||
{
|
||||
//Required
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(50));
|
||||
GUILayout.Space(-18);
|
||||
RequiredSettings();
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
//Optional
|
||||
GUILayout.Space(10);
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(50));
|
||||
GUILayout.Space(-18);
|
||||
OptionalSettings();
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
GUILayout.Space(15);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(50));
|
||||
GUILayout.Space(-18);
|
||||
RequiredSettings();
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(50));
|
||||
GUILayout.Space(-18);
|
||||
OptionalSettings();
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
}
|
||||
|
||||
void RequiredSettings()
|
||||
{
|
||||
CustomEditorProperties.TextTitleWithDescription("(Required Components)", "The components that are required to make Emerald AI run. " +
|
||||
"Enabled components will automatically be added to the assigned object during the setting up process. Each component has its own editor with various settings related to its functionality. You can hover over the ? icon for a tooltip description of each component.", true);
|
||||
m_EmeraldSystem = ToggleComponentElement(true, "Emerald System", "The base Emerald AI script that controls the management and updating off all other required scripts.");
|
||||
m_EmeraldAnimation = ToggleComponentElement(true, "Emerald Animation", "Handles the management of an AI's animations through Animation Profiles.");
|
||||
m_EmeraldCombat = ToggleComponentElement(true, "Emerald Combat", "Gives AI the ability to engage in combat, setup an AI's abilities, and other combat related settings.");
|
||||
m_EmeraldSounds = ToggleComponentElement(true, "Emerald Sounds", "Handles the management of an AI's sounds through Sound Profiles.");
|
||||
m_EmeraldMovement = ToggleComponentElement(true, "Emerald Movement", "Controls various movement, turn, and alignement related settings.");
|
||||
m_EmeraldHealth = ToggleComponentElement(true, "Emerald Health", "Gives AI the ability to take damage. Users can access this component through the IDamageable interface component.");
|
||||
m_EmeraldBehaviors = ToggleComponentElement(true, "Emerald Behaviors", "Gives AI the ability to use preset behaviors. Users can create custom behavior script derived from the EmeraldBehavior class, if desired.");
|
||||
m_EmeraldDetection = ToggleComponentElement(true, "Emerald Detection", "Gives AI the ability to see and detect targets.");
|
||||
}
|
||||
|
||||
void OptionalSettings ()
|
||||
{
|
||||
CustomEditorProperties.TextTitleWithDescription("(Optional Components)", "The components that add optional features to Emerald AI. " +
|
||||
"Enabled components will automatically be added to the assigned object during the setting up process. Each component has its own editor with various settings related to its functionality. You can hover over the ? icon for a tooltip description of each component.", true);
|
||||
m_EmeraldEvents = ToggleComponentElement(m_EmeraldEvents, "Emerald Events", "Adds the ability for users to create custom events for most of an AI's actions.", false);
|
||||
m_EmeraldItems = ToggleComponentElement(m_EmeraldItems, "Emerald Items", "Adds the ability for users to setup droppable items, equippable items, and more on their AI.", false);
|
||||
m_EmeraldInverseKinematics = ToggleComponentElement(m_EmeraldInverseKinematics, "Emerald Inverse Kinematics", "Adds the ability for AI to use Inverse Kinematics using Unity's Animation Rigging system.", false);
|
||||
m_EmeraldOptimization = ToggleComponentElement(m_EmeraldOptimization, "Emerald Optimization", "Adds the ability for AI to be disabled when culled or not in view of the camera. This increases performance by disabling AI that are not currently seen.", false);
|
||||
m_EmeraldUI = ToggleComponentElement(m_EmeraldUI, "Emerald UI", "Adds the ability for AI to use Emerald AI's built-in UI system which can display health bars and an AI's name above their head.", false);
|
||||
m_EmeraldFootsteps = ToggleComponentElement(m_EmeraldFootsteps, "Emerald Footsteps", "Adds the ability to play footstep sounds and effects based on the detected surfaces. Surfaces can be customized through Footstep Surface Objects. " +
|
||||
"Both textures from Unity Terrains and tags from game objects can be used.\n\nNote: Footstep Animation Events are required for footsteps to trigger. Ensure that these have been set up on your AI's movement animations.", false);
|
||||
m_EmeraldSoundDetector = ToggleComponentElement(m_EmeraldSoundDetector, "Emerald Sound Detector", "Adds the ability for AI to hear player targets and other sounds made by external sources.", false);
|
||||
m_EmeraldDebugger = ToggleComponentElement(m_EmeraldDebugger, "Emerald Debugger", "Adds the ability for AI to use and display debugging information (debug logs, debug lines, pathfinding information, etc). This can help with development or identifying possible issues.", false);
|
||||
m_TargetPositionModifier = ToggleComponentElement(m_TargetPositionModifier, "Target Position Modifier", "Adds the ability to modify the target height of targets allowing AI agents to target the modified position, which can improve targeting accuracy.", false);
|
||||
}
|
||||
|
||||
void InitializeSetup()
|
||||
{
|
||||
if (ObjectToSetup != null && ObjectToSetup.GetComponent<EmeraldSystem>() == null && ObjectToSetup.GetComponent<Animator>() != null)
|
||||
{
|
||||
AssignComponents();
|
||||
SetupBoxCollider();
|
||||
SetupAudio();
|
||||
SetupTagsAndLayers();
|
||||
SetupMovement();
|
||||
AutoFindHeadTransform();
|
||||
|
||||
if (!DontShowDisplayConfirmation)
|
||||
{
|
||||
DisplayConfirmation = true;
|
||||
}
|
||||
|
||||
ObjectToSetup = null;
|
||||
}
|
||||
else if (ObjectToSetup == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Emerald Setup Manager - Oops!", "There is no object assigned to the AI Object slot. Please assign one and try again.", "Okay");
|
||||
return;
|
||||
}
|
||||
else if (ObjectToSetup.GetComponent<EmeraldSystem>() != null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Emerald Setup Manager - Oops!", "There is already an Emerald AI component on this object. Please choose another object to apply an Emerald AI component to.", "Okay");
|
||||
return;
|
||||
}
|
||||
else if (ObjectToSetup.GetComponent<Animator>() == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Emerald Setup Manager - Oops!", "There is no Animator component attached to your AI. Please assign one and try again.", "Okay");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns and order the assigned componenets.
|
||||
/// </summary>
|
||||
void AssignComponents()
|
||||
{
|
||||
ObjectToSetup.AddComponent<EmeraldSystem>();
|
||||
if (m_EmeraldEvents) ObjectToSetup.AddComponent<EmeraldEvents>();
|
||||
if (m_EmeraldItems) ObjectToSetup.AddComponent<EmeraldItems>();
|
||||
if (m_EmeraldInverseKinematics) ObjectToSetup.AddComponent<EmeraldInverseKinematics>();
|
||||
if (m_EmeraldOptimization) ObjectToSetup.AddComponent<EmeraldOptimization>();
|
||||
if (m_EmeraldUI) ObjectToSetup.AddComponent<EmeraldUI>();
|
||||
if (m_EmeraldFootsteps) ObjectToSetup.AddComponent<EmeraldFootsteps>();
|
||||
if (m_EmeraldSoundDetector) ObjectToSetup.AddComponent<SoundDetection.EmeraldSoundDetector>();
|
||||
if (m_EmeraldDebugger) ObjectToSetup.AddComponent<EmeraldDebugger>();
|
||||
if (m_TargetPositionModifier) ObjectToSetup.AddComponent<TargetPositionModifier>();
|
||||
|
||||
if (m_AnimationProfile != null) ObjectToSetup.GetComponent<EmeraldAnimation>().m_AnimationProfile = m_AnimationProfile;
|
||||
if (m_SoundProfile != null) ObjectToSetup.GetComponent<EmeraldSounds>().SoundProfile = m_SoundProfile;
|
||||
|
||||
MoveToBottom(ObjectToSetup.GetComponent<EmeraldSystem>());
|
||||
MoveToBottom(ObjectToSetup.GetComponent<Animator>());
|
||||
MoveToBottom(ObjectToSetup.GetComponent<BoxCollider>());
|
||||
MoveToBottom(ObjectToSetup.GetComponent<UnityEngine.AI.NavMeshAgent>());
|
||||
MoveToBottom(ObjectToSetup.GetComponent<AudioSource>());
|
||||
}
|
||||
|
||||
void MoveToBottom (Component ComponentToMove)
|
||||
{
|
||||
Component[] AllComponents = ObjectToSetup.GetComponents<Component>();
|
||||
|
||||
for (int i = 0; i < AllComponents.Length; i++)
|
||||
{
|
||||
UnityEditorInternal.ComponentUtility.MoveComponentDown(ComponentToMove);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the AI's movement as some settings are dependent on Root Motion.
|
||||
/// </summary>
|
||||
void SetupMovement ()
|
||||
{
|
||||
EmeraldMovement EmeraldMovement = ObjectToSetup.GetComponent<EmeraldMovement>();
|
||||
EmeraldMovement.MovementType = (EmeraldMovement.MovementTypes)AnimatorType;
|
||||
if (EmeraldMovement.MovementType == EmeraldMovement.MovementTypes.RootMotion)
|
||||
{
|
||||
EmeraldMovement.StationaryTurningSpeedCombat = 30;
|
||||
EmeraldMovement.StationaryTurningSpeedNonCombat = 30;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the AI's user set tags and layers.
|
||||
/// </summary>
|
||||
void SetupTagsAndLayers ()
|
||||
{
|
||||
ObjectToSetup.tag = AITag;
|
||||
ObjectToSetup.layer = AILayer;
|
||||
ObjectToSetup.GetComponent<EmeraldDetection>().DetectionLayerMask = (1 << LayerMask.NameToLayer("Water"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to automatically find the AI's head transform.
|
||||
/// </summary>
|
||||
void AutoFindHeadTransform ()
|
||||
{
|
||||
foreach (Transform root in ObjectToSetup.GetComponentsInChildren<Transform>())
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (i < root.childCount && root.GetChild(i).name == "root" || i < root.childCount && root.GetChild(i).name == "Root" || i < root.childCount && root.GetChild(i).name == "ROOT") //Only look in the root transform - 3 child index in
|
||||
{
|
||||
foreach (Transform t in root.GetChild(i).GetComponentsInChildren<Transform>())
|
||||
{
|
||||
if (t.name.Contains("head") || t.name.Contains("Head") || t.name.Contains("HEAD")) //Look for the word head within all transforms within the root transform
|
||||
{
|
||||
ObjectToSetup.GetComponent<EmeraldDetection>().HeadTransform = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If no head transform was found, the model may not have a root named bone, try again with less conditions.
|
||||
if (ObjectToSetup.GetComponent<EmeraldDetection>().HeadTransform == null)
|
||||
{
|
||||
foreach (Transform t in ObjectToSetup.GetComponentsInChildren<Transform>())
|
||||
{
|
||||
//Ignore transforms with a MultiAimConstraint as these can be for the Animation Rigging system/constraint.
|
||||
if (t.GetComponent<UnityEngine.Animations.Rigging.MultiAimConstraint>() == null)
|
||||
{
|
||||
if (t.name.Contains("head") || t.name.Contains("Head") || t.name.Contains("HEAD")) //Look for the word head within all transforms within all the transforms
|
||||
{
|
||||
ObjectToSetup.GetComponent<EmeraldDetection>().HeadTransform = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the AI's audio and its defaul AudioSource settings.
|
||||
/// </summary>
|
||||
void SetupAudio ()
|
||||
{
|
||||
ObjectToSetup.GetComponent<AudioSource>().spatialBlend = 1;
|
||||
ObjectToSetup.GetComponent<AudioSource>().dopplerLevel = 0;
|
||||
ObjectToSetup.GetComponent<AudioSource>().rolloffMode = AudioRolloffMode.Linear;
|
||||
ObjectToSetup.GetComponent<AudioSource>().minDistance = 1;
|
||||
ObjectToSetup.GetComponent<AudioSource>().maxDistance = 50;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the AI's default box collider based on its main renderer's bounds.
|
||||
/// </summary>
|
||||
void SetupBoxCollider ()
|
||||
{
|
||||
List<SkinnedMeshRenderer> TempSkinnedMeshes = new List<SkinnedMeshRenderer>();
|
||||
List<float> TempSkinnedMeshBounds = new List<float>();
|
||||
|
||||
foreach (SkinnedMeshRenderer SMR in ObjectToSetup.GetComponentsInChildren<SkinnedMeshRenderer>())
|
||||
{
|
||||
if (!TempSkinnedMeshes.Contains(SMR))
|
||||
{
|
||||
TempSkinnedMeshes.Add(SMR);
|
||||
TempSkinnedMeshBounds.Add(SMR.bounds.size.sqrMagnitude);
|
||||
}
|
||||
}
|
||||
|
||||
float m_LargestBounds = TempSkinnedMeshBounds.Max();
|
||||
var AIRenderer = TempSkinnedMeshes[TempSkinnedMeshBounds.IndexOf(m_LargestBounds)];
|
||||
|
||||
ObjectToSetup.GetComponent<BoxCollider>().size = new Vector3(AIRenderer.bounds.size.x / 3 / ObjectToSetup.transform.localScale.y, AIRenderer.bounds.size.y / ObjectToSetup.transform.localScale.y, AIRenderer.bounds.size.z / 3 / ObjectToSetup.transform.localScale.y);
|
||||
ObjectToSetup.GetComponent<BoxCollider>().center = new Vector3(ObjectToSetup.GetComponent<BoxCollider>().center.x, ObjectToSetup.GetComponent<BoxCollider>().size.y / 2, ObjectToSetup.GetComponent<BoxCollider>().center.z);
|
||||
}
|
||||
|
||||
bool ToggleComponentElement (bool Setting, string Name, string Description, bool RequiredComponent = true)
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(RequiredComponent);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
var textDimensions = GUI.skin.label.CalcSize(new GUIContent(Name));
|
||||
Setting = EditorGUILayout.ToggleLeft(new GUIContent(Name), Setting, GUILayout.ExpandWidth(false), GUILayout.Width(textDimensions.x + 13.5f));
|
||||
EditorGUILayout.LabelField(new GUIContent(EditorGUIUtility.IconContent("_Help").image, Description), GUILayout.Width(25));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUI.EndDisabledGroup();
|
||||
GUILayout.Space(5);
|
||||
return Setting;
|
||||
}
|
||||
|
||||
void DescriptionElement (string DescriptionText)
|
||||
{
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox(DescriptionText, MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01f95d3fc726cc24ab4a91e0176a9614
|
||||
timeCreated: 1507164265
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,191 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Linq;
|
||||
|
||||
namespace EmeraldAI.Utility
|
||||
{
|
||||
public class EmeraldWikiManager : EditorWindow
|
||||
{
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Home", false, 250)]
|
||||
public static void Home()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Getting Started", false, 250)]
|
||||
public static void GettingStarted()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/getting-started/getting-started");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Upgrading to URP or HDRP", false, 251)]
|
||||
public static void UpgradingToURPAndHDRP()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/getting-started/upgrading-to-urp-and-hdrp");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Animation Component", false, 300)]
|
||||
public static void AnimationComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/animation-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Behaviors Component", false, 300)]
|
||||
public static void BehaviorsComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/behaviors-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Combat Component", false, 300)]
|
||||
public static void CombatComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/combat-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Detection Component", false, 300)]
|
||||
public static void DetectionComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/detection-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Movement Component", false, 300)]
|
||||
public static void MovementComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/movement-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Health Component", false, 300)]
|
||||
public static void HealthComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/health-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Sounds Component", false, 300)]
|
||||
public static void SoundsComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/sounds-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Debugger Component", false, 400)]
|
||||
public static void DebuggerComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/debugger-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Events Component", false, 400)]
|
||||
public static void EventsComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/events-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Inverse Kinematics Component", false, 400)]
|
||||
public static void IKComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-optional/inverse-kinematics-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Items Component", false, 400)]
|
||||
public static void ItemsComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/items-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Location Based Damage Component", false, 400)]
|
||||
public static void LBDComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-optional/location-based-damage-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Optimization Component", false, 400)]
|
||||
public static void OptimizationComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/optimization-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Sound Detector Component", false, 400)]
|
||||
public static void SoundDetectorComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-optional/sound-detector-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Target Position Modifier Component", false, 400)]
|
||||
public static void TPMComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-optional/target-position-modifier-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/UI Component", false, 400)]
|
||||
public static void UIComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/ui-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Weapon Collisions Component", false, 400)]
|
||||
public static void WeaponCollisionsComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-optional/weapon-collisions-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Integrations/FPS Engine", false, 251)]
|
||||
public static void FPSEngineIntegration()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/integrations/integrations/fps-engine");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Integrations/Invector", false, 252)]
|
||||
public static void InvectorIntegration()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/integrations/invector");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Integrations/Final IK", false, 253)]
|
||||
public static void FinalIKIntegration()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/integrations/final-ik");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Integrations/Dialogue System", false, 253)]
|
||||
public static void DialogueSystemIntegration()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/integrations/integrations/dialogue-system");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Integrations/Quest Machine", false, 254)]
|
||||
public static void QuestMachineIntegration()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/integrations/integrations/quest-machine");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Integrations/Love Hate", false, 255)]
|
||||
public static void LoveHateIntegration()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/integrations/integrations/love-hate");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Support/AI Generated Solutions", false, 253)]
|
||||
public static void AIGeneratedSolutions()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/help/using-the-wiki-search-tool");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Support/Solutions to Possible Issues", false, 253)]
|
||||
public static void SolutionsToPossibleIssues()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/help/solutions-to-possible-issues");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Support/Contact", false, 253)]
|
||||
public static void ContactSupport()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/help/support");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Report a Bug", false, 300)]
|
||||
public static void ReportBug()
|
||||
{
|
||||
Application.OpenURL("https://github.com/Black-Horizon-Studios/Emerald-AI-2024/issues");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea76c308fe3a2484cb2a751af4029708
|
||||
timeCreated: 1507164265
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user