Unity编辑器拓展

一、脚本标签

1.常用的脚本标签:调整脚本组件的数据编辑以及编辑器上的一些功能菜单的拓展

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

[RequireComponent(typeof(AudioSource))] //给类添加。在给物体添加该标签标记的脚本类后,如果物体没有typeof中的组件则会自动给物体添加
[AddComponentMenu("Test/ComponentTest")] //给类添加。会出现在Component菜单中,点击后可以为物体添加对应的脚本类
[HelpURL("https://www.baidu.com")]//给类添加。将标记的脚本组件的右上角的小问号链接到给定的URL(那个小问号本身是链接到官方脚本文档的,相当于这里的标签修改了它的链接地址)
[DisallowMultipleComponent]//给类添加。标记的脚本只能对同一个物体添加一个该标记脚本,而不能添加多个该脚本
[ExecuteInEditMode]//给类添加。使脚本在编辑模式下也能执行生命周期函数,如Start、Update等。但是使用起来效果并不好。
public class Test : MonoBehaviour
{
    private void Start()
    {
        Debug.Log("Start");
    }
    [HideInInspector]//隐藏公有变量在Inspector面板上显示
    public int num;
    [NonSerialized]//序列化属性,并可以隐藏公有属性
    public int Nonnum;
    [SerializeField]//使非公有属性序列化,并显示在面板上
    private string playerName;
    public Student stu;
    [Header("文章标题")]//给变量加个标题
    public string title;
    [Space(30)]//设置上边距
    public int edgeDistance;
    [Tooltip("鼠标悬停在属性名上")]//鼠标悬停在属性名上显示的信息
    public int hover;
    [Range(0, 10)]//限制int、float、double类型变量的大小范围
    public int hitDamage;
    [Multiline(3)]//给string类型变量设置行数
    public string text;
    [TextArea]//给string类型变量设置成文本区域的形式显示
    public string area;
    [ColorUsage(true)]//调用颜色复选框,其中的true表示显示透明度设置
    //注:如果要调节3D物体的透明度,需要将3D物体的材质的渲染模式改为Fade
    public Color color;
    [FormerlySerializedAs("obj")]//防止GameObject类型变量在拖拽赋值后,因改变了变量名而使引用丢失的情况,里面的参数是最初拖拽赋值时的变量名
    public GameObject obj;
    [MenuItem("菜单/菜单项 &%Q", priority =1)]//给unity菜单栏加功能菜单,该标签下可以加静态方法,表示功能按钮调用的方法,&%Q表示快捷键,%:Ctrl #:Shift &:Alt。存在多个菜单项时priority可以控制菜单项的显示优先级
    static void ShowMenuItem()
    {
        Debug.Log("MenuItem");
    }
    [MenuItem("CONTEXT/Test/buttonl")]//Test表示脚本名,button1表示功能按钮名。在对应的脚本组件右上角的三个小点或齿轮的那里,点击button1,就会调用下面的静态方法
    static void ShowInpectorInfo()
    {
        Debug.Log("InpectorInfo");
    }
    [ContextMenuItem("Test", "MyTest")]//给属性添加右键菜单功能按钮,Test表示右键菜单的功能按钮名,MyTest表示该功能对应得触发方法
    public string printMyTest;
    void MyTest()
    {
        Debug.Log("MyTest");
    }
}
[System.Serializable]//使序列化的类中的公有变量可以在Inspector上显示
public class Student
{
    public int id;
    public string name;
    private int age;
}

2.CreateAssetMenu标签:在Project的Create菜单中可以看到该标签表现得功能

(1)写一个继承ScriptableObject的脚本,给脚本类加上CreateAssetMenu标签

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

[CreateAssetMenu(fileName = "ScriptFile", menuName = "玩家/玩家信息", order = 1)]//Project的Create菜单下可以看到,点击“玩家信息”会自动创建名为“ScriptFile”的文件。order表示显示顺序
public class ScriptObject : ScriptableObject
{
    //添加一些属性
    [Header("ID")]
    public int id=2;
    [Tooltip("Name")]
    public string playerName="张三";
    [Range(1, 90)]
    public int age=40;
    [Tooltip("PlayerHitDamage")]
    public float hitDamage=40;
    //.........常用标签基本都可以使用
    //添加一些非生命周期函数也行
    public void Attack()
    {
        Debug.Log("Attack");
    }
    public void Hurt()
    {
        Debug.Log("Hurt");
    }
    public void ShowInfo()
    {
        Debug.Log($"id:{id},playerName:{playerName},age:{age},hitDamage:{hitDamage}");
    }
}

(2)方法一:右键Project窗口中的Resources文件夹(不是必须的,看个人如何引用),在Create菜单中创建.asset玩家信息文件,会生成一个ScriptFile文件。(需要使用CreateAssetMenu标签)

方法二:ScriptObject文件是通过ScriptableObject的静态方法创建的。虽然方法不一样,但是使用效果没有太大区别。(不需要使用CreateAssetMenu标签)

Unity编辑器拓展_第1张图片

(3)挂载下面的脚本(脚本中两种方法都使用了),运行unity,查看控制台输出的信息

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

public class ScriptableObjectTest : MonoBehaviour
{
    delegate void DelegateTest();//委托
    public ScriptObject objTest;//方法一:以拖拽复制的方式引用ScriptObjec
    // Start is called before the first frame update
    void Start()
    {
        //方法一:可在Inspector面板上实时调整,使用更方便,需要用到CreateAssetMenu标签
        DelegateTest test = new DelegateTest(objTest.ShowInfo);//注册方法
        test += objTest.Attack;//注册方法
        test += objTest.Hurt;
        test();
        //方法二:不需要用到CreateAssetMenu标签
        ScriptObject obj = ScriptableObject.CreateInstance();//创建ScriptObject实例
        AssetDatabase.CreateAsset(obj, "Assets/Resources/ScriptObject.asset"); //在Assets/Resources文件夹创建资源文件ScriptObject.asset
        AssetDatabase.SaveAssets();//保存创建的资源
        AssetDatabase.Refresh();//刷新
        DelegateTest test2 = new DelegateTest(obj.ShowInfo);//注册方法
        test2 += obj.Attack;//注册方法
        test2 += obj.Hurt;
        test2();
    }
}

Unity编辑器拓展_第2张图片

 注:ScriptableObject脚本类说明:在项目中经常需要创建多个相同的预制体,而这些预制体上又都挂载了相同的脚本,相当于同一脚本中的属性方法等数据被复制了多份,造成了资源的浪费,而使用一个继承ScriptableObject的脚本去保存哪些需要重复使用的属性方法等数据,然后在需要使用到这些数据的预制体的脚本组件上定义一个指向该ScriptableObject脚本对象的引用去获得数据,可以提高性能,尤其是这类数据非常多的时候是相当有用的。
平时我们从Inspector面板上为脚本组件公开的变量输入数据去改变项目效果,实际上是当你输入数据后,unity编辑器会实时自动去修改对应的.asset场景文件中的数据从而改变项目效果。在需要频繁修改大量数据时,这样是不方便的,unity也会很卡。而使用ScriptableObject脚本就可以将一些需要经常修改的属性或方法从主要的逻辑脚本中隔离出来,方便策划修改数据参数。

二、Inspector和Scene拓展

1.需要挂载InpectorEditor脚本才会生效

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

public class InspectorEditor : MonoBehaviour
{
    [HideInInspector]
    public Texture texture;
    [HideInInspector]
    public string mark;
    [HideInInspector]
    public float progessBar;
    [HideInInspector]
    public float progessBar2;
    [HideInInspector]
    public bool isOpen;
    [HideInInspector]
    public new string name;
    [HideInInspector]
    public bool open;
    [HideInInspector]
    public string wupin;
    [HideInInspector]
    public float size;
    public enum Dir
    {
        东, 南, 西, 北
    };
    [HideInInspector]
    public Dir dir = Dir.东;
    [HideInInspector]
    public Rect rect;
    [HideInInspector]
    public string str;
    private void Start()
    {
        Debug.Log(texture.name + ":" + mark);
    }
}

2.将下面的脚本放在Editor文件夹中

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(InspectorEditor))]
[CanEditMultipleObjects]//加上这个字段后,表示控制多个InspectorEditor的物体对象,否则是控制单个
public class EditorInspectorTest : Editor
{
    public override void OnInspectorGUI()//Inspector拓展
    {
        base.OnInspectorGUI();
        InspectorEditor inspector = (InspectorEditor)target;

        inspector.texture = EditorGUILayout.ObjectField("选择图片", inspector.texture, typeof(Texture), true) as Texture;//编辑图片
        
        inspector.mark = EditorGUILayout.TextField("标签", inspector.mark);
        
        if (inspector.progessBar > 80)
        {
            GUI.color = Color.green;
        }
        if (inspector.progessBar < 20)
        {
            GUI.color = Color.red;
        }
        inspector.progessBar = EditorGUILayout.Slider("进度条1", inspector.progessBar, 0, 100);
        GUI.color = Color.white;//将后面的属性颜色改回白色

        inspector.progessBar2 = EditorGUILayout.Slider("进度条2", inspector.progessBar2, 0, 100);
        if (inspector.progessBar2 > 80)
        {
            EditorGUILayout.HelpBox("进度值过高", MessageType.Info);
        }
        if (inspector.progessBar2 < 20)
        {
            EditorGUILayout.HelpBox("进度值过低", MessageType.Warning);
        }

        GUILayout.Label("下面是一些信息");
        inspector.isOpen = EditorGUILayout.BeginToggleGroup("开启信息", inspector.isOpen);
        EditorGUI.indentLevel = 3;//控制缩进
        inspector.name = EditorGUILayout.TextField("名字", inspector.name);
        EditorGUI.indentLevel = 0;
        EditorGUILayout.EndToggleGroup();

        inspector.open = EditorGUILayout.Foldout(inspector.open, "展开信息");
        if (inspector.open)
        {
            EditorGUI.indentLevel = 1;
            inspector.wupin = EditorGUILayout.TextField("武器的伤害", inspector.wupin);
            inspector.size = EditorGUILayout.FloatField("武器的名字", inspector.size);
            EditorGUI.indentLevel = 0;
        }

        inspector.dir = (InspectorEditor.Dir)EditorGUILayout.EnumPopup("方向", inspector.dir);

        inspector.rect = EditorGUILayout.RectField("窗口", inspector.rect);
        if (GUILayout.Button("角色选择"))
        {
            Debug.Log("角色选择");
        }
        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("男一号"))
        {
            Debug.Log("男一号");
        }
        if (GUILayout.Button("男二号"))
        {
            Debug.Log("男二号");
        }
        if (GUILayout.Button("女一号"))
        {
            Debug.Log("女一号");
        }
        EditorGUILayout.EndHorizontal();

        inspector.str = EditorGUILayout.TextArea(inspector.str);
    }
    private void OnSceneGUI()//Scence拓展
    {
        InspectorEditor scence = (InspectorEditor)target;
        Handles.Label(scence.transform.position + Vector3.up * 2, scence.name + ":" + scence.transform.position);
        Handles.BeginGUI();
        GUILayout.BeginArea(new Rect(0, 0, 200, 200));
        GUI.color = Color.red;
        if (GUILayout.Button("红色"))
        {
            scence.GetComponent().material.color = Color.red;
        }
        GUI.color = Color.green;
        if (GUILayout.Button("绿色"))
        {
            scence.GetComponent().material.color = Color.green;
        }
        GUI.color = Color.blue;
        if (GUILayout.Button("蓝色"))
        {
            scence.GetComponent().material.color = Color.blue;
        }
        GUI.color = Color.white;
        GUILayout.EndArea();
        Handles.EndGUI();
    }
}

3.展示效果

Unity编辑器拓展_第3张图片

Unity编辑器拓展_第4张图片

 三、窗口拓展

将脚本放到Editor文件夹中

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

public class EditorWindowTest : EditorWindow
{
    float t;
    bool isOpen;
    float t1;
    float t2;
    [MenuItem("Window/MyWindow &%Q", false, 12)]//&%Q 表示设置快捷键
    static void MyWindow()
    {
        Rect rect = new Rect(0, 0, 500, 500);
        EditorWindowTest ew = (EditorWindowTest)EditorWindow.GetWindowWithRect(typeof(EditorWindowTest), rect, false, "我的窗口");
    }
    private void OnGUI()//里面可以添加的东西与Inspector拓展类似
    {
        if (GUILayout.Button("打开通知", GUILayout.Width(150)))
        {
            this.ShowNotification(new GUIContent("元旦提前放假了"));
        }
        if (GUILayout.Button("关闭通知", GUILayout.Width(150)))
        {
            this.RemoveNotification();
        }
        if (GUILayout.Button("关闭窗口", GUILayout.Width(150)))
        {
            this.Close();
        }
        t = EditorGUILayout.Slider("时间", t, 0, 100);

        isOpen = EditorGUILayout.BeginToggleGroup("开关数据", isOpen);

        t1 = EditorGUILayout.FloatField("值1", t1);
        t2 = EditorGUILayout.FloatField("值2", t2);
        EditorGUILayout.EndToggleGroup();

    }
    private void OnFocus()//进入焦点
    {
        Debug.Log("进入窗口");
    }
    private void OnLostFocus()//离开焦点
    {
        Debug.Log("离开窗口");
    }
    public void OnHierarchyChange()//打开脚本窗口后才有效
    {
        Debug.Log("改变了");
    }
    public void OnSelectionChange()//打开脚本窗口后才有效
    {
        Debug.Log(Selection.activeGameObject.name);
    }
}

展示效果

Unity编辑器拓展_第5张图片

参考:

Unity编辑器扩展——标签属性Attribute_mb60e5417d375b8的技术博客_51CTO博客

Unity常用标签-CSDN博客

Unity进阶:ScriptableObject使用指南_YY-nb的博客-CSDN博客

结语:合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。

你可能感兴趣的:(Unity编辑器拓展,unity,编辑器,游戏引擎)