Unity编辑器开发:2020更新_leichao168的博客-程序员信息网

技术标签: c#  unity  Unity  windows  

正所谓:人靠衣装,佛靠金装。学会插件开发,成就大神人生。

  • 欢迎关注公众号:雷潮课堂
    公众号二维码.jpg

编辑器开发

一、课程内容

1-1本课程目标
1-2插件开发的起因
1-3插件的作用

二、菜单项制作

2-1菜单项(MenuItem)
2-2组件菜单(AddComponentMenu)
2-3上下文菜单(ContextMenu)
2-4必要组件菜单(RequireComponent)

三、自定义InsPector界面

3-1属性相关标识(HideInInspector,SerializeField,NonSerialized,Serializable)
3-2自定义界面属性 (CustomEditor)
3-3自定序列化属性字段 (PropertyField)

四、 自定义操作界面(Window)

4-1Windows窗口
4-2界面的生命周期(事件)
4-3编辑器下的UI控件
文本颜色字段,标签字段,数字输入字段,滑动条,位置大小输入字段,选择字段,标签、层、对象选择字段,单选字段,分组字段,其他字段

1-1本课程目标

学会自定义编辑器,开发一款属于自己的编辑器插件

1-2插件开发的起因

如果一个模型师想要在Unity中查看效果,但是她又是程序小白,作为公司一员应该给到她非常好帮助,比如制作一套工具集合(插件),让她根本离不开你。

1-3插件的作用

帮助企业建立技术护城河,提高开发效率,减少沟通成本,减少工作。

2-1菜单项

Asset目录下新建一个Editor文件夹,新建MyEditor脚本

using UnityEngine;
using UnityEditor; // 引用命名空间

public class MyEditor : Editor   // 继承Editor类
{
    // 添加工具栏并设置创建方块方法 (false)显示出来
    [MenuItem("工具/创建方块, &_f",false)]  // % Ctrl  & Alt # Shift
    public static void CreatCube()
    {   
        GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
        cube.name = "方块";
    }
  
    // 用来处理编辑器的问题(Update)  
    private void OnSceneGUI()
    {     
        if (Input.GetKeyDown(KeyCode.F) && Input.GetKeyDown(KeyCode.LeftAlt))
        {
            CreatCube();
        }
    }
  • 效果图:
    创建方块
    详细图

  • 注意

OnSceneGUI方法中主要使用Handles系列来绘制可视化调试控件,比如线、盒子、球体、文本等(可以自行操作)

private void OnSceneGUI()
{
  Handles.color = Color.green;
}
  • 2-1-1多级菜单:
    多级菜单
    详细图
using UnityEngine;
using UnityEditor; // 引用命名空间

public class MyEditor : Editor   // 继承Editor类
{
    public static GameObject cube;
    // 添加工具栏并设置创建方块方法 (false)显示出来
    [MenuItem("工具/创建方块, &_f",false)]  // # Ctrl  & Alt
    public static void CreatCube()
    {   
        cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
        cube.name = "方块";
    }

    // 添加工具栏并设置创建方块方法 (false)显示出来
    [MenuItem("工具/销毁方块, &_G", false)]  // # 表示Ctrl  & 表示Alt
    public static void DestroyCube()
    {
        if (cube == null)
        {
            cube = GameObject.Find("方块");
        }
        Editor.Destroy(cube);
    }

    // 用来处理编辑器的问题(Scene绘制图形方法类似Update)
    private void OnSceneGUI()
    {
        Handles.color = Color.green;
        if (Input.GetKeyDown(KeyCode.F) && Input.GetKeyDown(KeyCode.LeftAlt))
        {
            CreatCube();
        }
        if (Input.GetKeyDown(KeyCode.G) && Input.GetKeyDown(KeyCode.LeftAlt))
        {
            DestroyCube();
        }
    }
}
2-2组件菜单(AddComponentMenu)
using UnityEngine;
//给Component菜单下增加One子菜单,并将TestOne类添加到对象上
[AddComponentMenu("One",1)]  
public class TestOne : MonoBehaviour
{
}

one子菜单

选中物体后,点击one子菜单即可添加Test one脚本

2-3上下文菜单(ContextMenu)

在面板中右键出现的菜单
Inspector面板中右键出现

public class TestOne : MonoBehaviour
{
    [ContextMenu("测试")]
    public void Test()
    {
        Debug.Log("123");
    }
}
2-4必要组件菜单(RequireComponent)

添加某个组件必须要有其他组件

using UnityEngine;

[RequireComponent(typeof(Rigidbody))] // 添加组件必须要加上刚体
//给Component菜单下增加One子菜单,并将TestOne类添加到对象上
[AddComponentMenu("One",1)]  
public class TestOne : MonoBehaviour
{

}
3-1属性相关标识
  • 隐藏属性显示 (HideInInspector)
  • 不显示的属性(SerializeField)
  • 关键字序列化(Serializable) (针对类,结构体,枚举,委托有效)
  • 不会被序列(NonSerialized)
using System;
using UnityEngine;

[Serializable] // 可以使类显示在视图面板
public enum MyColor
{
    red,
    green,
    blue
}
public class TestOne : MonoBehaviour
{
    [HideInInspector]
    public int age; //  将显示的属性隐藏
    [SerializeField]
    private float height; // 显示隐藏的属性
    [NonSerialized]
    public Vector3 v = new Vector3(10, 0, 0); // 防止变量序列化
    public MyColor c;
}
3-2自定义界面属性
  • 自定义显示面板(CustomEditor)

我们添加系统组件的时候,发现面板很好看,而脚本做不到这种效果。这里就需要自定义显示面板,如下图所示:
针对BoxCollider自定义面板

using UnityEngine;
using UnityEditor;// 引用命名空间

// CustomEditor是对应标识的组件脚本
[CustomEditor(typeof(BoxCollider))] // 设置自定义BoxCollider的内容
public class MyInsPectorTest : Editor // 继承Editor类
{
    private bool b;
    private string s = "";
    private string m;

    // 重写Editor 绘制面板图形界面方法
    public override void OnInspectorGUI() 
    {
        // 当我们使用自动布局技术的时候,就不要混合使用GUI.Lable,以免产生不必要的约束问题 
        GUILayout.Label("雷潮");
        b = GUILayout.Toggle(b,"关闭音效");
        GUILayout.Label("请输入账号");
        s = GUILayout.TextField(s);
        GUILayout.Label("请输入密码");
        m = GUILayout.PasswordField(m,'*'); // 后面是占位符,将密码字符更替为*
       
        if (GUILayout.Button("登录"))
        {
            if (s == "leichao" && m == "123456")
            {
                Debug.Log("登录成功");
            }
            else
            {
                Debug.Log("账号或者密码错误");
            }
        }
    }
}
3-3自定序列化属性字段 (PropertyField)

命名空间在UnityEditor
SerializedObject 序列化对象 请点击查看序列化对象类
SerializedProperty 序列化属性 请点击查看序列化属性类

using System;
using UnityEngine;

[Serializable] // 可以使类显示在视图面板
public enum MyColor
{
    red,
    green,
    blue
}
public class TestOne : MonoBehaviour
{
    public int age; 
    public float height;
    public Vector3 v = new Vector3(10, 0, 0);
    public MyColor c;
}
using UnityEditor;

[CustomEditor(typeof(TestOne))]
public class Test1 : Editor
{
    public SerializedObject mObj;
    public SerializedProperty mAge;
    public SerializedProperty mHeight;
    public SerializedProperty mVector;
    public SerializedProperty mColor;

    private void OnEnable()
    {
        mObj = new SerializedObject(target); // 创建一个目标,即挂载脚本物体
        mAge = this.mObj.FindProperty("age");
        mHeight = this.mObj.FindProperty("height");
        mVector = this.mObj.FindProperty("v");
        mColor = this.mObj.FindProperty("c");
    }
    public override void OnInspectorGUI()
    {
        this.mObj.Update();
        EditorGUILayout.PropertyField(this.mAge);
        EditorGUILayout.PropertyField(this.mHeight);
        EditorGUILayout.PropertyField(this.mVector);
        EditorGUILayout.PropertyField(this.mColor, true);
    }
}
4.自定义窗口菜单
4-1自定义窗口

自定义Window

using UnityEngine;
using UnityEditor;

public class MyWindow :EditorWindow
{
    private bool b;
    private string s;
    private string m;
    private Color c = Color.white;
    private string t;

    [MenuItem("Tools/Window")]
    public static void ShowW()
    {
        EditorWindow.GetWindow<MyWindow>().Show();
    }

    private void OnGUI()
    {
        // 编辑MyWindow
        EditorGUILayout.LabelField("雷潮邀请你输入");
        EditorGUILayout.Space(); // 隔开一行
      
        EditorGUILayout.LabelField("请输入账号");
        s = EditorGUILayout.TextField(s);
     
        EditorGUILayout.SelectableLabel("请输入密码");
        s = EditorGUILayout.PasswordField(s);
      
        EditorGUILayout.Space(); // 隔开一行
        EditorGUILayout.LabelField("请选择颜色");
        this.c = EditorGUILayout.ColorField(c);

        EditorGUILayout.Space(); // 隔开一行
        EditorGUILayout.LabelField("请输入你要提交的信息");
        this.t = EditorGUILayout.TextArea(this.t); // 文本区域可以空格换行输入

        b = GUILayout.Toggle(b, "记住账号");
        b = GUILayout.Toggle(b, "记住密码");
    }

}
  • 另一种显示窗口的方法
[MenuItem("Tools/Window")]
public static void ShowW()
{
      // 无法关闭
      MyWindow.CreateInstance<MyWindow>().ShowPopup();
}
  • 填充更多内容

3.png

4.png

4-2界面的生命周期(事件)
public class WindowEditorLife : EditorWindow
{
    [MenuItem("Window/MyWindow")]
    public static void CreatWindow()
    {
        EditorWindow.GetWindow<WIndowEditorLife>().Show();
    }
    private void OnEnable() // 当窗口可用时
    {
        Debug.Log("OnEnable ==== 1");
    }
    private void OnFocus() // 当鼠标在窗口中,获得焦点时,调用一次
    {
        Debug.Log("OnFocus ==== 2");
    }
   private void Update()
    {
        Debug.Log("所有可见窗口上每帧调用");
    }
    private void OnLostFocus() // 失去焦点时调用
    {
        Debug.Log("OnLostFocus");
    }
    private void OnGUI() // 绘制图形
    {
        Debug.Log("绘制图形");
        if (GUILayout.Button("关闭窗口"))
        {
            this.Close();
        }
    }
    private void OnDisable() // 窗口不可用的时候调用
    {
        Debug.Log("OnDisable");
    }
    private void OnDestroy()  // 销毁的时候调用
    {
        Debug.Log("OnDestroy");
    }
    private void OnHierarchyChange() // 当场景层次面板发生改变的时候调用
    {
        Debug.Log("OnHierarchyChange");
    }
    private void OnValidate() // 当脚本被加载或者检视面板值备修改的时候调用
    {
        Debug.Log("OnValidate");
    }
    private void OnProjectChange() // 当工程目录更新时调用(有资源变动)
    {
        Debug.Log("OnProjectChange");
    }
    private void OnSelectionChange() // 当在窗口选中某个物体时
    {
        Debug.Log("OnSelectionChange"); 
    }
    private void OnInspectorUpdate()  // 窗口的更新调用,每秒10次
    {
        Debug.Log("OnInspectorUpdate");
    }
4-3编辑器下的UI控件
  • 滚动条

EditorGUILayout.Slider(…) 浮点数滚动条
EditorGUILayout.IntSlider(…)整数滚动条
EditorGUILayout.MinMaxSlider(…) 最小最大滚动条

         number = EditorGUILayout.Slider(number, 0, 100);
         number1 = EditorGUILayout.IntSlider(number1, 0, 100);
         EditorGUILayout.MinMaxSlider(ref number2, ref number3, 0, 100);
  • 输入框

EditorGUILayout.TextField(string) 文本输入框
EditorGUILayout.TextArea(string); 可换行的文本输入框
EditorGUILayout.PasswordField(string); 文本输入框
EditorGUILayout.ColorField(Color); 颜色输入框
EditorGUILayout.IntField(int); 整数输入
EditorGUILayout.FloatField(float); 浮点数输入
EditorGUILayout.CurveField(string,objec) // 动画曲线输入
EditorGUILayout.ObjectField(objec,typeof()) // 获取选择的物体,后面可以限制选择的物体类型
EditorGUILayout.InspectorTitlebar(bool,object) // 选择的物体放在面板

 s1 = EditorGUILayout.TextField(s1); // 文本输入
 s1 = EditorGUILayout.TextArea(s1); // 换行输入
 s1 = EditorGUILayout.PasswordField(s1); // 密码文本输入
 c1 = EditorGUILayout.ColorField(c1);
 a1 = EditorGUILayout.CurveField("动画",a1);
 // 选择选中的物体,类型就是后面的参数typeof(Transform)
 o1 = EditorGUILayout.ObjectField(o1, typeof(Transform));
 if (o1 != null)
 {
    b = EditorGUILayout.InspectorTitlebar(b, o1); // 选择的物体放在面板
    Debug.Log(b);
 }
  • 向量

EditorGUILayout.RectField(Rect); 矩阵
EditorGUILayout.BoundsField(Bounds);XYZ距离
EditorGUILayout.Vector2Field(string, Vector2) 二维坐标
EditorGUILayout.Vector3Field(string,Vector3); 三维坐标
EditorGuILayout.Vector4Field(string,Vector4); 三维坐标(表示旋转)(增加了W)
(

        r = EditorGUILayout.RectField(r); // X,Y,width,height
        b = EditorGUILayout.BoundsField(b); // 中心,大小
        v2 = EditorGUILayout.Vector2Field("平面位置", v2);
        v3 = EditorGUILayout.Vector3Field("3D位置", v3);
        v4 = EditorGUILayout.Vector4Field("四元数", v4);

效果

  • 下拉菜单

EditorGUILayout.Popup(int, string[]); 字符串选择,返回字符串数组下标
EditorGUILayout.IntPopup(int,int[],string[]) 字符串选择,返回字符串对应下标的整形数组值
EditorGUILayout.EnumPopup(Enum) 枚举选择,返回枚举

 Enum e = People.man;

enum People
{ 
    man,
    woman
}
// 下拉菜单
index = EditorGUILayout.Popup(index, strs);
index = EditorGUILayout.IntPopup(index, strs, numbers);
e = EditorGUILayout.EnumPopup(e);
Debug.Log(e);

  • 标签、层、对象

EditorGUILayout.TagField(this.t);
EditorGUILayout.LayerField(this.layer);
EditorGUILayout.ObjectField(this.mObj, typeof(BoxCollider));

  • 展开

EditorGUILayout.Foldout(bool,string)

  s1 = "Player";
        s1 = EditorGUILayout.TagField(s1);
        // Layer
        number1 = EditorGUILayout.LayerField(number1);
        b1 = EditorGUILayout.Foldout(b1, "点击打开"); // 展开控件
        if (b1)
        {
            number = EditorGUILayout.Slider(number, 0, 100);
            o1 = EditorGUILayout.ObjectField(o1 as UnityEngine.Object, typeof(GameObject)); // 选中的游戏物体菜单
        }
        else
        {
            Debug.Log("没有任何物体");
        }

  • 布局分组
  • 水平分组
    EditorGUILayout.BeginHorizontal();
    EditorGUILayout.EndHorizontal();
  • 垂直分组
    EditorGUILayout.BeginVertical();
    EditorGUILayout.EndVertical();
  • Toggle分组
    EditorGUILayout.BeginToggleGroup(“选择分组”,this.mbool);
    EditorGUILayout.EndToggleGroup();
  • 滚动分组
    EditorGUILayout.BeginScrollView(this.mPos);
    EditorGUILayout.EndScrollView();
        b1 =  EditorGUILayout.BeginToggleGroup("灵敏度设置", b1);
        b2 = EditorGUILayout.Toggle("鼠标设置",b2);
        b3 = EditorGUILayout.Toggle("鼠标设置", b3);
        b4 = EditorGUILayout.Toggle("鼠标设置", b4);
        EditorGUILayout.EndToggleGroup();

####5总结:
顶部编辑器菜单:引用using UnityEditor; 使用[MenuItem()];
Inspector面板的处理:继承Editor,重写虚方法:OnInspectorGUI(),并调用(父类的此方法)base.OnInspectorGUI()
窗口的面板:继承:EditorWindow, 使用EditorWindow.GetWindow(); 调用窗口的Show()方法。

  • 欢迎购买本课程:

腾讯课堂
腾讯课堂
微信小程序
网易云课堂
网易云课堂

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/leichao168/article/details/108849931

智能推荐

c# rar解压大小_C#解压RAR压缩文件_weixin_39611340的博客-程序员信息网

C# RAR压缩或解压文件using System;using System.Collections.Generic;using System.Text;using System.IO;using Microsoft.Win32;using System.Diagnostics;namespace Uni.UniCustoms{public class clsWinrar{/// /// 是否安...

C++与Qt实现MySQL数据库的增删改查_卧_听风雨的博客-程序员信息网

头文件MySQL数据库的安装与环境配置,网上大把,可参考:https://www.cnblogs.com/xtu-wlf1212/p/8764015.html头文件包含:#include &lt;QtSql/qsql.h&gt;#include &lt;QtSql/qsqldatabase.h&gt;#include &lt;QtSql/qsqlquery.h&gt;#include...

Sping学习笔记_kaibinsj的博客-程序员信息网

一、Spring包结构图: 二、              First Step举个简单的例子:(使用WSAD的包结构)Java Soucecom         ……..test                   ………domain                            Action.java                            LowerAction.jav

android 学习之路_黑键手记的博客-程序员信息网

源址:http://diycode.cc/topics/122本篇文章是从stormzhang那瞅到的,作为参考了解一下有一些链接可能还不是特别准确,因为我只能根据图片上的书或者资源的名字去Google可能的书籍,所以链接上有什么不对的,欢迎大家评论指出,我会及时更正。请参考原文:另一份 Android 开发学习路线图 帮助修改。谢谢。基础工具部分: 中

ViewPager2使用和探究_何以解忧唯有睡觉的博客-程序员信息网_viewpager2

1.ViewPager2简单介绍    ViewPage2是Jetpack中的其中一个组件,可以实现滑动切换页面的效果,通常可以搭配其他组件实现banner切换、以及类似于抖音短视频上下滑动切换播放的效果。ViewPager2是基于RecyclerView实现的,自然继承了RecyclerView的众多优点,并且针对ViewPager存在的问题做了优化。支持垂直方向的滑动且实现极其简单。完全支持RecyclerView的相关配置功能。支持多个PageTransformer。支持DiffUtil

随便推点

【beautifulsoup】python标准库解析器解析网页问题解决_Jessie-via的博客-程序员信息网

使用python标准库解析(即BeautifulSoup(page, "html.parse"))出现网页解析问题如下,页面明明没有结束,却提前出现了,导致有些text没有了原本与之对应的标签,也就取不到了在尝试了很多种方法之后,发现是解析器的问题,所以更换了解析器BeautifulSoup(page, "html5lib")from bs4 import Beau

webstorm的ctrl+shift+F快捷键与微软输入法繁简体切换快捷键冲突_neimeng0的博客-程序员信息网

参考:https://www.zhihu.com/question/49313987设置里面可以改的,微软拼音输入法的选项藏的有点深,设置-语言-中文(选项)-微软拼音(选项)。如果觉得切换语言提示大,可以用alt+shift快捷键,不弹提示。...

Linux日志查找——正则表达式_ginkgo_biloba的博客-程序员信息网

文章目录基础文件内容一行以xx开头 ^一行以xx结尾 $只包含xx一个单词 ^ $空行 ^$词首 \\&lt; 或 \b词尾 \\&gt; 或 \b独立单词 \\&lt; \\&gt;非单词边界 \B连续次数匹配文件内容连续n个字符 \\{ \\}只出现n次的字符 \\&lt; \{ \}\\&gt;出现的次数在x与y之间 \\{x,y\\}至少连续出现x次 \\{x,\\}至多出现x次...

cJson的学习与总结_编程老鸟的博客-程序员信息网

场景:需要进行大量数据解析,存储,传输1.数据结构typedef struct cJSON { struct cJSON *next,*prev; struct cJSON *child; int type; char *valuestring; int valueint; double valuedouble; ch...

[置顶] Linux网络编程必看书籍推荐_wangsh217的博客-程序员信息网

分类: TCP/IP协议 Linux环境编程2011-08-17 12:27 26881人阅读 评论(27)收藏 举报linuxLinuxLINUXlinux内核unixUNIXUnix编程网络目录(?)[+]C语言经典著作导读 Linux内核学习四库全书 首先要说讲述计算机网络和TCP/IP的书很多。 先要学习网络知识才

剑指offer之 字符流中第一个不重复的字符_weixin_38312163的博客-程序员信息网

题目描述请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。思路Hash:创建数组表示每个字符的出现次数,挨个读入字符后将字符对应数组数字加一,之后遍历字符串,若该字符的出现次数统计为1,则返回代码class Solution{...

推荐文章

热门文章

相关标签