Unity3D學習筆記(二十八):Editor
阿新 • • 發佈:2019-02-12
到你 box -a del eset tools 才會 resources 關於 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); } }
Unity3D學習筆記(二十八):Editor