Unity3D学习笔记(二十八):Editor

Editor:
对编辑器进行一些拓展开发
关于继承Mono类的扩展开发
 
特性:
[特性]:声明性的标签(类,方法,结构体,变量)
特性只对字段声明有效,后面必须接字段;多个特性,可以修饰一个字段
 
修饰变量:
[Tooltip("玩家名字")]:在Inspector面板鼠标悬停在修饰的变量上时,显示一个提示性的信息
[Range(1, 50)]:修饰整型和浮点类型的变量,生成一个滑动条,范围是最小值和最大值之间
----第一个参数:最小值
----第二个参数:最大值
[Header("玩家的信息")]:显示一个标题,只修饰字段
[Space(10)]:可以与上面形成一个空白区域
[Multiline(5)]:只能修饰string类型的变量,可以让string类型的变量在Inspector面板显示多行
参数就是显示几行
[TextArea()]:只能修饰string类型的变量,在Inspector面板显示带滚动的文本区域
[TextArea(2, 5)]:同上,最少显示2行,最多显示5行,文本内容超过5行出现滑动条
[Delayed()]:
[HideInInspector]:把公共的变量在Inspector面板隐藏,但是还是可序列化的。
[SerializeField]:可以让私有变量可以被序列化,在Inspector面板显示
[System.NonSerialized]:让公有的变量不可序列化
[ContextMenuItem("function", "Test")]:是修饰的字段当右键点击时,出现一个自定义的方法的菜单,选中这个方法菜单时,执行这个方法,可以用于还原重置字段
----第一个参数:显示的名字
----第二个参数:方法名,这个方法必须是非静态的方法
[Delayed()]:修饰的变量,只有当按下回车键或鼠标失焦时,才会把新输入的值赋值给变量
 
修饰方法:
[ContextMenu("重置方法")]:可以让组件在面板的该组件的齿轮设置菜单里多出一个选项,如果选中了该选项,执行该特性修饰的方法,参数显示在面板的名字,修饰的方法是非静态的方法。
[UnityEditor.MenuItem("Menu/Open")]:添加菜单项,在编辑器的上方跟File,Edit等同级的菜单项,修饰的必须是静态的方法。
 
修饰类:
[System.Serializable]:让该类的对象是可序列化的。
[AddComponentMenu("Lesson/MonoEditor")]:修饰继承Mono的类,在Add Component按钮添加选项,当选中该选项时,就把该特性修饰的脚本添加到了该物体上,支持层级化的结构(用"/"分隔)
名字可以和类名不一样,只对继承Mono的类有效
[ExecuteInEditMode]:可以在编辑模式下执行该脚本,与运行时执行不一样,运行时Updata一直每帧执行一次,只有场景中的某个物体发生改变时,才执行一次Updata
与[Delayed()]:不同,只要值发生改变,就把新值赋给变量
[RequireComponent(typeof(Rigidbody))]:使脚本依赖于一个组件,当把脚本添加到一个物体上时,该脚本先判断该物体上是否有依赖的组件,如果没有,添加上依赖的组件,如果有,不会多次添加组件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
[ExecuteInEditMode]
//[AddComponentMenu("LessonMonoEditor")]
[AddComponentMenu("Lesson/MonoEditor")]
public class LessonMonoEditor : MonoBehaviour {
    [Header("玩家的信息")]//显示标题
    [Space(10)]
    [Tooltip("玩家名字")]//鼠标悬停,显示信息
    public string playerName;
    [Delayed()]
    public int id;
    [HideInInspector]//面板隐藏(可序列化)
    public int id1;
    [SerializeField]//面板显示(可序列化标识)
    private bool sex;
    [System.NonSerialized]//面板隐藏(不可序列化标识)
    public bool sex1;
    [Range(1, 1000)]//滑动条
    public float hp;
    [Range(1, 50)]
    public int level;
    [Multiline(5)]
    public string message;//多行显示
    [TextArea()]
    public string message1;//带滑动条的多行显示
    [ContextMenuItem("function", "Test")]
    [TextArea(2, 5)]
    public string message2;
    // Use this for initialization
    void Start() {
        hp = 2000;
        Debug.Log("hp:" + hp);
    }
    // Update is called once per frame
    void Update() {
        Debug.Log(id);
    }
    void Test()
    {
        Debug.Log("Test方法执行");
        message2 = "这是一个玩家信息";
    }
    [ContextMenu("重置方法")]
    private void ResetPosition()//必须是非静态的方法
    {
        Debug.Log("ResetPosition");
    }
    [UnityEditor.MenuItem("Menu/Open")]
    static void OpenMenu()
    {
        Debug.Log("OpenMenu");
    }
    [System.Serializable]
    public class A
    {
        public string name;
    }
}
 
项目打包报错问题:脚本中有 UnityEditor特性,工程将无法打包
解决方法:把UnityEditor的代码放在Editor文件夹下
 
特殊文件夹
Resources:可以Resources.Load加载资源
StreamingAssets:一起打包,不会压缩
Editor:引用了UnityEditor命名空间或使用UnityEditor下的方法,这个类如果没放在Editor文件夹下,打包的时候会报错。Editor文件夹下的所有脚本都不会打进发布的包,并且该脚本一般都是在编辑时使用的。只要使用了UnityEditor命名空间,那么这个脚本一定要放在Editor文件夹下。
Editor文件夹不一定在根目录,子目录也可以,可以有多个Editor文件夹
 
关于Inspector面板界面的编辑器开发
1、引用命名空间using UnityEditor;
2、继承Editor类
3、关联该类扩展开发的脚本[CustomEditor(typeof(类名))]
4、利用OnEnable和OnDisable做一些初始化和清理的工作,
----OnEnable选择挂着关联脚本的物体时执行一次
----OnDisable当取消选择挂着关联脚本的物体时执行一次
5、利用Target来获取选中物体身上的关联的脚本的对象
6、重写OnInspectorGUI方法,对关联脚本的Inspector界面进行重新绘制
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;//1、引用命名空间
[CustomEditor(typeof(Player))]//3、关联该类扩展开发的脚本
public class PlayerEditor : Editor//2、继承Editor类
{
    public Player player;
    //4、利用OnEnable和OnDisable做一些初始化和清理的工作
    private void OnEnable()
    {
        //5、利用Target来获取到你选择的物体上的LessonPlayer对象
        player = (Player)target;
        Debug.Log("OnEnable:" + player.gameObject.name);
    }
    //6、重写OnInspectorGUI,在这个方法内对LessonPlayer里的变量等进行重写操作
    public override void OnInspectorGUI()
    {
        //base.OnInspectorGUI();
        //针对字符串变量
        //第一个参数:输入框前面显示的文本内容
        //第二个参数:输入框里面显示的文本内容
        //返回值:每次输入的内容
        player.playerName = EditorGUILayout.TextField("玩家名字:", player.playerName);
        //针对int类型
        player.id = EditorGUILayout.IntField("玩家ID:", player.id);
        //针对float类型
        player.hp = EditorGUILayout.FloatField("玩家血量:", player.hp);
        //针对bool类型
        player.sex = EditorGUILayout.Toggle("玩家性别:", player.sex);
        //针对枚举的单选
        player.type = (Player.PlayerType)EditorGUILayout.EnumPopup("玩家职业:", player.type);
        //针对枚举的多选,对于每个枚举值对应的int值是2的n次方,类似标签和层级的枚举
        player.work = (Player.PlayerWork)EditorGUILayout.EnumMaskField("玩家职业:", player.work);
        //针对向量类型
        player.birthPosition =  EditorGUILayout.Vector3Field("玩家出生位置", player.birthPosition);
        //针对颜色类型
        player.color = EditorGUILayout.ColorField("玩家颜色", player.color);
        //对于基类是Object类型的对象,我们可以使用这种方式去查找相应的变量
        //第一个参数,显示的标签
        //第二个参数,要找的变量
        //第三个参数,该变量的类型,一般使用typeof(类型)
        //第四个参数,是否可以使用场景中的物体
        player.weaponObj = (GameObject)EditorGUILayout.ObjectField("武器的对象", player.weaponObj, typeof(GameObject), true);
        player.tex = (Texture)EditorGUILayout.ObjectField("玩家的贴图", player.tex, typeof(Texture), true);
        player.weapon = (Weapon)EditorGUILayout.ObjectField("武器的脚本", player.weapon, typeof(Weapon), false);
        //终极方法,支持各种类型
        //通过变量的名字来获取序列化的对象
        SerializedProperty items = serializedObject.FindProperty("items");
        //绘制序列化的对象,
        //第二个参数:显示的名字
        //第三个参数:true证明该对象里的所有的属性也是可以序列化的
        EditorGUILayout.PropertyField(items, new GUIContent("物品:"), true);
        //针对类的
        SerializedProperty pet = serializedObject.FindProperty("pet");
        EditorGUILayout.PropertyField(pet, new GUIContent("宠物:"), true);
        //用于保存序列化的对象,如果没有这个方法,对对象的修改无效
        serializedObject.ApplyModifiedProperties();
        //布局调整
        EditorGUILayout.BeginHorizontal();//开始水平布局
        player.playerName = EditorGUILayout.TextField("玩家名字:", player.playerName);
        player.id = EditorGUILayout.IntField("玩家ID:", player.id);
        EditorGUILayout.EndHorizontal();//结束水平布局
        //针对float类型的滑动条,第三个和第四个参数,最小值和最大值
        player.maxHp = EditorGUILayout.Slider("玩家的最大血量:", player.maxHp, 100, 5000);
        //针对float类型的
        player.hp = EditorGUILayout.FloatField("玩家血量:", player.hp);
        //获取自动布局的位置信息,按照顺序往后的位置
        Rect rect = GUILayoutUtility.GetRect(50, 50);
        //绘制一个进度条
        EditorGUI.ProgressBar(rect, player.hp / player.maxHp, "玩家的血量");
        //绘制提示信息
        player.atk = EditorGUILayout.FloatField("攻击力", player.atk);
        if (player.atk > 100)
        {
            EditorGUILayout.HelpBox("攻击力太高了", MessageType.Error);
        }
        else if (player.atk < 10)
        {
            EditorGUILayout.HelpBox("攻击力太低了", MessageType.Warning);
        }
        else
        {
            EditorGUILayout.HelpBox("攻击力适中", MessageType.Info);
        }
    }
    private void OnDisable()
    {
        Debug.Log("OnDisable");
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour {
    public string playerName;
    public int id;
    public float hp;
    public float maxHp;
    public bool sex;
    public PlayerType type;
    public PlayerWork work;
    public Vector3 birthPosition;
    public Color color;
    public GameObject weaponObj;
    public Texture tex;
    public Weapon weapon;
    public Pet pet;
    public List<string> items;
    public float atk;
    [ContextMenu("打印")]
    private void DebugMessage()
    {
        Debug.Log(playerName);
        Debug.Log("宠物的名字:" + pet.name);
    }
    public enum PlayerType//针对枚举的单选
    {
        战士,//战士,0,0000
        法师,//法师,1,0001
        牧师,//牧师,2,0010
        术士,//术士,3,0011 = 0001或上0010,针对枚举的多选时,选择刺客会同时选上法师和道士
        盗贼,//盗贼,4,0100
        猎人,//猎人,5,0101
    }
    public enum PlayerWork//针对枚举的多选,对于每个枚举值对应的int值是2的n次方
    {
        分解师 = 1,//分解师,0,0001
        炼金师 = 2,//炼金师,0,0010
        采矿师 = 4,//采矿师,0,0100
        锻造师 = 8,//锻造师,0,1000
    }
    [System.Serializable]
    public class Pet
    {
        public string name;
        public float hp;
    }
}
 
 
关于Window界面的编辑器开发
1、创建一个处理窗口的类
2、该类需要引用命名空间UntiyEditor
3、该类需要继承EditorWindow
4、利用EditorWindow.GetWindow<当前处理窗口类的名字>();创建一个窗口(使用这种方式[MenuItem("MyMenu/创建窗口")])
5、利用OnEnable和OnDisable去初始化工作和清理工作
6、利用OnGUI方法,对窗口显示自定义的绘制
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;//2、引用命名空间UnityEditor
//1、先创建一个处理窗口的类
public class WindowEditor : EditorWindow//3、继承EditorWindow类
{
    [MenuItem("MyMenu/创建窗口")]
    static void CreateWindow()
    {
        //4、创建一个创建
        EditorWindow.GetWindow<WindowEditor>();
    }
    //5、利用OnEnable方法和OnDisable方法,实现初始化和清理工作
    public void OnEnable()
    {
        Debug.Log("OnEnable");
    }
    public GameObject obj;
    private Vector2 scrollPosition;
    //6、利用OnGUI方法实现对窗口内容的绘制
    private void OnGUI()
    {
        //Debug.Log("OnGUI");
        EditorGUILayout.LabelField("我的窗口");
        //绘制一个进度条
        Rect rect = GUILayoutUtility.GetRect(50,50);
        EditorGUI.ProgressBar(rect, 0.5f, "玩家血量");
        //对于基类是Object类型的对象
        obj = (GameObject)EditorGUILayout.ObjectField("GameObject", obj, typeof(GameObject), true);
        //绘制滚动窗口
        scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
        for (int i = 0; i < 50; i++)
        {
            EditorGUILayout.LabelField("我的窗口");
        }
        EditorGUILayout.EndScrollView();
    }
    public void OnDisable()
    {
        Debug.Log("OnDisable");
    }
}
 
使用EditorWindow快速开发Json
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BagData
{
    private static BagData instance;
    public static BagData Instance
    {
        get { return instance; }
    }
    public static void SetInstance(BagData bag)
    {
        if (bag == null)
        {
            instance = new BagData();
            instance.items = new List<ItemData>();
        }
        else
        {
            instance = bag;
        }
    }
    private BagData() { }
    public List<ItemData> items;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class BagDataWindow : EditorWindow
{
    [MenuItem("MyMenu/背包数据窗口")]
    static void CreateWindow()
    {
        EditorWindow.GetWindow<BagDataWindow>();
    }
    private void OnEnable()
    {
        string json = FileTools.ReadJson(Application.streamingAssetsPath + "/BagJson.txt");
        if (json == "")
        {
            BagData.SetInstance(null);
        }
        else
        {
            BagData.SetInstance(JsonUtility.FromJson<BagData>(json));
        }
        Debug.Log(BagData.Instance.items.Count);
        data = new ItemData();
    }
    private Vector2 scrollPosition;
    private ItemData data;
    private Texture itemTex;
    private void OnGUI()
    {
        GUI.skin.label.alignment = TextAnchor.MiddleCenter;
        GUI.skin.label.fontSize = 24;
        GUILayout.Label("背包数据");
        GUILayout.Space(20);
        scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
        for (int i = 0; i < BagData.Instance.items.Count; i++)
        {
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("ID:" + BagData.Instance.items[i].ID);
            EditorGUILayout.LabelField("ID:" + BagData.Instance.items[i].itemName);
            EditorGUILayout.LabelField("ID:" + BagData.Instance.items[i].iconName);
            if (GUILayout.Button("删除"))
            {
                BagData.Instance.items.Remove(BagData.Instance.items[i]);
            }
            EditorGUILayout.EndHorizontal();
        }
        EditorGUILayout.EndScrollView();
        EditorGUILayout.BeginHorizontal();
        data.ID = EditorGUILayout.IntField("物品ID:", data.ID);
        data.itemName = EditorGUILayout.TextField("物品名字:", data.itemName);
        itemTex = (Texture)EditorGUILayout.ObjectField("物品图片:", itemTex, typeof(Texture), false);
        if (GUILayout.Button("添加"))
        {
            bool isFindID = false;
            for (int i = 0; i < BagData.Instance.items.Count; i++)
            {
                if (BagData.Instance.items[i].ID == data.ID)
                {
                    isFindID = true;
                    break;
                }
            }
            if (isFindID)
            {
                Debug.LogError("ID重复了");
            }
            else
            {
                data.iconName = itemTex.name;
                BagData.Instance.items.Add(data);
                data = new ItemData();
                for (int i = 0; i < BagData.Instance.items.Count; i++)
                {
                    if (data.ID < BagData.Instance.items[i].ID)
                    {
                        data.ID = BagData.Instance.items[i].ID;
                    }
                }
                data.ID++;
            }
        }
        EditorGUILayout.EndHorizontal();
        bool isID = false;
        for (int i = 0; i < BagData.Instance.items.Count; i++)
        {
            if (BagData.Instance.items[i].ID == data.ID)
            {
                isID = true;
                break;
            }
        }
        if (isID)
        {
            EditorGUILayout.HelpBox("ID重复了", MessageType.Error);
        }
    }
    private void OnDisable()
    {
        //关闭界面时,需要把数据写入到Json
        string json = JsonUtility.ToJson(BagData.Instance, true);
        FileTools.WriteJson(Application.streamingAssetsPath + "/BagJson.txt", json);
    }
}

猜你喜欢

转载自www.cnblogs.com/ciaowoo/p/10363687.html