当前位置:   article > 正文

【Unity】Editor常用_untiy editor代码

untiy editor代码

持续更新中。。

Inspector面板

编辑

  1. using (new EditorGUILayout.VerticalScope("box")) 加个框
  2. type = (enumType)EditorGUILayout.EnumPopup("枚举", type);

重写代码面板

复合元素 要显示在面板使用[serializable]
比如一个类 就是这么简单,但加了之后就不能为Null了

  1. [Serializable]
  2. public class TestClass
  3. {
  4. public enum TestType
  5. {
  6. a,
  7. b,
  8. }
  9. public TestType testType;
  10. public bool testBool = false;
  11. public float testFloat = 1;
  12. }

如果要重绘这个类...使用CustomPropertyDrawer,

  1. [CustomPropertyDrawer(typeof(UScript))]
  2. public class UScriptDrawer : PropertyDrawer
  3. {
  4. public override void OnGUI(Rect pos, SerializedProperty property, GUIContent label)
  5. {
  6. var targetObj = property.serializedObject.targetObject;
  7. var asset = targetObj as 持有他的对象类型(如Asset);
  8. }
  9. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
  10. {
  11. //如有需要返回Height
  12. }
  13. }

用代码重新绘制了Inspector,并赋值,所以注意不能使用Ctrl+Z,要做Undo?...


如加个按钮

  1. [CustomEditor(typeof(UrScript))]
  2. public class UrScriptEditor : Editor
  3. {
  4. UrScript script;
  5. private void OnEnable()
  6. {
  7. script = (UrScript)target;
  8. }
  9. public override void OnInspectorGUI()
  10. {
  11. base.OnInspectorGUI();
  12. serializedObject.Update();
  13. EditorGUI.BeginChangeCheck();
  14. if (script.boolValue)
  15. {
  16. EditorGUI.indentLevel++;
  17. EditorGUILayout.LabelField("Label面板提示1");
  18. EditorGUILayout.HelpBox("HelpBox面板提示2", MessageType.Warning);
  19. EditorGUILayout.PropertyField(任意变量);
  20. EditorGUI.indentLevel--;
  21. }
  22. else
  23. {
  24. EditorGUILayout.LabelField("面板提示");
  25. 数组变量.arraySize = EditorGUILayout.IntField("Length", uscript.arraySize);
  26. for (int i = 0; i < 数组变量.arraySize; i++)
  27. EditorGUILayout.PropertyField(数组变量.GetArrayElementAtIndex(i));
  28. }
  29. if (GUILayout.Button("创建对象"))
  30. {
  31. TestScript myScript = (TestScript)target;
  32. Debug.LogError(myScript.self.gameObject.GetComponent<Light>());
  33. }
  34. if (EditorGUI.EndChangeCheck())
  35. UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
  36. serializedObject.ApplyModifiedProperties();
  37. }
  38. }

绘制套娃

  1. Editor newEditor;
  2. public override void OnInspectorGUI()
  3. {
  4. serializedObject.Update();
  5. EditorGUI.BeginChangeCheck();
  6. EditorGUILayout.PropertyField(m_Prop, new GUIContent("套娃对象"));
  7. if (EditorGUI.EndChangeCheck() || assetEditor == null)
  8. {
  9. newEditor= CreateEditor(m_Prop.objectReferenceValue);
  10. }
  11. newEditor.OnInspectorGUI();
  12. serializedObject.ApplyModifiedProperties();
  13. }

选择层

                FindProperty("xx1").FindPropertyRelative("xx2").intValue = UnityEditorInternal.InternalEditorUtility.ConcatenatedLayersMaskToLayerMask(EditorGUILayout.MaskField("", UnityEditorInternal.InternalEditorUtility.LayerMaskToConcatenatedLayersMask(xx2), UnityEditorInternal.InternalEditorUtility.layers));

自定义Attribute

发现了自定义更简洁的方法  参考Odin插件,
新增LabelText标签。可以放到属性上,显示出中文

  1. public class LabelTextAttribute:PropertyAttribute
  2. {
  3. public string label;
  4. public string showCondition;
  5. public string editorCondition;
  6. public LabelTextAttribute(string label, string showCondition = null, string editorCondition = null)
  7. {
  8. this.label = label;
  9. this.showCondition = showCondition;
  10. this.editorCondition = editorCondition;
  11. }
  12. }

自定义Drawer

还可以针对一些非Unity

  1. using UnityEditor;
  2. using UnityEngine;
  3. [CustomPropertyDrawer(typeof(LabelTextAttribute),false)]
  4. public class LabelDrawer : PropertyDrawer
  5. {
  6. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
  7. {
  8. var attr = attribute as LabelTextAttribute;
  9. label.text = attr.label;
  10. // 如果字段为false则不显示
  11. if (!string.IsNullOrEmpty(attr.showCondition))
  12. {
  13. var field = property.serializedObject.FindProperty(attr.showCondition);
  14. if (!field.boolValue)
  15. return;
  16. }
  17. // 是否能编辑
  18. var notEdit = false;
  19. if (!string.IsNullOrEmpty(attr.editorCondition))
  20. {
  21. if (attr.editorCondition == "false")
  22. notEdit = true;
  23. }
  24. if (notEdit)
  25. GUI.enabled = false;
  26. EditorGUI.PropertyField(position, property, label);
  27. if (notEdit)
  28. GUI.enabled = true;
  29. }
  30. //如果不显示返回没间隔
  31. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
  32. {
  33. LabelTextAttribute labelAttr = (LabelTextAttribute)attribute;
  34. var showCondition = property.serializedObject.FindProperty(labelAttr.showCondition);
  35. if (showCondition != null)
  36. {
  37. bool show = showCondition.boolValue;
  38. if (!show)
  39. return -EditorGUIUtility.standardVerticalSpacing;
  40. }
  41. return EditorGUI.GetPropertyHeight(property, label);
  42. }
  43. }

资源面板重写

场景:[CustomEditor(typeof(UnityEditor.SceneAsset))]
txt/byte   :  TextAsset
模型  :  ?
文件夹和一些unity不支持的文件类型:[CustomEditor(typeof(UnityEditor.DefaultAsset))]

  1. public override void OnInspectorGUI()
  2. {
  3. string path = AssetDatabase.GetAssetPath(target);
  4. if (path.EndsWith(".bytes") && path.Contains("Animations"))
  5. {
  6. using (BinaryReader reader = new BinaryReader(File.Open(path, FileMode.Open)))
  7. {
  8. int length = reader.ReadInt32();
  9. EditorGUILayout.LabelField("长度:" + length);
  10. for (int i = 0; i < length; i++)
  11. {
  12. EditorGUILayout.LabelField("X:" + (reader.ReadInt32() / 10000f).ToString() +
  13. " Y:" + (reader.ReadInt32() / 10000f).ToString() +
  14. " Z:" + (reader.ReadInt32() / 10000f).ToString());
  15. if (i != 0 && (i + 1) % 10 == 0)
  16. EditorGUILayout.LabelField("----" + (i + 1) + "----");
  17. }
  18. }
  19. }
  20. else
  21. base.OnInspectorGUI();
  22. }

EditorWindow(自定义窗口)

GUIStyle和Icon 查看器

https://github.com/756915370/UnityEditorExtension

EditorGUI

需要Rect、带输入、有默认间隔

GUILayout

不需要Rect、自动排布、不带输入、所以没有很大间隔、自定义间隔
一般结合BeginArea、BeginScrollView、BeginHorizontal、BeginVertical使用

EditorGUILayout

带输入,EditorGUILayout会间隔比较大

包围框
            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
            EditorGUILayout.EndVertical();

附加侧边小按钮

  1. Operation op = (Operation)EditorGUILayout.EnumPopup(GUIContent.none, op, "ShurikenDropdown");
  2. switch (op)
  3. {
  4. case Operation.one:
  5. break;
  6. case Operation.two:
  7. break;
  8. }

Undo

类似打快照一样
Undo - Unity 脚本 API

拓展选项编辑

  1. //继承IHasCustomMenu
  2. public void AddItemsToMenu(GenericMenu menu) {
  3. menu.AddItem(new GUIContent("A"), false, () => { Debug.Log("A"); });
  4. }

使用unity内置样式和Icon

  1. GUIStyle HelpStyle = new GUIStyle("IconButton");
  2. HelpStyle.normal.background = EditorGUIUtility.FindTexture("d__Help");

添加一个小问号

  1. void ShowButton(Rect position)
  2. {
  3. if (lockButtonStyle == null)
  4. lockButtonStyle = new GUIStyle();
  5. lockButtonStyle.normal.background = EditorGUIUtility.FindTexture("d__Help");
  6. if (GUI.Button(position, GUIContent.none, lockButtonStyle))
  7. {
  8. }
  9. }

分上下区域

  1. public class MyEditorWindow : EditorWindow
  2. {
  3. private float splitBarHeight = 5.0f; //拖动条的高度
  4. private float topPanelHeight = 300.0f; //上部分区域的初始高度
  5. [MenuItem("Window/My Editor Window")]
  6. static void ShowWindow()
  7. {
  8. EditorWindow.GetWindow(typeof(MyEditorWindow));
  9. }
  10. void OnGUI()
  11. {
  12. // 上部分区域
  13. Rect topPanelRect = new Rect(0, 0, position.width, topPanelHeight);
  14. GUILayout.BeginArea(topPanelRect, GUI.skin.box);
  15. GUILayout.Label("Top Panel");
  16. GUILayout.EndArea();
  17. // 拖动条区域
  18. Rect splitBarRect = new Rect(0, topPanelHeight, position.width, splitBarHeight);
  19. EditorGUIUtility.AddCursorRect(splitBarRect, MouseCursor.ResizeVertical);
  20. if (Event.current.type == EventType.MouseDown && splitBarRect.Contains(Event.current.mousePosition))
  21. {
  22. GUIUtility.hotControl = GUIUtility.GetControlID(FocusType.Passive);
  23. }
  24. if (GUIUtility.hotControl == GUIUtility.GetControlID(FocusType.Passive) && Event.current.isMouse)
  25. {
  26. topPanelHeight = Event.current.mousePosition.y;
  27. Repaint();
  28. }
  29. if (Event.current.rawType == EventType.MouseUp)
  30. {
  31. GUIUtility.hotControl = 0;
  32. }
  33. GUI.Box(splitBarRect, "");
  34. // 下部分区域
  35. Rect bottomPanelRect = new Rect(0, topPanelHeight + splitBarHeight, position.width, position.height - topPanelHeight - splitBarHeight);
  36. GUILayout.BeginArea(bottomPanelRect, GUI.skin.box);
  37. GUILayout.Label("Bottom Panel");
  38. GUILayout.EndArea();
  39. }
  40. }

画个横线

GUILayout.Label("", new GUIStyle { normal = new GUIStyleState { background = EditorGUIUtility.whiteTexture } }, GUILayout.Height(1));

预制

新预制系统说明

Scene ModePrefab Mode
Hierarchy 里操作的对象本质Prefab Instance 实例Assets里的UnityEngine.Object文件
Apply 保存过程从Instance写入到Asset里的Prefab文件直接操作Assets里的Prefab文件
SetDirty 脚本修改Prefab上的对象EditorUtility.SetDirty(Object)暂无(目前U3D问答上很多人在问)
设置场景DirtyEditorSceneManager.GetActiveScene()PrefabStageUtility.GetCurrentPrefabStage().scene)
Apply时的回调PrefabUtility.prefabInstanceUpdatedPrefabStage.prefabSaving


此外Project面板也分为one/two column也许也要写不同的代码?

 

输出选择的资源路径

Scene/Hierarchy   (prefab mode场景会get source失败,因为操作的是Instance不是prefab所以在)

  1. Object source = PrefabUtility.GetCorrespondingObjectFromSource (TObject componentOrGameObject);
  2. string path = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(source);

Prefab Mode

string path = UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage().prefabAssetPath;


Project      (本质操作Asset)

string assetPath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);

组件(例如Aniamtor)

AssetDatabase.GetAssetPath(Object);

当前选择目标

Selection.activeObject
 

修改prefab

直接修改Asset
使用AssetDatabase.LoadAssetAtPath后
直接操作:
    ①删Object使用GameObject.DestroyImmediate(go,true)
    ②改变脚本属性记得SetDirty否则保存可能无效

  1. static void MakePrefab()
  2. {
  3. string selectPath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);
  4. var files = Directory.GetFiles(selectPath, "*.prefab", SearchOption.AllDirectories);
  5. foreach (var path in files)
  6. {
  7. GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
  8. GameObject delect = prefab.transform.Find("delect");
  9. if (delect != null)
  10. {
  11. GameObject.DestroyImmediate(delect, true);
  12. AssetDatabase.SaveAssets();
  13. AssetDatabase.Refresh();
  14. }
  15. }
  16. }

问题:使用Asset如果删除一些组件要使用DestroyImmediate,当目标是一个ParticleSystem时,Material不会一同被删除,使用实例化解决。

prefab实例化不断链

首先实例化要用↓

  1. GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
  2. GameObject go = PrefabUtility.InstantiatePrefab(prefab);//如果使用Instantiate会丢失链接 因为算是clone

判断是否有Missing代码

         go.GetComponentsInChildren<MonoBehaviour>().Any(mono => mono == null))     

保存预制

bool succe = PrefabUtility.SaveAsPrefabAsset(inst, prefabPath);
这个时候想要进行一些删除或者SetParent操作,会提示不允许。
要断链root,使用PrefabUtility.UnpackPrefabInstanceAndReturnNewOutermostRoots(go, PrefabUnpackMode.OutermostRoot);

prefab监听改变
如果想要添加代码  有人也遇到和我一样的问题 想使用SaveAsPrefabAsset改 但是发现不起效 链接
根据我蹩脚的英语理解 官方技术人员的意思大概就是这个delegate不对SaveAsPrefabAsset起效 
要用SaveAsPrefabAssetAndConnect

 

习得Delay大发

  1. using UnityEditor;
  2. using UnityEngine;
  3. public class ApplyPrefab
  4. {
  5. [InitializeOnLoadMethod]
  6. static void StartInitializeOnLoadMethod()
  7. {
  8. PrefabUtility.prefabInstanceUpdated = delegate (GameObject instance)
  9. {
  10. //prefab保存的路径
  11. Debug.Log(PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(instance));
  12. };
  13. }
  14. }

Prefab保存多个

  1. [UnityEditor.MenuItem("Tools/Apply Selected Prefabs")]
  2. static void ApplySelectedPrefabs(){
  3. //获取选中的gameobject对象
  4. GameObject [] selectedsGameobject= Selection.gameObjects;
  5. GameObject prefab = PrefabUtility.FindPrefabRoot (selectedsGameobject[0]);
  6. for (int i = 0; i < selectedsGameobject.Length; i++) {
  7. GameObject obj=selectedsGameobject[i];
  8. UnityEngine.Object newsPref= PrefabUtility.GetPrefabObject(obj);
  9. //判断选择的物体,是否为预设
  10. if(PrefabUtility.GetPrefabType(obj) == PrefabType.PrefabInstance){
  11. UnityEngine.Object parentObject = PrefabUtility.GetPrefabParent(obj);
  12. //string path = AssetDatabase.GetAssetPath(parentObject);//获取路径
  13. PrefabUtility.ReplacePrefab(obj,parentObject,ReplacePrefabOptions.ConnectToPrefab);//替换预设
  14. AssetDatabase.Refresh();//刷新
  15. }
  16. }
  17. }


 

遍历所有文件包括子目录

  1. static void Scan()
  2. {
  3. ScanFolder(EditorUtil.GetSelectedFilePath());
  4. }
  5. static void ScanFolder(path)
  6. {
  7. DirectoryInfo folder = new DirectoryInfo(path);
  8. FileSystemInfo[] files = folder.GetFileSystemInfos();
  9. for(int i = 0;i<files.Length;i++)
  10. {
  11. if (files[i] is DirectoryInfo)
  12. ScanFolder(files[i].FullName);
  13. else
  14. ScanFile(files[i].FullName);
  15. }
  16. }
  17. static void ScanFile(path)
  18. {
  19. if(path.EndsWith("prefab"))
  20. {
  21. path = path.Substring(path.IndexOf("Assets"));
  22. GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
  23. }
  24. }

响应保存
 

  1. [InitializeOnLoad]
  2. internal class PrefabExtension
  3. {
  4. //依赖Instance修改,直接修改Asset无效
  5. static PrefabExtension()
  6. {
  7. PrefabUtility.prefabInstanceUpdated += OnPrefabInstanceUpdate;
  8. }
  9. static void OnPrefabInstanceUpdate(GameObject instance)
  10. {
  11. EditorApplication.delayCall += delegate()
  12. {
  13. if (instance == null)
  14. return;
  15. var asset = PrefabUtility.GetCorrespondingObjectFromSource(instance);
  16. if (asset == null)
  17. return;
  18. var assetPath = AssetDatabase.GetAssetPath(asset);
  19. PrefabUtility.prefabInstanceUpdated -= OnPrefabInstanceUpdate;
  20. //Do something
  21. PrefabUtility.SaveAsPrefabAssetAndConnect(instance, assetPath, InteractionMode.UserAction);
  22. PrefabUtility.prefabInstanceUpdated += OnPrefabInstanceUpdate;
  23. };
  24. }
  25. }
  26. public class Example : AssetPostprocessor
  27. {
  28. void OnPostprocessPrefab(GameObject g)
  29. {
  30. Debug.LogError(g.name);
  31. }
  32. [InitializeOnLoadMethod]
  33. static void StartInitializeOnLoadMethod()
  34. {
  35. PrefabUtility.prefabInstanceUpdated = delegate (GameObject instance)
  36. {
  37. //prefab保存的路径
  38. var sourrce = PrefabUtility.GetCorrespondingObjectFromSource(instance);
  39. var path = AssetDatabase.GetAssetPath(sourrce);
  40. if (PrefabUtility.IsAnyPrefabInstanceRoot(instance))
  41. PrefabUtility.UnpackPrefabInstance(instance, PrefabUnpackMode.OutermostRoot, InteractionMode.AutomatedAction);
  42. //do something
  43. };
  44. }
  45. }
  46. [InitializeOnLoad]
  47. public class PrefabSaveListener : UnityEditor.AssetModificationProcessor
  48. {
  49. private static string[] OnWillSaveAssets(string[] paths)
  50. {
  51. // 在这里可以添加一些逻辑代码,比如检查预制的属性或组件等
  52. foreach (string path in paths)
  53. {
  54. // Example: 更新预制体的某个属性
  55. var asset = AssetDatabase.LoadAssetAtPath<GameObject>(path);
  56. if (asset != null)
  57. {
  58. // 修改预制体的属性
  59. }
  60. }
  61. return paths;
  62. }
  63. }


 监听场景变换
 

  1. public class SaveScene :UnityEditor.AssetModificationProcessor {
  2. static public void OnWillSaveAssets(string[]names)
  3. {
  4. foreach (string name in names)
  5. {
  6. if (name.EndsWith (".unity")) {
  7. Scene scene = SceneManager.GetSceneByPath (name);
  8. Debug.Log ("保存场景 :" + scene.name);
  9. }
  10. }
  11. }
  12. }

 菜单栏

菜单添加与快捷键 

快捷键api参考:Unity - Scripting API: MenuItem

  1. public class testTools
  2. {
  3. [MenuItem("Tools/Hello %#a")]
  4. static void Hello()
  5. {
  6. Debug.Log("hello");
  7. }
  8. [MenuItem("Assets/Hello")] //如果放在Assets下右键文件夹也会出现
  9. static void Hello2()
  10. {
  11. Debug.Log("hello2");
  12. }
  13. }
  14. 快捷键
  15. % - CTRL
  16. # - Shift
  17. & - Alt
  18. LEFT/RIGHT/UP/DOWN - 光标键
  19. F1…F12
  20. HOME,END,PGUP,PDDN
  21. Example

清空控制台 

  1. public static void ClearConsole()
  2.     {
  3.         Assembly assembly = Assembly.GetAssembly(typeof(SceneView));
  4.         Type logEntries = assembly.GetType("UnityEditor.LogEntries");
  5.         MethodInfo clearConsoleMethod = logEntries.GetMethod("Clear");
  6.         clearConsoleMethod.Invoke(new object(), null);
  7.     }

   复制粘贴

  1. //复制
  2. GUIUtility.systemCopyBuffer = "test";
  3. //粘贴 或者 Ctrl+V
  4. string str = GUIUtility.systemCopyBuffer;

视图

刷新Scene视图
 

 SceneView.lastActiveSceneView.Repaint();

editor 每秒/改变时绘制

在Scene预览模型 例子:

  1. GameObject preview;
  2. void OnDrawGizmos()
  3. {
  4. if(!Application.isPlaying)
  5. {
  6. if(preview == null)
  7. Resources.Load<GameObject>("path");
  8. else
  9. {
  10. MeshFilter[] meshFilters = mPreview.GetComponentsInChildren<MeshFilter>();
  11. for(int i = 0; i < meshFilters.Length; i++)
  12. {
  13. Gizmos.DrawMesh(meshFilter[i].sharedMesh,
  14. transform.position + mPreview.transform.GetChild(i).position * scale,
  15. mPreview.transform.GetChild(i).rotation,
  16. transform.localScale)
  17. }
  18. }
  19. }
  20. }

其他

ScriptObject

  1. using UnityEngine;
  2. [CreateAssetMenu(fileName = "FileName", menuName = "ScriptableObjects/menuName", order = 1)]
  3. public class YourScriptObject : ScriptableObject
  4. {
  5. [Header("大小")]
  6. public int Size = 16;
  7. }

反射调用

  1. public class ReflectionTools
  2. {
  3. public static void Call(string typeName, string methodName, params object[] args)
  4. {
  5. Call<object>(typeName, methodName, args);
  6. }
  7. public static T Call<T>(string typeName, string methodName, params object[] args)
  8. {
  9. System.Type type = System.Type.GetType(typeName);
  10. T defaultValue = default(T);
  11. if (null == type) return defaultValue;
  12. System.Type[] argTypes = new System.Type[args.Length];
  13. for (int i = 0, count = args.Length; i < count; ++i)
  14. {
  15. argTypes[i] = null != args[i] ? args[i].GetType() : null;
  16. }
  17. MethodInfo method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, argTypes, null);
  18. if (null == method)
  19. {
  20. Debug.LogError(string.Format("method {0} does not exist!", methodName));
  21. return defaultValue;
  22. }
  23. object result = method.Invoke(null, args);
  24. if (null == result)
  25. return defaultValue;
  26. if (!(result is T))
  27. {
  28. Debug.LogError(string.Format("method {0} cast failed!", methodName));
  29. return defaultValue;
  30. }
  31. return (T)result;
  32. }
  33. }

还可以把asmdef命名为内部internal 例如Unity.InternalAPIEditorBridge.001
这样可以直接访问
InternalsVisibleToAttribute Class (System.Runtime.CompilerServices) | Microsoft Learn

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/585611
推荐阅读
相关标签
  

闽ICP备14008679号