using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using UnityEditor.Experimental.SceneManagement; using BF; using UnityEditor.SceneManagement; namespace BFEditor { [InitializeOnLoad] [CustomEditor(typeof(CharacterSpineHelper))] public class CharacterSpineHelperInspector : Editor { private const uint HASH_SEED = 31; private string m_CurAnimationName; CharacterSpineHelper helper; static CharacterSpineHelperInspector() { PrefabStage.prefabSaving += OnPrefabSaved; PrefabStage.prefabStageOpened += OnPrefabStageOpened; } void OnEnable() { helper = target as CharacterSpineHelper; } void OnDisable() { } private static void OnPrefabSaved(GameObject go) { var characterSpineHelper = go.GetComponent(); if (characterSpineHelper != null) { UpdateCharacterSpineHelper(go, characterSpineHelper); } } private static void OnPrefabStageOpened(PrefabStage go) { var characterSpineHelper = go.prefabContentsRoot.GetComponent(); if (characterSpineHelper != null) { UpdateCharacterSpineHelper(go.prefabContentsRoot, characterSpineHelper); } } private static void UpdateCharacterSpineHelper(GameObject go, CharacterSpineHelper characterSpineHelper) { if(Application.isPlaying) return; UpdateCharacterInfo(go, characterSpineHelper); } private static void UpdateCharacterInfo(GameObject go, CharacterSpineHelper characterSpineHelper) { bool dirty = false; Spine.Unity.SkeletonGraphic rootAni = go.transform.GetComponentInChildren(true); //主spine if (rootAni != null && characterSpineHelper.SpineObject != rootAni.gameObject) { characterSpineHelper.SpineObject = rootAni.gameObject; EditorUtility.SetDirty(characterSpineHelper); dirty = true; } //缓存挂点列表 ISet gameObjectSet = new HashSet(); //用于记录缓存数据 ISet hashNameSet = new HashSet(); //用于记录缓存数据 var list = characterSpineHelper.ObjectList; int len = list.Count; //遍历缓存数据 for (int i = 0; i < len; i++) { CharacterSpineObjectInfo info = list[i]; if (info.gameObject == null) { list.RemoveAt(i); i--; len--; dirty = true; EditorUtility.SetDirty(characterSpineHelper); continue; } else if (!gameObjectSet.Add(info.gameObject)) { list.RemoveAt(i); i--; len--; dirty = true; EditorUtility.SetDirty(characterSpineHelper); Debug.LogError("character list have the same object"); continue; } else if (!hashNameSet.Add(info.hashName)) { gameObjectSet.Remove(info.gameObject); list.RemoveAt(i); i--; len--; dirty = true; EditorUtility.SetDirty(characterSpineHelper); Debug.LogError("character list have the same hash name"); } } // 获取当前prefab数据,与缓存数据对比并更新 ISet prefabGameObjectSet = new HashSet();//用于记录prefab数据 ISet prefabHashNameSet = new HashSet();//用于记录prefab数据 Transform[] children = go.GetComponentsInChildren(true); int realChildNum = children.Length - 1; // 真实prefab数量 需要和缓存数据对比 foreach (Transform child in children) { //非主模型节点 if (child.name.CompareTo(go.name) != 0) { //记录prefab 用于对比判断 prefabGameObjectSet.Add(child.gameObject); if (!prefabHashNameSet.Add(BKDRHash(child.name))) { Debug.LogError("character list have the same hash name :" + child.name); } //有新增节点 if (!gameObjectSet.Contains(child.gameObject)) { CharacterSpineObjectInfo info; info.gameObject = child.gameObject; info.hashName = BKDRHash(child.name); gameObjectSet.Add(info.gameObject); if (hashNameSet.Add(info.hashName)) { characterSpineHelper.ObjectList.Add(info); dirty = true; EditorUtility.SetDirty(characterSpineHelper); } else { Debug.LogError("character list have the same hash name :" + child.name); } } //已有节点 else { //变更了名字 if (!hashNameSet.Contains(BKDRHash(child.name))) { var objList = characterSpineHelper.ObjectList; int listLen = objList.Count; for (int i = 0; i < listLen; i++) { if (objList[i].gameObject == child.gameObject) { CharacterSpineObjectInfo info; info.gameObject = child.gameObject; info.hashName = BKDRHash(child.name); objList[i] = info; dirty = true; EditorUtility.SetDirty(characterSpineHelper); break; } } } } } } //遍历缓存数据 删除不存在的节点 for (int i = 0; i < len; i++) { CharacterSpineObjectInfo info = list[i]; if (!prefabGameObjectSet.Contains(info.gameObject) || !hashNameSet.Contains(info.hashName)) { list.RemoveAt(i); i--; len--; dirty = true; EditorUtility.SetDirty(characterSpineHelper); Debug.Log("remove not exist info"); } } //check dirty if (dirty) { var prefabStage = PrefabStageUtility.GetPrefabStage(go); if (prefabStage != null) { EditorSceneManager.MarkSceneDirty(prefabStage.scene); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } } private void DrawNodeList() { for (int i = 0; i < helper.ObjectList.Count; ++i) { CharacterSpineObjectInfo info = helper.ObjectList[i]; if (info.gameObject == null) { helper.ObjectList.RemoveAt(i); i--; SetHelperDirty(); continue; } GUILayout.BeginHorizontal(); GUILayout.Label(info.gameObject.name, EditorStyles.label); EditorGUI.BeginDisabledGroup(true); GameObject go = (GameObject)EditorGUILayout.ObjectField("", info.gameObject, typeof(GameObject), true, GUILayout.Width(140)); EditorGUI.EndDisabledGroup(); GUILayout.EndHorizontal(); } } private void SetHelperDirty() { var prefabStage = PrefabStageUtility.GetPrefabStage(helper.gameObject); if (prefabStage != null) { EditorSceneManager.MarkSceneDirty(prefabStage.scene); } EditorUtility.SetDirty(helper); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } public override void OnInspectorGUI() { UpdateCharacterSpineHelper(helper.gameObject, helper); GUILayout.Space(10); GUILayout.BeginHorizontal(); GUILayout.Label("主Spine:", EditorStyles.label); EditorGUI.BeginDisabledGroup(true); GameObject go = (GameObject)EditorGUILayout.ObjectField("", helper.SpineObject, typeof(GameObject), true, GUILayout.Width(140)); EditorGUI.EndDisabledGroup(); GUILayout.EndHorizontal(); EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); GUILayout.Label("挂点列表:", EditorStyles.label); DrawNodeList(); EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); GUILayout.Label("动作列表:", EditorStyles.label); DrawAnimationList(); } private void DrawAnimationList() { GUIStyle normalStyle = new GUIStyle(GUI.skin.button);//普通style normalStyle.alignment = TextAnchor.MiddleLeft; GUIStyle highlightStyle = new GUIStyle(GUI.skin.button);//高亮style highlightStyle.alignment = TextAnchor.MiddleLeft; highlightStyle.normal.textColor = Color.blue; Spine.Unity.SkeletonGraphic skeletonGraphic = helper.transform.GetComponentInChildren(true); var list = skeletonGraphic.skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations; for (int i = 0; i < list.Count; i ++) { bool isPlay = list.Items[i].Name == m_CurAnimationName; GUILayout.BeginHorizontal(); if (GUILayout.Button(string.Format("动作:{0}==============时长:{1}s", list.Items[i].Name, list.Items[i].Duration), isPlay ? highlightStyle : normalStyle)) { } GUILayout.EndHorizontal(); } EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); } private static uint BKDRHash(string name) { uint h = 0; for (int i = 0; i < name.Length; ++i) { h = h * HASH_SEED + (byte)name[i]; } return h; } } }