using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEngine; using UnityEditor; namespace TweenAnimation { [CustomEditor(typeof(TweenAnimation), false)] public partial class TweenAnimationInspector : Editor { static Dictionary s_TweenModuleClasses; static string[] s_TweenModuleClassNames; static Dictionary s_TweenModuleGUI; static int s_ModuleTabHash = "TweenModuleTab".GetHashCode(); static int s_ModuleEnabledHash = "TweenModuleEnabled".GetHashCode(); TweenModuleGUIStyles styles { get { return TweenModuleGUIStyles.Get(); } } TweenModuleUI ui { get { return TweenModuleUI.Get(); } } TweenAnimation animation; bool m_ShowAnimationTab = false; // 是否在时间轴上显示 bool m_ShowEvents; bool m_ShowModules; bool m_Play; bool m_Pause; bool m_Stop; HashSet m_EventTimeSet; float m_SelectedEventTime; Dictionary m_CallbackUnfold; public void OnEnable() { animation = target as TweenAnimation; if (s_TweenModuleGUI == null) s_TweenModuleGUI = new Dictionary(); m_ShowAnimationTab = false; m_ShowEvents = true; m_ShowModules = true; if (m_EventTimeSet == null) m_EventTimeSet = new HashSet(); m_EventTimeSet.Clear(); m_SelectedEventTime = -1; m_Play = false; EditorStop(); m_DragState = DragState.Release; if (m_PlaybackTimer == null) m_PlaybackTimer = new PlaybackTimer(animation); m_CallbackUnfold = new Dictionary(); } public void OnDisable() { m_Play = false; if (m_Play) EditorStop(); m_PlaybackTimer.Reset(); } public override void OnInspectorGUI() { animation = target as TweenAnimation; if (animation == null) return; this.serializedObject.Update(); EditorGUILayout.Space(); AnimationTab(); AddNewModule(); AnimationModules(); EditorGUILayout.Space(); serializedObject.ApplyModifiedProperties(); } void AnimationTab() { Rect rect = ui.GetControlRect(40); Rect headerRect = rect; headerRect.x -= 2; headerRect.width += 8; if (GUI.Button(headerRect, "", styles.headerBg)) { m_ShowAnimationTab = !m_ShowAnimationTab; } GUI.Label(new Rect(rect.x + 10, rect.y + 3, 100, 20), "Tween Animation", styles.headerTitle); Vector2 size = styles.text.CalcSize(new GUIContent(animation.name)); GUI.Label(new Rect(rect.x + rect.width - size.x - 10, rect.y + 3, 100, 20), animation.name, styles.text); if (m_ShowAnimationTab) { animation.name = ui.GUIText("Name", animation.name); // 播放风格 animation.playbackStyle = (TweenAnimation.PlaybackStyle) ui.GUIEnum("Playback Style", animation.playbackStyle); if(animation.playbackStyle != TweenAnimation.PlaybackStyle.Once) { // 播放次数限制 string fmt = animation.playbackLimit <= 0 ? "Unlimited" : ""; float limit = ui.GUIFloat("Playback Limit", animation.playbackLimit, fmt); limit = Mathf.Clamp(limit, 0, Int32.MaxValue); animation.playbackLimit = animation.playbackLimit > limit ? (int)Mathf.Floor(limit) : (int)Mathf.Ceil(limit); } // 时间间隔 float duration = ui.GUIFloat("Duration", animation.duration, "f2"); animation.duration = Mathf.Clamp(duration, 0, float.MaxValue); // 事件触发方向 if(animation.playbackStyle == TweenAnimation.PlaybackStyle.PingPong) { animation.eventTriggeredDirection = (TweenAnimation.EventTriggeredDirection)ui.GUIEnumMask("Event Direction", animation.eventTriggeredDirection); } // 是否触发事件 animation.triggerEvents = ui.GUIToggle("Trigger Events", animation.triggerEvents); // 是否只触发一次 animation.triggerOnce = ui.GUIToggle("Trigger Once", animation.triggerOnce); // ITweenEventHandler animation.scriptHandler = ui.GUIToggle("Event Handler", animation.scriptHandler); GUILayout.Space(5); // 时间轴 DrawRuler(1f, true); // default event callbacks DefaultEventCallbacks(); // event list EventList(); } DrawBgWire(rect, (m_ShowAnimationTab ? 4 : -2)); if(!m_ShowAnimationTab) GUILayout.Space(-4); else GUILayout.Space(2); } void AddNewModule() { Rect rect = ui.GetControlRect(20); Rect labelRect = rect; labelRect.y += 1; GUI.Label(labelRect, "Modules", styles.textBold); rect.x = rect.x + rect.width - 18; rect.width = 20; rect.height = 20; //if (GUI.Button(rect, "", styles.plus)) { if(s_TweenModuleClasses == null) { s_TweenModuleClasses = new Dictionary(); // 获得TweenModule的派生类 var classes = Assembly .GetAssembly(typeof(TweenModule)) .GetTypes() .Where(t => t.IsSubclassOf(typeof(TweenModule))); foreach(var itor in classes) { string name = itor.Name; Type type = itor; s_TweenModuleClasses.Add(name, type); } s_TweenModuleClassNames = null; } if(s_TweenModuleClassNames == null) { s_TweenModuleClassNames = new string[s_TweenModuleClasses.Count]; int i = 0; var itor = s_TweenModuleClasses.GetEnumerator(); while(itor.MoveNext()) { s_TweenModuleClassNames[i++] = itor.Current.Key; } } int selected = EditorGUI.Popup(rect, -1, s_TweenModuleClassNames, styles.plus); if(selected >= 0 && selected < s_TweenModuleClassNames.Length) { string name = s_TweenModuleClassNames[selected]; Type type = s_TweenModuleClasses[name]; var module = Activator.CreateInstance(type); animation.AddModule(module as TweenModule); } } GUILayout.Space(-4); } void AnimationModules() { if (animation.modules == null) return; for(int i = 0; i < animation.modules.Count; ++i) { TweenModule module = animation.modules[i]; AnimaitonModule(module, i == animation.modules.Count - 1); } } void AnimaitonModule(TweenModule module, bool isLast) { if (module == null) return; Event e = Event.current; string name = module.name; string description = module.description; bool enabled = module.enabled; // header Rect rect = ui.GetControlRect(20); Rect checkRect = rect; checkRect.x += 1; checkRect.y += 1; checkRect.width = 20; checkRect.height = 20; Rect headerRect = rect; headerRect.x -= 2; headerRect.width += 8; Rect deleteRect = rect; deleteRect.x = rect.x + rect.width - 18; deleteRect.width = 20; deleteRect.height = 20; //if (Event.current.type == EventType.MouseDown || Event.current.type == EventType.MouseUp || Event.current.type == EventType.Layout) //{ // module.enabled = GUI.Toggle(checkRect, module.enabled, "", styles.checkmark); // if (GUI.Button(deleteRect, "-", styles.textBoldBig)) // animation.RemoveModule(module); // if (GUI.Button(headerRect, "", styles.headerBg)) // module.unfold = !module.unfold; //} //else if (Event.current.type == EventType.Repaint /*|| Event.current.type == EventType.Layout*/) //{ // GUI.Button(headerRect, "", styles.headerBg); // GUI.Toggle(checkRect, enabled, "", styles.checkmark); // GUI.Button(deleteRect, "-", styles.textBoldBig); //} //https://answers.unity.com/questions/1562998/how-to-draw-two-button-overlap-in-editor-window-an.html Rect headerRegion = headerRect; headerRegion.x += 20; headerRegion.width -= 50; GUI.enabled = !(e.isMouse && !headerRegion.Contains(e.mousePosition)); if (GUI.Button(headerRect, "", styles.headerBg)) module.unfold = !module.unfold; GUI.enabled = true; module.enabled = GUI.Toggle(checkRect, module.enabled, "", styles.checkmark); if (GUI.Button(deleteRect, "-", styles.textBoldBig)) animation.RemoveModule(module); Vector2 size = styles.text.CalcSize(new GUIContent(name)); GUI.Label(new Rect(rect.x + 15, rect.y + 2, size.x, 20), name, styles.text); // content if (module.unfold) { GUILayout.Space(-3); MethodInfo method; string classname = module.GetType().Name; if (!s_TweenModuleGUI.TryGetValue(classname, out method)) { Type type = this.GetType(); method = type.GetMethod("OnInspector_" + classname); s_TweenModuleGUI.Add(classname, method); } if (method != null) { object[] parameter = new object[]{ module}; method.Invoke(this, parameter); } else { Debug.LogError("没有对应的绘制函数" + "OnInspector_" + classname); } } // wire DrawBgWire(rect, module.unfold ? 4 : -2); if (!module.unfold) GUILayout.Space(-4); else GUILayout.Space(2); if (!isLast) GUILayout.Space(-1); } void DrawBgWire(Rect firstRect, int hOff) { Rect bgRect = ui.GetLastControlRect(); bgRect.height = bgRect.y + bgRect.height - firstRect.y + hOff/*(m_ShowAnimationTab ? 4 : -2)*/; bgRect.x = firstRect.x - 3; bgRect.y = firstRect.y - 1; bgRect.width = firstRect.width + 5; GUI.Label(bgRect, "", styles.bgSmall); } // 绘制时间轴 void DrawRuler(float alpha, bool bLabels) { if (animation.duration <= 0) return; float duration = animation.duration; Rect timelineRect = ui.GetControlRect(110); bool bRepaint = Event.current.type == EventType.Repaint; GUI.BeginGroup(timelineRect); // draw ruler Rect rulerRect = new Rect(5, 5, timelineRect.width - 10, 60); Rect bgRect = rulerRect; EditorGUI.DrawRect(bgRect, new Color(1, 1, 1, 1f) * 0.7f); int rulerWidth = (int)rulerRect.width; styles.defaultUIMaterail.SetPass(0); GL.PushMatrix(); GL.LoadPixelMatrix(); bool bWin = Application.platform == RuntimePlatform.WindowsEditor; if (bWin) GL.Begin(GL.QUADS); else GL.Begin(GL.LINES); //ui.DrawVerticalLineFast(rulerRect.x, 0, rulerRect.height, Color.red); //ui.DrawVerticalLineFast(rulerRect.x + rulerRect.width, 0, rulerRect.height, Color.red); //ui.DrawHorizontalLineFast(rulerRect.y, rulerRect.x, rulerRect.x + rulerRect.width, Color.blue); //ui.DrawHorizontalLineFast(rulerRect.y + rulerRect.height, rulerRect.x, rulerRect.x + rulerRect.width, Color.blue); int yOffBase = (int)rulerRect.height + (int)rulerRect.y; if (bRepaint) ui.DrawHorizontalLineFast(yOffBase, rulerRect.x, rulerRect.x + rulerRect.width, Color.gray); int log = (int)Mathf.Floor(Mathf.Log10(duration)) ; float stepCount; int stepSlice = 5; // 每个stepBig分为几个小step do { float stepBig = Mathf.Pow(10, log); float stepSmall = stepBig / stepSlice; stepCount = duration / stepSmall; log--; stepSlice *= 2; } while (stepCount < 20); log++; stepSlice /= 2; int j = 1; while (stepCount > 30) { float stepBig = Mathf.Pow(10, log) * j * 2; float stepSmall = stepBig / stepSlice; stepCount = duration / stepSmall; ++j; //Debug.Log("stepBig=" + stepBig + ",stepSmall=" + stepSmall); } float stepPixelWidth = rulerWidth / stepCount; float timePerStep = duration / stepCount; if(bRepaint) { for (int i = 0; i <= stepCount; ++i) { int len = 15; if (i % stepSlice == 0) len = 30; ui.DrawVerticalLineFast(rulerRect.x + i * stepPixelWidth, yOffBase, yOffBase - len, Color.gray); } } // 绘制事件的竖线 //if (bRepaint && m_ShowEvents && animation.eventList != null) //{ // m_EventTimeSet.Clear(); // for (int i = 0; i < animation.eventList.Count; ++i) // { // TweenEvent e = animation.eventList[i]; // if (e != null) // { // if (!m_EventTimeSet.Contains(e.time)) // m_EventTimeSet.Add(e.time); // } // } // float y = rulerRect.y + 3; // Rect eventRect = new Rect(0, y, 15, 15); // foreach (float time in m_EventTimeSet) // { // float x = time / duration * rulerWidth; // eventRect.x = x + 5; // ui.DrawVerticalLineFast(eventRect.x, yOffBase, yOffBase - 50, Color.gray); // } //} // 绘制module时间 if(m_ShowModules && bRepaint) { int xStart = 5; int yStart = 25; for(int i = 0; i < animation.modules.Count; ++i) { TweenModule module = animation.modules[i]; if(/*module.unfold*/module.enabled) { float left = module.timeOffset / duration * rulerRect.width; float right = (module.timeOffset + module.duration) / duration * rulerRect.width; left = Mathf.Clamp(left, 0, rulerRect.width) + xStart; right = Mathf.Clamp(right, 0, rulerRect.width) + xStart; ui.DrawHorizontalLineFast(yStart + (rulerRect.height - 20) / animation.modules.Count * i, left, right, Color.white); } } } // 绘制playback竖线 if(bRepaint && (m_Play || m_Pause)) { float time = animation.HandleTime(playbackTime); float x = (time / animation.duration) * rulerRect.width + 5; ui.DrawVerticalLineFast(x, yOffBase, yOffBase - (rulerRect.height), Color.red); } GL.PopMatrix(); GL.End(); // draw labels if(bLabels) { for (int i = 0; i <= stepCount; ++i) { if (i % stepSlice == 0 || stepSlice == 10 && i % (stepSlice / 2) == 0) { float time = i * timePerStep; // sec string timeStr = time == 0 ? "0" : time.ToString("f1"); Vector2 size = styles.text.CalcSize(new GUIContent(timeStr)); GUI.Label(new Rect(rulerRect.x + i * stepPixelWidth - size.x / 2, yOffBase + 2, 50, 15), timeStr, styles.text ); } } } // draw events if(m_ShowEvents && animation.eventList != null) { m_EventTimeSet.Clear(); for (int i = 0; i < animation.eventList.Count; ++i) { TweenEvent e = animation.eventList[i]; if(e != null) { if(!m_EventTimeSet.Contains(e.time)) m_EventTimeSet.Add(e.time); } } float y = rulerRect.y - 1; Rect eventRect = new Rect(0, y, 15, 15); foreach (float time in m_EventTimeSet) { float x = time / duration * rulerWidth - 15 / 4; eventRect.x = x; if (GUI.Button(eventRect, TweenModuleGUIStyles.eventIcon, styles.text)) { m_SelectedEventTime = time; } ui.DrawVerticalLineFast(eventRect.x, yOffBase, yOffBase - eventRect.y, Color.gray); } } // buttons // 控制时间轴显示 { Rect buttonRect = new Rect(5 + rulerRect.width - 20 * 2, yOffBase + 20, 20, 20); Color pressedColor = Color.gray; Rect eventRect = buttonRect; Color col = GUI.color; if (m_ShowEvents) GUI.color = pressedColor; if (GUI.Button(eventRect, TweenModuleGUIStyles.eventIcon, EditorStyles.miniButtonLeft)) { m_ShowEvents = !m_ShowEvents; if (!m_ShowEvents) m_SelectedEventTime = -1; } GUI.color = col; Rect moduleRect = buttonRect; moduleRect.x += 20; col = GUI.color; if (m_ShowModules) GUI.color = pressedColor; if (GUI.Button(moduleRect, TweenModuleGUIStyles.moduleIcon, EditorStyles.miniButtonRight)) m_ShowModules = !m_ShowModules; GUI.color = col; } // 播放、暂停等 { Rect buttonRect = new Rect(5 , yOffBase + 20, 30, 20); Rect eventRect = buttonRect; Color pressedColor = Color.gray; Rect playRect = buttonRect; Color col = GUI.color; if (m_Play) GUI.color = pressedColor; if (GUI.Button(eventRect, "Play", styles.miniLeft)) { m_Play = !m_Play; if(m_Play) { m_Pause = m_Stop = false; EditorPlay(); m_PlaybackTimer.Resume(); } } GUI.color = col; Rect pauseRect = buttonRect; pauseRect.x += buttonRect.width; col = GUI.color; if (m_Pause) GUI.color = pressedColor; if (GUI.Button(pauseRect, "Pause", styles.miniMid)) { m_Pause = !m_Pause; if (m_Pause) { if(!m_Play) { EditorPlay(); m_PlaybackTimer.Resume(); } m_PlaybackTimer.Pause(); m_Play = m_Stop = false; } } GUI.color = col; Rect stopRect = buttonRect; stopRect.x += 2 * buttonRect.width; if (GUI.Button(stopRect, "Stop", styles.miniRight)) { EditorStop(); m_PlaybackTimer.Stop(); m_Pause = m_Play = false; } } // playback time { if (m_Play || m_Pause) { Rect playbackTimeRect = new Rect(100, yOffBase + 23, 90, 20); ; GUI.Label(playbackTimeRect, "Playback Time: " + playbackTime.ToString("f1") + "s", styles.text); } } // 拖拽进度 DragPlay(rulerRect); GUI.EndGroup(); } void DefaultEventCallbacks() { Rect evetRect = ui.GetControlRect(15); Rect labelRect = evetRect; labelRect.x += 2; GUI.Label(labelRect, "Callbacks", styles.textBold); EventCallback("onStart"); EventCallback("onEnd"); EventCallback("onTurnStart"); EventCallback("onTurnEnd"); } void EventCallback(string callbackName) { Rect rect = ui.GetControlRect(15); Rect labelRect = rect; labelRect.x += 20; GUI.Label(labelRect, callbackName, styles.text); Rect unfoldRect = rect; unfoldRect.x = rect.x + 5/*+ rect.width - 19*/; unfoldRect.width = 13; unfoldRect.height = 13; unfoldRect.y += 2; bool unfold; if(!m_CallbackUnfold.TryGetValue(callbackName, out unfold)) { m_CallbackUnfold.Add(callbackName, false); } m_CallbackUnfold[callbackName]= GUI.Button(unfoldRect, (m_CallbackUnfold[callbackName] ? "▲" : "▼"), styles.textSmall) ? !m_CallbackUnfold[callbackName] : m_CallbackUnfold[callbackName]; if(m_CallbackUnfold[callbackName]) { var prop = serializedObject.FindProperty(callbackName); if(prop != null) { EditorGUILayout.PropertyField(prop); } } } void EventList() { Rect evetRect = ui.GetControlRect(15); Rect labelRect = evetRect; labelRect.x += 2; GUI.Label(labelRect, "Events", styles.textBold); Rect addButton = evetRect; addButton.x = evetRect.x + evetRect.width - 20; if(GUI.Button(addButton, "+", styles.textBoldBig)) { animation.AddEvent(new TweenEvent()); } if (animation.eventList == null || animation.eventList.Count == 0) return; for(int i = 0; i < animation.eventList.Count; ++i) { DrawEvent(animation.eventList[i], i); } } void DrawEvent (TweenEvent e, int index) { Rect rect = ui.GetControlRect(15); // delete button Rect deleteButton = rect; deleteButton.x = rect.x + rect.width - 19; deleteButton.width = 10; deleteButton.y -= 2; if (GUI.Button(deleteButton, "-", styles.textBoldBig)) { animation.RemoveEvent(e); return; } // event time Rect timeLabelRect = rect; timeLabelRect.x += 20; timeLabelRect.width = 30; timeLabelRect.height = 13; GUI.Label(timeLabelRect, "time:", e.time == m_SelectedEventTime ? styles.textBold : styles.text); Rect timeRect = timeLabelRect; timeRect.x += 25; float time = EditorGUI.FloatField(timeRect, e.time, styles.floatfiled); time = Mathf.Clamp(time, 0, float.MaxValue); e.time = time; // event name Rect nameLabelRect = rect; nameLabelRect.x += 150; nameLabelRect.width = 100; nameLabelRect.height = 13; GUI.Label(nameLabelRect, "name:", styles.text); Rect nameRect = nameLabelRect; nameRect.x += 32; e.name = EditorGUI.TextField(nameRect, e.name, styles.textField); // 折叠 Rect unfoldRect = rect; unfoldRect.x += 5; unfoldRect.width = 13; unfoldRect.height = 13; unfoldRect.y += 2; e.unfold = GUI.Button(unfoldRect, (e.unfold ? "▲" : "▼"), styles.textSmall) ? !e.unfold : e.unfold; if(e.unfold) { SerializedProperty eventHandler = null; SerializedProperty list = serializedObject.FindProperty("eventList"); for (int i = 0; i < list.arraySize; ++i) { if (i != index) continue; SerializedProperty ent = list.GetArrayElementAtIndex(i); if (ent != null) { foreach(var property in ent) { SerializedProperty prop = property as SerializedProperty; if(prop != null && prop.name == "eventHandler") { eventHandler = prop; break; } } } break; } if(eventHandler != null) { EditorGUILayout.PropertyField(eventHandler); } } } static EditorWindow s_SceneView; private void OnSceneGUI() { } } }