【Unity】Editor常用

本文深入探讨Unity编辑器的高级定制技巧,包括Inspector面板重写、资源面板重写、自定义属性绘制器、EditorWindow自定义、预制系统操作及监听、场景变换监听等核心功能。同时介绍了如何通过代码实现Inspector界面元素的动态调整,以及如何利用反射调用和ScriptableObject进行数据存储。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

持续更新中。。

Inspector面板

 重写脚本面板

#if UNITY_EDITOR
[CustomEditor(typeof(UrScript))]
public class UrScriptEditor : Editor
{
    UrScript script;
    private void OnEnable()    {      script = (UrScript)target;    }

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        serializedObject.Update();
        EditorGUI.BeginChangeCheck();
        if (GUILayout.Button("Test"))
        {
        }
        if (script.boolValue)
        {
            EditorGUI.indentLevel++;
            EditorGUILayout.LabelField("Label面板提示1");
            EditorGUILayout.HelpBox("HelpBox面板提示2", MessageType.Warning);
            EditorGUILayout.PropertyField(任意变量);
            数组变量.arraySize = EditorGUILayout.IntField("Length", uscript.arraySize);
            for (int i = 0; i < 数组变量.arraySize; i++)
                EditorGUILayout.PropertyField(数组变量.GetArrayElementAtIndex(i));
            EditorGUI.indentLevel--;
        }
        using (new EditorGUILayout.VerticalScope("box")) 加个框
        type = (enumType)EditorGUILayout.EnumPopup("枚举", type);
        if (GUILayout.Button("创建对象"))
        {
            TestScript myScript = (TestScript)target;
            Debug.LogError(myScript.self.gameObject.GetComponent<Light>());
        }
        if (EditorGUI.EndChangeCheck())
                UnityEditorInternal.InternalEditorUtility.RepaintAllViews();

        serializedObject.ApplyModifiedProperties();
    }
}
#endif

序列化类 

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

[Serializable]
public class TestClass
{
    public enum TestType
    {
        a,
        b,
    }
    public TestType testType;
    public bool testBool = false;
    public float testFloat = 1;
}

单独重绘某个类...使用CustomPropertyDrawer,

[CustomPropertyDrawer(typeof(UScript))]
public class UScriptDrawer : PropertyDrawer
{
    public override void OnGUI(Rect pos, SerializedProperty property, GUIContent label)
    {
        var targetObj = property.serializedObject.targetObject;
        var asset = targetObj as 持有他的对象类型(如Asset);
    }
        
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        //如有需要返回Height
    }
}

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

Editor newEditor;
public override void OnInspectorGUI()
{
    serializedObject.Update();

    EditorGUI.BeginChangeCheck();
    EditorGUILayout.PropertyField(m_Prop, new GUIContent("套娃对象"));
    if (EditorGUI.EndChangeCheck() || assetEditor == null)
    {
        newEditor= CreateEditor(m_Prop.objectReferenceValue);
    }
    newEditor.OnInspectorGUI();

    serializedObject.ApplyModifiedProperties();
}

选择层

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

 根据Debug面板反序列化

ex:vfx

SerializedObject psSource = new SerializedObject(vfxAsset);
SerializedProperty property = psSource.FindProperty("m_Infos");
SerializedProperty ps = property.FindPropertyRelative("m_PropertySheet");

SerializedProperty no = ps.FindPropertyRelative("m_NamedObject");
SerializedProperty array = no.FindPropertyRelative("m_Array");
for (int j = array.arraySize - 1; j >= 0; j--)
{
    var value = array.GetArrayElementAtIndex(j).FindPropertyRelative("m_Value").objectReferenceValue;
    Debug.LogError(value.GetType() + " ,name:" + value.name);
}

自定义Attribute

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

public class LabelTextAttribute:PropertyAttribute
{
    public string label;
    public string showCondition;
    public string editorCondition;

    public LabelTextAttribute(string label, string showCondition = null, string editorCondition = null)
    {
        this.label = label;
        this.showCondition = showCondition;
        this.editorCondition = editorCondition;
    }
}

自定义Drawer

还可以针对一些非Unity类

using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(LabelTextAttribute),false)]
public class LabelDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        var attr = attribute as LabelTextAttribute;
        label.text = attr.label;

        // 如果字段为false则不显示
        if (!string.IsNullOrEmpty(attr.showCondition))
        {
            var field = property.serializedObject.FindProperty(attr.showCondition);
            if (!field.boolValue)
                return;
        }
        
        // 是否能编辑
        var notEdit = false;
        if (!string.IsNullOrEmpty(attr.editorCondition))
        {
            if (attr.editorCondition == "false")
                notEdit = true;
        }        
        if (notEdit)
            GUI.enabled = false;
        EditorGUI.PropertyField(position, property, label);
        
        if (notEdit)
            GUI.enabled = true;
    }

    //如果不显示返回没间隔
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        LabelTextAttribute labelAttr = (LabelTextAttribute)attribute;
        var showCondition = property.serializedObject.FindProperty(labelAttr.showCondition);
        if (showCondition != null)
        {
            bool show = showCondition.boolValue;
            if (!show)
                return -EditorGUIUtility.standardVerticalSpacing;
        }
        return EditorGUI.GetPropertyHeight(property, label);
    }
}

资源面板重写

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

    public override void OnInspectorGUI()
    {
        string path = AssetDatabase.GetAssetPath(target);
        if (path.EndsWith(".bytes") && path.Contains("Animations"))
        {
            using (BinaryReader reader = new BinaryReader(File.Open(path, FileMode.Open)))
            {
                int length = reader.ReadInt32();
                EditorGUILayout.LabelField("长度:" + length);
                for (int i = 0; i < length; i++)
                {
                    EditorGUILayout.LabelField("X:" + (reader.ReadInt32() / 10000f).ToString() +
                       "    Y:" + (reader.ReadInt32() / 10000f).ToString() +
                       "     Z:" + (reader.ReadInt32() / 10000f).ToString());
                    if (i != 0 && (i + 1) % 10 == 0)
                        EditorGUILayout.LabelField("----" + (i + 1) + "----");
                }
            }
        }
        else
            base.OnInspectorGUI();
    }

EditorWindow(自定义窗口)

GUIStyle和Icon 查看器

https://github.com/756915370/UnityEditorExtension

EditorGUI

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

GUILayout

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

EditorGUILayout

带输入,EditorGUILayout会间隔比较大

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

附加侧边小按钮

Operation op = (Operation)EditorGUILayout.EnumPopup(GUIContent.none, op, "ShurikenDropdown");
switch (op)
{
    case Operation.one:
        break;
    case Operation.two:
        break;
}

Undo

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

拓展选项编辑

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

使用unity内置样式和Icon

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

添加一个小问号

 void ShowButton(Rect position)
    {
        if (lockButtonStyle == null)
            lockButtonStyle = new GUIStyle();
        lockButtonStyle.normal.background = EditorGUIUtility.FindTexture("d__Help");
        if (GUI.Button(position, GUIContent.none, lockButtonStyle))        {        }
    }

分上下区域

public class MyEditorWindow : EditorWindow
{
    private float splitBarHeight = 5.0f; //拖动条的高度
    private float topPanelHeight = 300.0f; //上部分区域的初始高度

    [MenuItem("Window/My Editor Window")]
    static void ShowWindow()
    {
        EditorWindow.GetWindow(typeof(MyEditorWindow));
    }

    void OnGUI()
    {
        // 上部分区域
        Rect topPanelRect = new Rect(0, 0, position.width, topPanelHeight);
        GUILayout.BeginArea(topPanelRect, GUI.skin.box);
        GUILayout.Label("Top Panel");
        GUILayout.EndArea();

        // 拖动条区域
        Rect splitBarRect = new Rect(0, topPanelHeight, position.width, splitBarHeight);
        EditorGUIUtility.AddCursorRect(splitBarRect, MouseCursor.ResizeVertical);
        if (Event.current.type == EventType.MouseDown && splitBarRect.Contains(Event.current.mousePosition))
        {
            GUIUtility.hotControl = GUIUtility.GetControlID(FocusType.Passive);
        }
        if (GUIUtility.hotControl == GUIUtility.GetControlID(FocusType.Passive) && Event.current.isMouse)
        {
            topPanelHeight = Event.current.mousePosition.y;
            Repaint();
        }
        if (Event.current.rawType == EventType.MouseUp)
        {
            GUIUtility.hotControl = 0;
        }
        GUI.Box(splitBarRect, "");

        // 下部分区域
        Rect bottomPanelRect = new Rect(0, topPanelHeight + splitBarHeight, position.width, position.height - topPanelHeight - splitBarHeight);
        GUILayout.BeginArea(bottomPanelRect, GUI.skin.box);
        GUILayout.Label("Bottom Panel");
        GUILayout.EndArea();
    }
}

画个横线

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所以在)

Object source = PrefabUtility.GetCorrespondingObjectFromSource (TObject componentOrGameObject);
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否则保存可能无效

    static void MakePrefab()
    {
        string selectPath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);
        var files = Directory.GetFiles(selectPath, "*.prefab", SearchOption.AllDirectories);
        foreach (var path in files)
        {
            GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
            GameObject delect = prefab.transform.Find("delect");
            if (delect != null)
            {
                GameObject.DestroyImmediate(delect, true);
                AssetDatabase.SaveAssets();
                AssetDatabase.Refresh();
            }
        }
    }

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

prefab实例化不断链

首先实例化要用↓

GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
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大发

using UnityEditor;
using UnityEngine;

public class ApplyPrefab
{
	[InitializeOnLoadMethod]
    static void StartInitializeOnLoadMethod()
	{
		PrefabUtility.prefabInstanceUpdated = delegate (GameObject instance)
		{
			//prefab保存的路径
			Debug.Log(PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(instance));		
		};
	}
}

Prefab保存多个

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

遍历所有文件包括子目录

static void Scan()
{
    ScanFolder(EditorUtil.GetSelectedFilePath());
}
static void ScanFolder(path)
{
    DirectoryInfo folder = new DirectoryInfo(path);
    FileSystemInfo[] files = folder.GetFileSystemInfos();
    for(int i = 0;i<files.Length;i++)
    {
        if (files[i] is DirectoryInfo)
            ScanFolder(files[i].FullName);
        else
            ScanFile(files[i].FullName);
    }
}
static void ScanFile(path)
{
    if(path.EndsWith("prefab"))
    {
        path = path.Substring(path.IndexOf("Assets"));
        GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);        
    }
}

响应保存

[InitializeOnLoad]
internal class PrefabExtension
{
	//依赖Instance修改,直接修改Asset无效
    static PrefabExtension()
    {
        PrefabUtility.prefabInstanceUpdated += OnPrefabInstanceUpdate;
    }
    static void OnPrefabInstanceUpdate(GameObject instance)
    {
        EditorApplication.delayCall += delegate()
        {
            if (instance == null)
                return;
            var asset = PrefabUtility.GetCorrespondingObjectFromSource(instance);
            if (asset == null)
                return;
            var assetPath = AssetDatabase.GetAssetPath(asset);
            PrefabUtility.prefabInstanceUpdated -= OnPrefabInstanceUpdate;            
            //Do something
            PrefabUtility.SaveAsPrefabAssetAndConnect(instance, assetPath, InteractionMode.UserAction);
            PrefabUtility.prefabInstanceUpdated += OnPrefabInstanceUpdate;
        };
    }
}

public class Example : AssetPostprocessor
{
    void OnPostprocessPrefab(GameObject g)
    {
        Debug.LogError(g.name);
    }
    [InitializeOnLoadMethod]
    static void StartInitializeOnLoadMethod()
    {
        PrefabUtility.prefabInstanceUpdated = delegate (GameObject instance)
        {
            //prefab保存的路径
                var sourrce = PrefabUtility.GetCorrespondingObjectFromSource(instance);
                var path = AssetDatabase.GetAssetPath(sourrce);
                if (PrefabUtility.IsAnyPrefabInstanceRoot(instance))
                    PrefabUtility.UnpackPrefabInstance(instance, PrefabUnpackMode.OutermostRoot, InteractionMode.AutomatedAction);
//do something
        };
    }
}
[InitializeOnLoad]
public class PrefabSaveListener : UnityEditor.AssetModificationProcessor
{
    private static string[] OnWillSaveAssets(string[] paths)
    {
        // 在这里可以添加一些逻辑代码,比如检查预制的属性或组件等

        foreach (string path in paths)
        {
            // Example: 更新预制体的某个属性
            var asset = AssetDatabase.LoadAssetAtPath<GameObject>(path);
            if (asset != null)
            {
                // 修改预制体的属性
            }
        }
        return paths;
    }
}


 监听场景变换
 

public class SaveScene :UnityEditor.AssetModificationProcessor   { 
	static public void OnWillSaveAssets(string[]names)
	{ 
		foreach (string name in names) 
		{
			if (name.EndsWith (".unity")) {
				Scene scene = SceneManager.GetSceneByPath (name);
				Debug.Log ("保存场景 :" + scene.name);
			} 
		}
	} 
}

 UnityEditor

菜单添加

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

public class testTools
{
    [MenuItem("Tools/Hello %#a")] 
    static void Hello()
    {
        Debug.Log("hello");
    }

    [MenuItem("Assets/Hello")] //如果放在Assets下右键文件夹也会出现
    static void Hello2()
    {
        Debug.Log("hello2");
    }
}

快捷键
% - CTRL
# - Shift
& - Alt
LEFT/RIGHT/UP/DOWN - 光标键
F1…F12
HOME,END,PGUP,PDDN
Example

快捷键

全局添加

FieldInfo info = typeof(EditorApplication).GetField("globalEventHandler", BindingFlags.Static | BindingFlags.NonPublic);
        EditorApplication.CallbackFunction value = (EditorApplication.CallbackFunction)info.GetValue(null);
        value += HandleUnityEvent;
void ShortcutKey()
{
    if(recordShortcut) return;
    Event e = Event.current;
    inputKey.alt = e.alt;
    inputKey.shift = e.shift;
    inputKey.control = e.control;
    if (e.type == EventType.KeyUp)
    {
        inputKey.key = e.keyCode;
        if (inputKey == shortcutKeys[0])
        {
        GUI.FocusControl(null);
        DoThing();
        }
    }
}
void InitShortcutKey()
{
    isGlobalShortcutKey = PlayerPrefs.GetInt("UseGlobalShortKey", 0) == 0 ? false : true;
    for (int i = 0; i < buttonsName.Count; i++)
    {
    var saveValue = PlayerPrefs.GetString(buttonsName[i], "");
    var keyInfos = saveValue.Split('|');
    if (keyInfos.Length == 4)
    {
        var keyCode = int.TryParse(keyInfos[0], out var number) ? (KeyCode)number : 0;
        var alt = bool.Parse(keyInfos[1]);
        var control = bool.Parse(keyInfos[2]);
        var shift = bool.Parse(keyInfos[3]);
        shortcutKeys.Add(new Shortcutkey(keyCode, alt, control, shift));
    }
    else
    {
    shortcutKeys.Add(new Shortcutkey());
    }
    }
}
void DrawInputShortcutKey()
{
if (GUILayout.Toggle(isGlobalShortcutKey, "GlobalShortcutKey") != isGlobalShortcutKey)
{
isGlobalShortcutKey = !isGlobalShortcutKey;
PlayerPrefs.SetInt("GlobalShortcutKey", isGlobalShortcutKey ? 1 : 0);
}

for (int i = 0; i < buttonsName.Count; i++)
{
GUILayout.BeginHorizontal();
GUILayout.Label(buttonsName[i], editorWidth);
if (recordIndex == i)
{
var combination = recordKeyName + (recordKey.alt ? " + Alt" : "") +
                (recordKey.control ? " + Ctrl" : "") +
                (recordKey.shift ? " + Shift" : "");
    if (string.IsNullOrEmpty(combination))
        combination = "Input...";
GUILayout.Label(combination, editorWidth);
if (GUILayout.Button("Cancel", fourWidth))
{
recordIndex = -1;
}
}
else
{
var currentName = shortcutKeys[i].key +
            (shortcutKeys[i].alt ? " + Alt" : "") +
            (shortcutKeys[i].control ? " + Ctrl" : "") +
            (shortcutKeys[i].shift ? " + Shift" : "");
GUILayout.Label(currentName, editorWidth);
if (GUILayout.Button("Input", fourWidth))
{
recordIndex = i;
}
if (GUILayout.Button("Delect", fourWidth))
{
shortcutKeys[i] = new Shortcutkey();
PlayerPrefs.SetString(buttonsName[i], "0|false|false|false");
}
}

GUILayout.EndHorizontal();
}
RecordShortcutKey();

if (GUILayout.Button("退出"))
recordShortcut = false;
}

//非Shift Ctrl Alt抬起时结束
void RecordShortcutKey()
{
if (recordIndex == -1) return;

var keyEvent = Event.current;
recordKey.alt = keyEvent.alt;
recordKey.shift = keyEvent.shift;
recordKey.control = keyEvent.control;
if (Event.current.type == EventType.KeyDown)
{
if (keyEvent.keyCode != KeyCode.None &&
keyEvent.keyCode == KeyCode.LeftAlt || keyEvent.keyCode == KeyCode.RightAlt &&
keyEvent.keyCode == KeyCode.LeftControl || keyEvent.keyCode == KeyCode.RightControl &&
keyEvent.keyCode == KeyCode.LeftShift || keyEvent.keyCode == KeyCode.RightShift)
{
recordKey.key = keyEvent.keyCode;
recordKeyName = keyEvent.keyCode.ToString();
}
}
else if (Event.current.type == EventType.KeyUp)
{
recordKey.key = keyEvent.keyCode;
recordKeyName = "";
shortcutKeys[recordIndex] = recordKey;
var saveStr = $"{((int)recordKey.key).ToString()}|" +
$"{recordKey.alt.ToString()}|" +
$"{recordKey.control.ToString()}|" +
$"{recordKey.shift.ToString()}";
Debug.LogError("input: " + saveStr);
PlayerPrefs.SetString(buttonsName[recordIndex], saveStr);
recordIndex = -1;
Repaint();
}
}

清空控制台 

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

   复制粘贴

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

视图

刷新Scene视图

这里总感觉unity界面有bug,经常scene/game视图不能自动刷新

 SceneView.lastActiveSceneView.Repaint();

在Scene预览模型 

例子:
editor 每秒/改变时绘制

GameObject preview;
void OnDrawGizmos()
{
    if(!Application.isPlaying)
    {
        if(preview == null)
            Resources.Load<GameObject>("path");
        else
        {
            MeshFilter[] meshFilters = mPreview.GetComponentsInChildren<MeshFilter>();
            for(int i = 0; i < meshFilters.Length; i++)
            {
                Gizmos.DrawMesh(meshFilter[i].sharedMesh,
                transform.position + mPreview.transform.GetChild(i).position * scale,
                mPreview.transform.GetChild(i).rotation,
                transform.localScale)
            }
        }
    }
}

其他

ScriptObject

using UnityEngine;

[CreateAssetMenu(fileName = "FileName", menuName = "ScriptableObjects/menuName", order = 1)]
public class YourScriptObject : ScriptableObject
{
    [Header("大小")]
    public int Size = 16;
}

反射调用

    public class ReflectionTools
    {
        public static void Call(string typeName, string methodName, params object[] args)
        {
            Call<object>(typeName, methodName, args);
        }

        public static T Call<T>(string typeName, string methodName, params object[] args)
        {
            System.Type type = System.Type.GetType(typeName);
            T defaultValue = default(T);
            if (null == type) return defaultValue;

            System.Type[] argTypes = new System.Type[args.Length];
            for (int i = 0, count = args.Length; i < count; ++i)
            {
                argTypes[i] = null != args[i] ? args[i].GetType() : null;
            }
            MethodInfo method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, argTypes, null);
            if (null == method)
            {
                Debug.LogError(string.Format("method {0} does not exist!", methodName));
                return defaultValue;
            }
            object result = method.Invoke(null, args);
            if (null == result)
                return defaultValue;
            if (!(result is T))
            {
                Debug.LogError(string.Format("method {0} cast failed!", methodName));
                return defaultValue;
            }
            return (T)result;
        }
    }

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值