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面板界面的編輯器開發
一、引用命名空間using UnityEditor;
二、繼承Editor類
三、關聯該類擴展開發的腳本[CustomEditor(typeof(類名))]
四、利用OnEnable和OnDisable作一些初始化和清理的工做,
----OnEnable選擇掛着關聯腳本的物體時執行一次
----OnDisable當取消選擇掛着關聯腳本的物體時執行一次
五、利用Target來獲取選中物體身上的關聯的腳本的對象
六、重寫OnInspectorGUI方法,對關聯腳本的Inspector界面進行從新繪製
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;//一、引用命名空間
[CustomEditor(typeof(Player))]//三、關聯該類擴展開發的腳本
public class PlayerEditor : Editor//二、繼承Editor類
{
    public Player player;
    //四、利用OnEnable和OnDisable作一些初始化和清理的工做
    private void OnEnable()
    {
        //五、利用Target來獲取到你選擇的物體上的LessonPlayer對象
        player = (Player)target;
        Debug.Log("OnEnable:" + player.gameObject.name);
    }
    //六、重寫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界面的編輯器開發
一、建立一個處理窗口的類
二、該類須要引用命名空間UntiyEditor
三、該類須要繼承EditorWindow
四、利用EditorWindow.GetWindow<當前處理窗口類的名字>();建立一個窗口(使用這種方式[MenuItem("MyMenu/建立窗口")])
五、利用OnEnable和OnDisable去初始化工做和清理工做
六、利用OnGUI方法,對窗口顯示自定義的繪製
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;//二、引用命名空間UnityEditor
//一、先建立一個處理窗口的類
public class WindowEditor : EditorWindow//三、繼承EditorWindow類
{
    [MenuItem("MyMenu/建立窗口")]
    static void CreateWindow()
    {
        //四、建立一個建立
        EditorWindow.GetWindow<WindowEditor>();
    }
    //五、利用OnEnable方法和OnDisable方法,實現初始化和清理工做
    public void OnEnable()
    {
        Debug.Log("OnEnable");
    }
    public GameObject obj;
    private Vector2 scrollPosition;
    //六、利用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);
    }
}
相關文章
相關標籤/搜索