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标签)

(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>();//创建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();
    }
}

 注: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<MeshRenderer>().material.color = Color.red;
        }
        GUI.color = Color.green;
        if (GUILayout.Button("绿色"))
        {
            scence.GetComponent<MeshRenderer>().material.color = Color.green;
        }
        GUI.color = Color.blue;
        if (GUILayout.Button("蓝色"))
        {
            scence.GetComponent<MeshRenderer>().material.color = Color.blue;
        }
        GUI.color = Color.white;
        GUILayout.EndArea();
        Handles.EndGUI();
    }
}

3.展示效果

 三、窗口拓展

将脚本放到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编辑器扩展——标签属性Attribute_mb60e5417d375b8的技术博客_51CTO博客

Unity常用标签-CSDN博客

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

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

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Unity编辑器拓展Editor Extension)可以通过自定义的脚本来扩展Unity编辑器的功能和界面,以满足特定项目的需求。通过编辑器拓展,开发者可以创建自定义的编辑器窗口、工具栏按钮、菜单项、检视面板等,来增强Unity编辑器的功能和流程。 要创建一个Unity编辑器拓展,你可以使用C#编写一个继承自Editor类的脚本。这个脚本可以通过Unity的Inspector面板来设置相关的属性和行为。以下是一个简单的示例: ```csharp using UnityEngine; using UnityEditor; public class MyEditorExtension : EditorWindow { [MenuItem("Custom Tools/My Editor Window")] public static void OpenWindow() { // 创建并打开一个自定义的编辑器窗口 MyEditorExtension window = (MyEditorExtension)EditorWindow.GetWindow(typeof(MyEditorExtension)); window.Show(); } private void OnGUI() { // 在编辑器窗口中绘制UI元素 GUILayout.Label("Hello, I am a custom editor window!"); if (GUILayout.Button("Click Me")) { Debug.Log("Button clicked!"); } } } ``` 上述代码创建了一个自定义的编辑器窗口,并在窗口中绘制了一个标签和一个按钮。通过在Unity编辑器中点击"Custom Tools"菜单下的"My Editor Window",可以打开这个自定义的编辑器窗口。 除了编辑器窗口,你还可以通过继承Editor类来创建自定义的检视面板、菜单项等。Unity官方文档中有更详细的教程和示例,可以帮助你更深入地了解和使用Unity编辑器拓展

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒冷的晚风

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值