【Unity3D编辑器开发】Unity3D编辑器开发基础性框架结构【全面总结】

news2024/10/6 10:29:08

推荐阅读

  • CSDN主页
  • GitHub开源地址
  • Unity3D插件分享
  • 简书地址
  • 我的个人博客

大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。

一、前言

嗨,大家好,我是恬静的小魔龙

同学们国庆节好呀,放假了有没有好好学习呢。

最近学习了Unity3D编辑器方面的知识,在学习的过程中,发现一些比较容易混杂的点,特意总结了一下,方便自己和同学们学习,做了一份Unity3D编辑器开发脉络图,恳请敬请批评指正。

二、Unity3D编辑器开发

2-1、Unity3D编辑器开发脉络图

首先,放一张脉络图。
在这里插入图片描述
大图,可放大查看。

看着这张图,是不是感觉有些不知道从哪里开始看起好呢,接下来就来分析一下如何查看。

2-2、Unity3D编辑器开发分类

博主刚开始学习编辑器开发也是一脸懵的样子,为啥一会用OnGUI绘制窗口,一会用Editor绘制窗口,一会用EditorWindows绘制窗口,还有继承PropertyDrawer后进行属性绘制的。

它们之间有什么区别与联系呢。

  • 它们简单可以分成窗口绘制检视器绘制场景绘制属性绘制
  • 窗口绘制需要继承与EditorWindow类,然后在OnGUI里面进行窗口绘制。
  • 检视器绘制需要继承与Editor类,然后在OnInspectorGUI里面进行窗口绘制。
  • 场景绘制需要继承与Editor类,然后在OnSceneGUI里面进行绘制。
  • 属性绘制需要继承与PropertyDrawer类,然后在OnGUI里面进行绘制。
  • 还有一个检视器属性,这个单独来说。

这么一分析,是不是有些明了了,就是如果要绘制窗口的话,就需要继承EditorWindow类,然后在OnGUI里面进行窗口绘制。

重新绘制检视器窗口也就是Inspector窗口,或者场景Scene窗口就需要继承Editor类,然后在OnInspectorGUI里面进行Inspector窗口绘制,在OnSceneGUI里面进行Scene窗口绘制。

2-3、检视器属性

2-3-1、HideInInspector

介绍:可以隐藏公共成员变量,防止Inspector的值影响到他,同时保证脚本中变量的可访问度。

举个例子:

不加[HideInInspector]

using UnityEngine;

public class Test01 : MonoBehaviour
{
    public string Name;//注意这是public访问权限
}

在这里插入图片描述

加[HideInInspector]

using UnityEngine;

public class Test01 : MonoBehaviour
{
    [HideInInspector]
    public string Name;//注意这是public访问权限
}

在这里插入图片描述

2-3-2、SerializeField

介绍:将私有变量设置为检视面板可见可修改,Unity会将对象进行序列化存储,即使是私有的,标记为可序列化后也会显示,公有变量默认是可序列化的。

举个例子:

不加[SerializeField]

using UnityEngine;

public class Test01 : MonoBehaviour
{
    private string Name;
}

在这里插入图片描述

加[SerializeField]

using UnityEngine;

public class Test01 : MonoBehaviour
{
    [SerializeField]
    private string Name;
}

在这里插入图片描述

2-3-3、Space

介绍:在当前成员变量上方留 50 像素空白区域

举个例子:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    [Space]
    public string Name;
}

在这里插入图片描述

2-3-4、Header

介绍:在当前成员变量上方加入一个标题文字

举个例子:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    [Header("标题")]
    public string Name;
}

在这里插入图片描述

2-3-5、Tooltip

介绍:添加变量悬浮提示,当鼠标放入后会有提示

举个例子:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    [Tooltip("输入名字")]
    public string Name;
}

在这里插入图片描述

2-3-6、Range

介绍:给数值设定范围。

举个例子:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    [Range(0,10)]
    public int Age;
}

在这里插入图片描述

2-3-7、Multiline

介绍:指定输入行字符,参数为行数。

举个例子:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    [Multiline(5)]
    public string Name;
}

在这里插入图片描述

2-3-8、TextArea

介绍:设置默认显示 5 行,最多显示 10 行内容,再多用滚动条控制显示。

举个例子:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    [TextArea(5,10)]//(最小行数,最大行数)
    public string Name;
}

在这里插入图片描述

2-3-9、ContextMenu

介绍:在小齿轮中添加一个回调函数,参数为函数名称,用于调用该特性标记的方法。

举个例子:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    [ContextMenu("CallBack")]
    public void CallBackFun()
    {
        Debug.Log("回调函数");
    }
}

在这里插入图片描述
在这里插入图片描述

2-3-10、ContextMenuItem

介绍:给一个变量添加右键菜单,第一个参数是菜单名称,第二个参数是回调函数。

举个例子:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    [ContextMenuItem("点击调用函数", "CallBackFun")]
    public string Name;

    public void CallBackFun()
    {
        Debug.Log("回调函数");
    }
}

在这里插入图片描述

在这里插入图片描述

2-3-11、AddComponentMenu

介绍:在编辑器添加一个用于添加组件的菜单项,将拥有该属性的脚本添加到选中的物体上。第一个参数:分类名/组件名,第二个参数:列表中显示的顺序。

举个例子:

using UnityEngine;

[AddComponentMenu("点击添加组件函数")]
public class Test01 : MonoBehaviour
{
}

在这里插入图片描述

2-3-12、ExecuteInEditMode

介绍:使生命周期函数,在编辑器状态下可以执行,游戏中也可以正常使用,Update()在场景中对象发生变化或项目组织发生变化时会在编辑器下执行。也就是说,不在运行状态,也可以运行Start、Awake函数。

举个例子:

using UnityEngine;

[ExecuteInEditMode]
public class Test01 : MonoBehaviour
{
    private void Awake()
    {
        Debug.Log("Awake");
    }

    private void Start()
    {
        Debug.Log("Start");
    }

    private void Update()
    {
        Debug.Log("Update");
    }
}

在这里插入图片描述

2-3-13、RequireComponent

介绍:依赖、绑定,作用是,当我们把一个Script绑定到GameObject上时会同时把需要依赖的脚本也一起绑定(添加)上去。

举个例子:

using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class Test01 : MonoBehaviour
{
}

在这里插入图片描述

2-3-14、CanEditMultipleObjects

介绍:告诉 Unity 可以使用此编辑器来选择多个对象并同时更改所有对象。

举个例子:

新建脚本Test01.cs编辑代码:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    public int m_MyInt = 75;
    public Vector3 m_MyVector = new Vector3(20, 1, 0);
    public GameObject m_MyGameObject;
}

在Project视图中,Script文件夹内,新建Editor文件夹,在这个文件夹新建Test01Editor.cs脚本,编辑代码:

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(Test01))]
public class Test01Editor : Editor
{
    SerializedProperty m_IntProp;
    SerializedProperty m_VectorProp;
    SerializedProperty m_GameObjectProp;

    void OnEnable()
    {
        m_IntProp = serializedObject.FindProperty("m_MyInt");
        m_VectorProp = serializedObject.FindProperty("m_MyVector");
        m_GameObjectProp = serializedObject.FindProperty("m_MyGameObject");
    }

    public override void OnInspectorGUI()
    {
        EditorGUILayout.PropertyField(m_IntProp, new GUIContent("Int Field"), GUILayout.Height(20));
        EditorGUILayout.PropertyField(m_VectorProp, new GUIContent("Vector Object"));
        EditorGUILayout.PropertyField(m_GameObjectProp, new GUIContent("Game Object"));
        serializedObject.ApplyModifiedProperties();
    }
}

这时候,给多个对象挂载Test01脚本,然后选中多个对象修改脚本,就会显示Multi-object editing not supported.不支持多对象编辑
在这里插入图片描述
这时候修改Test01Editor.cs脚本:

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(Test01))]
[CanEditMultipleObjects]
public class Test01Editor : Editor
{
    SerializedProperty m_IntProp;
    SerializedProperty m_VectorProp;
    SerializedProperty m_GameObjectProp;

    void OnEnable()
    {
        m_IntProp = serializedObject.FindProperty("m_MyInt");
        m_VectorProp = serializedObject.FindProperty("m_MyVector");
        m_GameObjectProp = serializedObject.FindProperty("m_MyGameObject");
    }

    public override void OnInspectorGUI()
    {
        EditorGUILayout.PropertyField(m_IntProp, new GUIContent("Int Field"), GUILayout.Height(20));
        EditorGUILayout.PropertyField(m_VectorProp, new GUIContent("Vector Object"));
        EditorGUILayout.PropertyField(m_GameObjectProp, new GUIContent("Game Object"));
        serializedObject.ApplyModifiedProperties();
    }
}

就可以同时编辑多个对象了:
在这里插入图片描述

2-3-15、MenuItem

介绍:在顶部显示"工具"菜单。

举个例子:

新建脚本Test01.cs编辑代码:

using UnityEditor;
using UnityEngine;

public class Test01 : MonoBehaviour
{
    [MenuItem("Test/顶部菜单")]
    public static void CallBackFun()
    {

    }
}

在这里插入图片描述

2-3-16、CustomEditor

介绍:自定义编辑器,可以修改所关联组件检视面板的属性,然后重新绘制,Editor目录下建立编辑器脚本,将编辑器脚本与原始脚本关联。

举个例子:

新建脚本Test01.cs编辑代码:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    public int m_MyInt = 75;
    public Vector3 m_MyVector = new Vector3(20, 1, 0);
    public GameObject m_MyGameObject;
}

在Project视图中,Script文件夹内,新建Editor文件夹,在这个文件夹新建Test01Editor.cs脚本,编辑代码:

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(Test01))]
public class Test01Editor : Editor
{
    SerializedProperty m_IntProp;
    SerializedProperty m_VectorProp;
    SerializedProperty m_GameObjectProp;

    void OnEnable()
    {
        m_IntProp = serializedObject.FindProperty("m_MyInt");
        m_VectorProp = serializedObject.FindProperty("m_MyVector");
        m_GameObjectProp = serializedObject.FindProperty("m_MyGameObject");
    }

    // 用于重新绘制Inspector面板中的属性
    public override void OnInspectorGUI()
    {
        // 设置高度
        EditorGUILayout.PropertyField(m_IntProp, new GUIContent("Int Field"), GUILayout.Height(100));
        EditorGUILayout.PropertyField(m_VectorProp, new GUIContent("Vector Object"));
        EditorGUILayout.PropertyField(m_GameObjectProp, new GUIContent("Game Object"));
        serializedObject.ApplyModifiedProperties();
    }
}

在这里插入图片描述

2-4、窗口绘制

2-4-1、使用窗口绘制

好!到这里暂停一下,回想一下,还记得前文说的窗口绘制要继承什么类吗?




窗口绘制需要继承与EditorWindow类,然后在OnGUI里面进行窗口绘制。

继承EditorWindow类需要将脚本放到Editor脚本中,才能生效。

让我们在Editor文件夹中,新建Test02EditorWindow.cs脚本,编辑代码:

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class Test02EditorWindow : EditorWindow
{
    [MenuItem("工具/创建窗口")]
    static void OpenWindow()
    {
        //泛型T 窗口类型。必须派生自 EditorWindow。
        //第一个参数设置为 true 可创建浮动实用程序窗口,设置为 false 可创建正常窗口。
        //第三个参数设置是否为窗口提供焦点(如果已存在)。
        Test02EditorWindow window = GetWindow<Test02EditorWindow>(false, "弹窗标题", true);
        window.minSize = new Vector2(40, 30);
        window.minSize = new Vector2(80, 60);
    }

    //开窗口调用
    private void OnEnable()
    {
        Debug.Log("enable");
    }

    //关窗口调用
    private void OnDisable()
    {
        Debug.Log("disable");
    }

    //窗口开启就调用
    private void Update()
    {
        Debug.Log("update");
    }

    //用于绘制窗口内容
    private void OnGUI()
    {
        if (GUILayout.Button("测试点击"))
        {
            Debug.Log("测试点击");
        }
    }

    //场景结构发生变化,执行回调函数
    private void OnHierarchyChange()
    {
        Debug.Log("hierarchy");
    }

    //项目结构发生变化,执行回调函数
    private void OnProjectChange()
    {
        Debug.Log("project");
    }

    //选中物体发生变化,执行回调函数
    private void OnSelectionChange()
    {
        //获取当前选中的物体的名称
        Debug.Log(Selection.activeGameObject.name);
    }
}

编辑器编译通过后,在编辑器的菜单栏找到工具→创建窗口
在这里插入图片描述
这个就是渲染出来的窗口了:
在这里插入图片描述
绘制就在OnGUI里面,绘制的UI也是OnGUI支持的UI就可以了。

具体OnGUI怎么用,这里就不再赘述了。

这里再分享一些,常用的小功能代码。

2-4-2、检查开启mipmap的非2的幂贴图
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;

public class Test02EditorWindow : EditorWindow
{
    [MenuItem("工具/检查开启mipmap的非2的幂贴图")]
    static void OpenWindow()
    {
        Test02EditorWindow window = GetWindow<Test02EditorWindow>(false, "弹窗标题", true);
    }

    //用于绘制窗口内容
    private void OnGUI()
    {
        if (GUILayout.Button("检查开启mipmap的非2的幂贴图"))
        {
            CheckNPOT();
        }
    }

    private void CheckNPOT()
    {
        List<string> files = AssetDatabase.FindAssets("t:Texture").Select(AssetDatabase.GUIDToAssetPath).ToList();
        List<string> outputList = new List<string>();
        foreach (var file in files)
        {
            TextureImporter textureImporter = AssetImporter.GetAtPath(file) as TextureImporter;
            if (textureImporter)
            {
                //贴图为Sprite或设置了2的幂scale
                if (textureImporter.textureType == TextureImporterType.Sprite || textureImporter.npotScale != TextureImporterNPOTScale.None)
                {
                    continue;
                }
                //贴图长宽均为2的幂
                textureImporter.GetSourceTextureWidthAndHeight(out var width, out var height);
                if (IsPowerOfTwo(width) && IsPowerOfTwo(height))
                {
                    continue;
                }
                if (textureImporter.mipmapEnabled)
                {
                    outputList.Add(file);
                    Debug.Log(file);
                }
            }
        }
        WriteLog("NPOT.log", outputList);
    }

    private void WriteLog(string fileName, List<string> outputList)
    {
        if (!Directory.Exists(@"Logs"))
        {
            Directory.CreateDirectory(@"Logs");
        }
        if (!File.Exists("Logs/" + fileName))
        {
            using (FileStream fs = new FileStream("Logs/" + fileName, FileMode.CreateNew))
            {
            }
        }
        File.WriteAllLines("Logs/" + fileName, outputList);
    }

    private bool IsPowerOfTwo(int value)
    {
        return (value & (value - 1)) == 0;
    }
}
2-4-3、获取选中文件夹下的所有资源
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;

public class Test02EditorWindow : EditorWindow
{
    [MenuItem("工具/获取选中文件夹下的所有资源")]
    static void OpenWindow()
    {
        Test02EditorWindow window = GetWindow<Test02EditorWindow>(false, "获取选中文件夹下的所有资源", true);
    }

    //用于绘制窗口内容
    private void OnGUI()
    {
        if (GUILayout.Button("获取选中文件夹下的所有资源"))
        {
            List<string> pathList = new List<string>();
            Object[] m_objects = Selection.GetFiltered(typeof(Object), SelectionMode.Unfiltered | SelectionMode.DeepAssets);
            foreach (var obj in m_objects)
            {
                string path = AssetDatabase.GetAssetPath(obj);
                if (!pathList.Contains(path))
                {
                    pathList.Add(path);
                }
            }
            foreach (var item in pathList)
            {
                Debug.Log(item);
            }
        }
    }
}
2-4-4、删除prefab中missing的script
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;

public class Test02EditorWindow : EditorWindow
{
    [MenuItem("工具/删除prefab中missing的script")]
    static void OpenWindow()
    {
        Test02EditorWindow window = GetWindow<Test02EditorWindow>(false, "删除prefab中missing的script", true);
    }

    //用于绘制窗口内容
    private void OnGUI()
    {
        if (GUILayout.Button("删除prefab中missing的script"))
        {
            List<string> logList = new List<string>();
            List<string> prefabPathList = new List<string>();
            foreach (var prefabPath in prefabPathList)
            {
                if (EditorUtility.DisplayCancelableProgressBar("Processing", string.Format("{0} {1}/{2}",
                       prefabPath, prefabPathList.IndexOf(prefabPath), prefabPathList.Count),
                       prefabPathList.IndexOf(prefabPath) / (float)prefabPathList.Count))
                {
                    EditorUtility.ClearProgressBar();
                    return;
                }
                GameObject go = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
                if (go)
                {
                    int count = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(go);
                    if (count > 0)
                    {
                        GameObjectUtility.RemoveMonoBehavioursWithMissingScript(go);
                        logList.Add(string.Format("删除了{0}中的{1}个missing的script", prefabPath, count));
                    }
                }
            }
            EditorUtility.ClearProgressBar();
        }
    }
}

未完待续,欢迎补充。。。

2-5、检视器Inspector绘制

将普通的类关联Editor工具,实现特殊功能。

比如一个Test01.cs类是普通类,在Editor文件夹内新建一个Test01Editor.cs编辑类,Test01Editor.cs就是一个自定义编辑器,然后在Test01Editor.cs的OnInspectorGUI函数内进行属性的修改绘制。

接下来,一个老例子来说明一下如何使用:

举个例子:

新建脚本Test01.cs编辑代码:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    public int m_MyInt = 75;
    public Vector3 m_MyVector = new Vector3(20, 1, 0);
    public GameObject m_MyGameObject;
}

默认是这样的:

在这里插入图片描述

在Project视图中,Script文件夹内,新建Editor文件夹,在这个文件夹新建Test01Editor.cs脚本,编辑代码:

using UnityEngine;
using UnityEditor;

//CustomEditor 属性告知 Unity 应该作为哪个组件的编辑器。
[CustomEditor(typeof(Test01))]
public class Test01Editor : Editor
{
    SerializedProperty m_IntProp;
    SerializedProperty m_VectorProp;
    SerializedProperty m_GameObjectProp;

    void OnEnable()
    {
        m_IntProp = serializedObject.FindProperty("m_MyInt");
        m_VectorProp = serializedObject.FindProperty("m_MyVector");
        m_GameObjectProp = serializedObject.FindProperty("m_MyGameObject");
    }

    // 用于重新绘制Inspector面板中的属性
    public override void OnInspectorGUI()
    {
        // 设置高度
        EditorGUILayout.PropertyField(m_IntProp, new GUIContent("Int Field"), GUILayout.Height(100));
        EditorGUILayout.PropertyField(m_VectorProp, new GUIContent("Vector Object"));
        EditorGUILayout.PropertyField(m_GameObjectProp, new GUIContent("Game Object"));
        serializedObject.ApplyModifiedProperties();
    }
}

修改后是这样的:
在这里插入图片描述

2-6、场景Scene绘制

OnSceneGUI的运行方式很像 OnInspectorGUI,只不过在 Scene 视图中运行而已。

为了方便创建自己的编辑控件,可以使用在 Handles 类中定义的函数。

其中的所有函数都是为 3D 模式的 Scene 视图设计的。

举个例子:
新建脚本Test01.cs编辑代码:

using UnityEditor;
using UnityEngine;

[ExecuteInEditMode]
public class Test01 : MonoBehaviour
{
    public Vector3 lookAtPoint = Vector3.zero;

    public void Update()
    {
        transform.LookAt(lookAtPoint);
    }
}

在Project视图中,Script文件夹内,新建Editor文件夹,在这个文件夹新建Test01Editor.cs脚本,编辑代码:

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(Test01))]
[CanEditMultipleObjects]
public class Test01Editor : Editor
{
    SerializedProperty lookAtPoint;

    void OnEnable()
    {
        lookAtPoint = serializedObject.FindProperty("lookAtPoint");
    }

    public void OnSceneGUI()
    {
        var t = (target as Test01);

        EditorGUI.BeginChangeCheck();
        Vector3 pos = Handles.PositionHandle(t.lookAtPoint, Quaternion.identity);
        if (EditorGUI.EndChangeCheck())
        {
            Undo.RecordObject(target, "Move point");
            t.lookAtPoint = pos;
            t.Update();
        }
    }
}

在这里插入图片描述

2-7、属性Property绘制

用于从中派生自定义属性绘制器的基类。使用此基类可为您自己的 Serializable 类或者具有自定义 PropertyAttribute 的脚本变量创建自定义绘制器。

PropertyDrawer 有两种用途: 自定义 Serializable 类的每个实例的 GUI。 自定义具有自定义 PropertyAttribute 的脚本成员的 GUI。 如果您有自定义的 Serializable 类,可以使用 PropertyDrawer 来控制它在 Inspector 中的外观。

举个例子:
新建脚本Recipe.cs,编辑代码:

using System;
using UnityEngine;


public enum IngredientUnit { Spoon,Cup,Bowl,Piece}

[Serializable]
public class Ingredient
{
    public string name;
    public int amount = 1;
    public IngredientUnit unit;
}

public class Recipe : MonoBehaviour
{
    public Ingredient potionResult;
    public Ingredient[] pointIngredients;
}

在Editor文件夹内新建脚本IngredientDrawerUIE.cs,编辑代码:

using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;

[CustomPropertyDrawer(typeof(Ingredient))]
public class IngredientDrawerUIE : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        EditorGUI.BeginProperty(position, label, property);

        // label
        position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);

        // 控制字段缩进 设置为不缩进
        var indent = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 0;

        // 计算矩形范围
        var nameRect = new Rect(position.x, position.y, 30, position.height); 
        var amountRect = new Rect(position.x + 35, position.y, 50, position.height);
        var unitRect = new Rect(position.x + 90, position.y, position.width - 90, position.height);


        // 绘制字段
        EditorGUI.PropertyField(nameRect, property.FindPropertyRelative("name"), GUIContent.none);
        EditorGUI.PropertyField(amountRect, property.FindPropertyRelative("amount"), GUIContent.none);
        EditorGUI.PropertyField(unitRect, property.FindPropertyRelative("unit"), GUIContent.none);
        

        // 控制字段缩进 设置为原来的数值
        EditorGUI.indentLevel = indent;

        EditorGUI.EndProperty();
    }
}

将Recipe脚本添加到对象上查看效果:
在这里插入图片描述

2-8、参考链接

Unity学习笔记之编辑器开发

Unity编辑器工具开发经验总结

自定义编辑器

属性绘制器

三、后记

OK。总结一下。

Unity3D编辑器开发,就是基于Unity3D编辑器,做一些帮助开发的小工具。

在这里可以分成检视器属性界面绘制属性绘制三个大方面。

检视器属性参考2-3小节。

界面绘制可以分为窗口绘制检视器界面绘制场景绘制

窗口绘制的话就需要继承EditorWindow类,然后在OnGUI里面渲染UI。

检视器界面绘制需要继承Editor类,然后在OnInspectorGUI里面进行绘制。

场景绘制需要继承Editor类,然后在OnSceneGUI里面进行绘制。

属性绘制需要继承PropertyDrawer类,然后在OnGUI里面进行绘制。

如果觉得本篇文章有用别忘了点个关注,关注不迷路,持续分享更多Unity干货文章。


你的点赞就是对博主的支持,有问题记得留言:

博主主页有联系方式。

博主还有跟多宝藏文章等待你的发掘哦:

专栏方向简介
Unity3D开发小游戏小游戏开发教程分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。
Unity3D从入门到进阶入门从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。
Unity3D之UGUIUGUIUnity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。
Unity3D之读取数据文件读取使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。
Unity3D之数据集合数据集合数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。
Unity3D之VR/AR(虚拟仿真)开发虚拟仿真总结博主工作常见的虚拟仿真需求进行案例讲解。
Unity3D之插件插件主要分享在Unity开发中用到的一些插件使用方法,插件介绍等
Unity3D之日常开发日常记录主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等
Unity3D之日常BUG日常记录记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1062548.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

open62541学习:文件传输

作为一种通信协议&#xff0c;文件传输是非常重要的。例如传输执行程序&#xff0c;图片&#xff0c;配置文件等等。文件传输的机制和类型在 OPC UA 中已经存在很长时间了。FileType &#xff08;作为ObjectType&#xff09;和ImageType长期以来一直是内置模型的一部分&#xf…

Linux:TCP三握四挥简析

文章目录 1. 前言2. 背景3. TCP连接的建立和断开3.1 TCP协议状态机3.2 TCP的三握四挥3.2.1 TCP 连接建立的三次握手过程分析3.2.1.1 服务端和客户端套接字的创建3.2.1.2 服务端进入 LISTEN 状态3.2.1.3 服务端在 LISTEN 状态等待客户端的 SYN 请求3.2.1.4 客户端向服务端发送 S…

【云笔记篇】Microsoft OneNote笔记分区数据删除方法

【云笔记篇】Microsoft OneNote笔记分区数据删除方法 Microsoft OneNote删除分区数据需要在网页端操作才能彻底删除—【蘇小沐】 1、实验 系统版本Windows 11 专业工作站版22H2&#xff08;22621.1485&#xff09;&#xff1b;OneNoteOneNote 2016(版本 2303 Build 16.0.162…

香橙派Zero3安装miniconda3(问题多多,已全部解决)

文章目录 前言一、miniconda3版本二、使用步骤1.安装2.添加环境变量3.更新源4.创建新环境 总结另外 前言 你会遇到很多问题&#xff0c;按照我说的基本没问题。 香橙派是zero3。 一、miniconda3版本 Miniconda3-py37_4.9.2-Linux-aarch64.sh这个版本是测试没问题的&#xff0…

HDLbits : Module addsub

module top_module(input [31:0] a,input [31:0] b,input sub,output [31:0] sum );wire w1;add16 add16_1(a[15:0],b[15:0]^{16{sub}},sub,sum[15:0],w1);add16 add16_2(a[31:16],b[31:16]^{16{sub}},w1,sum[31:16],);endmodule 注意&#xff1a;sub位扩展

latex方程组编写,一种可以保证方程编号自适应的方法

问题描述&#xff1a; 在利用latex编写方程组时&#xff0c;可以有很多种方法&#xff0c;但不总是编辑好的公式能够显示出编号&#xff0c;故提出一种有效的方程组编写方法 方法&#xff1a; \begin{equation}X_{ t1}\left \{ \begin{matrix}\frac{x_{i}}{a} \quad\quad 0&l…

数学建模预测模型MATLAB代码大合集及皮尔逊相关性分析(无需调试、开源)

已知2010-2020数据&#xff0c;预测2021-2060数据 一、Logistic预测人口 %%logistic预测2021-2060年结果 clear;clc; X[7869.34, 8022.99, 8119.81, 8192.44, 8281.09, 8315.11, 8381.47, 8423.50, 8446.19, 8469.09, 8477.26]; nlength(X)-1; for t1:nZ(t)(X(t1)-X(t))/X(t1…

redis高可用(主从复制,哨兵,集群)

目录 一、主从复制&#xff1a; 1.主从复制介绍&#xff1a; 2.主从复制的作用&#xff1a; 3.主从复制流程&#xff1a; 4.搭建Redis 主从复制&#xff1a; 4.1 环境准备&#xff1a; 4.2 安装redis&#xff1a; 4.3 master节点修改 Redis 配置文件&#xff1a; 4.4 slave节点…

判断三条边是否构成三角形(Python实现)

组成三角形的三条边a,b,c需满足条件: ab>c ac>b bc>a 已知&#xff1a;三角形任意三条边的长度之和大于第三条边。 解题&#xff1a;定义3个变量a、b、c&#xff0c;让用户输入任意三个数字赋值给三个变量。判断三个变量中是否任意两个之和大于第三个数值。 判断条件之…

Docker快速搭建漏洞靶场指南

user: admin passwrod&#xff1a;password 查看容器是否已开启&#xff1a; 最常见漏洞&#xff1a; 防护级别提高后&#xff1a; 更改防护级别&#xff1a; 复现vulhub漏洞靶场&#xff1a; 开启&#xff1a; 一个是漏洞类型一个是真实的漏洞靶场。

VS的调式技巧你真的掌握了吗?

目录 什么是bug? 调式是什么&#xff1f;有多重要&#xff1f; 调试是什么&#xff1f; 调试的基本步骤 debug和release的介绍 windows环境调试介绍 1.调试环境的准备 2.学会快捷键 F11 VS F10 F9 & F5 3.调试时查看程序当前信息 查看临时变量的值 查看内存信…

LLMs: 近端策略优化PPO Proximal policy optimization

Dr. Ehsan Kamalinejad&#xff0c;通常简称为EK&#xff0c;是一位机器学习应用科学家。他目前是亚马逊NLP开发中的精英科学家。以前&#xff0c;他共同创办了Visual One&#xff0c;一家Y Combinator计算机视觉初创公司。在此之前&#xff0c;他曾担任苹果的首席机器学习工程…

WPS/word 表格跨行如何续表、和表的名称

1&#xff1a;具体操作&#xff1a; 将光标定位在跨页部分的第一行任意位置&#xff0c;按下快捷键ctrlshiftenter&#xff0c;就可以在跨页的表格上方插入空行&#xff08;在空行可以写&#xff0c;表1-3 xxxx&#xff08;续&#xff09;&#xff09; 在空行中输入…

NUWA论文阅读

论文链接&#xff1a;NUWA: Visual Synthesis Pre-training for Neural visUal World creAtion 文章目录 摘要引言相关工作视觉自回归模型视觉稀疏自注意 方法3D数据表征3D Nearby Self-Attention3D编码器-解码器训练目标 实验实现细节与SOTA比较T2I微调T2V微调V2V微调Sketch-t…

高效的ProtoBuf

一、背景 Google ProtoBuf介绍 这篇文章我们讲了怎么使用ProtoBuf进行序列化&#xff0c;但ProtoBuf怎么做到最高效的&#xff0c;它的数据又是如何压缩的&#xff0c;下面先看一个例子&#xff0c;然后再讲ProtoBuf压缩机制。 二、案例 网上有各种序列化方式性能对比&#…

elment以及elementPlus选中组件出现黑框问题解决!!

目录 问题&#xff1a; 图示&#xff1a; 解决方案&#xff1a; 问题&#xff1a; 使用elementPlus的按钮组件&#xff0c;点击按钮后会出现黑框&#xff0c;除非点击其他地方才能取消掉&#xff08;之前使用elment-ui其它组件时也出现过&#xff09; 图示&#xff1a; 解决方案…

2023网络安全工程师可以考哪些证书?只学习不考证怎么行呢?

想入网安行业的小可爱们&#xff0c;如果你在疯狂学习知识技能的同时&#xff0c;也计划着考取一些证书当做入门的金钥匙&#x1f511;。 先别急&#xff0c;如果还没有进入网络安全工作岗位&#xff0c;那么&#xff0c;首要任务是打好基础&#xff0c;学好网络安全技术。若是…

docker容器启动成功外部访问不到

1.重启docker服务 systemctl restart docker

9张图深入剖析ConcurrentHashMap

前言 在日常的开发中&#xff0c;我们经常使用key-value键值对的HashMap&#xff0c;其使用哈希表实现&#xff0c;用空间换取时间&#xff0c;提升查询性能 但在多线程的并发场景中&#xff0c;HashMap并不是线程安全的 如果想使用线程安全的&#xff0c;可以使用Concurren…

Swing程序设计(5)绝对布局,流布局

文章目录 前言一、布局管理器二、介绍 1.绝对布局2.流布局总结 前言 Swing窗体中&#xff0c;每一个组件都有大小和具体的位置。而在容器中摆放各种组件时&#xff0c;很难判断其组件的具体位置和大小。即一个完整的界面中&#xff0c;往往有多个组件&#xff0c;那么如何将这…